This repository has been archived on 2026-05-07. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
instructions-clear/src/entity.js
T

141 lines
4.1 KiB
JavaScript

import { Vector2 } from 'three'
import SAT from 'sat'
import SATX from './satx.js'
import Pathfind from './pathfind.js'
export default class Entity {
id = crypto.randomUUID()
speed = 400
radius = 0
#position = new Vector2()
#dest = null
#game = null
#path = []
static collider(x, y, radius) {
return new SAT.Circle(new SAT.Vector(x, y), radius)
}
constructor(...options) {
Object.entries(options).forEach((value, key) => this[key] = value)
}
get game() { return this.#game }
get position() { return this.#position }
get x() { return this.position.x }
get y() { return this.position.y }
set game(value) { this.#game = value }
set x(value) { this.position.x = value }
set y(value) { this.position.y = value }
get collider() {
return new SAT.Circle(new SAT.Vector(this.x, this.y), this.radius)
}
get colliders() {
return [this.collider]
}
get unadjustedWaypoints() {
const numberOfWaypoints = 8
const margin = 1
const enclosingRegularPolygonRadius = SATX.enclosingRegularPolygonRadius(numberOfWaypoints)
const radius = this.radius * enclosingRegularPolygonRadius + margin
const baseWaypoint = new Vector2(radius, 0)
const waypoints = []
const origin = new Vector2
const unitOfRotation = (Math.PI * 2 / numberOfWaypoints)
for (let i = 0; i < numberOfWaypoints; i++) {
waypoints.push(baseWaypoint.clone().rotateAround(origin, unitOfRotation * i))
}
return waypoints.map((w) => [
w.clone().add(this.position),
w.clone().normalize().multiplyScalar(enclosingRegularPolygonRadius),
])
}
collidables() {
const entityColliders = (this.game?.entities ?? []).filter((e) => e.id != this.id).map((e) => e.collider)
const terrainColliders = (this.game?.terrains ?? []).map((t) => t.colliders).flat()
return entityColliders.concat(terrainColliders)
}
isColliding(...colliders) {
return SATX.collideObjects(this.collider, colliders)
}
moveAction(x, y) {
this.#dest = new Vector2(x, y)
}
state() {
return {
...this,
position: {
x: this.x,
y: this.y,
},
}
}
teleport(x, y) {
this.position.set(x, y)
}
async takeStep(distanceTraveled = 0) {
const speed = (this.speed / (this.game?.tickBudget ?? 1000)) - distanceTraveled
if (this.#dest != null) {
const fixedDest = SATX.clamp(SATX.fixCollisions(this.#dest, this.collidables(), this.radius), this.game?.width, this.game?.height, this.radius)
if (this.#path.length < 1 || !this.#path.at(-1).equals(this.#dest)) {
console.time('pathfinding')
console.time('waypoints')
const waypoints = (this.game?.unadjustedWaypoints.map(([unadjusted, direction]) => unadjusted.clone().add(direction.clone().multiplyScalar(this.radius))) ?? []).concat([this.position, fixedDest])
console.timeEnd('waypoints')
console.time('graph')
const graph = Pathfind.buildGraph(waypoints, this.collidables(), this.radius)
console.timeEnd('graph')
console.time('path')
this.#path = Pathfind.shortestPath(graph, this.position, fixedDest)
console.timeEnd('path')
console.timeEnd('pathfinding')
}
if (this.#path.length > 0) {
const destination = this.#path.at(0)
const distance = this.position.clone().sub(destination).length()
const direction = destination.clone().sub(this.position).normalize()
const stepTaken = this.position.clone().add(direction.multiplyScalar(speed))
const position = distance <= speed ? destination : stepTaken
const collider = Entity.collider(position.x, position.y, this.radius)
const isColliding = SATX.collideObjects(collider, this.collidables())
if (!isColliding) {
this.position.copy(position)
}
if (this.position.equals(destination)) {
this.#path = this.#path.slice(1)
if (this.#path.length > 0) {
await this.takeStep(distance)
}
else {
this.#dest = null
}
}
}
}
}
async update() {
await Promise.allSettled([
this.takeStep(),
])
}
}