- reimplement animation as AnimatedBehavior
- remove deprecated in Character - remove RenderBehavior
This commit is contained in:
parent
dd84e157bd
commit
99d523a761
@ -1,46 +0,0 @@
|
|||||||
local anim8 = require "lib.utils.anim8"
|
|
||||||
|
|
||||||
--- Скорость между кадрами в анимации
|
|
||||||
local ANIMATION_SPEED = 0.1
|
|
||||||
|
|
||||||
LEFT = -1
|
|
||||||
RIGHT = 1
|
|
||||||
|
|
||||||
--- @class Animation
|
|
||||||
--- @field animationTable table<string, table>
|
|
||||||
--- @field animationGrid table
|
|
||||||
--- @field state "idle"|"run"|"hurt"|"attack"
|
|
||||||
--- @field side 1|-1
|
|
||||||
local animation = {}
|
|
||||||
animation.__index = animation
|
|
||||||
|
|
||||||
local function new(spriteDir)
|
|
||||||
local anim = {
|
|
||||||
animationTable = {},
|
|
||||||
animationGrid = {}
|
|
||||||
}
|
|
||||||
|
|
||||||
-- n: name; i: image
|
|
||||||
for n, i in pairs(spriteDir) do
|
|
||||||
local aGrid = anim8.newGrid(96, 64, i:getWidth(), i:getHeight())
|
|
||||||
local tiles = '1-' .. math.ceil(i:getWidth() / 96)
|
|
||||||
anim.animationGrid[n] = aGrid(tiles, 1)
|
|
||||||
end
|
|
||||||
|
|
||||||
anim.state = "idle"
|
|
||||||
anim.side = RIGHT
|
|
||||||
|
|
||||||
return setmetatable(anim, animation)
|
|
||||||
end
|
|
||||||
|
|
||||||
function animation:getState()
|
|
||||||
return self.state
|
|
||||||
end
|
|
||||||
|
|
||||||
--- @param state CharacterState
|
|
||||||
function animation:setState(state, onLoop)
|
|
||||||
self.animationTable[state] = anim8.newAnimation(self.animationGrid[state], ANIMATION_SPEED, onLoop)
|
|
||||||
self.state = state
|
|
||||||
end
|
|
||||||
|
|
||||||
return { new = new }
|
|
||||||
70
lib/character/behaviors/animated.lua
Normal file
70
lib/character/behaviors/animated.lua
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
local anim8 = require "lib.utils.anim8"
|
||||||
|
|
||||||
|
--- @class AnimatedBehavior : Behavior
|
||||||
|
--- @field animationTable table<string, table>
|
||||||
|
--- @field animationGrid table
|
||||||
|
--- @field state "idle"|"run"|"hurt"|"attack"
|
||||||
|
--- @field side 1|-1
|
||||||
|
local animated = {}
|
||||||
|
animated.__index = animated
|
||||||
|
animated.id = "animated"
|
||||||
|
animated.dependencies = { Tree.behaviors.map }
|
||||||
|
animated.LEFT = -1
|
||||||
|
animated.RIGHT = 1
|
||||||
|
--- Скорость между кадрами в анимации
|
||||||
|
animated.ANIMATION_SPEED = 0.1
|
||||||
|
|
||||||
|
function animated.new(spriteDir)
|
||||||
|
local anim = setmetatable({}, animated)
|
||||||
|
anim.animationTable = {}
|
||||||
|
anim.animationGrid = {}
|
||||||
|
|
||||||
|
-- n: name; i: image
|
||||||
|
for n, i in pairs(spriteDir) do
|
||||||
|
local aGrid = anim8.newGrid(96, 64, i:getWidth(), i:getHeight())
|
||||||
|
local tiles = '1-' .. math.ceil(i:getWidth() / 96)
|
||||||
|
anim.animationGrid[n] = aGrid(tiles, 1)
|
||||||
|
end
|
||||||
|
|
||||||
|
anim.state = "idle"
|
||||||
|
anim.side = animated.RIGHT
|
||||||
|
anim:play("idle")
|
||||||
|
return anim
|
||||||
|
end
|
||||||
|
|
||||||
|
function animated:update(dt)
|
||||||
|
local anim = self.animationTable[self.state] or self.animationTable["idle"] or nil
|
||||||
|
|
||||||
|
if not anim then return end
|
||||||
|
anim:update(dt)
|
||||||
|
end
|
||||||
|
|
||||||
|
function animated:draw()
|
||||||
|
if not self.animationTable[self.state] or not Tree.assets.files.sprites.character[self.state] then return end
|
||||||
|
|
||||||
|
self.owner:try(Tree.behaviors.map,
|
||||||
|
function(map)
|
||||||
|
local ppm = Tree.level.camera.pixelsPerMeter
|
||||||
|
local position = map.displayedPosition
|
||||||
|
if Tree.level.selector.id == self.owner.id then love.graphics.setColor(0.5, 1, 0.5) end
|
||||||
|
self.animationTable[self.state]:draw(Tree.assets.files.sprites.character[self.state],
|
||||||
|
position.x + 0.5,
|
||||||
|
position.y + 0.5, nil, 1 / ppm * self.side, 1 / ppm, 38, 47)
|
||||||
|
love.graphics.setColor(1, 1, 1)
|
||||||
|
end
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
--- @param state string
|
||||||
|
--- @param loop boolean | nil
|
||||||
|
function animated:play(state, loop)
|
||||||
|
if not self.animationGrid[state] then
|
||||||
|
return print("[AnimatedBehavior]: no animation for '" .. state .. "'")
|
||||||
|
end
|
||||||
|
self.animationTable[state] = anim8.newAnimation(self.animationGrid[state], self.ANIMATION_SPEED, function()
|
||||||
|
if not loop then self:play("idle", true) end
|
||||||
|
end)
|
||||||
|
self.state = state
|
||||||
|
end
|
||||||
|
|
||||||
|
return animated
|
||||||
@ -27,7 +27,9 @@ end
|
|||||||
function mapBehavior:followPath(path)
|
function mapBehavior:followPath(path)
|
||||||
if path:is_empty() then return end
|
if path:is_empty() then return end
|
||||||
self.position = self.displayedPosition
|
self.position = self.displayedPosition
|
||||||
self.owner:setState("run")
|
self.owner:try(Tree.behaviors.animated, function(animated)
|
||||||
|
animated:play("run", true)
|
||||||
|
end)
|
||||||
self.path = path;
|
self.path = path;
|
||||||
---@type Vec3
|
---@type Vec3
|
||||||
local nextCell = path:peek_front()
|
local nextCell = path:peek_front()
|
||||||
@ -39,19 +41,19 @@ end
|
|||||||
function mapBehavior:runTo(target)
|
function mapBehavior:runTo(target)
|
||||||
self.t0 = love.timer.getTime()
|
self.t0 = love.timer.getTime()
|
||||||
self.runTarget = target
|
self.runTarget = target
|
||||||
self.owner:try(Tree.behaviors.render,
|
self.owner:try(Tree.behaviors.animated,
|
||||||
function(render)
|
function(animated)
|
||||||
if target.x < self.position.x then
|
if target.x < self.position.x then
|
||||||
render.animation.side = LEFT
|
animated.side = Tree.behaviors.animated.LEFT
|
||||||
elseif target.x > self.position.x then
|
elseif target.x > self.position.x then
|
||||||
render.animation.side = RIGHT
|
animated.side = Tree.behaviors.animated.RIGHT
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
function mapBehavior:update(dt)
|
function mapBehavior:update(dt)
|
||||||
if self.owner.state == "run" and self.runTarget then
|
if self.runTarget then
|
||||||
local delta = love.timer.getTime() - self.t0 or love.timer.getTime()
|
local delta = love.timer.getTime() - self.t0 or love.timer.getTime()
|
||||||
local fraction = delta /
|
local fraction = delta /
|
||||||
(0.5 * self.runTarget:subtract(self.position):length()) -- бежим одну клетку за 500 мс, по диагонали больше
|
(0.5 * self.runTarget:subtract(self.position):length()) -- бежим одну клетку за 500 мс, по диагонали больше
|
||||||
@ -61,7 +63,9 @@ function mapBehavior:update(dt)
|
|||||||
self:runTo(self.path:peek_front())
|
self:runTo(self.path:peek_front())
|
||||||
self.path:pop_front()
|
self.path:pop_front()
|
||||||
else -- мы добежали до финальной цели
|
else -- мы добежали до финальной цели
|
||||||
self.owner:setState("idle")
|
self.owner:try(Tree.behaviors.animated, function(animated)
|
||||||
|
animated:play("idle", true)
|
||||||
|
end)
|
||||||
self.runTarget = nil
|
self.runTarget = nil
|
||||||
end
|
end
|
||||||
else -- анимация перемещения не завершена
|
else -- анимация перемещения не завершена
|
||||||
|
|||||||
@ -1,37 +0,0 @@
|
|||||||
--- @class DrawBehavior : Behavior
|
|
||||||
--- @field animation Animation
|
|
||||||
local renderBehavior = {}
|
|
||||||
renderBehavior.__index = renderBehavior
|
|
||||||
renderBehavior.id = "render"
|
|
||||||
renderBehavior.dependencies = { Tree.behaviors.map }
|
|
||||||
|
|
||||||
|
|
||||||
--- @param spriteDir table
|
|
||||||
function renderBehavior.new(spriteDir)
|
|
||||||
return setmetatable({
|
|
||||||
animation = (require 'lib.character.animation').new(spriteDir)
|
|
||||||
}, renderBehavior)
|
|
||||||
end
|
|
||||||
|
|
||||||
function renderBehavior:update(dt)
|
|
||||||
self.animation.animationTable[self.owner:getState()]:update(dt)
|
|
||||||
end
|
|
||||||
|
|
||||||
function renderBehavior:draw()
|
|
||||||
self.owner:try(Tree.behaviors.map,
|
|
||||||
function(map)
|
|
||||||
local ppm = Tree.level.camera.pixelsPerMeter
|
|
||||||
local position = map.displayedPosition
|
|
||||||
local state = self.owner:getState()
|
|
||||||
|
|
||||||
if Tree.level.selector.id == self.owner.id then love.graphics.setColor(0.5, 1, 0.5) end
|
|
||||||
|
|
||||||
self.animation.animationTable[state]:draw(Tree.assets.files.sprites.character[state],
|
|
||||||
position.x + 0.5,
|
|
||||||
position.y + 0.5, nil, 1 / ppm * self.animation.side, 1 / ppm, 38, 47)
|
|
||||||
love.graphics.setColor(1, 1, 1)
|
|
||||||
end
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
return renderBehavior
|
|
||||||
@ -1,12 +1,10 @@
|
|||||||
require 'lib.utils.vec3'
|
require 'lib.utils.vec3'
|
||||||
|
|
||||||
--- @alias CharacterState "idle"|"run"|"attack"|"hurt"
|
|
||||||
|
|
||||||
--- @alias Id integer
|
--- @alias Id integer
|
||||||
--- @type Id
|
--- @type Id
|
||||||
local characterId = 1
|
local characterId = 1
|
||||||
|
|
||||||
--- @todo Композиция лучше наследования, но не до такой же степени! Надо отрефакторить и избавиться от сотни полей в таблице
|
|
||||||
--- @class Character
|
--- @class Character
|
||||||
--- @field id Id
|
--- @field id Id
|
||||||
--- @field behaviors Behavior[]
|
--- @field behaviors Behavior[]
|
||||||
@ -32,11 +30,9 @@ local function spawn(name, template, spriteDir, position, size, level)
|
|||||||
|
|
||||||
char:addBehavior {
|
char:addBehavior {
|
||||||
Tree.behaviors.map.new(position, size),
|
Tree.behaviors.map.new(position, size),
|
||||||
Tree.behaviors.render.new(spriteDir),
|
Tree.behaviors.animated.new(spriteDir),
|
||||||
Tree.behaviors.spellcaster.new()
|
Tree.behaviors.spellcaster.new()
|
||||||
}
|
}
|
||||||
char:setState("idle") --- @todo сделать это отдельным модулем
|
|
||||||
|
|
||||||
|
|
||||||
Tree.level.characters[char.id] = char
|
Tree.level.characters[char.id] = char
|
||||||
return char
|
return char
|
||||||
@ -96,30 +92,7 @@ function character:addBehavior(behaviors)
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- геттеры и сеттеры для "внешних" данных
|
|
||||||
--- забей, это в поведения
|
|
||||||
--- @deprecated
|
|
||||||
--- @return CharacterState
|
|
||||||
function character:getState()
|
|
||||||
return self.state or "idle"
|
|
||||||
end
|
|
||||||
|
|
||||||
--- @param state CharacterState
|
|
||||||
--- @deprecated
|
|
||||||
function character:setState(state) --- @todo это вообще должно быть отдельное поведение
|
|
||||||
self.state = state
|
|
||||||
self:has(Tree.behaviors.render).animation:setState(state, (state ~= "idle" and state ~= "run") and function()
|
|
||||||
self:setState("idle")
|
|
||||||
end or nil)
|
|
||||||
end
|
|
||||||
|
|
||||||
--- @param path Deque
|
|
||||||
function character:followPath(path)
|
|
||||||
self:has(Tree.behaviors.map):followPath(path)
|
|
||||||
end
|
|
||||||
|
|
||||||
function character:update(dt)
|
function character:update(dt)
|
||||||
--- @todo ну ты понел
|
|
||||||
for _, b in ipairs(self.behaviors) do
|
for _, b in ipairs(self.behaviors) do
|
||||||
if b.update then b:update(dt) end
|
if b.update then b:update(dt) end
|
||||||
end
|
end
|
||||||
|
|||||||
@ -10,7 +10,7 @@ Tree.panning = require "lib/panning"
|
|||||||
Tree.controls = require "lib.controls"
|
Tree.controls = require "lib.controls"
|
||||||
Tree.level = (require "lib.level.level").new("procedural", "flower_plains") -- для теста у нас только один уровень, который сразу же загружен
|
Tree.level = (require "lib.level.level").new("procedural", "flower_plains") -- для теста у нас только один уровень, который сразу же загружен
|
||||||
|
|
||||||
Tree.behaviors = {} --- @todo написать нормальную загрузку поведений
|
Tree.behaviors = {} --- @todo написать нормальную загрузку поведений
|
||||||
Tree.behaviors.map = require "lib.character.behaviors.map"
|
Tree.behaviors.map = require "lib.character.behaviors.map"
|
||||||
Tree.behaviors.render = require "lib.character.behaviors.render"
|
|
||||||
Tree.behaviors.spellcaster = require "lib.character.behaviors.spellcaster"
|
Tree.behaviors.spellcaster = require "lib.character.behaviors.spellcaster"
|
||||||
|
Tree.behaviors.animated = require "lib.character.behaviors.animated"
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user