UNPKG

ecspresso

Version:

A minimal Entity-Component-System library for typescript and javascript.

74 lines (73 loc) 3.48 kB
/** * 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 {};