world/entity.tstypescript
import { AABB } from "../physics/aabb";
import { SharedState } from "./state";
export class EntitiesManager {
/**
* Split chunks into 16x16x16 sections, so each chunk sections will contain a set of entities
*
* A key is `{chunkSectionX}:{chunkSectionY}:{chunkSectionZ}`
*/
public sections: Map<string, Set<number>> = new Map();
/**
* A map mapping entity ID to section key.
*/
public entityToSection: Map<number, string> = new Map();
constructor(private state: SharedState) { }
/**
* Clear data
*/
public wipe() {
this.sections = new Map();
this.entityToSection = new Map();
}
/**
* Convert real-world position into section key
*/
public static getSectionKey(x: number, y: number, z: number) {
// Divide 2^4 = 16
const sx = x >> 4, sy = y >> 4, sz = z >> 4;
return `${sx}:${sy}:${sz}`;
}
/**
* Add an entity (represented by ID)
*/
public add(entityId: number, x: number, y: number, z: number) {
const key = EntitiesManager.getSectionKey(x, y, z);
if (!this.sections.has(key)) this.sections.set(key, new Set());
this.sections.get(key)!.add(entityId);
this.entityToSection.set(entityId, key);
}
/**
* Remove entity
*/
public remove(entityId: number) {
const key = this.entityToSection.get(entityId);
if (key) {
this.sections.get(key)?.delete(entityId);
this.entityToSection.delete(entityId);
}
}
/**
* Update entity position
*/
public update(entityId: number, x: number, y: number, z: number) {
const newKey = EntitiesManager.getSectionKey(x, y, z);
if (this.entityToSection.get(entityId) !== newKey) {
this.remove(entityId);
this.add(entityId, x, y, z);
}
}
/**
* Query the entity in the section that intersec with BB
* @param queryBB The querying bounding box
* @returns
*/
public queryAABB(queryBB: AABB, exclude: number[] = []) {
this.state.checkReady();
const { minX, minY, minZ, maxX, maxY, maxZ } = queryBB;
const results: AABB[] = [];
const sx0 = minX >> 4, sx1 = maxX >> 4;
const sy0 = minY >> 4, sy1 = maxY >> 4;
const sz0 = minZ >> 4, sz1 = maxZ >> 4;
for (let sx = sx0; sx <= sx1; sx++) {
for (let sy = sy0; sy <= sy1; sy++) {
for (let sz = sz0; sz <= sz1; sz++) {
const bucket = this.sections.get(`${sx},${sy},${sz}`);
if (bucket) for (const id of bucket) {
if (exclude.includes(id)) continue;
const entity = this.state.world!.entities[id];
const entityBB = AABB.fromEntityType(entity!.type);
if (queryBB.isIntersect(entityBB))
results.push(entityBB);
}
}
}
}
return results;
}
}