优化内部组件索引机制(更改为SparseSet索引)减少用户切换索引成本

修复内部系统初始化逻辑 - 不应该再onInitialize中初始内部entities,移动到initialize中
ci跳过cocos项目避免ci失败
soa开放更多安全类型接口
This commit is contained in:
YHH
2025-08-15 12:58:55 +08:00
parent 6730a5d625
commit c27d5022fd
24 changed files with 1866 additions and 435 deletions

View File

@@ -1,24 +1,12 @@
import { Entity } from '../Entity';
import { ComponentType } from './ComponentStorage';
import { ComponentSparseSet } from '../Utils/ComponentSparseSet';
/**
* 组件索引类型
*/
export enum IndexType {
/** 哈希索引 - 最快查找 */
HASH = 'hash',
/** 位图索引 - 内存高效 */
BITMAP = 'bitmap',
/** 排序索引 - 支持范围查询 */
SORTED = 'sorted'
}
/**
* 索引统计信息
*/
export interface IndexStats {
/** 索引类型 */
type: IndexType;
/** 索引大小 */
size: number;
/** 内存使用量(字节) */
@@ -35,8 +23,6 @@ export interface IndexStats {
* 组件索引接口
*/
export interface IComponentIndex {
/** 索引类型 */
readonly type: IndexType;
/** 添加实体到索引 */
addEntity(entity: Entity): void;
/** 从索引中移除实体 */
@@ -52,84 +38,45 @@ export interface IComponentIndex {
}
/**
* 哈希索引实现
* 通用组件索引实现
*
* 使用Map数据结构提供O(1)的查找性能。
* 适合大多数查询场景。
* 基于Sparse Set算法
* - O(1)的实体添加、删除、查找
* - 高效的位运算查询
* - 内存紧凑的存储结构
* - 缓存友好的遍历性能
*/
export class HashComponentIndex implements IComponentIndex {
public readonly type = IndexType.HASH;
export class ComponentIndex implements IComponentIndex {
private _componentToEntities = new Map<ComponentType, Set<Entity>>();
private _entityToComponents = new Map<Entity, Set<ComponentType>>();
/**
* 组件稀疏集合
*
* 核心存储结构,处理所有实体和组件的索引操作。
*/
private _sparseSet: ComponentSparseSet;
// 性能统计
private _queryCount = 0;
private _totalQueryTime = 0;
private _lastUpdated = Date.now();
private _setPool: Set<Entity>[] = [];
private _componentTypeSetPool: Set<ComponentType>[] = [];
constructor() {
this._sparseSet = new ComponentSparseSet();
}
public addEntity(entity: Entity): void {
if (entity.components.length === 0) {
const componentTypes = this._componentTypeSetPool.pop() || new Set<ComponentType>();
componentTypes.clear();
this._entityToComponents.set(entity, componentTypes);
this._lastUpdated = Date.now();
return;
}
const componentTypes = this._componentTypeSetPool.pop() || new Set<ComponentType>();
componentTypes.clear();
for (const component of entity.components) {
const componentType = component.constructor as ComponentType;
componentTypes.add(componentType);
let entities = this._componentToEntities.get(componentType);
if (!entities) {
entities = this._setPool.pop() || new Set<Entity>();
entities.clear();
this._componentToEntities.set(componentType, entities);
}
entities.add(entity);
}
this._entityToComponents.set(entity, componentTypes);
this._sparseSet.addEntity(entity);
this._lastUpdated = Date.now();
}
public removeEntity(entity: Entity): void {
const componentTypes = this._entityToComponents.get(entity);
if (!componentTypes) return;
for (const componentType of componentTypes) {
const entities = this._componentToEntities.get(componentType);
if (entities) {
entities.delete(entity);
if (entities.size === 0) {
this._componentToEntities.delete(componentType);
if (this._setPool.length < 50) {
entities.clear();
this._setPool.push(entities);
}
}
}
}
this._entityToComponents.delete(entity);
if (this._componentTypeSetPool.length < 50) {
componentTypes.clear();
this._componentTypeSetPool.push(componentTypes);
}
this._sparseSet.removeEntity(entity);
this._lastUpdated = Date.now();
}
public query(componentType: ComponentType): Set<Entity> {
const startTime = performance.now();
const entities = this._componentToEntities.get(componentType);
const result = entities ? new Set(entities) : new Set<Entity>();
const result = this._sparseSet.queryByComponent(componentType);
this._queryCount++;
this._totalQueryTime += performance.now() - startTime;
@@ -140,212 +87,16 @@ export class HashComponentIndex implements IComponentIndex {
public queryMultiple(componentTypes: ComponentType[], operation: 'AND' | 'OR'): Set<Entity> {
const startTime = performance.now();
if (componentTypes.length === 0) {
return new Set();
}
if (componentTypes.length === 1) {
return this.query(componentTypes[0]);
}
let result: Set<Entity>;
if (operation === 'AND') {
let smallestSet: Set<Entity> | undefined;
let smallestSize = Infinity;
for (const componentType of componentTypes) {
const entities = this._componentToEntities.get(componentType);
if (!entities || entities.size === 0) {
this._queryCount++;
this._totalQueryTime += performance.now() - startTime;
return new Set();
}
if (entities.size < smallestSize) {
smallestSize = entities.size;
smallestSet = entities;
}
}
result = new Set();
if (smallestSet) {
for (const entity of smallestSet) {
let hasAll = true;
for (const componentType of componentTypes) {
const entities = this._componentToEntities.get(componentType);
if (!entities || !entities.has(entity)) {
hasAll = false;
break;
}
}
if (hasAll) {
result.add(entity);
}
}
}
} else {
result = new Set();
for (const componentType of componentTypes) {
const entities = this._componentToEntities.get(componentType);
if (entities) {
for (const entity of entities) {
result.add(entity);
}
}
}
}
this._queryCount++;
this._totalQueryTime += performance.now() - startTime;
return result;
}
public clear(): void {
this._componentToEntities.clear();
this._entityToComponents.clear();
this._lastUpdated = Date.now();
}
public getStats(): IndexStats {
let memoryUsage = 0;
memoryUsage += this._componentToEntities.size * 64;
memoryUsage += this._entityToComponents.size * 64;
for (const entities of this._componentToEntities.values()) {
memoryUsage += entities.size * 8;
}
for (const components of this._entityToComponents.values()) {
memoryUsage += components.size * 8;
}
return {
type: this.type,
size: this._componentToEntities.size,
memoryUsage,
queryCount: this._queryCount,
avgQueryTime: this._queryCount > 0 ? this._totalQueryTime / this._queryCount : 0,
lastUpdated: this._lastUpdated
};
}
}
/**
* 位图索引实现
*
* 使用位操作进行快速集合运算,内存效率高。
* 适合有限组件类型和大量实体的场景。
*/
export class BitmapComponentIndex implements IComponentIndex {
public readonly type = IndexType.BITMAP;
private _componentTypeToBit = new Map<ComponentType, number>();
private _entityToBitmap = new Map<Entity, number>();
private _bitToEntities = new Map<number, Set<Entity>>();
private _nextBit = 0;
private _queryCount = 0;
private _totalQueryTime = 0;
private _lastUpdated = Date.now();
public addEntity(entity: Entity): void {
let bitmap = 0;
for (const component of entity.components) {
const componentType = component.constructor as ComponentType;
let bit = this._componentTypeToBit.get(componentType);
if (bit === undefined) {
bit = this._nextBit++;
this._componentTypeToBit.set(componentType, bit);
}
bitmap |= (1 << bit);
let entities = this._bitToEntities.get(1 << bit);
if (!entities) {
entities = new Set();
this._bitToEntities.set(1 << bit, entities);
}
entities.add(entity);
}
this._entityToBitmap.set(entity, bitmap);
this._lastUpdated = Date.now();
}
public removeEntity(entity: Entity): void {
const bitmap = this._entityToBitmap.get(entity);
if (bitmap === undefined) return;
// 从所有相关的位集合中移除实体
for (const [bitMask, entities] of this._bitToEntities) {
if ((bitmap & bitMask) !== 0) {
entities.delete(entity);
if (entities.size === 0) {
this._bitToEntities.delete(bitMask);
}
}
}
this._entityToBitmap.delete(entity);
this._lastUpdated = Date.now();
}
public query(componentType: ComponentType): Set<Entity> {
const startTime = performance.now();
const bit = this._componentTypeToBit.get(componentType);
if (bit === undefined) {
this._queryCount++;
this._totalQueryTime += performance.now() - startTime;
return new Set();
}
const result = new Set(this._bitToEntities.get(1 << bit) || []);
this._queryCount++;
this._totalQueryTime += performance.now() - startTime;
return result;
}
public queryMultiple(componentTypes: ComponentType[], operation: 'AND' | 'OR'): Set<Entity> {
const startTime = performance.now();
if (componentTypes.length === 0) {
return new Set();
}
let targetBitmap = 0;
const validBits: number[] = [];
for (const componentType of componentTypes) {
const bit = this._componentTypeToBit.get(componentType);
if (bit !== undefined) {
targetBitmap |= (1 << bit);
validBits.push(1 << bit);
}
}
const result = new Set<Entity>();
if (operation === 'AND') {
for (const [entity, entityBitmap] of this._entityToBitmap) {
if ((entityBitmap & targetBitmap) === targetBitmap) {
result.add(entity);
}
}
result = new Set();
} else if (componentTypes.length === 1) {
result = this.query(componentTypes[0]);
} else if (operation === 'AND') {
result = this._sparseSet.queryMultipleAnd(componentTypes);
} else {
for (const bitMask of validBits) {
const entities = this._bitToEntities.get(bitMask);
if (entities) {
for (const entity of entities) {
result.add(entity);
}
}
}
result = this._sparseSet.queryMultipleOr(componentTypes);
}
this._queryCount++;
@@ -354,29 +105,18 @@ export class BitmapComponentIndex implements IComponentIndex {
return result;
}
public clear(): void {
this._componentTypeToBit.clear();
this._entityToBitmap.clear();
this._bitToEntities.clear();
this._nextBit = 0;
this._sparseSet.clear();
this._lastUpdated = Date.now();
}
public getStats(): IndexStats {
let memoryUsage = 0;
memoryUsage += this._componentTypeToBit.size * 12;
memoryUsage += this._entityToBitmap.size * 12;
memoryUsage += this._bitToEntities.size * 64;
for (const entities of this._bitToEntities.values()) {
memoryUsage += entities.size * 8;
}
const memoryStats = this._sparseSet.getMemoryStats();
return {
type: this.type,
size: this._componentTypeToBit.size,
memoryUsage,
size: this._sparseSet.size,
memoryUsage: memoryStats.totalMemory,
queryCount: this._queryCount,
avgQueryTime: this._queryCount > 0 ? this._totalQueryTime / this._queryCount : 0,
lastUpdated: this._lastUpdated
@@ -384,130 +124,58 @@ export class BitmapComponentIndex implements IComponentIndex {
}
}
/**
* 智能组件索引管理器
* 组件索引管理器
*
* 根据使用模式自动选择最优的索引策略
* 支持动态切换索引类型以获得最佳性能。
* 使用统一的组件索引实现,自动优化查询性能
*/
export class ComponentIndexManager {
private _activeIndex: IComponentIndex;
private _indexHistory: Map<IndexType, IndexStats> = new Map();
private _autoOptimize = true;
private _optimizationThreshold = 1000;
private _index: ComponentIndex;
constructor(initialType: IndexType = IndexType.HASH) {
this._activeIndex = this.createIndex(initialType);
constructor() {
this._index = new ComponentIndex();
}
/**
* 添加实体到索引
*/
public addEntity(entity: Entity): void {
this._activeIndex.addEntity(entity);
if (this._autoOptimize && this._activeIndex.getStats().queryCount % 100 === 0) {
this.checkOptimization();
}
this._index.addEntity(entity);
}
/**
* 从索引中移除实体
*/
public removeEntity(entity: Entity): void {
this._activeIndex.removeEntity(entity);
this._index.removeEntity(entity);
}
/**
* 查询包含指定组件的实体
*/
public query(componentType: ComponentType): Set<Entity> {
return this._activeIndex.query(componentType);
return this._index.query(componentType);
}
/**
* 批量查询多个组件
*/
public queryMultiple(componentTypes: ComponentType[], operation: 'AND' | 'OR'): Set<Entity> {
return this._activeIndex.queryMultiple(componentTypes, operation);
return this._index.queryMultiple(componentTypes, operation);
}
/**
* 手动切换索引类型
*/
public switchIndexType(type: IndexType): void {
if (type === this._activeIndex.type) return;
this._indexHistory.set(this._activeIndex.type, this._activeIndex.getStats());
const oldIndex = this._activeIndex;
this._activeIndex = this.createIndex(type);
oldIndex.clear();
}
/**
* 启用/禁用自动优化
*/
public setAutoOptimize(enabled: boolean): void {
this._autoOptimize = enabled;
}
/**
* 获取当前索引统计信息
* 获取索引统计信息
*/
public getStats(): IndexStats {
return this._activeIndex.getStats();
}
/**
* 获取所有索引类型的历史统计信息
*/
public getAllStats(): Map<IndexType, IndexStats> {
const current = this._activeIndex.getStats();
return new Map([
...this._indexHistory,
[current.type, current]
]);
return this._index.getStats();
}
/**
* 清空索引
*/
public clear(): void {
this._activeIndex.clear();
}
/**
* 创建指定类型的索引
*/
private createIndex(type: IndexType): IComponentIndex {
switch (type) {
case IndexType.HASH:
return new HashComponentIndex();
case IndexType.BITMAP:
return new BitmapComponentIndex();
case IndexType.SORTED:
return new HashComponentIndex();
default:
return new HashComponentIndex();
}
}
/**
* 检查是否需要优化索引
*/
private checkOptimization(): void {
if (!this._autoOptimize) return;
const stats = this._activeIndex.getStats();
if (stats.queryCount < this._optimizationThreshold) return;
if (stats.avgQueryTime > 1.0 && stats.type !== IndexType.HASH) {
this.switchIndexType(IndexType.HASH);
} else if (stats.memoryUsage > 10 * 1024 * 1024 && stats.type !== IndexType.BITMAP) {
this.switchIndexType(IndexType.BITMAP);
}
this._index.clear();
}
}

View File

@@ -407,6 +407,86 @@ export class ComponentStorageManager {
private static readonly _logger = createLogger('ComponentStorage');
private storages = new Map<Function, ComponentStorage<any> | SoAStorage<any>>();
/**
* 检查组件类型是否启用SoA存储
* @param componentType 组件类型
* @returns 是否为SoA存储
*/
public isSoAStorage<T extends Component>(componentType: ComponentType<T>): boolean {
const storage = this.storages.get(componentType);
return storage instanceof SoAStorage;
}
/**
* 获取SoA存储器类型安全
* @param componentType 组件类型
* @returns SoA存储器或null
*/
public getSoAStorage<T extends Component>(componentType: ComponentType<T>): SoAStorage<T> | null {
const storage = this.getStorage(componentType);
return storage instanceof SoAStorage ? storage : null;
}
/**
* 直接获取SoA字段数组类型安全
* @param componentType 组件类型
* @param fieldName 字段名
* @returns TypedArray或null
*/
public getFieldArray<T extends Component>(
componentType: ComponentType<T>,
fieldName: string
): Float32Array | Float64Array | Int32Array | null {
const soaStorage = this.getSoAStorage(componentType);
return soaStorage ? soaStorage.getFieldArray(fieldName) : null;
}
/**
* 直接获取SoA字段数组类型安全带字段名检查
* @param componentType 组件类型
* @param fieldName 字段名(类型检查)
* @returns TypedArray或null
*/
public getTypedFieldArray<T extends Component, K extends keyof T>(
componentType: ComponentType<T>,
fieldName: K
): Float32Array | Float64Array | Int32Array | null {
const soaStorage = this.getSoAStorage(componentType);
return soaStorage ? soaStorage.getTypedFieldArray(fieldName) : null;
}
/**
* 获取SoA存储的活跃索引
* @param componentType 组件类型
* @returns 活跃索引数组或空数组
*/
public getActiveIndices<T extends Component>(componentType: ComponentType<T>): number[] {
const soaStorage = this.getSoAStorage(componentType);
return soaStorage ? soaStorage.getActiveIndices() : [];
}
/**
* 获取实体在SoA存储中的索引
* @param componentType 组件类型
* @param entityId 实体ID
* @returns 存储索引或undefined
*/
public getEntityIndex<T extends Component>(componentType: ComponentType<T>, entityId: number): number | undefined {
const soaStorage = this.getSoAStorage(componentType);
return soaStorage ? soaStorage.getEntityIndex(entityId) : undefined;
}
/**
* 根据索引获取实体ID
* @param componentType 组件类型
* @param index 存储索引
* @returns 实体ID或undefined
*/
public getEntityIdByIndex<T extends Component>(componentType: ComponentType<T>, index: number): number | undefined {
const soaStorage = this.getSoAStorage(componentType);
return soaStorage ? soaStorage.getEntityIdByIndex(index) : undefined;
}
/**
* 获取或创建组件存储器(默认原始存储)
* @param componentType 组件类型

View File

@@ -2,7 +2,7 @@ import { Entity } from '../Entity';
import { Component } from '../Component';
import { ComponentType } from './ComponentStorage';
import { IdentifierPool } from '../Utils/IdentifierPool';
import { ComponentIndexManager, IndexType } from './ComponentIndex';
import { ComponentIndexManager } from './ComponentIndex';
import { ArchetypeSystem } from './ArchetypeSystem';
import { DirtyTrackingSystem, DirtyFlag } from './DirtyTrackingSystem';
import { EventBus } from './EventBus';
@@ -339,7 +339,7 @@ export class EntityManager {
this._identifierPool = new IdentifierPool();
// 初始化性能优化系统
this._componentIndexManager = new ComponentIndexManager(IndexType.HASH);
this._componentIndexManager = new ComponentIndexManager();
this._archetypeSystem = new ArchetypeSystem();
this._dirtyTrackingSystem = new DirtyTrackingSystem();
this._eventBus = new EventBus(false);

View File

@@ -1,8 +1,8 @@
export {
ComponentIndexManager,
HashComponentIndex,
BitmapComponentIndex,
IndexType
ComponentIndexManager,
ComponentIndex,
IComponentIndex,
IndexStats
} from '../ComponentIndex';
export {

View File

@@ -6,7 +6,7 @@ import { createLogger } from '../../Utils/Logger';
import { getComponentTypeName } from '../Decorators';
import { ComponentPoolManager } from './ComponentPool';
import { ComponentIndexManager, IndexType } from './ComponentIndex';
import { ComponentIndexManager } from './ComponentIndex';
import { ArchetypeSystem, Archetype, ArchetypeQueryResult } from './ArchetypeSystem';
import { DirtyTrackingSystem, DirtyFlag } from './DirtyTrackingSystem';
@@ -126,7 +126,7 @@ export class QuerySystem {
// 初始化优化组件
this.componentPoolManager = ComponentPoolManager.getInstance();
// 初始化新的性能优化系统
this.componentIndexManager = new ComponentIndexManager(IndexType.HASH);
this.componentIndexManager = new ComponentIndexManager();
this.archetypeSystem = new ArchetypeSystem();
this.dirtyTrackingSystem = new DirtyTrackingSystem();
}
@@ -973,14 +973,6 @@ export class QuerySystem {
};
}
/**
* 切换组件索引类型
*
* @param indexType 新的索引类型
*/
public switchComponentIndexType(indexType: IndexType): void {
this.componentIndexManager.switchIndexType(indexType);
}
/**
* 配置脏标记系统
@@ -1000,11 +992,7 @@ export class QuerySystem {
this.cleanupCache();
const stats = this.componentIndexManager.getStats();
if (stats.avgQueryTime > 2.0 && stats.type !== IndexType.HASH) {
this.switchComponentIndexType(IndexType.HASH);
} else if (stats.memoryUsage > 50 * 1024 * 1024 && stats.type !== IndexType.BITMAP) {
this.switchComponentIndexType(IndexType.BITMAP);
}
// 基于SparseSet的索引已自动优化无需手动切换索引类型
}
/**

View File

@@ -473,6 +473,18 @@ export class SoAStorage<T extends Component> {
return this.fields.get(fieldName) || null;
}
public getTypedFieldArray<K extends keyof T>(fieldName: K): Float32Array | Float64Array | Int32Array | null {
return this.fields.get(String(fieldName)) || null;
}
public getEntityIndex(entityId: number): number | undefined {
return this.entityToIndex.get(entityId);
}
public getEntityIdByIndex(index: number): number | undefined {
return this.indexToEntity[index];
}
public size(): number {
return this._size;
}

View File

@@ -126,6 +126,11 @@ export abstract class EntitySystem implements ISystemBase {
this._initialized = true;
// 框架内部初始化:触发一次实体查询,以便正确跟踪现有实体
if (this.scene) {
this.queryEntities();
}
// 调用用户可重写的初始化方法
this.onInitialize();
}
@@ -136,10 +141,6 @@ export abstract class EntitySystem implements ISystemBase {
* 子类可以重写此方法进行初始化操作。
*/
protected onInitialize(): void {
// 初始化时触发一次实体查询,以便正确跟踪现有实体
if (this.scene) {
this.queryEntities();
}
// 子类可以重写此方法进行初始化
}

View File

@@ -0,0 +1,419 @@
import { Entity } from '../Entity';
import { ComponentType, ComponentRegistry } from '../Core/ComponentStorage';
import { IBigIntLike, BigIntFactory } from './BigIntCompatibility';
import { SparseSet } from './SparseSet';
import { Pool } from '../../Utils/Pool/Pool';
import { IPoolable } from '../../Utils/Pool/IPoolable';
/**
* 可池化的实体集合
*
* 实现IPoolable接口支持对象池复用以减少内存分配开销。
*/
class PoolableEntitySet extends Set<Entity> implements IPoolable {
constructor(...args: unknown[]) {
super();
}
reset(): void {
this.clear();
}
}
/**
* 组件稀疏集合实现
*
* 结合通用稀疏集合和组件位掩码
*
* 存储结构:
* - 稀疏集合存储实体
* - 位掩码数组存储组件信息
* - 组件类型映射表
*/
export class ComponentSparseSet {
/**
* 实体稀疏集合
*
* 存储所有拥有组件的实体提供O(1)的实体操作。
*/
private _entities: SparseSet<Entity>;
/**
* 组件位掩码数组
*
* 与实体稀疏集合的密集数组对应,存储每个实体的组件位掩码。
* 数组索引与稀疏集合的密集数组索引一一对应。
*/
private _componentMasks: IBigIntLike[] = [];
/**
* 组件类型到实体集合的映射
*
* 维护每个组件类型对应的实体集合,用于快速的单组件查询。
*/
private _componentToEntities = new Map<ComponentType, PoolableEntitySet>();
/**
* 实体集合对象池
*
* 使用core库的Pool系统来管理PoolableEntitySet对象的复用。
*/
private static _entitySetPool = Pool.getPool(PoolableEntitySet, 50, 512);
constructor() {
this._entities = new SparseSet<Entity>();
}
/**
* 添加实体到组件索引
*
* 分析实体的组件组成,生成位掩码,并更新所有相关索引。
*
* @param entity 要添加的实体
*/
public addEntity(entity: Entity): void {
// 如果实体已存在,先移除旧数据
if (this._entities.has(entity)) {
this.removeEntity(entity);
}
let componentMask = BigIntFactory.zero();
const entityComponents = new Set<ComponentType>();
// 分析实体组件并构建位掩码
for (const component of entity.components) {
const componentType = component.constructor as ComponentType;
entityComponents.add(componentType);
// 确保组件类型已注册
if (!ComponentRegistry.isRegistered(componentType)) {
ComponentRegistry.register(componentType);
}
// 获取组件位掩码并合并
const bitMask = ComponentRegistry.getBitMask(componentType);
componentMask = componentMask.or(bitMask);
}
// 添加实体到稀疏集合
this._entities.add(entity);
const entityIndex = this._entities.getIndex(entity)!;
// 确保位掩码数组有足够空间
while (this._componentMasks.length <= entityIndex) {
this._componentMasks.push(BigIntFactory.zero());
}
this._componentMasks[entityIndex] = componentMask;
// 更新组件类型到实体的映射
this.updateComponentMappings(entity, entityComponents, true);
}
/**
* 从组件索引中移除实体
*
* 清理实体相关的所有索引数据,保持数据结构的紧凑性。
*
* @param entity 要移除的实体
*/
public removeEntity(entity: Entity): void {
const entityIndex = this._entities.getIndex(entity);
if (entityIndex === undefined) {
return; // 实体不存在
}
// 获取实体的组件类型集合
const entityComponents = this.getEntityComponentTypes(entity);
// 更新组件类型到实体的映射
this.updateComponentMappings(entity, entityComponents, false);
// 从稀疏集合中移除实体
this._entities.remove(entity);
// 维护位掩码数组的紧凑性
const lastIndex = this._componentMasks.length - 1;
if (entityIndex !== lastIndex) {
// 将最后一个位掩码移动到当前位置
this._componentMasks[entityIndex] = this._componentMasks[lastIndex];
}
this._componentMasks.pop();
}
/**
* 查询包含指定组件的所有实体
*
* @param componentType 组件类型
* @returns 包含该组件的实体集合
*/
public queryByComponent(componentType: ComponentType): Set<Entity> {
const entities = this._componentToEntities.get(componentType);
return entities ? new Set(entities) : new Set<Entity>();
}
/**
* 多组件查询AND操作
*
* 查找同时包含所有指定组件的实体。
*
* @param componentTypes 组件类型数组
* @returns 满足条件的实体集合
*/
public queryMultipleAnd(componentTypes: ComponentType[]): Set<Entity> {
if (componentTypes.length === 0) {
return new Set<Entity>();
}
if (componentTypes.length === 1) {
return this.queryByComponent(componentTypes[0]);
}
// 构建目标位掩码
let targetMask = BigIntFactory.zero();
for (const componentType of componentTypes) {
if (!ComponentRegistry.isRegistered(componentType)) {
return new Set<Entity>(); // 未注册的组件类型,结果为空
}
const bitMask = ComponentRegistry.getBitMask(componentType);
targetMask = targetMask.or(bitMask);
}
const result = ComponentSparseSet._entitySetPool.obtain();
// 遍历所有实体,检查位掩码匹配
this._entities.forEach((entity, index) => {
const entityMask = this._componentMasks[index];
if ((entityMask.and(targetMask)).equals(targetMask)) {
result.add(entity);
}
});
return result;
}
/**
* 多组件查询OR操作
*
* 查找包含任意一个指定组件的实体。
*
* @param componentTypes 组件类型数组
* @returns 满足条件的实体集合
*/
public queryMultipleOr(componentTypes: ComponentType[]): Set<Entity> {
if (componentTypes.length === 0) {
return new Set<Entity>();
}
if (componentTypes.length === 1) {
return this.queryByComponent(componentTypes[0]);
}
// 构建目标位掩码
let targetMask = BigIntFactory.zero();
for (const componentType of componentTypes) {
if (ComponentRegistry.isRegistered(componentType)) {
const bitMask = ComponentRegistry.getBitMask(componentType);
targetMask = targetMask.or(bitMask);
}
}
if (targetMask.equals(BigIntFactory.zero())) {
return new Set<Entity>(); // 没有有效的组件类型
}
const result = ComponentSparseSet._entitySetPool.obtain();
// 遍历所有实体,检查位掩码匹配
this._entities.forEach((entity, index) => {
const entityMask = this._componentMasks[index];
if (!(entityMask.and(targetMask)).equals(BigIntFactory.zero())) {
result.add(entity);
}
});
return result;
}
/**
* 检查实体是否包含指定组件
*
* @param entity 实体
* @param componentType 组件类型
* @returns 是否包含该组件
*/
public hasComponent(entity: Entity, componentType: ComponentType): boolean {
const entityIndex = this._entities.getIndex(entity);
if (entityIndex === undefined) {
return false;
}
if (!ComponentRegistry.isRegistered(componentType)) {
return false;
}
const entityMask = this._componentMasks[entityIndex];
const componentMask = ComponentRegistry.getBitMask(componentType);
return !(entityMask.and(componentMask)).equals(BigIntFactory.zero());
}
/**
* 获取实体的组件位掩码
*
* @param entity 实体
* @returns 组件位掩码如果实体不存在则返回undefined
*/
public getEntityMask(entity: Entity): IBigIntLike | undefined {
const entityIndex = this._entities.getIndex(entity);
if (entityIndex === undefined) {
return undefined;
}
return this._componentMasks[entityIndex];
}
/**
* 获取所有实体
*
* @returns 所有实体的数组
*/
public getAllEntities(): Entity[] {
return this._entities.toArray();
}
/**
* 获取实体数量
*/
public get size(): number {
return this._entities.size;
}
/**
* 检查是否为空
*/
public get isEmpty(): boolean {
return this._entities.isEmpty;
}
/**
* 遍历所有实体
*
* @param callback 遍历回调函数
*/
public forEach(callback: (entity: Entity, mask: IBigIntLike, index: number) => void): void {
this._entities.forEach((entity, index) => {
callback(entity, this._componentMasks[index], index);
});
}
/**
* 清空所有数据
*/
public clear(): void {
this._entities.clear();
this._componentMasks.length = 0;
// 清理时将所有持有的实体集合返回到池中
for (const entitySet of this._componentToEntities.values()) {
ComponentSparseSet._entitySetPool.release(entitySet);
}
this._componentToEntities.clear();
}
/**
* 获取内存使用统计
*/
public getMemoryStats(): {
entitiesMemory: number;
masksMemory: number;
mappingsMemory: number;
totalMemory: number;
} {
const entitiesStats = this._entities.getMemoryStats();
const masksMemory = this._componentMasks.length * 16; // 估计每个BigInt 16字节
let mappingsMemory = this._componentToEntities.size * 16; // Map条目开销
for (const entitySet of this._componentToEntities.values()) {
mappingsMemory += entitySet.size * 8; // 每个实体引用8字节
}
return {
entitiesMemory: entitiesStats.totalMemory,
masksMemory,
mappingsMemory,
totalMemory: entitiesStats.totalMemory + masksMemory + mappingsMemory
};
}
/**
* 验证数据结构完整性
*/
public validate(): boolean {
// 检查稀疏集合的有效性
if (!this._entities.validate()) {
return false;
}
// 检查位掩码数组长度一致性
if (this._componentMasks.length !== this._entities.size) {
return false;
}
// 检查组件映射的一致性
const allMappedEntities = new Set<Entity>();
for (const entitySet of this._componentToEntities.values()) {
for (const entity of entitySet) {
allMappedEntities.add(entity);
}
}
// 验证映射中的实体都在稀疏集合中
for (const entity of allMappedEntities) {
if (!this._entities.has(entity)) {
return false;
}
}
return true;
}
/**
* 获取实体的组件类型集合
*/
private getEntityComponentTypes(entity: Entity): Set<ComponentType> {
const componentTypes = new Set<ComponentType>();
for (const component of entity.components) {
componentTypes.add(component.constructor as ComponentType);
}
return componentTypes;
}
/**
* 更新组件类型到实体的映射
*/
private updateComponentMappings(
entity: Entity,
componentTypes: Set<ComponentType>,
add: boolean
): void {
for (const componentType of componentTypes) {
let entities = this._componentToEntities.get(componentType);
if (add) {
if (!entities) {
entities = ComponentSparseSet._entitySetPool.obtain();
this._componentToEntities.set(componentType, entities);
}
entities.add(entity);
} else {
if (entities) {
entities.delete(entity);
if (entities.size === 0) {
this._componentToEntities.delete(componentType);
ComponentSparseSet._entitySetPool.release(entities);
}
}
}
}
}
}

View File

@@ -0,0 +1,310 @@
/**
* 稀疏集合实现
*
* 提供O(1)的插入、删除、查找操作,同时保持数据的紧凑存储。
* 使用密集数组存储实际数据,稀疏映射提供快速访问
*
* @template T 存储的数据类型
*
* @example
* ```typescript
* const sparseSet = new SparseSet<Entity>();
*
* sparseSet.add(entity1);
* sparseSet.add(entity2);
*
* if (sparseSet.has(entity1)) {
* sparseSet.remove(entity1);
* }
*
* sparseSet.forEach((entity, index) => {
* console.log(`Entity at index ${index}: ${entity.name}`);
* });
* ```
*/
export class SparseSet<T> {
/**
* 密集存储数组
*
* 连续存储所有有效数据,确保遍历时的缓存友好性。
*/
private _dense: T[] = [];
/**
* 稀疏映射表
*
* 将数据项映射到密集数组中的索引提供O(1)的查找性能。
*/
private _sparse = new Map<T, number>();
/**
* 添加元素到集合
*
* @param item 要添加的元素
* @returns 是否成功添加false表示元素已存在
*/
public add(item: T): boolean {
if (this._sparse.has(item)) {
return false; // 元素已存在
}
const index = this._dense.length;
this._dense.push(item);
this._sparse.set(item, index);
return true;
}
/**
* 从集合中移除元素
*
* 使用swap-and-pop技术保持数组紧凑性
* 1. 将要删除的元素与最后一个元素交换
* 2. 删除最后一个元素
* 3. 更新映射表
*
* @param item 要移除的元素
* @returns 是否成功移除false表示元素不存在
*/
public remove(item: T): boolean {
const index = this._sparse.get(item);
if (index === undefined) {
return false; // 元素不存在
}
const lastIndex = this._dense.length - 1;
// 如果不是最后一个元素,则与最后一个元素交换
if (index !== lastIndex) {
const lastItem = this._dense[lastIndex];
this._dense[index] = lastItem;
this._sparse.set(lastItem, index);
}
// 移除最后一个元素
this._dense.pop();
this._sparse.delete(item);
return true;
}
/**
* 检查元素是否存在于集合中
*
* @param item 要检查的元素
* @returns 元素是否存在
*/
public has(item: T): boolean {
return this._sparse.has(item);
}
/**
* 获取元素在密集数组中的索引
*
* @param item 要查询的元素
* @returns 索引如果元素不存在则返回undefined
*/
public getIndex(item: T): number | undefined {
return this._sparse.get(item);
}
/**
* 根据索引获取元素
*
* @param index 索引
* @returns 元素如果索引无效则返回undefined
*/
public getByIndex(index: number): T | undefined {
return this._dense[index];
}
/**
* 获取集合大小
*/
public get size(): number {
return this._dense.length;
}
/**
* 检查集合是否为空
*/
public get isEmpty(): boolean {
return this._dense.length === 0;
}
/**
* 遍历集合中的所有元素
*
* 保证遍历顺序与添加顺序一致(除非中间有删除操作)。
* 遍历性能优秀,因为数据在内存中连续存储。
*
* @param callback 遍历回调函数
*/
public forEach(callback: (item: T, index: number) => void): void {
for (let i = 0; i < this._dense.length; i++) {
callback(this._dense[i], i);
}
}
/**
* 映射集合中的所有元素
*
* @param callback 映射回调函数
* @returns 映射后的新数组
*/
public map<U>(callback: (item: T, index: number) => U): U[] {
const result: U[] = [];
for (let i = 0; i < this._dense.length; i++) {
result.push(callback(this._dense[i], i));
}
return result;
}
/**
* 过滤集合中的元素
*
* @param predicate 过滤条件
* @returns 满足条件的元素数组
*/
public filter(predicate: (item: T, index: number) => boolean): T[] {
const result: T[] = [];
for (let i = 0; i < this._dense.length; i++) {
if (predicate(this._dense[i], i)) {
result.push(this._dense[i]);
}
}
return result;
}
/**
* 查找第一个满足条件的元素
*
* @param predicate 查找条件
* @returns 找到的元素如果没有则返回undefined
*/
public find(predicate: (item: T, index: number) => boolean): T | undefined {
for (let i = 0; i < this._dense.length; i++) {
if (predicate(this._dense[i], i)) {
return this._dense[i];
}
}
return undefined;
}
/**
* 检查是否存在满足条件的元素
*
* @param predicate 检查条件
* @returns 是否存在满足条件的元素
*/
public some(predicate: (item: T, index: number) => boolean): boolean {
for (let i = 0; i < this._dense.length; i++) {
if (predicate(this._dense[i], i)) {
return true;
}
}
return false;
}
/**
* 检查是否所有元素都满足条件
*
* @param predicate 检查条件
* @returns 是否所有元素都满足条件
*/
public every(predicate: (item: T, index: number) => boolean): boolean {
for (let i = 0; i < this._dense.length; i++) {
if (!predicate(this._dense[i], i)) {
return false;
}
}
return true;
}
/**
* 获取密集数组的只读副本
*
* 返回数组的浅拷贝,确保外部无法直接修改内部数据。
*/
public getDenseArray(): readonly T[] {
return [...this._dense];
}
/**
* 获取密集数组的直接引用(内部使用)
*
* 警告:直接修改返回的数组会破坏数据结构的完整性。
* 仅在性能关键场景下使用,并确保不会修改数组内容。
*/
public getDenseArrayUnsafe(): readonly T[] {
return this._dense;
}
/**
* 清空集合
*/
public clear(): void {
this._dense.length = 0;
this._sparse.clear();
}
/**
* 转换为数组
*/
public toArray(): T[] {
return [...this._dense];
}
/**
* 转换为Set
*/
public toSet(): Set<T> {
return new Set(this._dense);
}
/**
* 获取内存使用统计信息
*/
public getMemoryStats(): {
denseArraySize: number;
sparseMapSize: number;
totalMemory: number;
} {
const denseArraySize = this._dense.length * 8; // 估计每个引用8字节
const sparseMapSize = this._sparse.size * 16; // 估计每个Map条目16字节
return {
denseArraySize,
sparseMapSize,
totalMemory: denseArraySize + sparseMapSize
};
}
/**
* 验证数据结构的完整性
*
* 调试用方法,检查内部数据结构是否一致。
*/
public validate(): boolean {
// 检查大小一致性
if (this._dense.length !== this._sparse.size) {
return false;
}
// 检查映射关系的正确性
for (let i = 0; i < this._dense.length; i++) {
const item = this._dense[i];
const mappedIndex = this._sparse.get(item);
if (mappedIndex !== i) {
return false;
}
}
// 检查稀疏映射中的所有项都在密集数组中
for (const [item, index] of this._sparse) {
if (index >= this._dense.length || this._dense[index] !== item) {
return false;
}
}
return true;
}
}

View File

@@ -5,4 +5,6 @@ export { IdentifierPool } from './IdentifierPool';
export { Matcher } from './Matcher';
export { Bits } from './Bits';
export { ComponentTypeManager } from './ComponentTypeManager';
export { BigIntFactory } from './BigIntCompatibility';
export { BigIntFactory } from './BigIntCompatibility';
export { SparseSet } from './SparseSet';
export { ComponentSparseSet } from './ComponentSparseSet';