fix waypoints going out of bounds

This commit is contained in:
2025-01-10 23:47:58 +09:00
parent fe4dc8b8bc
commit f1c191f61f
3 changed files with 30 additions and 33 deletions
+24 -27
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()
@@ -69,8 +68,19 @@ export default class Entity {
return SATX.collideObjects(this.collider, colliders) return SATX.collideObjects(this.collider, colliders)
} }
// TODO: calculate convex hulls of collidables because tris handle collisions sequentially
moveAction(x, y) { moveAction(x, y) {
this.#dest = new Vector2(x, y) const temp = SATX.fixCollisions(new Vector2(x, y), this.collidables(), this.radius)
// const entity = new Entity()
// entity.teleport(temp.x, temp.y)
// entity.radius = this.radius
// this.game.spawn_entity(entity)
this.#dest = SATX.clamp(
temp,
this.game?.width,
this.game?.height,
this.radius,
)
} }
state() { state() {
@@ -87,9 +97,8 @@ export default class Entity {
this.position.set(x, y) 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?) // TODO: pathfinding stops if wall is clicked (did you forget to fix the destination?)
async takeStep(distanceTraveled = 0) { takeStep(distanceTraveled = 0) {
const speed = (this.speed / (this.game?.tickBudget ?? 1000)) - distanceTraveled const speed = (this.speed / (this.game?.tickBudget ?? 1000)) - distanceTraveled
const collidables = this.collidables() const collidables = this.collidables()
if (this.#dest != null) { if (this.#dest != null) {
@@ -97,29 +106,12 @@ export default class Entity {
if (this.#path.length < 1 || !this.#path.at(-1).equals(fixedDest)) { if (this.#path.length < 1 || !this.#path.at(-1).equals(fixedDest)) {
console.time('pathfinding') console.time('pathfinding')
console.time('waypoints')
const start = SATX.vectorToFloat32Array(this.position) const start = SATX.vectorToFloat32Array(this.position)
const goal = SATX.vectorToFloat32Array(fixedDest) const goal = SATX.vectorToFloat32Array(fixedDest)
const nonUniqueWaypoints = this.waypoints().map((w) => SATX.vectorToFloat32Array(w)).concat([start, goal]) const nonUniqueWaypoints = this.waypoints().map((w) => SATX.vectorToFloat32Array(w)).concat([start, goal])
const waypoints = Pathfind.uniqueWaypoints(nonUniqueWaypoints) const waypoints = Pathfind.uniqueWaypoints(nonUniqueWaypoints)
console.timeEnd('waypoints')
console.time('graph')
const graph = Pathfind.buildGraph(waypoints, collidables, this.radius) const graph = Pathfind.buildGraph(waypoints, collidables, this.radius)
// 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))
// }
// tunnels.map((t) => SATX.satPolygonToVectors(t)).forEach((t) => this.#game.add_terrain(new Terrain(t)))
// this.#dest = null
console.timeEnd('graph')
console.time('path')
this.#path = Pathfind.shortestPath(graph, start, goal).map((waypoint) => new Vector2(waypoint[0], waypoint[1])) this.#path = Pathfind.shortestPath(graph, start, goal).map((waypoint) => new Vector2(waypoint[0], waypoint[1]))
console.log(this.#path)
console.timeEnd('path')
console.timeEnd('pathfinding') console.timeEnd('pathfinding')
} }
@@ -140,7 +132,7 @@ export default class Entity {
if (this.position.equals(destination)) { if (this.position.equals(destination)) {
this.#path = this.#path.slice(1) this.#path = this.#path.slice(1)
if (this.#path.length > 0) { if (this.#path.length > 0) {
await this.takeStep(distance) this.takeStep(distance)
} }
else { else {
this.#dest = null this.#dest = null
@@ -150,10 +142,8 @@ export default class Entity {
} }
} }
async update() { update() {
await Promise.allSettled([ this.takeStep()
this.takeStep(),
])
} }
waypoints() { waypoints() {
@@ -161,6 +151,13 @@ export default class Entity {
const terrainColliders = (this.game?.terrains ?? []) const terrainColliders = (this.game?.terrains ?? [])
const unadjustedWaypoints = entityColliders.concat(terrainColliders).map((e) => e.unadjustedWaypoints).flat() const unadjustedWaypoints = entityColliders.concat(terrainColliders).map((e) => e.unadjustedWaypoints).flat()
return unadjustedWaypoints.map(([waypoint, direction]) => waypoint.clone().add(direction.clone().multiplyScalar(this.radius))) ?? [] return unadjustedWaypoints.map(([waypoint, direction]) => {
return SATX.clamp(
waypoint.clone().add(direction.clone().multiplyScalar(this.radius)),
this.game?.width,
this.game?.height,
this.radius,
)
}) ?? []
} }
} }
+4 -4
View File
@@ -42,14 +42,14 @@ export default class Game {
} }
} }
async start() { start() {
const start = performance.now() const start = performance.now()
await this.update() this.update()
setTimeout(() => this.start(), Math.max(0, this.#tickBudget - (performance.now() - start))) setTimeout(() => this.start(), Math.max(0, this.#tickBudget - (performance.now() - start)))
} }
async update() { update() {
Promise.allSettled(this.#entities.map((e) => e.update())) this.#entities.map((e) => e.update())
this.currentTick++ this.currentTick++
this.eventEmitter.emit('tick') this.eventEmitter.emit('tick')
} }
+2 -2
View File
@@ -115,9 +115,9 @@ app.listen(port, () => {
pole.id = 'pole' pole.id = 'pole'
game.add_terrain(pole) game.add_terrain(pole)
entity1.moveAction(1000, 500) // entity1.moveAction(1000, 500)
setTimeout(() => entity1.moveAction(100, 400), 10) // setTimeout(() => entity1.moveAction(100, 400), 10)
game.start() game.start()
}) })