ecspresso
Version:
A minimal Entity-Component-System library for typescript and javascript.
74 lines (73 loc) • 3.48 kB
TypeScript
/**
* Spatial Index Plugin for ECSpresso
*
* Provides a uniform-grid spatial hash for broadphase collision detection
* and proximity queries. Replaces O(n²) brute-force with O(n·d) where
* d = local density.
*
* Standalone usage: queryRect / queryRadius for proximity queries.
* Automatic acceleration: collision and physics2D plugins detect the
* spatialIndex resource at runtime and use it for broadphase when present.
*/
import type { TransformComponentTypes } from './transform';
import type { CollisionComponentTypes } from '../physics/collision';
import { type SpatialIndex } from '../../utils/spatial-hash';
export interface SpatialIndexResourceTypes {
spatialIndex: SpatialIndex;
}
type SpatialIndexComponentTypes = TransformComponentTypes & Pick<CollisionComponentTypes<string>, 'aabbCollider' | 'circleCollider'>;
export type SpatialIndexPhase = 'fixedUpdate' | 'postUpdate';
export interface SpatialIndexPluginOptions<G extends string = 'spatialIndex'> {
/** Cell size for the spatial hash grid (default: 64) */
cellSize?: number;
/** System group name (default: 'spatialIndex') */
systemGroup?: G;
/** Priority for rebuild systems (default: 2000, before collision) */
priority?: number;
/**
* Phases to register rebuild systems in (default: ['fixedUpdate', 'postUpdate']).
*
* When both phases are registered, the `postUpdate` rebuild is
* automatically skipped on cycles where:
* 1. The `fixedUpdate` rebuild already ran this cycle, AND
* 2. No entity hierarchy exists.
*
* In flat-hierarchy scenes `transform-propagation` copies
* `localTransform → worldTransform` unchanged, so the postUpdate
* grid would duplicate the fixedUpdate one. The skip is automatic
* and re-engages once any parent relationship is set, or whenever
* `fixedUpdate` doesn't run that cycle (sub-fixed-DT frames).
*
* Edge case: if you write to `worldTransform` directly between
* phases without using `transform-propagation` or entity hierarchy,
* the auto-skip will leave your writes unindexed. Set
* `phases: ['postUpdate']` explicitly to bypass the auto-skip.
*/
phases?: ReadonlyArray<SpatialIndexPhase>;
}
/**
* Create a spatial index plugin for ECSpresso.
*
* Provides a uniform-grid spatial hash that accelerates collision detection.
* When installed alongside the collision or physics2D plugins, they
* automatically use the spatial index for broadphase instead of O(n²)
* brute-force.
*
* Also provides proximity query methods for game logic (e.g. "find all
* enemies within 200 units").
*
* @example
* ```typescript
* const ecs = ECSpresso.create()
* .withPlugin(createTransformPlugin())
* .withPlugin(createCollisionPlugin({ layers }))
* .withPlugin(createSpatialIndexPlugin({ cellSize: 128 }))
* .build();
*
* // Proximity query in a system:
* const si = ecs.getResource('spatialIndex');
* const nearby = si.queryRadius(playerX, playerY, 200);
* ```
*/
export declare function createSpatialIndexPlugin<G extends string = 'spatialIndex'>(options?: SpatialIndexPluginOptions<G>): import("ecspresso").Plugin<import("ecspresso").WithResources<import("ecspresso").WithComponents<import("ecspresso").EmptyConfig, SpatialIndexComponentTypes>, SpatialIndexResourceTypes>, import("ecspresso").EmptyConfig, "spatial-index-rebuild-fixedUpdate" | "spatial-index-rebuild-postUpdate", G, never, never>;
export {};