@rpgjs/physic
Version:
A deterministic 2D top-down physics library for RPG, sandbox and MMO games
212 lines (211 loc) • 5.46 kB
JavaScript
import { Region } from "./index25.js";
import { AABB } from "./index4.js";
class RegionManager {
/**
* Creates a new region manager
*
* @param config - Manager configuration
*/
constructor(config) {
this.regions = [];
this.regionMap = /* @__PURE__ */ new Map();
this.entityRegionMap = /* @__PURE__ */ new Map();
this.config = {
worldBounds: config.worldBounds,
regionSize: config.regionSize,
overlap: config.overlap ?? 0,
autoActivate: config.autoActivate ?? true
};
this.createRegions();
}
/**
* Creates the grid of regions
*/
createRegions() {
const { worldBounds, regionSize, overlap } = this.config;
const worldWidth = worldBounds.getWidth();
const worldHeight = worldBounds.getHeight();
const cols = Math.ceil(worldWidth / regionSize);
const rows = Math.ceil(worldHeight / regionSize);
for (let row = 0; row < rows; row++) {
for (let col = 0; col < cols; col++) {
const minX = worldBounds.minX + col * regionSize;
const minY = worldBounds.minY + row * regionSize;
const maxX = Math.min(minX + regionSize, worldBounds.maxX);
const maxY = Math.min(minY + regionSize, worldBounds.maxY);
const bounds = new AABB(minX, minY, maxX, maxY);
const region = new Region({
bounds,
overlap,
active: !this.config.autoActivate
// Start inactive if auto-activate is enabled
});
const key = this.getRegionKey(col, row);
this.regions.push(region);
this.regionMap.set(key, region);
}
}
}
/**
* Gets a region key from grid coordinates
*
* @param col - Column index
* @param row - Row index
* @returns Region key string
*/
getRegionKey(col, row) {
return `${col},${row}`;
}
/**
* Gets the region containing a point
*
* @param point - Point to find region for
* @returns Region or null
*/
getRegionAt(point) {
for (const region of this.regions) {
if (region.contains(point)) {
return region;
}
}
return null;
}
/**
* Gets all regions that overlap with an AABB
*
* @param bounds - AABB to check
* @returns Array of overlapping regions
*/
getRegionsInBounds(bounds) {
const result = [];
for (const region of this.regions) {
if (region.getExpandedBounds().intersects(bounds)) {
result.push(region);
}
}
return result;
}
/**
* Adds an entity to the appropriate region
*
* @param entity - Entity to add
*/
addEntity(entity) {
const region = this.getRegionAt(entity.position);
if (region) {
region.addEntity(entity);
this.entityRegionMap.set(entity, region);
if (this.config.autoActivate) {
region.activate();
}
}
}
/**
* Removes an entity from its region
*
* @param entity - Entity to remove
*/
removeEntity(entity) {
const region = this.entityRegionMap.get(entity);
if (region) {
region.removeEntity(entity);
this.entityRegionMap.delete(entity);
if (this.config.autoActivate && region.getEntities().length === 0) {
region.deactivate();
}
}
}
/**
* Updates entity positions and migrates them between regions if needed
*/
updateEntities() {
const entitiesToMigrate = [];
for (const [entity, currentRegion] of this.entityRegionMap) {
if (!currentRegion.shouldContain(entity)) {
const newRegion = this.getRegionAt(entity.position);
if (newRegion && newRegion !== currentRegion) {
entitiesToMigrate.push({ entity, newRegion });
}
}
}
for (const { entity, newRegion } of entitiesToMigrate) {
const oldRegion = this.entityRegionMap.get(entity);
if (oldRegion) {
oldRegion.removeEntity(entity);
}
newRegion.addEntity(entity);
this.entityRegionMap.set(entity, newRegion);
if (this.config.autoActivate) {
newRegion.activate();
}
}
}
/**
* Steps all active regions
*/
step() {
this.updateEntities();
for (const region of this.regions) {
if (region.isActive()) {
region.step();
}
}
}
/**
* Gets all regions
*
* @returns Array of all regions
*/
getRegions() {
return [...this.regions];
}
/**
* Gets active regions
*
* @returns Array of active regions
*/
getActiveRegions() {
return this.regions.filter((r) => r.isActive());
}
/**
* Gets the region containing an entity
*
* @param entity - Entity to find region for
* @returns Region or null
*/
getEntityRegion(entity) {
return this.entityRegionMap.get(entity) ?? null;
}
/**
* Clears all entities from all regions
*/
clear() {
for (const region of this.regions) {
const entities = region.getEntities();
for (const entity of entities) {
region.removeEntity(entity);
}
}
this.entityRegionMap.clear();
}
/**
* Gets statistics about regions
*
* @returns Statistics object
*/
getStats() {
let totalEntities = 0;
for (const region of this.regions) {
totalEntities += region.getEntities().length;
}
return {
totalRegions: this.regions.length,
activeRegions: this.getActiveRegions().length,
totalEntities
};
}
}
export {
RegionManager
};
//# sourceMappingURL=index26.js.map