From 37eb71251801f00114122005f402ded134a1d84f Mon Sep 17 00:00:00 2001 From: PeaAshMeter Date: Fri, 15 Aug 2025 05:53:01 +0300 Subject: [PATCH] implement 'followPath' --- lib/character/character.lua | 6 +++--- lib/character/graphics.lua | 2 +- lib/character/logic.lua | 32 ++++++++++++++++++++++---------- lib/character/map_logic.lua | 8 ++++++-- lib/pathfinder.lua | 9 ++++++--- lib/selector.lua | 10 ++++++++++ main.lua | 7 +++---- 7 files changed, 51 insertions(+), 23 deletions(-) diff --git a/lib/character/character.lua b/lib/character/character.lua index fe654a7..323f222 100644 --- a/lib/character/character.lua +++ b/lib/character/character.lua @@ -37,9 +37,9 @@ local function spawn(name, template, spriteDir, position, size, level) return char end ---- @param target Vec3 -function character:runTo(target) - self.logic:runTo(target) +--- @param path Deque +function character:followPath(path) + self.logic:followPath(path) end function character:update(dt) diff --git a/lib/character/graphics.lua b/lib/character/graphics.lua index f9a37ae..34d3a0a 100644 --- a/lib/character/graphics.lua +++ b/lib/character/graphics.lua @@ -20,7 +20,7 @@ end function graphics:draw() local ppm = Tree.level.camera.pixelsPerMeter - local position = Tree.level.characters[self.id].logic.mapLogic.position + local position = Tree.level.characters[self.id].logic.mapLogic.displayedPosition local state = Tree.level.characters[self.id].logic.state if Tree.level.selector.id == self.id then love.graphics.setColor(0.5, 1, 0.5) end diff --git a/lib/character/logic.lua b/lib/character/logic.lua index 537176e..73453f4 100644 --- a/lib/character/logic.lua +++ b/lib/character/logic.lua @@ -13,30 +13,42 @@ logic.__index = logic local function new(id, position, size) return setmetatable({ id = id, - mapLogic = (require 'lib.character.map_logic').new(id, position, size) + mapLogic = (require 'lib.character.map_logic').new(id, position, size), + state = "idle" }, logic) end ---- @param path Vec3 +--- @param path Deque function logic:followPath(path) - + if path:is_empty() then return end + self.mapLogic.path = path; + self:runTo(path:peek_front()) + path:pop_front() end --- @param target Vec3 function logic:runTo(target) + self.mapLogic.t0 = love.timer.getTime() self.state = "run" self.mapLogic.runTarget = target end function logic:update(dt) if self.state == "run" and self.mapLogic.runTarget then - if self.mapLogic.position:floor() == self.mapLogic.runTarget:floor() then -- мы добежали до цели и сейчас в целевой клетке - self.state = "idle" - self.mapLogic.runTarget = nil - else -- мы не добежали до цели - local vel = (self.mapLogic.runTarget:subtract(self.mapLogic.position):normalize() --[[@as Vec3]] - ):scale(1 * dt) -- бежим 2 условных метра в секунду - self.mapLogic.position = self.mapLogic.position:add(vel) + local vel = self.mapLogic.runTarget:subtract(self.mapLogic.position) --[[@as Vec3]] + local delta = love.timer.getTime() - self.mapLogic.t0 or love.timer.getTime() + local fraction = delta / 0.5 + if fraction >= 1 then -- мы добежали до цели и сейчас в целевой клетке (возможно, промежуточной) + self.mapLogic.position = self.mapLogic.runTarget + if not self.mapLogic.path:is_empty() then -- еще есть, куда бежать + self:runTo(self.mapLogic.path:peek_front()) + self.mapLogic.path:pop_front() + else -- мы добежали до финальной цели + self.state = "idle" + self.mapLogic.runTarget = nil + end + else -- мы не добежали до цели + self.mapLogic.displayedPosition = self.mapLogic.position:add(vel:scale(fraction)) end end diff --git a/lib/character/map_logic.lua b/lib/character/map_logic.lua index 8717bff..805af01 100644 --- a/lib/character/map_logic.lua +++ b/lib/character/map_logic.lua @@ -1,8 +1,10 @@ --- @class MapLogic --- @field id Id --- @field position Vec3 ---- @field latestPosition Vec3 позиция, где character был один тик назад --- @field runTarget Vec3 точка, в которую в данный момент бежит персонаж +--- @field displayedPosition Vec3 точка, в которой персонаж отображается +--- @field t0 number время начала движения для анимациии +--- @field path Deque путь, по которому сейчас бежит персонаж --- @field size Vec3 local mapLogic = {} @@ -13,7 +15,9 @@ local function new(id, position, size) return setmetatable({ id = id, position = position or Vec3({}), - size = size or Vec3({ 1, 1 }) + displayedPosition = position or Vec3({}), + size = size or Vec3({ 1, 1 }), + path = (require "lib.utils.deque").new() }, mapLogic) end diff --git a/lib/pathfinder.lua b/lib/pathfinder.lua index 20a7aca..cf1ef9f 100644 --- a/lib/pathfinder.lua +++ b/lib/pathfinder.lua @@ -1,6 +1,8 @@ +local deque = require "lib.utils.deque" + --- @param cur Vec3 --- @param to Vec3 ---- @param acc Vec3[] +--- @param acc Deque local function greedy_trace_step(cur, to, acc) local lengthTable = {} for x = -1, 1 do @@ -15,7 +17,7 @@ local function greedy_trace_step(cur, to, acc) end local next = min[1] - table.insert(acc, cur) + acc = acc:push_back(cur) if cur == to then return acc end @@ -24,8 +26,9 @@ end --- @param from Vec3 --- @param to Vec3 +--- @return Deque local function trace(from, to) - return greedy_trace_step(from, to, {}) + return greedy_trace_step(from, to, deque.new()) end return trace diff --git a/lib/selector.lua b/lib/selector.lua index 768ac30..8784b25 100644 --- a/lib/selector.lua +++ b/lib/selector.lua @@ -27,7 +27,17 @@ function selector:update(dt) end local characterId = Tree.level.characterGrid:get(mousePosition.x, mousePosition.y) + if not characterId and self.id then -- временная обработка события "побежать к точке" + local char = Tree.level.characters[self.id] + local charPos = char.logic.mapLogic.position + local path = (require "lib.pathfinder")(charPos, mousePosition) + path:pop_front() + print("Following path: ") + for p in path:values() do print(p) end + char:followPath(path) + end self:select(characterId) + print("[Selector]:", mousePosition, characterId and "selected " .. characterId or "deselected") end diff --git a/main.lua b/main.lua index ea98365..22f3af7 100644 --- a/main.lua +++ b/main.lua @@ -12,8 +12,7 @@ end local width = 30 local height = 30 function love.load() - local char = character.spawn("Hero", "warrior", Tree.assets.files.sprites.character) - char:runTo(Vec3 { 5, 5 }) + character.spawn("Hero", "warrior", Tree.assets.files.sprites.character) Grass = Tree.assets.files.tiles.grass.atlas Gr1 = love.graphics.newQuad(0, 32, 32, 32, Grass) @@ -76,8 +75,8 @@ function love.draw() local path = (require "lib.pathfinder")(charPos, mpos) love.graphics.setColor(1, 0, 0) - for _, p in ipairs(path) do - love.graphics.rectangle("fill", p.x + 0.5, p.y + 0.5, 0.1, 0.1) + for p in path:values() do + love.graphics.rectangle("fill", p.x + 0.45, p.y + 0.45, 0.1, 0.1) end love.graphics.setColor(1, 1, 1) end