Compare commits
2 Commits
ec2d98a707
...
9dc81e02e3
Author | SHA1 | Date |
---|---|---|
mars | 9dc81e02e3 | |
mars | 78b09b35de |
|
@ -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
|
43
main.lua
43
main.lua
|
@ -1,10 +1,11 @@
|
|||
local Animation = require "animation"
|
||||
local Animator = require "animator"
|
||||
local Atlas = require "atlas"
|
||||
local Font = require "font"
|
||||
local MultiMenu = require "multimenu"
|
||||
local push = require "lib/push"
|
||||
|
||||
local gameWidth, gameHeight = 320, 200
|
||||
local gameWidth, gameHeight = 480, 360
|
||||
local windowWidth, windowHeight = love.window.getDesktopDimensions()
|
||||
windowWidth, windowHeight = windowWidth * .7, windowHeight * .7 --make the window a bit smaller than the screen itself
|
||||
local pushOpts = {
|
||||
|
@ -31,38 +32,31 @@ function love.load()
|
|||
local leafRangerSprites = love.graphics.newImage("assets/leaf-ranger.png")
|
||||
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
|
||||
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
|
||||
|
||||
LeafRangerAnimations = {
|
||||
local states = {
|
||||
idle = sprites(1, 12),
|
||||
run = sprites(2, 10),
|
||||
hurt = sprites(16, 6),
|
||||
death = sprites(17, 20),
|
||||
hurt = sprites(16, 6, "goto", "idle"),
|
||||
death = sprites(17, 19, "stop"),
|
||||
}
|
||||
|
||||
LeafRanger = Animator(states, "idle")
|
||||
|
||||
local options = {}
|
||||
|
||||
for name, anim in pairs(LeafRangerAnimations) do
|
||||
for name, state in pairs(states) do
|
||||
options[#options + 1] = {
|
||||
text = name,
|
||||
callback = function()
|
||||
LeafRanger.animation = anim
|
||||
LeafRanger.step = 0
|
||||
LeafRanger.frame = 1
|
||||
end,
|
||||
callback = function() LeafRanger:setState(name) end,
|
||||
}
|
||||
end
|
||||
|
||||
Menu = MultiMenu(BlockyFont, options)
|
||||
|
||||
LeafRanger = {
|
||||
animation = LeafRangerAnimations.idle,
|
||||
step = 0,
|
||||
frame = 1,
|
||||
}
|
||||
end
|
||||
|
||||
function love.draw()
|
||||
|
@ -70,22 +64,13 @@ function love.draw()
|
|||
|
||||
Menu:draw()
|
||||
BlockyFont:draw("Hello, world!")
|
||||
|
||||
LeafRanger.animation:draw(LeafRanger.frame, 0, 0)
|
||||
LeafRanger:draw(0, 0)
|
||||
|
||||
push:finish()
|
||||
end
|
||||
|
||||
function love.update(dt)
|
||||
LeafRanger.step = LeafRanger.step + 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
|
||||
LeafRanger:update(dt)
|
||||
end
|
||||
|
||||
function love.keypressed(key)
|
||||
|
|
Loading…
Reference in New Issue