use bounding boxes to optimize collision detection

This commit is contained in:
2025-01-19 14:24:19 +09:00
parent 0a4853aff9
commit e75c0d2944
10 changed files with 275 additions and 94 deletions
+9 -57
View File
@@ -1,8 +1,16 @@
import { Vector2 } from 'three'
import Entity from './entity.js'
import SAT from 'sat'
export default class SATX {
static bboxCheck(bbox1, bbox2) {
if (bbox1[0] <= bbox2[2]) { return false }
if (bbox1[1] <= bbox2[3]) { return false }
if (bbox1[2] >= bbox2[0]) { return false }
if (bbox1[3] >= bbox2[1]) { return false }
return true
}
static clamp(vectorOrObject, maxX = Infinity, maxY = Infinity, radius = 0) {
let modified = null
if (vectorOrObject instanceof Vector2) {
@@ -41,66 +49,10 @@ export default class SATX {
return false
}
static collideObjects(collider, colliders) {
return colliders.some((c) => this.collideObject(collider, c))
}
static collideObstacles(collider, obstacles) {
return obstacles.filter((obstacle) => obstacle.colliders().some((c) => this.collideObject(collider, c)))
}
static enclosingRegularPolygonRadius(numberOfVertices) {
return 1 / Math.cos(Math.PI / numberOfVertices)
}
static entityTunnel(fromX, fromY, toX, toY, radius = 0) {
if (radius <= 0) {
return this.line(fromX, fromY, toX, toY)
}
const sides = new Float32Array(5)
sides[0] = toX - fromX
sides[1] = toY - fromY
sides[4] = Math.hypot(sides[0], sides[1])
sides[2] = (sides[1] / sides[4]) * -radius // optimization: negation and swapping rotates
sides[3] = (sides[0] / sides[4]) * radius
return new SAT.Polygon(new SAT.Vector(fromX - sides[2], fromY - sides[3]), [
new SAT.Vector(),
new SAT.Vector(sides[0], sides[1]),
new SAT.Vector(sides[0] + (2 * sides[2]), sides[1] + (2 * sides[3])),
new SAT.Vector(2 * sides[2], 2 * sides[3]),
])
}
static fixCollisions(entityPosition, colliders, radius = 0, maxX = Infinity, maxY = Infinity) {
if (!this.collideObjects(Entity.collider(entityPosition.x, entityPosition.y, radius), colliders)) {
return entityPosition
}
// console.time('fixCollisions')
let direction = new Vector2(0, 5)
let multiplier = 1
const rotationSlices = 16
for (let limit = 1; limit <= 10000; limit++) {
const rads = (limit % rotationSlices) * 2 * Math.PI / rotationSlices
const offset = direction.clone().rotateAround(new Vector2(), rads).multiplyScalar(multiplier)
const position = SATX.clamp(entityPosition.clone().add(offset), maxX, maxY, radius)
if (!this.collideObjects(Entity.collider(position.x, position.y, radius), colliders)) {
// console.timeEnd('fixCollisions')
return position
}
if (limit % rotationSlices == 0) {
multiplier++
}
}
// console.timeEnd('fixCollisions')
console.error('ERROR: can\'t fix collision')
}
static line(fromX, fromY, toX, toY) {
return new SAT.Polygon(new SAT.Vector(fromX, fromY), [new SAT.Vector(), new SAT.Vector(toX - fromX, toY - fromY)])
}