Compare commits
4 Commits
c450fc0efb
...
ec2d98a707
Author | SHA1 | Date |
---|---|---|
mars | ec2d98a707 | |
mars | a228729f52 | |
mars | eb159db4b5 | |
mars | 4bbdb426ea |
|
@ -0,0 +1,65 @@
|
|||
local Object = require "lib/classic"
|
||||
|
||||
---@class Frame
|
||||
---@field sprite number The index of the sprite in the atlas.
|
||||
---@field duration? number The duration (in seconds) of this frame.
|
||||
---@field atlas? Atlas The sprite atlas this frame is drawn from.
|
||||
|
||||
---@class Animation
|
||||
---A timed sequence of sprites.
|
||||
local Animation = Object:extend()
|
||||
|
||||
---Creates a new animation.
|
||||
---@param frames Frame[] A list of frames.
|
||||
---@param duration? number The default duration to use for this animation. Defaults to 15 FPS.
|
||||
---@param atlas? Atlas The default atlas to use for this animation.
|
||||
function Animation:new(frames, duration, atlas)
|
||||
local default_duration = duration or (1.0 / 15.0)
|
||||
|
||||
---The list of frames.
|
||||
self.frames = {}
|
||||
|
||||
local total_duration = 0
|
||||
|
||||
for idx, frame in ipairs(frames) do
|
||||
total_duration = total_duration + (frame.duration or default_duration)
|
||||
|
||||
self.frames[idx] = {
|
||||
sprite = frame.sprite,
|
||||
duration = frame.duration or duration,
|
||||
atlas = frame.atlas or atlas or error("Missing default atlas"),
|
||||
}
|
||||
end
|
||||
|
||||
---The total duration of this animation.
|
||||
self.duration = total_duration
|
||||
end
|
||||
|
||||
---Creates a new animation from the sprites between start and end indices, plus an atlas.
|
||||
---@param atlas Atlas The atlas that the sprites are from.
|
||||
---@param start_idx number The first sprite index in the animation.
|
||||
---@param end_idx number The last sprite index in the animation.
|
||||
---@param duration? number The duration of each frame. Defaults to 15 FPS.
|
||||
---@return Animation
|
||||
function Animation:new_spanned(atlas, start_idx, end_idx, duration)
|
||||
local duration = duration or (1.0 / 15.0)
|
||||
local frames = {}
|
||||
|
||||
for sprite = start_idx, end_idx do
|
||||
frames[#frames + 1] = { sprite = sprite, atlas = atlas, duration = duration }
|
||||
end
|
||||
|
||||
return Animation(frames)
|
||||
end
|
||||
|
||||
---Draws a frame of this animation.
|
||||
---@param index number The index of the frame.
|
||||
---@param x number The X position to draw at.
|
||||
---@param y number The Y position to draw at.
|
||||
function Animation:draw(index, x, y)
|
||||
local frame = self.frames[index]
|
||||
local mesh = frame.atlas.meshes[frame.sprite]
|
||||
love.graphics.draw(mesh, x, y)
|
||||
end
|
||||
|
||||
return Animation
|
|
@ -1,6 +1,7 @@
|
|||
local Math = require "math"
|
||||
local Object = require "lib/classic"
|
||||
|
||||
---@class Atlas
|
||||
---A spriteset laid out on a regular grid.
|
||||
local Atlas = Object:extend()
|
||||
|
||||
|
|
1
font.lua
1
font.lua
|
@ -1,5 +1,6 @@
|
|||
local Atlas = require "atlas"
|
||||
|
||||
---@class Font
|
||||
---A set of character sprites that can be drawn as strings.
|
||||
local Font = Atlas:extend()
|
||||
|
||||
|
|
113
main.lua
113
main.lua
|
@ -1,5 +1,7 @@
|
|||
local Animation = require "animation"
|
||||
local Atlas = require "atlas"
|
||||
local Font = require "font"
|
||||
local MultiMenu = require "multimenu"
|
||||
local push = require "lib/push"
|
||||
|
||||
local gameWidth, gameHeight = 320, 200
|
||||
|
@ -12,74 +14,7 @@ local pushOpts = {
|
|||
|
||||
push:setupScreen(gameWidth, gameHeight, windowWidth, windowHeight, pushOpts)
|
||||
|
||||
MultiMenu = {}
|
||||
MultiMenu.__index = MultiMenu
|
||||
|
||||
setmetatable(
|
||||
MultiMenu,
|
||||
{
|
||||
__call = function(self, options)
|
||||
setmetatable({}, self)
|
||||
self:initialize(options)
|
||||
return self
|
||||
end
|
||||
}
|
||||
)
|
||||
|
||||
function MultiMenu:initialize(options)
|
||||
self.options = options
|
||||
self.selected = 1
|
||||
end
|
||||
|
||||
function MultiMenu:draw()
|
||||
-- option drawing constants
|
||||
local x = 400
|
||||
local y_anchor = 200
|
||||
local spacing = 50
|
||||
y_anchor = y_anchor - spacing
|
||||
|
||||
-- draw all the options
|
||||
for idx, option in ipairs(self.options) do
|
||||
local y = y_anchor + idx * spacing
|
||||
love.graphics.print(option, x, y)
|
||||
end
|
||||
|
||||
-- cursor metric constants
|
||||
local cursor_width = 10
|
||||
local cursor_depth = 20
|
||||
local cursor_spacing = 20
|
||||
|
||||
-- calculate the cursor's position
|
||||
local cursor_x = x - cursor_spacing
|
||||
local cursor_y = y_anchor + spacing * self.selected
|
||||
|
||||
-- draw the cursor
|
||||
love.graphics.polygon("fill",
|
||||
cursor_x, cursor_y,
|
||||
cursor_x - cursor_depth, cursor_y - cursor_width,
|
||||
cursor_x - cursor_depth, cursor_y + cursor_width
|
||||
)
|
||||
end
|
||||
|
||||
function MultiMenu:up()
|
||||
if self.selected == 1 then
|
||||
self.selected = #self.options
|
||||
else
|
||||
self.selected = self.selected - 1
|
||||
end
|
||||
end
|
||||
|
||||
function MultiMenu:down()
|
||||
if self.selected == #self.options then
|
||||
self.selected = 1
|
||||
else
|
||||
self.selected = self.selected + 1
|
||||
end
|
||||
end
|
||||
|
||||
function love.load()
|
||||
Menu = MultiMenu({ "Fight", "Use", "Pkmn", "Flee" })
|
||||
|
||||
local blockyFont = love.graphics.newImage("assets/small-blocky-font.png")
|
||||
|
||||
local blockFontChars = {
|
||||
|
@ -93,14 +28,40 @@ function love.load()
|
|||
|
||||
BlockyFont = Font(blockyFont, 8, 8, blockFontChars)
|
||||
|
||||
local leafRanger = love.graphics.newImage("assets/leaf-ranger.png")
|
||||
local leafRangerSprites = love.graphics.newImage("assets/leaf-ranger.png")
|
||||
local leafRangerAtlas = Atlas(leafRangerSprites, 288, 128)
|
||||
|
||||
local function sprites(row, len)
|
||||
local start = (row - 1) * 22 + 1
|
||||
return Animation:new_spanned(leafRangerAtlas, start, start + len - 1)
|
||||
end
|
||||
|
||||
LeafRangerAnimations = {
|
||||
idle = sprites(1, 12),
|
||||
run = sprites(2, 10),
|
||||
hurt = sprites(16, 6),
|
||||
death = sprites(17, 20),
|
||||
}
|
||||
|
||||
local options = {}
|
||||
|
||||
for name, anim in pairs(LeafRangerAnimations) do
|
||||
options[#options + 1] = {
|
||||
text = name,
|
||||
callback = function()
|
||||
LeafRanger.animation = anim
|
||||
LeafRanger.step = 0
|
||||
LeafRanger.frame = 1
|
||||
end,
|
||||
}
|
||||
end
|
||||
|
||||
Menu = MultiMenu(BlockyFont, options)
|
||||
|
||||
LeafRanger = {
|
||||
atlas = Atlas(leafRanger, 288, 128),
|
||||
animation = LeafRangerAnimations.idle,
|
||||
step = 0,
|
||||
duration = 1.0 / 15.0,
|
||||
frame = 1,
|
||||
max = 12,
|
||||
}
|
||||
end
|
||||
|
||||
|
@ -110,7 +71,7 @@ function love.draw()
|
|||
Menu:draw()
|
||||
BlockyFont:draw("Hello, world!")
|
||||
|
||||
love.graphics.draw(LeafRanger.atlas.meshes[LeafRanger.frame])
|
||||
LeafRanger.animation:draw(LeafRanger.frame, 0, 0)
|
||||
|
||||
push:finish()
|
||||
end
|
||||
|
@ -118,11 +79,11 @@ end
|
|||
function love.update(dt)
|
||||
LeafRanger.step = LeafRanger.step + dt
|
||||
|
||||
if LeafRanger.step > LeafRanger.duration then
|
||||
if LeafRanger.step > LeafRanger.animation.frames[LeafRanger.frame].duration then
|
||||
LeafRanger.step = 0
|
||||
LeafRanger.frame = LeafRanger.frame + 1
|
||||
if LeafRanger.frame > LeafRanger.max then
|
||||
LeafRanger.frame = 1
|
||||
if LeafRanger.frame > #LeafRanger.animation.frames then
|
||||
LeafRanger.frame = 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -132,6 +93,8 @@ function love.keypressed(key)
|
|||
Menu:up()
|
||||
elseif key == "down" then
|
||||
Menu:down()
|
||||
elseif key == "space" then
|
||||
Menu:select()
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
local Object = require "lib/classic"
|
||||
|
||||
---@class MultiMenu
|
||||
local MultiMenu = Object:extend()
|
||||
|
||||
---Creates a new multimenu.
|
||||
---@param font Font
|
||||
---@param options table
|
||||
function MultiMenu:new(font, options)
|
||||
self.font = font
|
||||
self.options = options
|
||||
self.selected = 1
|
||||
end
|
||||
|
||||
function MultiMenu:draw()
|
||||
-- option drawing constants
|
||||
local x = 20
|
||||
local y_anchor = 50
|
||||
local spacing = 10
|
||||
y_anchor = y_anchor - spacing
|
||||
|
||||
-- draw all the options
|
||||
for idx, option in ipairs(self.options) do
|
||||
local y = y_anchor + idx * spacing
|
||||
self.font:draw(option.text, x, y)
|
||||
end
|
||||
|
||||
-- cursor metric constants
|
||||
local cursor_width = 4
|
||||
local cursor_depth = 8
|
||||
local cursor_spacing = 8
|
||||
local cursor_offset = 4
|
||||
|
||||
-- calculate the cursor's position
|
||||
local cursor_x = x - cursor_spacing
|
||||
local cursor_y = y_anchor + cursor_offset + spacing * self.selected
|
||||
|
||||
-- draw the cursor
|
||||
love.graphics.polygon("fill",
|
||||
cursor_x, cursor_y,
|
||||
cursor_x - cursor_depth, cursor_y - cursor_width,
|
||||
cursor_x - cursor_depth, cursor_y + cursor_width
|
||||
)
|
||||
end
|
||||
|
||||
function MultiMenu:up()
|
||||
if self.selected == 1 then
|
||||
self.selected = #self.options
|
||||
else
|
||||
self.selected = self.selected - 1
|
||||
end
|
||||
end
|
||||
|
||||
function MultiMenu:down()
|
||||
if self.selected == #self.options then
|
||||
self.selected = 1
|
||||
else
|
||||
self.selected = self.selected + 1
|
||||
end
|
||||
end
|
||||
|
||||
function MultiMenu:select()
|
||||
self.options[self.selected].callback()
|
||||
end
|
||||
|
||||
return MultiMenu
|
Loading…
Reference in New Issue