fix pathfinding for real

This commit is contained in:
2024-12-25 18:12:53 +09:00
parent 8fe48fb679
commit fe4dc8b8bc
3 changed files with 71 additions and 73 deletions
+3 -1
View File
@@ -87,6 +87,8 @@ export default class Entity {
this.position.set(x, y)
}
// TODO: waypoints go out of bounds
// TODO: pathfinding stops if wall is clicked (did you forget to fix the destination?)
async takeStep(distanceTraveled = 0) {
const speed = (this.speed / (this.game?.tickBudget ?? 1000)) - distanceTraveled
const collidables = this.collidables()
@@ -100,11 +102,11 @@ export default class Entity {
const goal = SATX.vectorToFloat32Array(fixedDest)
const nonUniqueWaypoints = this.waypoints().map((w) => SATX.vectorToFloat32Array(w)).concat([start, goal])
const waypoints = Pathfind.uniqueWaypoints(nonUniqueWaypoints)
console.timeEnd('waypoints')
console.time('graph')
const graph = Pathfind.buildGraph(waypoints, collidables, this.radius)
// console.log(Pathfind.formatFloat32Array(graph, 5, true))
// const tunnels = []
// for (let i = 0; i < graph.length; i += 5) {
// tunnels.push(SATX.entityTunnel(graph[i], graph[i + 1], graph[i + 2], graph[i + 3], 1))
+60 -60
View File
@@ -46,78 +46,78 @@ app.listen(port, () => {
entity1.radius = 50
game.spawn_entity(entity1)
// const entity2 = new Entity()
// entity2.id = '2'
// entity2.teleport(110, 110)
// entity2.radius = 50
// game.spawn_entity(entity2)
const triangle = new Terrain([
{ x: 400, y: 200 },
{ x: 400, y: 600 },
{ x: 600, y: 300 },
])
triangle.id = 'triangle'
game.add_terrain(triangle)
const entity2 = new Entity()
entity2.id = '2'
entity2.teleport(110, 110)
entity2.radius = 50
game.spawn_entity(entity2)
// const horseshoe = new Terrain([
// const triangle = new Terrain([
// { x: 400, y: 200 },
// { x: 600, y: 200 },
// { x: 700, y: 300 },
// { x: 650, y: 600 },
// { x: 400, y: 600 },
// { x: 400, y: 450 },
// { x: 600, y: 500 },
// { x: 600, y: 300 },
// { x: 400, y: 300 },
// ])
// horseshoe.id = 'horseshoe'
// game.add_terrain(horseshoe)
// triangle.id = 'triangle'
// game.add_terrain(triangle)
// const stopsign = new Terrain([
// { x: 800, y: 800 },
// { x: 900, y: 900 },
// { x: 900, y: 1000 },
// { x: 800, y: 1100 },
// { x: 800, y: 1100 },
// { x: 700, y: 1100 },
// { x: 600, y: 1000 },
// { x: 600, y: 900 },
// { x: 700, y: 800 },
// ])
// stopsign.id = 'stopsign'
// game.add_terrain(stopsign)
const horseshoe = new Terrain([
{ x: 400, y: 200 },
{ x: 600, y: 200 },
{ x: 700, y: 300 },
{ x: 650, y: 600 },
{ x: 400, y: 600 },
{ x: 400, y: 450 },
{ x: 600, y: 500 },
{ x: 600, y: 300 },
{ x: 400, y: 300 },
])
horseshoe.id = 'horseshoe'
game.add_terrain(horseshoe)
// const box = new Terrain([
// { x: 1200, y: 700 },
// { x: 1200, y: 800 },
// { x: 1300, y: 800 },
// { x: 1300, y: 700 },
// ])
// box.id = 'box'
// game.add_terrain(box)
const stopsign = new Terrain([
{ x: 800, y: 800 },
{ x: 900, y: 900 },
{ x: 900, y: 1000 },
{ x: 800, y: 1100 },
{ x: 800, y: 1100 },
{ x: 700, y: 1100 },
{ x: 600, y: 1000 },
{ x: 600, y: 900 },
{ x: 700, y: 800 },
])
stopsign.id = 'stopsign'
game.add_terrain(stopsign)
// const diamond = new Terrain([
// { x: 1000, y: 300 },
// { x: 1100, y: 400 },
// { x: 1000, y: 500 },
// { x: 900, y: 400 },
// ])
// diamond.id = 'diamond'
// game.add_terrain(diamond)
const box = new Terrain([
{ x: 1200, y: 700 },
{ x: 1200, y: 800 },
{ x: 1300, y: 800 },
{ x: 1300, y: 700 },
])
box.id = 'box'
game.add_terrain(box)
// const pole = new Terrain([
// { x: 400, y: 1000 },
// { x: 410, y: 1000 },
// { x: 410, y: 1010 },
// { x: 400, y: 1010 },
// ])
// pole.id = 'pole'
// game.add_terrain(pole)
const diamond = new Terrain([
{ x: 1000, y: 300 },
{ x: 1100, y: 400 },
{ x: 1000, y: 500 },
{ x: 900, y: 400 },
])
diamond.id = 'diamond'
game.add_terrain(diamond)
const pole = new Terrain([
{ x: 400, y: 1000 },
{ x: 410, y: 1000 },
{ x: 410, y: 1010 },
{ x: 400, y: 1010 },
])
pole.id = 'pole'
game.add_terrain(pole)
entity1.moveAction(1000, 500)
// setTimeout(() => entity1.moveAction(100, 400), 10)
setTimeout(() => entity1.moveAction(100, 400), 10)
game.start()
})
+8 -12
View File
@@ -7,10 +7,11 @@ export default class Pathfind {
static precision = 0.001
static multiplier = 1000 // (1 / this.precision)
static key(pos) {
return `${pos.x},${pos.y}`
static key2(a, b) {
return `${a},${b}`
}
// TODO: Value exceeds safe integer limit: collisions cause waypointing anomalies
static floatKey4(a, b, c, d) {
return Math.floor(a * Pathfind.multiplier) +
Math.floor(b * Pathfind.multiplier) * Pathfind.multiplier +
@@ -18,16 +19,11 @@ export default class Pathfind {
Math.floor(d * Pathfind.multiplier) * Pathfind.multiplier ** 3
}
static floatKey2(a, b) {
return Math.floor(a * Pathfind.multiplier) +
Math.floor(b * Pathfind.multiplier) * Pathfind.multiplier
}
static uniqueWaypoints(waypoints) {
const included = new Set()
const uniqueWaypoints = []
for (const waypoint of waypoints) {
const key = Pathfind.floatKey2(waypoint[0], waypoint[1])
const key = Pathfind.key2(waypoint[0], waypoint[1])
if (!included.has(key)) {
included.add(key)
uniqueWaypoints.push(waypoint)
@@ -52,16 +48,16 @@ export default class Pathfind {
return path
}
const waypointKey = Pathfind.floatKey2(waypoint)
const waypointKey = Pathfind.key2(waypoint[0], waypoint[1])
if (!visited.has(waypointKey) || visited.get(waypointKey) > cost) {
visited.set(waypointKey, cost)
for (let i = 0; i < graph.length; i += 5) {
if (Math.abs(waypoint[0] - graph[i]) < Pathfind.precision && Math.abs(waypoint[1] - graph[i + 1]) < Pathfind.precision) {
continue
if (Math.abs(waypoint[0] - graph[i]) > Pathfind.precision || Math.abs(waypoint[1] - graph[i + 1]) > Pathfind.precision) {
continue // waypoint and graph.from aren't the same (so graph.to isn't a neighbor)
}
const nextKey = `${graph[i + 2]},${graph[i + 3]}`
const nextKey = Pathfind.key2(graph[i + 2], graph[i + 3])
if (!visited.has(nextKey) || visited.get(nextKey) > cost + graph[i + 4]) {
const next = new Float32Array(2)
next[0] = graph[i + 2]