new circleVectors (midpoint circle algorithm) and pathToClosestCharacter

function
This commit is contained in:
neckrat 2026-04-15 13:42:33 +03:00
parent 7526e3064f
commit b77c07eef0
2 changed files with 77 additions and 32 deletions

View File

@ -25,45 +25,85 @@ local function closestCharacter(char)
return charTarget return charTarget
end end
--- Возвращает все точки в радиусе в виде векторов -- --- Возвращает все точки в радиусе в виде векторов (должен по крайней мере)
-- --- @param radius integer
-- --- @param center Vec3
-- --- @return Vec3[]
-- local function circleVectors(center, radius)
-- local vecs = {}
-- local res = {}
-- for t = 0, 2 * math.pi, EPSILON do
-- local x = math.cos(t) * radius + center.x
-- local y = math.sin(t) * radius + center.y
-- table.insert(vecs, Vec3 { math.floor(x), math.floor(y) })
-- end
-- for _, v in pairs(vecs) do
-- local i = 1
-- while i <= #res and (res[i].x ~= v.x or res[i].y ~= v.y) do
-- i = i + 1
-- end
-- if i == #res + 1 or #res == 0 then
-- table.insert(res, v)
-- print('[AI]: circle vecs:', v)
-- end
-- end
-- return res
-- end
--- Возвращает все точки в радиусе в виде векторов (должен по крайней мере)
--- @param radius integer --- @param radius integer
local function circleVectors(radius) --- @param center Vec3
local cam = Tree.level.camera --- @return Vec3[]
local vecs = {} local function circleVectors(center, radius)
for t = 0, 2 * math.pi, EPSILON do local dx, dy, err = radius, 0, 1 - radius
local x = math.sin(t) * radius local vecs, res = {}, {}
local y = math.cos(t) * radius while dx >= dy do
vecs[cam:toWorldPosition(Vec3 { x, y })] = true table.insert(vecs, Vec3 { center.x + dx, center.y + dy })
table.insert(vecs, Vec3 { center.x - dx, center.y + dy })
table.insert(vecs, Vec3 { center.x + dx, center.y - dy })
table.insert(vecs, Vec3 { center.x - dx, center.y - dy })
table.insert(vecs, Vec3 { center.x + dy, center.y + dx })
table.insert(vecs, Vec3 { center.x - dy, center.y + dx })
table.insert(vecs, Vec3 { center.x + dy, center.y - dx })
table.insert(vecs, Vec3 { center.x - dy, center.y - dx })
dy = dy + 1
if err < 0 then
err = err + 2 * dy + 1
else
dx, err = dx - 1, err + 2 * (dy - dx) + 1
end end
return utils.keys(vecs) end
for _, v in pairs(vecs) do
local i = 1
while i <= #res and (res[i].x ~= v.x or res[i].y ~= v.y) do
i = i + 1
end
if i == #res + 1 or #res == 0 then
table.insert(res, v)
print('[AI]: circle vecs:', v)
end
end
return vecs
end end
--- @param owner Character --- @param owner Character
--- @param space integer здесь мы должны сами определять, сколько должны не доходить до персонажа (1 <= n) --- @param radius integer здесь мы должны сами определять, сколько должны не доходить до персонажа (1 <= n)
--- @return Vec3|nil --- @return Vec3|nil
local function pathToClosestCharacter(owner, space) local function pathToClosestCharacter(owner, radius)
local charTarget = closestCharacter(owner) local charTarget = closestCharacter(owner)
local targetPosition, ownerPosition = charTarget:has(Tree.behaviors.positioned), owner:has(Tree.behaviors.positioned) local targetPosition, ownerPosition = charTarget:has(Tree.behaviors.positioned), owner:has(Tree.behaviors.positioned)
if not targetPosition or not ownerPosition then return end if not targetPosition or not ownerPosition then return end
local target = Vec3 {}
print(ownerPosition.position, targetPosition.position) local circleVecs = circleVectors(targetPosition.position, radius)
local path = pf(ownerPosition.position, targetPosition.position) local target = circleVecs[#circleVecs]
for c in path:values() do local path = pf(ownerPosition.position, target)
print(c) for i, c in ipairs(circleVecs) do
local newPath = pf(ownerPosition.position, c)
if newPath:size() < path:size() then
path = newPath
target = c
end end
print(path)
space = math.min(space, path:size())
print(space, path:size())
for _ = 0, space - 1 do
path:pop_back()
end end
if path:size() ~= 0 then
target = path:pop_back()
else
target = ownerPosition.position
end
print(target, targetPosition.position)
--- @todo тут захардкожено + 1, но мы должны как-то хитро определять с какой стороны обойти
return target return target
end end
@ -72,7 +112,7 @@ local aiNature = {
["dev_warrior"] = function(self) ["dev_warrior"] = function(self)
return function(callback) -- почему так, описано в Task return function(callback) -- почему так, описано в Task
self.owner:try(Tree.behaviors.spellcaster, function(spellB) self.owner:try(Tree.behaviors.spellcaster, function(spellB)
self.target = pathToClosestCharacter(self.owner, 2) self.target = pathToClosestCharacter(self.owner, 1)
local task1 = spellB.spellbook[1]:cast(self.owner, self.target) local task1 = spellB.spellbook[1]:cast(self.owner, self.target)
if task1 then if task1 then
task1( task1(

View File

@ -41,7 +41,7 @@ function love.load()
:addBehavior { :addBehavior {
Tree.behaviors.residentsleeper.new(), Tree.behaviors.residentsleeper.new(),
Tree.behaviors.stats.new(nil, nil, 3), Tree.behaviors.stats.new(nil, nil, 3),
Tree.behaviors.positioned.new(Vec3 { 7, 1 }), Tree.behaviors.positioned.new(Vec3 { 7, 2 }),
Tree.behaviors.tiled.new(), Tree.behaviors.tiled.new(),
Tree.behaviors.sprite.new(Tree.assets.files.sprites.character), Tree.behaviors.sprite.new(Tree.assets.files.sprites.character),
Tree.behaviors.shadowcaster.new(), Tree.behaviors.shadowcaster.new(),
@ -128,9 +128,14 @@ function love.draw()
love.graphics.setColor(1, 1, 1) love.graphics.setColor(1, 1, 1)
love.graphics.setFont(Tree.fonts:getTheme("Roboto_Mono"):getVariant("small")) love.graphics.setFont(Tree.fonts:getTheme("Roboto_Mono"):getVariant("small"))
local mousePosX, mousePosY = love.mouse.getPosition()
local mousePos = Tree.level.camera:toWorldPosition(Vec3 { mousePosX, mousePosY }):floor()
local stats = "fps: " .. local stats = "fps: " ..
love.timer.getFPS() .. love.timer.getFPS() ..
" lt: " .. lt .. " dt: " .. dt .. " mem: " .. string.format("%.2f MB", collectgarbage("count") / 1000) " lt: " .. lt ..
" dt: " .. dt ..
" mem: " .. string.format("%.2f MB", collectgarbage("count") / 1000) ..
" mouse pos: " .. tostring(mousePos)
love.graphics.print(stats, 10, 10) love.graphics.print(stats, 10, 10)
local t2 = love.timer.getTime() local t2 = love.timer.getTime()