silphius/app/ecs-systems/colliders.js
2024-07-03 19:05:40 -05:00

118 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 {Ticking} = this.ecs.get(1);
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) {
entity.Collider.collisionStartScriptInstance.context.intersections = intersections;
entity.Collider.collisionStartScriptInstance.context.other = other;
Ticking.addTickingPromise(entity.Collider.collisionStartScriptInstance.tickingPromise());
}
if (other.Collider.collisionStartScriptInstance) {
other.Collider.collisionStartScriptInstance.context.intersections = intersections
.map(([l, r]) => [r, l]);
other.Collider.collisionStartScriptInstance.context.other = entity;
Ticking.addTickingPromise(other.Collider.collisionStartScriptInstance.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) {
entity.Collider.collisionEndScriptInstance.context.other = other;
Ticking.addTickingPromise(entity.Collider.collisionEndScriptInstance.tickingPromise());
}
if (other.Collider.collisionEndScriptInstance) {
other.Collider.collisionEndScriptInstance.context.other = entity;
Ticking.addTickingPromise(other.Collider.collisionEndScriptInstance.tickingPromise());
}
}
}
}
}
within(query) {
const within = new Set();
for (const id of this.hash.within(query)) {
within.add(this.ecs.get(id));
}
return within;
}
}