new circleVectors (midpoint circle algorithm) and pathToClosestCharacter

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

View File

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

View File

@ -39,7 +39,7 @@ function love.load()
:addBehavior {
Tree.behaviors.residentsleeper.new(),
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.sprite.new(Tree.assets.files.sprites.character),
Tree.behaviors.shadowcaster.new(),
@ -112,9 +112,14 @@ function love.draw()
love.graphics.setColor(1, 1, 1)
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: " ..
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)
local t2 = love.timer.getTime()