refactor: collisions
This commit is contained in:
parent
eaf144668a
commit
dd675efa5c
|
@ -11,7 +11,7 @@ export default class Collider extends Component {
|
|||
$$aabbs = [];
|
||||
$$collisionStart;
|
||||
$$collisionEnd;
|
||||
$$collidingWith = {};
|
||||
$$intersections = new Map();
|
||||
get aabb() {
|
||||
const {Position: {x: px, y: py}} = ecs.get(this.entity);
|
||||
return {
|
||||
|
@ -34,6 +34,105 @@ export default class Collider extends Component {
|
|||
}
|
||||
return aabbs;
|
||||
}
|
||||
endIntersections(other, intersections) {
|
||||
const otherEntity = ecs.get(other.entity);
|
||||
const thisEntity = ecs.get(this.entity);
|
||||
for (const intersection of intersections) {
|
||||
const [body, otherBody] = [
|
||||
intersection.entity.bodies[intersection.i],
|
||||
intersection.other.bodies[intersection.j],
|
||||
];
|
||||
if (this.$$collisionEnd) {
|
||||
const script = this.$$collisionEnd.clone();
|
||||
script.context.other = otherEntity;
|
||||
script.context.pair = [body, otherBody];
|
||||
thisEntity.Ticking.add(script.ticker());
|
||||
}
|
||||
if (other.$$collisionEnd) {
|
||||
const script = other.$$collisionEnd.clone();
|
||||
script.context.other = thisEntity;
|
||||
script.context.pair = [otherBody, body];
|
||||
otherEntity.Ticking.add(script.ticker());
|
||||
}
|
||||
}
|
||||
this.$$intersections.delete(other);
|
||||
other.$$intersections.delete(this);
|
||||
}
|
||||
checkCollision(other) {
|
||||
const otherEntity = ecs.get(other.entity);
|
||||
const thisEntity = ecs.get(this.entity);
|
||||
const intersections = this.intersectionsWith(other);
|
||||
const activeIntersections = this.$$intersections.get(other) || new Set();
|
||||
if (0 === intersections.length) {
|
||||
// had none; have none
|
||||
if (0 === activeIntersections.size) {
|
||||
return;
|
||||
}
|
||||
this.endIntersections(other, intersections);
|
||||
return;
|
||||
}
|
||||
for (const intersection of intersections) {
|
||||
// new pair - start
|
||||
const [body, otherBody] = [
|
||||
intersection.entity.bodies[intersection.i],
|
||||
intersection.other.bodies[intersection.j],
|
||||
];
|
||||
if (!activeIntersections.has(intersection)) {
|
||||
if (this.$$collisionStart) {
|
||||
const script = this.$$collisionStart.clone();
|
||||
script.context.other = otherEntity;
|
||||
script.context.pair = [body, otherBody];
|
||||
thisEntity.Ticking.add(script.ticker());
|
||||
}
|
||||
if (other.$$collisionStart) {
|
||||
const script = other.$$collisionStart.clone();
|
||||
script.context.other = thisEntity;
|
||||
script.context.pair = [otherBody, body];
|
||||
otherEntity.Ticking.add(script.ticker());
|
||||
}
|
||||
activeIntersections.add(intersection);
|
||||
}
|
||||
// undo restricted movement
|
||||
if (!body.unstoppable && otherBody.impassable) {
|
||||
const j = this.bodies.indexOf(body);
|
||||
const oj = other.bodies.indexOf(otherBody);
|
||||
const aabb = this.$$aabbs[j];
|
||||
const otherAabb = other.aabbs[oj];
|
||||
const {Position} = thisEntity;
|
||||
if (!intersects(
|
||||
{
|
||||
x0: aabb.x0 + Position.lastX,
|
||||
x1: aabb.x1 + Position.lastX,
|
||||
y0: aabb.y0 + Position.y,
|
||||
y1: aabb.y1 + Position.y,
|
||||
},
|
||||
otherAabb,
|
||||
)) {
|
||||
Position.x = Position.lastX
|
||||
}
|
||||
else if (!intersects(
|
||||
{
|
||||
x0: aabb.x0 + Position.x,
|
||||
x1: aabb.x1 + Position.x,
|
||||
y0: aabb.y0 + Position.lastY,
|
||||
y1: aabb.y1 + Position.lastY,
|
||||
},
|
||||
otherAabb,
|
||||
)) {
|
||||
Position.y = Position.lastY
|
||||
}
|
||||
else {
|
||||
Position.x = Position.lastX
|
||||
Position.y = Position.lastY
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (activeIntersections.size > 0) {
|
||||
this.$$intersections.set(other, activeIntersections);
|
||||
other.$$intersections.set(this, activeIntersections);
|
||||
}
|
||||
}
|
||||
closest(aabb) {
|
||||
const entity = ecs.get(this.entity);
|
||||
return Array.from(ecs.system('Colliders').within(aabb))
|
||||
|
@ -43,14 +142,12 @@ export default class Collider extends Component {
|
|||
});
|
||||
}
|
||||
destroy() {
|
||||
const entity = ecs.get(this.entity);
|
||||
for (const otherId in this.$$collidingWith) {
|
||||
const other = ecs.get(otherId);
|
||||
delete this.$$collidingWith[other.id];
|
||||
delete other.Collider.$$collidingWith[entity.id];
|
||||
for (const [other] of this.$$intersections) {
|
||||
other.$$intersections.delete(this);
|
||||
}
|
||||
this.$$intersections.clear();
|
||||
}
|
||||
isCollidingWith(other) {
|
||||
intersectionsWith(other) {
|
||||
const {aabb, aabbs} = this;
|
||||
const {aabb: otherAabb, aabbs: otherAabbs} = other;
|
||||
const intersections = [];
|
||||
|
@ -61,8 +158,14 @@ export default class Collider extends Component {
|
|||
const aabb = aabbs[i];
|
||||
for (const j in otherAabbs) {
|
||||
const otherAabb = otherAabbs[j];
|
||||
// todo accuracy
|
||||
if (intersects(aabb, otherAabb)) {
|
||||
intersections.push([this.bodies[i], other.bodies[j]]);
|
||||
intersections.push({
|
||||
entity: this,
|
||||
other,
|
||||
i,
|
||||
j,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
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 {
|
||||
|
@ -39,7 +38,7 @@ export default class Colliders extends System {
|
|||
}
|
||||
|
||||
tick() {
|
||||
const collisions = new Map();
|
||||
const checked = new Map();
|
||||
for (const entity of this.ecs.changed(['Direction'])) {
|
||||
if (!entity.Collider) {
|
||||
continue;
|
||||
|
@ -47,100 +46,32 @@ export default class Colliders extends System {
|
|||
entity.Collider.updateAabbs();
|
||||
}
|
||||
for (const entity of this.ecs.changed(['Position'])) {
|
||||
if (!entity.Collider) {
|
||||
continue;
|
||||
}
|
||||
this.updateHash(entity);
|
||||
}
|
||||
for (const entity of this.ecs.changed(['Position'])) {
|
||||
if (!entity.Collider) {
|
||||
continue;
|
||||
}
|
||||
collisions.set(entity, new Set());
|
||||
for (const other of this.within(entity.Collider.aabb)) {
|
||||
if (entity === other) {
|
||||
if (!checked.has(entity)) {
|
||||
checked.set(entity, new Set());
|
||||
}
|
||||
const within = this.within(entity.Collider.aabb);
|
||||
for (const other of within) {
|
||||
if (entity === other || !other.Collider) {
|
||||
continue;
|
||||
}
|
||||
if (!collisions.has(other)) {
|
||||
collisions.set(other, new Set());
|
||||
if (!checked.has(other)) {
|
||||
checked.set(other, new Set());
|
||||
}
|
||||
if (!other.Collider || collisions.get(other).has(entity)) {
|
||||
if (checked.get(entity).has(other)) {
|
||||
continue;
|
||||
}
|
||||
const intersections = entity.Collider.isCollidingWith(other.Collider);
|
||||
if (intersections.length > 0) {
|
||||
collisions.get(entity).add(other);
|
||||
if (!entity.Collider.$$collidingWith[other.id]) {
|
||||
entity.Collider.$$collidingWith[other.id] = true;
|
||||
other.Collider.$$collidingWith[entity.id] = true;
|
||||
if (entity.Collider.$$collisionStart) {
|
||||
const script = entity.Collider.$$collisionStart.clone();
|
||||
script.context.intersections = intersections;
|
||||
script.context.other = other;
|
||||
entity.Ticking.add(script.ticker());
|
||||
}
|
||||
if (other.Collider.$$collisionStart) {
|
||||
const script = other.Collider.$$collisionStart.clone();
|
||||
script.context.intersections = intersections
|
||||
.map(([l, r]) => [r, l]);
|
||||
script.context.other = entity;
|
||||
other.Ticking.add(script.ticker());
|
||||
}
|
||||
}
|
||||
for (const i in intersections) {
|
||||
const [body, otherBody] = intersections[i];
|
||||
const {unstoppable} = body;
|
||||
const {impassable} = otherBody;
|
||||
if (!unstoppable && 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (entity.Collider.$$collidingWith[other.id]) {
|
||||
if (entity.Collider.$$collisionEnd) {
|
||||
const script = entity.Collider.$$collisionEnd.clone();
|
||||
script.context.other = other;
|
||||
entity.Ticking.add(script.ticker());
|
||||
}
|
||||
if (other.Collider.$$collisionEnd) {
|
||||
const script = other.Collider.$$collisionEnd.clone();
|
||||
script.context.other = entity;
|
||||
other.Ticking.add(script.ticker());
|
||||
}
|
||||
delete entity.Collider.$$collidingWith[other.id];
|
||||
delete other.Collider.$$collidingWith[entity.id];
|
||||
}
|
||||
checked.get(other).add(entity);
|
||||
entity.Collider.checkCollision(other.Collider);
|
||||
}
|
||||
for (const [other, intersections] of entity.Collider.$$intersections) {
|
||||
if (!within.has(this.ecs.get(other.entity))) {
|
||||
entity.Collider.endIntersections(other, intersections);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user