silphius/app/ecs-systems/colliders.js
2024-07-04 09:24:49 -05:00

121 lines
3.6 KiB
JavaScript

import {System} from '@/ecs/index.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;
}
entity.Collider.recalculateAabbs();
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 [, {impassable}] of intersections) {
if (impassable) {
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;
}
}