revert "disable collision to fix pathfinding phasing through walls"

This reverts commit f48a6bf9aa.
This commit is contained in:
2024-12-25 09:36:34 +09:00
parent 2570f32592
commit 5acc827f7b
4 changed files with 28 additions and 71 deletions
+9 -18
View File
@@ -2,7 +2,6 @@ import { Vector2 } from 'three'
import SAT from 'sat' import SAT from 'sat'
import SATX from './satx.js' import SATX from './satx.js'
import Pathfind from './pathfind.js' import Pathfind from './pathfind.js'
import Terrain from './terrain.js'
export default class Entity { export default class Entity {
id = crypto.randomUUID() id = crypto.randomUUID()
@@ -95,18 +94,13 @@ export default class Entity {
if (this.#path.length < 1 || !this.#path.at(-1).equals(this.#dest)) { if (this.#path.length < 1 || !this.#path.at(-1).equals(this.#dest)) {
console.time('pathfinding') console.time('pathfinding')
console.time('waypoints') console.time('waypoints')
const extraWaypoints = [[this.position, new Vector2()], [fixedDest, new Vector2()]] const waypoints = (this.game?.unadjustedWaypoints.map(([unadjusted, direction]) => unadjusted.clone().add(direction.clone().multiplyScalar(this.radius))) ?? []).concat([this.position, fixedDest])
const waypoints = (this.game?.unadjustedWaypoints ?? []).concat(extraWaypoints)
console.timeEnd('waypoints') console.timeEnd('waypoints')
console.time('extraEntries') console.time('graph')
const extraGraphEntries = Pathfind.buildGraph(waypoints, this.collidables(), extraWaypoints) const graph = Pathfind.buildGraph(waypoints, this.collidables(), this.radius)
console.timeEnd('extraEntries') console.timeEnd('graph')
console.time('unadjustedGraph')
const unadjustedGraph = (this.game?.waypointGraph ?? []).concat(extraGraphEntries)
console.timeEnd('unadjustedGraph')
console.time('path') console.time('path')
this.#path = Pathfind.shortestPath(unadjustedGraph, this.position, fixedDest).map(([unadjusted, direction]) => unadjusted.clone().add(direction.clone().multiplyScalar(this.radius))) this.#path = Pathfind.shortestPath(graph, this.position, fixedDest)
console.log(this.#path)
console.timeEnd('path') console.timeEnd('path')
console.timeEnd('pathfinding') console.timeEnd('pathfinding')
} }
@@ -119,14 +113,11 @@ export default class Entity {
const position = distance <= speed ? destination : stepTaken const position = distance <= speed ? destination : stepTaken
const collider = Entity.collider(position.x, position.y, this.radius) const collider = Entity.collider(position.x, position.y, this.radius)
// TODO: fix collisions while pathfinding const isColliding = SATX.collideObjects(collider, this.collidables())
// const isColliding = SATX.collideObjects(collider, this.collidables())
// if (!isColliding) { if (!isColliding) {
// this.position.copy(position) this.position.copy(position)
// } }
this.position.copy(position)
if (this.position.equals(destination)) { if (this.position.equals(destination)) {
this.#path = this.#path.slice(1) this.#path = this.#path.slice(1)
+1 -16
View File
@@ -1,5 +1,4 @@
import { EventEmitter } from 'node:events' import { EventEmitter } from 'node:events'
import Pathfind from './pathfind.js'
export default class Game { export default class Game {
tickRate = 30 tickRate = 30
@@ -11,23 +10,14 @@ export default class Game {
#eventEmitter = new EventEmitter() #eventEmitter = new EventEmitter()
#terrains = [] #terrains = []
#tickBudget = Math.floor(1000 / this.tickRate) #tickBudget = Math.floor(1000 / this.tickRate)
#waypointGraph = []
get entities() { return this.#entities } get entities() { return this.#entities }
get eventEmitter() { return this.#eventEmitter } get eventEmitter() { return this.#eventEmitter }
get terrains() { return this.#terrains } get terrains() { return this.#terrains }
get tickBudget() { return this.#tickBudget } get tickBudget() { return this.#tickBudget }
get waypointGraph() { return this.#waypointGraph }
get unadjustedWaypoints() { get unadjustedWaypoints() {
// const terrainAndEntities = this.entities.concat(this.terrains) return this.terrains.map((t) => t.unadjustedWaypoints).concat(this.entities.map((e) => e.unadjustedWaypoints)).flat()
const terrainAndEntities = this.terrains
return terrainAndEntities.map((e) => e.unadjustedWaypoints).flat()
}
colliders() {
const terrainAndEntities = this.entities.concat(this.terrains)
return terrainAndEntities.map((e) => e.colliders).flat()
} }
spawn_entity(entity) { spawn_entity(entity) {
@@ -42,7 +32,6 @@ export default class Game {
add_terrain(terrain) { add_terrain(terrain) {
this.#terrains.push(terrain) this.#terrains.push(terrain)
this.#calculateWaypointGraph()
} }
state() { state() {
@@ -64,8 +53,4 @@ export default class Game {
this.currentTick++ this.currentTick++
this.eventEmitter.emit('tick') this.eventEmitter.emit('tick')
} }
#calculateWaypointGraph() {
this.#waypointGraph = Pathfind.buildGraph(this.unadjustedWaypoints, this.colliders())
}
} }
-10
View File
@@ -52,14 +52,6 @@ app.listen(port, () => {
entity2.radius = 50 entity2.radius = 50
game.spawn_entity(entity2) 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 horseshoe = new Terrain([ const horseshoe = new Terrain([
{ x: 400, y: 200 }, { x: 400, y: 200 },
{ x: 600, y: 200 }, { x: 600, y: 200 },
@@ -115,7 +107,5 @@ app.listen(port, () => {
pole.id = 'pole' pole.id = 'pole'
game.add_terrain(pole) game.add_terrain(pole)
entity1.moveAction(entity1.x + 600, entity1.y)
game.start() game.start()
}) })
+18 -27
View File
@@ -3,33 +3,28 @@ import PriorityQueue from "./priority-queue.js"
import SATX from "./satx.js" import SATX from "./satx.js"
export default class Pathfind { export default class Pathfind {
static keyFor(pos) {
return `${pos.x},${pos.y}`
}
static shortestPath(graph, start, goal) { static shortestPath(graph, start, goal) {
const key = (pos) => `${pos.x},${pos.y}`
const queue = new PriorityQueue((a, b) => a[1] < b[1]) const queue = new PriorityQueue((a, b) => a[1] < b[1])
const visited = new Map() const visited = new Map()
queue.push([[[start, null]], 0]) queue.push([[start], 0])
while (!queue.isEmpty()) { while (!queue.isEmpty()) {
const [path, cost] = queue.pop() const [path, cost] = queue.pop()
const waypoint = path.at(-1).at(0) const waypoint = path.at(-1)
if (waypoint.equals(goal)) { if (waypoint.equals(goal)) {
path.shift() path.shift()
return path return path
} }
const key = this.keyFor(waypoint) if (!visited.has(key(waypoint)) || visited.get(key(waypoint)) > cost) {
if (!visited.has(key) || visited.get(key) > cost) { visited.set(key(waypoint), cost)
visited.set(key, cost)
for (const { to, direction, distance } of graph.filter(e => e.from.equals(waypoint))) { for (const { to, distance } of graph.filter(e => e.from.equals(waypoint))) {
const keyTo = this.keyFor(to) if (!visited.has(key(to)) || visited.get(key(to)) > cost + distance) {
if (!visited.has(keyTo) || visited.get(keyTo) > cost + distance) { queue.push([[...path, to], cost + distance])
queue.push([[...path, [to, direction]], cost + distance])
} }
} }
} }
@@ -38,28 +33,24 @@ export default class Pathfind {
return [] return []
} }
static buildGraph(toWaypoints, colliders, fromWaypoints = null) { static buildGraph(waypoints = [], colliders = [], radius = 0) {
const graph = [] const graph = []
const calculated = new Set()
for (const [from, reverseDirection] of (fromWaypoints ?? toWaypoints)) { for (const from of waypoints) {
for (const [to, direction] of toWaypoints) { for (const to of waypoints) {
if (from.equals(to)) { if (from.equals(to)) {
continue continue
} }
const key = `${from.x},${from.y};${to.x},${to.y}` const tunnel = SATX.entityTunnel(from, to, radius)
if (!calculated.has(key)) { const collider = Entity.collider(from.x, from.y, radius)
calculated.add(key)
calculated.add(`${to.x},${to.y};${from.x},${from.y}`)
const lineOfSight = !SATX.collideObjects(SATX.line(from, to), colliders) const tunnelClear = !SATX.collideObjects(tunnel, colliders)
const waypointAvailable = !SATX.collideObjects(collider, colliders)
if (lineOfSight) { if (waypointAvailable && tunnelClear) {
const distance = from.distanceTo(to) const distance = from.distanceTo(to)
graph.push({ from, to, distance, direction }) graph.push({ from, to, distance })
graph.push({ to: from, from: to, distance, direction: reverseDirection })
}
} }
} }
} }