151 lines
4.7 KiB
JavaScript
151 lines
4.7 KiB
JavaScript
import {System} from '@/ecs/index.js';
|
|
import {intersects} from '@/util/math.js';
|
|
import SpatialHash from '@/util/spatial-hash.js';
|
|
|
|
export default class Colliders extends System {
|
|
|
|
hash;
|
|
|
|
deindex(entities) {
|
|
super.deindex(entities);
|
|
for (const id of entities) {
|
|
this.hash.remove(id);
|
|
}
|
|
}
|
|
|
|
static get priority() {
|
|
return {
|
|
after: 'IntegratePhysics',
|
|
};
|
|
}
|
|
|
|
reindex(entities) {
|
|
for (const id of entities) {
|
|
if (1 === id) {
|
|
this.hash = new SpatialHash(this.ecs.get(1).AreaSize);
|
|
}
|
|
}
|
|
super.reindex(entities);
|
|
for (const id of entities) {
|
|
this.updateHash(this.ecs.get(id));
|
|
}
|
|
}
|
|
|
|
updateHash(entity) {
|
|
if (!entity.Collider) {
|
|
return;
|
|
}
|
|
this.hash.update(entity.Collider.aabb, entity.id);
|
|
}
|
|
|
|
tick() {
|
|
const seen = {};
|
|
for (const entity of this.ecs.changed(['Position'])) {
|
|
if (seen[entity.id]) {
|
|
continue;
|
|
}
|
|
seen[entity.id] = true;
|
|
if (!entity.Collider) {
|
|
continue;
|
|
}
|
|
const {collidingWith: wasCollidingWith} = entity.Collider;
|
|
entity.Collider.collidingWith = {};
|
|
this.updateHash(entity);
|
|
for (const other of this.within(entity.Collider.aabb)) {
|
|
if (seen[other.id]) {
|
|
continue;
|
|
}
|
|
seen[other.id] = true;
|
|
if (!other.Collider) {
|
|
continue;
|
|
}
|
|
delete other.Collider.collidingWith[entity.id];
|
|
const intersections = entity.Collider.isCollidingWith(other.Collider);
|
|
if (intersections.length > 0) {
|
|
entity.Collider.collidingWith[other.id] = true;
|
|
other.Collider.collidingWith[entity.id] = true;
|
|
if (!wasCollidingWith[other.id]) {
|
|
if (entity.Collider.collisionStartScriptInstance) {
|
|
const script = entity.Collider.collisionStartScriptInstance.clone();
|
|
script.context.intersections = intersections;
|
|
script.context.other = other;
|
|
entity.Ticking.addTickingPromise(script.tickingPromise());
|
|
}
|
|
if (other.Collider.collisionStartScriptInstance) {
|
|
const script = other.Collider.collisionStartScriptInstance.clone();
|
|
script.context.intersections = intersections
|
|
.map(([l, r]) => [r, l]);
|
|
script.context.other = entity;
|
|
other.Ticking.addTickingPromise(script.tickingPromise());
|
|
}
|
|
}
|
|
for (const i in intersections) {
|
|
const [body, otherBody] = intersections[i];
|
|
const {impassable} = otherBody;
|
|
if (impassable) {
|
|
const j = entity.Collider.bodies.indexOf(body);
|
|
const oj = other.Collider.bodies.indexOf(otherBody);
|
|
const aabb = entity.Collider.$$aabbs[j];
|
|
const otherAabb = other.Collider.aabbs[oj];
|
|
if (!intersects(
|
|
{
|
|
x0: aabb.x0 + entity.Position.lastX,
|
|
x1: aabb.x1 + entity.Position.lastX,
|
|
y0: aabb.y0 + entity.Position.y,
|
|
y1: aabb.y1 + entity.Position.y,
|
|
},
|
|
otherAabb,
|
|
)) {
|
|
entity.Position.x = entity.Position.lastX
|
|
}
|
|
else if (!intersects(
|
|
{
|
|
x0: aabb.x0 + entity.Position.x,
|
|
x1: aabb.x1 + entity.Position.x,
|
|
y0: aabb.y0 + entity.Position.lastY,
|
|
y1: aabb.y1 + entity.Position.lastY,
|
|
},
|
|
otherAabb,
|
|
)) {
|
|
entity.Position.y = entity.Position.lastY
|
|
}
|
|
else {
|
|
entity.Position.x = entity.Position.lastX
|
|
entity.Position.y = entity.Position.lastY
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
for (const otherId in wasCollidingWith) {
|
|
if (!entity.Collider.collidingWith[otherId]) {
|
|
const other = this.ecs.get(otherId);
|
|
if (!other || !other.Collider) {
|
|
continue;
|
|
}
|
|
if (entity.Collider.collisionEndScriptInstance) {
|
|
const script = entity.Collider.collisionEndScriptInstance.clone();
|
|
script.context.other = other;
|
|
entity.Ticking.addTickingPromise(script.tickingPromise());
|
|
}
|
|
if (other.Collider.collisionEndScriptInstance) {
|
|
const script = other.Collider.collisionEndScriptInstance.clone();
|
|
script.context.other = entity;
|
|
other.Ticking.addTickingPromise(script.tickingPromise());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
within(query) {
|
|
const within = new Set();
|
|
for (const id of this.hash.within(query)) {
|
|
within.add(this.ecs.get(id));
|
|
}
|
|
return within;
|
|
}
|
|
|
|
}
|