125 lines
4.0 KiB
JavaScript
125 lines
4.0 KiB
JavaScript
import SAT from 'sat'
|
|
import { Vector2 } from 'three'
|
|
import Entity from './entity.js'
|
|
|
|
export default class SATX {
|
|
static clamp(vectorOrObject, maxX = Infinity, maxY = Infinity, radius = 0) {
|
|
let modified = null
|
|
if (vectorOrObject instanceof Vector2) {
|
|
modified = vectorOrObject.clone()
|
|
}
|
|
else if (vectorOrObject instanceof SAT.Vector) {
|
|
modified = new SAT.Vector(vectorOrObject.x, vectorOrObject.y)
|
|
}
|
|
else {
|
|
modified = { x: vectorOrObject.x, y: vectorOrObject.y }
|
|
}
|
|
|
|
modified.x = Math.min(Math.max(radius, vectorOrObject.x), (maxX ?? Infinity) - radius)
|
|
modified.y = Math.min(Math.max(radius, vectorOrObject.y), (maxY ?? Infinity) - radius)
|
|
|
|
return modified
|
|
}
|
|
|
|
static collideObject(collider1, collider2, result = null) {
|
|
if (collider1 instanceof SAT.Circle && collider2 instanceof SAT.Circle) {
|
|
return SAT.testCircleCircle(collider1, collider2, result)
|
|
}
|
|
|
|
if (collider1 instanceof SAT.Circle && collider2 instanceof SAT.Polygon) {
|
|
return SAT.testCirclePolygon(collider1, collider2, result)
|
|
}
|
|
|
|
if (collider1 instanceof SAT.Polygon && collider2 instanceof SAT.Circle) {
|
|
return SAT.testPolygonCircle(collider1, collider2, result)
|
|
}
|
|
|
|
if (collider1 instanceof SAT.Polygon && collider2 instanceof SAT.Polygon) {
|
|
return SAT.testPolygonPolygon(collider1, collider2, result)
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
static collideObjects(collider1, colliders) {
|
|
return colliders.some((c) => this.collideObject(collider1, 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)])
|
|
}
|
|
|
|
static satPolygonToVectors(polygon) {
|
|
const position = new Vector2(polygon.pos.x, polygon.pos.y)
|
|
return polygon.points.map((p) => new Vector2(p.x, p.y).add(position))
|
|
}
|
|
|
|
static vectorToFloat32Array(vector) {
|
|
const array = new Float32Array(2)
|
|
array[0] = vector.x
|
|
array[1] = vector.y
|
|
|
|
return array
|
|
}
|
|
|
|
static float32ArrayToVector(array) {
|
|
return new Vector2(array[0], array[1])
|
|
}
|
|
|
|
static float32ArrayWithIndexToVector(array, index) {
|
|
return new Vector2(array[index], array[index + 1])
|
|
}
|
|
}
|