Add Animator
This commit is contained in:
parent
78b09b35de
commit
9dc81e02e3
|
@ -0,0 +1,89 @@
|
||||||
|
local Object = require "lib/classic"
|
||||||
|
|
||||||
|
---@alias AnimationFinishBehavior
|
||||||
|
---| '"loop"' Loop the animation over from the beginning.
|
||||||
|
---| '"stop"' Freeze the animation on the last frame.
|
||||||
|
---| '"goto"' Go to another state.
|
||||||
|
|
||||||
|
---@class State
|
||||||
|
---@field animation Animation The animation frames for this state.
|
||||||
|
---@field on_finish? AnimationFinishBehavior What to do after this state's animation finishes. Defaults to "loop".
|
||||||
|
---@field next? string The next state to go to in the case of "goto" on_finish.
|
||||||
|
|
||||||
|
local Animator = Object:extend()
|
||||||
|
|
||||||
|
---Creates a new animator.
|
||||||
|
---@param states { [string]: State } A dictionary of all states in this animator.
|
||||||
|
---@param first_state string The first state of this animator.
|
||||||
|
function Animator:new(states, first_state)
|
||||||
|
---The states in this animator.
|
||||||
|
self.states = states
|
||||||
|
|
||||||
|
---The current state.
|
||||||
|
self.state = first_state
|
||||||
|
|
||||||
|
---The timestep of the current frame.
|
||||||
|
self.step = 0
|
||||||
|
|
||||||
|
---The current frame in the current animation.
|
||||||
|
self.frame = 1
|
||||||
|
|
||||||
|
--- The next state to progress to.
|
||||||
|
self.next_state = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
function Animator:update(dt)
|
||||||
|
local state = self.states[self.state]
|
||||||
|
local frames = state.animation.frames
|
||||||
|
|
||||||
|
--Step the current timestep and return if the frame hasn't changed.
|
||||||
|
self.step = self.step + dt
|
||||||
|
if self.step <= frames[self.frame].duration then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
--Step the current frame and return if there are more.
|
||||||
|
self.step = 0
|
||||||
|
self.frame = self.frame + 1
|
||||||
|
if self.frame <= #frames then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
--We've reached the end of the current state.
|
||||||
|
if self.next_state then
|
||||||
|
self.state = self.next_state
|
||||||
|
self.frame = 1
|
||||||
|
self.next_state = nil
|
||||||
|
elseif state.on_finish == "goto" then
|
||||||
|
self.state = state.next
|
||||||
|
self.frame = 1
|
||||||
|
elseif state.on_finish == "stop" then
|
||||||
|
self.frame = #frames
|
||||||
|
elseif state.on_finish == "loop" or state.on_finish == nil then
|
||||||
|
self.frame = 1
|
||||||
|
else
|
||||||
|
error("Invalid state on_finish " .. state.on_finish)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function Animator:draw(x, y)
|
||||||
|
local state = self.states[self.state]
|
||||||
|
state.animation:draw(self.frame, x, y)
|
||||||
|
end
|
||||||
|
|
||||||
|
---Sets the state of this animator.
|
||||||
|
---@param state string The name of the state to set.
|
||||||
|
---@param queue? boolean If true, queues the state change for after the current state.
|
||||||
|
function Animator:setState(state, queue)
|
||||||
|
if not self.states[state] then
|
||||||
|
error("Invalid state " .. state)
|
||||||
|
elseif queue then
|
||||||
|
self.next_state = state
|
||||||
|
else
|
||||||
|
self.state = state
|
||||||
|
self.step = 0
|
||||||
|
self.frame = 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return Animator
|
41
main.lua
41
main.lua
|
@ -1,4 +1,5 @@
|
||||||
local Animation = require "animation"
|
local Animation = require "animation"
|
||||||
|
local Animator = require "animator"
|
||||||
local Atlas = require "atlas"
|
local Atlas = require "atlas"
|
||||||
local Font = require "font"
|
local Font = require "font"
|
||||||
local MultiMenu = require "multimenu"
|
local MultiMenu = require "multimenu"
|
||||||
|
@ -31,38 +32,31 @@ function love.load()
|
||||||
local leafRangerSprites = love.graphics.newImage("assets/leaf-ranger.png")
|
local leafRangerSprites = love.graphics.newImage("assets/leaf-ranger.png")
|
||||||
local leafRangerAtlas = Atlas(leafRangerSprites, 288, 128)
|
local leafRangerAtlas = Atlas(leafRangerSprites, 288, 128)
|
||||||
|
|
||||||
local function sprites(row, len)
|
local function sprites(row, len, on_finish, next)
|
||||||
local start = (row - 1) * 22 + 1
|
local start = (row - 1) * 22 + 1
|
||||||
return Animation:new_spanned(leafRangerAtlas, start, start + len - 1)
|
local anim = Animation:new_spanned(leafRangerAtlas, start, start + len - 1)
|
||||||
|
return { animation = anim, on_finish = on_finish, next = next }
|
||||||
end
|
end
|
||||||
|
|
||||||
LeafRangerAnimations = {
|
local states = {
|
||||||
idle = sprites(1, 12),
|
idle = sprites(1, 12),
|
||||||
run = sprites(2, 10),
|
run = sprites(2, 10),
|
||||||
hurt = sprites(16, 6),
|
hurt = sprites(16, 6, "goto", "idle"),
|
||||||
death = sprites(17, 20),
|
death = sprites(17, 19, "stop"),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LeafRanger = Animator(states, "idle")
|
||||||
|
|
||||||
local options = {}
|
local options = {}
|
||||||
|
|
||||||
for name, anim in pairs(LeafRangerAnimations) do
|
for name, state in pairs(states) do
|
||||||
options[#options + 1] = {
|
options[#options + 1] = {
|
||||||
text = name,
|
text = name,
|
||||||
callback = function()
|
callback = function() LeafRanger:setState(name) end,
|
||||||
LeafRanger.animation = anim
|
|
||||||
LeafRanger.step = 0
|
|
||||||
LeafRanger.frame = 1
|
|
||||||
end,
|
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
Menu = MultiMenu(BlockyFont, options)
|
Menu = MultiMenu(BlockyFont, options)
|
||||||
|
|
||||||
LeafRanger = {
|
|
||||||
animation = LeafRangerAnimations.idle,
|
|
||||||
step = 0,
|
|
||||||
frame = 1,
|
|
||||||
}
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function love.draw()
|
function love.draw()
|
||||||
|
@ -70,22 +64,13 @@ function love.draw()
|
||||||
|
|
||||||
Menu:draw()
|
Menu:draw()
|
||||||
BlockyFont:draw("Hello, world!")
|
BlockyFont:draw("Hello, world!")
|
||||||
|
LeafRanger:draw(0, 0)
|
||||||
LeafRanger.animation:draw(LeafRanger.frame, 0, 0)
|
|
||||||
|
|
||||||
push:finish()
|
push:finish()
|
||||||
end
|
end
|
||||||
|
|
||||||
function love.update(dt)
|
function love.update(dt)
|
||||||
LeafRanger.step = LeafRanger.step + dt
|
LeafRanger:update(dt)
|
||||||
|
|
||||||
if LeafRanger.step > LeafRanger.animation.frames[LeafRanger.frame].duration then
|
|
||||||
LeafRanger.step = 0
|
|
||||||
LeafRanger.frame = LeafRanger.frame + 1
|
|
||||||
if LeafRanger.frame > #LeafRanger.animation.frames then
|
|
||||||
LeafRanger.frame = 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function love.keypressed(key)
|
function love.keypressed(key)
|
||||||
|
|
Loading…
Reference in New Issue