@rpgjs/physic
Version:
A deterministic 2D top-down physics library for RPG, sandbox and MMO games
147 lines (146 loc) • 4.56 kB
JavaScript
import { AABB } from "./index4.js";
import { createCollider } from "./index18.js";
import { raycastCollider } from "./index20.js";
class QuadNode {
constructor(bounds) {
this.items = [];
this.children = null;
this.bounds = bounds;
}
}
class Quadtree {
constructor(worldBounds, capacity = 8, maxDepth = 8) {
this.entityMap = /* @__PURE__ */ new Map();
this.root = new QuadNode(worldBounds);
this.capacity = capacity;
this.maxDepth = maxDepth;
}
clear() {
this.root = new QuadNode(this.root.bounds);
this.entityMap.clear();
}
insert(entity) {
const collider = createCollider(entity);
if (!collider) return;
const bounds = collider.getBounds();
const item = { entity, bounds };
this.entityMap.set(entity, item);
this.insertItem(this.root, item, 0);
}
remove(entity) {
if (!this.entityMap.has(entity)) return;
this.entityMap.delete(entity);
const items = Array.from(this.entityMap.values());
this.clear();
for (const it of items) this.insertItem(this.root, it, 0);
}
update(entity) {
const it = this.entityMap.get(entity);
const collider = createCollider(entity);
if (!collider) return;
const newBounds = collider.getBounds();
if (!it) {
this.entityMap.set(entity, { entity, bounds: newBounds });
this.insertItem(this.root, { entity, bounds: newBounds }, 0);
return;
}
const b = it.bounds;
if (b.minX !== newBounds.minX || b.minY !== newBounds.minY || b.maxX !== newBounds.maxX || b.maxY !== newBounds.maxY) {
it.bounds = newBounds;
const items = Array.from(this.entityMap.values());
this.clear();
for (const item of items) this.insertItem(this.root, item, 0);
}
}
query(entity) {
const collider = createCollider(entity);
if (!collider) return /* @__PURE__ */ new Set();
const bounds = collider.getBounds();
const results = /* @__PURE__ */ new Set();
this.queryBounds(this.root, bounds, results);
results.delete(entity);
return results;
}
queryAABB(bounds) {
const results = /* @__PURE__ */ new Set();
this.queryBounds(this.root, bounds, results);
return results;
}
raycast(ray, mask, filter) {
const origin = ray.origin;
const end = ray.getPoint(ray.length);
const bounds = new AABB(
Math.min(origin.x, end.x),
Math.min(origin.y, end.y),
Math.max(origin.x, end.x),
Math.max(origin.y, end.y)
);
const candidates = this.queryAABB(bounds);
let closestHit = null;
for (const entity of candidates) {
if (mask !== void 0 && (entity.collisionCategory & mask) === 0) continue;
if (filter && !filter(entity)) continue;
const collider = createCollider(entity);
if (collider) {
const hit = raycastCollider(collider, ray.origin, ray.direction, ray.length);
if (hit) {
if (!closestHit || hit.distance < closestHit.distance) {
closestHit = hit;
}
}
}
}
return closestHit;
}
insertItem(node, item, depth) {
if (!node.children && (node.items.length < this.capacity || depth >= this.maxDepth)) {
node.items.push(item);
return;
}
if (!node.children) this.subdivide(node);
const child = this.findChild(node, item.bounds);
if (child) {
this.insertItem(child, item, depth + 1);
} else {
node.items.push(item);
}
}
queryBounds(node, bounds, out) {
if (!node.bounds.intersects(bounds)) return;
for (const it of node.items) {
if (it.bounds.intersects(bounds)) out.add(it.entity);
}
if (node.children) {
for (const c of node.children) this.queryBounds(c, bounds, out);
}
}
subdivide(node) {
const b = node.bounds;
const midX = (b.minX + b.maxX) * 0.5;
const midY = (b.minY + b.maxY) * 0.5;
node.children = [
new QuadNode(new AABB(b.minX, b.minY, midX, midY)),
// SW
new QuadNode(new AABB(midX, b.minY, b.maxX, midY)),
// SE
new QuadNode(new AABB(b.minX, midY, midX, b.maxY)),
// NW
new QuadNode(new AABB(midX, midY, b.maxX, b.maxY))
// NE
];
}
findChild(node, bounds) {
if (!node.children) return null;
for (const c of node.children) {
if (containsAABB(c.bounds, bounds)) return c;
}
return null;
}
}
function containsAABB(outer, inner) {
return outer.minX <= inner.minX && outer.maxX >= inner.maxX && outer.minY <= inner.minY && outer.maxY >= inner.maxY;
}
export {
Quadtree
};
//# sourceMappingURL=index15.js.map