diff --git a/lib/character/animation.lua b/lib/character/animation.lua deleted file mode 100644 index 53a3628..0000000 --- a/lib/character/animation.lua +++ /dev/null @@ -1,46 +0,0 @@ -local anim8 = require "lib.utils.anim8" - ---- Скорость между кадрами в анимации -local ANIMATION_SPEED = 0.1 - -LEFT = -1 -RIGHT = 1 - ---- @class Animation ---- @field animationTable 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 } diff --git a/lib/character/behaviors/animated.lua b/lib/character/behaviors/animated.lua new file mode 100644 index 0000000..d3784ea --- /dev/null +++ b/lib/character/behaviors/animated.lua @@ -0,0 +1,70 @@ +local anim8 = require "lib.utils.anim8" + +--- @class AnimatedBehavior : Behavior +--- @field animationTable 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 diff --git a/lib/character/behaviors/map.lua b/lib/character/behaviors/map.lua index 2bd8565..c1573ca 100644 --- a/lib/character/behaviors/map.lua +++ b/lib/character/behaviors/map.lua @@ -27,7 +27,9 @@ end function mapBehavior:followPath(path) if path:is_empty() then return end self.position = self.displayedPosition - self.owner:setState("run") + self.owner:try(Tree.behaviors.animated, function(animated) + animated:play("run", true) + end) self.path = path; ---@type Vec3 local nextCell = path:peek_front() @@ -39,19 +41,19 @@ end function mapBehavior:runTo(target) self.t0 = love.timer.getTime() self.runTarget = target - self.owner:try(Tree.behaviors.render, - function(render) + self.owner:try(Tree.behaviors.animated, + function(animated) if target.x < self.position.x then - render.animation.side = LEFT + animated.side = Tree.behaviors.animated.LEFT elseif target.x > self.position.x then - render.animation.side = RIGHT + animated.side = Tree.behaviors.animated.RIGHT end end ) end 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 fraction = delta / (0.5 * self.runTarget:subtract(self.position):length()) -- бежим одну клетку за 500 мс, по диагонали больше @@ -61,7 +63,9 @@ function mapBehavior:update(dt) self:runTo(self.path:peek_front()) self.path:pop_front() else -- мы добежали до финальной цели - self.owner:setState("idle") + self.owner:try(Tree.behaviors.animated, function(animated) + animated:play("idle", true) + end) self.runTarget = nil end else -- анимация перемещения не завершена diff --git a/lib/character/behaviors/render.lua b/lib/character/behaviors/render.lua deleted file mode 100644 index 73066bb..0000000 --- a/lib/character/behaviors/render.lua +++ /dev/null @@ -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 diff --git a/lib/character/character.lua b/lib/character/character.lua index 0c40abb..9968731 100644 --- a/lib/character/character.lua +++ b/lib/character/character.lua @@ -1,12 +1,10 @@ require 'lib.utils.vec3' ---- @alias CharacterState "idle"|"run"|"attack"|"hurt" --- @alias Id integer --- @type Id local characterId = 1 ---- @todo Композиция лучше наследования, но не до такой же степени! Надо отрефакторить и избавиться от сотни полей в таблице --- @class Character --- @field id Id --- @field behaviors Behavior[] @@ -32,11 +30,9 @@ local function spawn(name, template, spriteDir, position, size, level) char:addBehavior { Tree.behaviors.map.new(position, size), - Tree.behaviors.render.new(spriteDir), + Tree.behaviors.animated.new(spriteDir), Tree.behaviors.spellcaster.new() } - char:setState("idle") --- @todo сделать это отдельным модулем - Tree.level.characters[char.id] = char return char @@ -96,30 +92,7 @@ function character:addBehavior(behaviors) return self 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) - --- @todo ну ты понел for _, b in ipairs(self.behaviors) do if b.update then b:update(dt) end end diff --git a/lib/tree.lua b/lib/tree.lua index 3bd5858..5c634cd 100644 --- a/lib/tree.lua +++ b/lib/tree.lua @@ -10,7 +10,7 @@ Tree.panning = require "lib/panning" Tree.controls = require "lib.controls" 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.render = require "lib.character.behaviors.render" Tree.behaviors.spellcaster = require "lib.character.behaviors.spellcaster" +Tree.behaviors.animated = require "lib.character.behaviors.animated"