import type { EntityId } from '../ecs/EntityId'; import type { Cell } from './Cell'; import type { GridMap } from './GridMap'; /** 空间网格索引(按格子桶加速选敌) */ export class SpatialGrid { /** 一维桶:index = row * cols + col,空桶为 undefined */ private readonly _buckets: (EntityId[] | undefined)[]; /** 实体当前行;未登记为 -1 */ private readonly _entityRow: number[] = []; /** 实体当前列;未登记为 -1 */ private readonly _entityCol: number[] = []; /** query 复用缓冲,仅在下次 query 前有效 */ private readonly _queryOut: EntityId[] = []; /** * @param _grid 关联的格子地图 */ constructor(private _grid: GridMap) { this._buckets = new Array(_grid.rows * _grid.cols); } /** * @param id 实体 ID * @param cell 当前所在格子 */ updateEntity(id: EntityId, cell: Cell): void { const prevRow = this._entityRow[id]; const prevCol = this._entityCol[id]; if (prevRow === cell.row && prevCol === cell.col) return; if (prevRow != null && prevRow >= 0) { this._removeFromBucket(id, prevRow, prevCol); } this._entityRow[id] = cell.row; this._entityCol[id] = cell.col; this._addToBucket(id, cell.row, cell.col); } /** 从空间索引中移除实体 */ removeEntity(id: EntityId): void { const row = this._entityRow[id]; if (row == null || row < 0) return; this._removeFromBucket(id, row, this._entityCol[id]); this._entityRow[id] = -1; this._entityCol[id] = -1; } /** * @param center 圆心格子 * @param radiusCells 半径(格) * @returns 范围内实体 ID */ queryRadius(center: Cell, radiusCells: number): EntityId[] { const out = this._queryOut; out.length = 0; const rows = this._grid.rows; const cols = this._grid.cols; const r = Math.ceil(radiusCells); const cr = center.row; const cc = center.col; for (let dr = -r; dr <= r; dr++) { const row = cr + dr; if (row < 0 || row >= rows) continue; for (let dc = -r; dc <= r; dc++) { const col = cc + dc; if (col < 0 || col >= cols) continue; const dRow = cr - row; const dCol = cc - col; if (Math.sqrt(dRow * dRow + dCol * dCol) > radiusCells) continue; this._appendBucket(out, row * cols + col); } } return out.slice(); } /** * @returns 全部已登记实体 */ queryAll(): EntityId[] { const out = this._queryOut; out.length = 0; const buckets = this._buckets; for (let i = 0; i < buckets.length; i++) { this._appendBucket(out, i); } return out.slice(); } /** * @param out 输出 * @param index 桶下标 */ private _appendBucket(out: EntityId[], index: number): void { const list = this._buckets[index]; if (!list) return; for (let i = 0; i < list.length; i++) out.push(list[i]); } /** * @param id 实体 * @param row 行 * @param col 列 */ private _addToBucket(id: EntityId, row: number, col: number): void { if (row < 0 || row >= this._grid.rows || col < 0 || col >= this._grid.cols) return; const index = row * this._grid.cols + col; let list = this._buckets[index]; if (!list) { list = []; this._buckets[index] = list; } for (let i = 0; i < list.length; i++) { if (list[i] === id) return; } list.push(id); } /** * @param id 实体 * @param row 行 * @param col 列 */ private _removeFromBucket(id: EntityId, row: number, col: number): void { const index = row * this._grid.cols + col; const list = this._buckets[index]; if (!list) return; for (let i = list.length - 1; i >= 0; i--) { if (list[i] === id) list.splice(i, 1); } if (list.length === 0) this._buckets[index] = undefined; } }