单一数据源 + 懒加载缓存

This commit is contained in:
YHH
2025-09-30 23:37:47 +08:00
parent 952247def0
commit 632864b361
4 changed files with 116 additions and 95 deletions

View File

@@ -15,6 +15,7 @@ export type ComponentType<T extends Component = Component> = new (...args: any[]
export class ComponentRegistry { export class ComponentRegistry {
protected static readonly _logger = createLogger('ComponentStorage'); protected static readonly _logger = createLogger('ComponentStorage');
private static componentTypes = new Map<Function, number>(); private static componentTypes = new Map<Function, number>();
private static bitIndexToType = new Map<number, Function>();
private static componentNameToType = new Map<string, Function>(); private static componentNameToType = new Map<string, Function>();
private static componentNameToId = new Map<string, number>(); private static componentNameToId = new Map<string, number>();
private static maskCache = new Map<string, BitMask64Data>(); private static maskCache = new Map<string, BitMask64Data>();
@@ -40,6 +41,7 @@ export class ComponentRegistry {
const bitIndex = this.nextBitIndex++; const bitIndex = this.nextBitIndex++;
this.componentTypes.set(componentType, bitIndex); this.componentTypes.set(componentType, bitIndex);
this.bitIndexToType.set(bitIndex, componentType);
this.componentNameToType.set(typeName, componentType); this.componentNameToType.set(typeName, componentType);
this.componentNameToId.set(typeName, bitIndex); this.componentNameToId.set(typeName, bitIndex);
@@ -83,6 +85,15 @@ export class ComponentRegistry {
return this.componentTypes.has(componentType); return this.componentTypes.has(componentType);
} }
/**
* 通过位索引获取组件类型
* @param bitIndex 位索引
* @returns 组件类型构造函数或null
*/
public static getTypeByBitIndex(bitIndex: number): ComponentType | null {
return (this.bitIndexToType.get(bitIndex) as ComponentType) || null;
}
/** /**
* 通过名称获取组件类型 * 通过名称获取组件类型
* @param componentName 组件名称 * @param componentName 组件名称
@@ -196,6 +207,7 @@ export class ComponentRegistry {
*/ */
public static reset(): void { public static reset(): void {
this.componentTypes.clear(); this.componentTypes.clear();
this.bitIndexToType.clear();
this.componentNameToType.clear(); this.componentNameToType.clear();
this.componentNameToId.clear(); this.componentNameToId.clear();
this.maskCache.clear(); this.maskCache.clear();

View File

@@ -95,11 +95,6 @@ export class Entity {
*/ */
public readonly id: number; public readonly id: number;
/**
* 组件集合
*/
public readonly components: Component[] = [];
/** /**
* 所属场景引用 * 所属场景引用
*/ */
@@ -141,19 +136,20 @@ export class Entity {
private _updateOrder: number = 0; private _updateOrder: number = 0;
/** /**
* 组件位掩码 * 组件位掩码(用于快速 hasComponent 检查)
*/ */
private _componentMask: BitMask64Data = BitMask64Utils.clone(BitMask64Utils.ZERO); private _componentMask: BitMask64Data = BitMask64Utils.clone(BitMask64Utils.ZERO);
/** /**
* 按组件类型ID直址的稀疏数组 * 懒加载的组件数组缓存
*/ */
private _componentsByTypeId: (Component | undefined)[] = []; private _componentCache: Component[] | null = null;
/** /**
* typeId到components数组中密集索引的映射表 * 本地组件存储(用于没有 Scene 的 Entity
* 当 Entity 添加到 Scene 时,组件会迁移到 Scene 的 componentStorageManager
*/ */
private _componentDenseIndexByTypeId: number[] = []; private _localComponents: Map<ComponentType, Component> = new Map();
/** /**
* 构造函数 * 构造函数
@@ -174,6 +170,55 @@ export class Entity {
return this._isDestroyed; return this._isDestroyed;
} }
/**
* 获取组件数组(懒加载)
* @returns 只读的组件数组
*/
public get components(): readonly Component[] {
if (this._componentCache === null) {
this._rebuildComponentCache();
}
return this._componentCache!;
}
/**
* 从存储重建组件缓存
*/
private _rebuildComponentCache(): void {
const components: Component[] = [];
const mask = this._componentMask;
// 遍历位掩码中设置的位
for (let bitIndex = 0; bitIndex < 64; bitIndex++) {
const bitMask = BitMask64Utils.create(bitIndex);
if (BitMask64Utils.hasAny(mask, bitMask)) {
const componentType = ComponentRegistry.getTypeByBitIndex(bitIndex);
if (componentType) {
let component: Component | null = null;
// 优先从 Scene 存储获取
if (this.scene?.componentStorageManager) {
component = this.scene.componentStorageManager.getComponent(
this.id,
componentType
);
}
// Fallback 到本地存储
if (!component) {
component = this._localComponents.get(componentType) || null;
}
if (component) {
components.push(component);
}
}
}
}
this._componentCache = components;
}
/** /**
* 获取父实体 * 获取父实体
* @returns 父实体如果没有父实体则返回null * @returns 父实体如果没有父实体则返回null
@@ -327,17 +372,16 @@ export class Entity {
ComponentRegistry.register(componentType); ComponentRegistry.register(componentType);
} }
const typeId = ComponentRegistry.getBitIndex(componentType); // 存储到本地 Map
this._localComponents.set(componentType, component);
this._componentsByTypeId[typeId] = component;
const denseIndex = this.components.length;
this._componentDenseIndexByTypeId[typeId] = denseIndex;
this.components.push(component);
// 更新位掩码
const componentMask = ComponentRegistry.getBitMask(componentType); const componentMask = ComponentRegistry.getBitMask(componentType);
BitMask64Utils.orInPlace(this._componentMask, componentMask); BitMask64Utils.orInPlace(this._componentMask, componentMask);
// 使缓存失效
this._componentCache = null;
return component; return component;
} }
@@ -389,45 +433,21 @@ export class Entity {
* @returns 组件实例或null * @returns 组件实例或null
*/ */
public getComponent<T extends Component>(type: ComponentType<T>): T | null { public getComponent<T extends Component>(type: ComponentType<T>): T | null {
if (!ComponentRegistry.isRegistered(type)) { // 快速检查:位掩码
if (!this.hasComponent(type)) {
return null; return null;
} }
const mask = ComponentRegistry.getBitMask(type); // 优先从 Scene 存储获取
if (BitMask64Utils.hasNone(this._componentMask, mask)) { if (this.scene?.componentStorageManager) {
return null; const component = this.scene.componentStorageManager.getComponent(this.id, type);
} if (component) {
return component;
const typeId = ComponentRegistry.getBitIndex(type);
const component = this._componentsByTypeId[typeId];
if (component && component.constructor === type) {
return component as T;
}
if (this.scene && this.scene.componentStorageManager) {
const storageComponent = this.scene.componentStorageManager.getComponent(this.id, type);
if (storageComponent) {
this._componentsByTypeId[typeId] = storageComponent;
if (!this.components.includes(storageComponent)) {
const denseIndex = this.components.length;
this._componentDenseIndexByTypeId[typeId] = denseIndex;
this.components.push(storageComponent);
}
return storageComponent;
} }
} }
for (let i = 0; i < this.components.length; i++) { // Fallback 到本地存储
const component = this.components[i]; return (this._localComponents.get(type) as T) || null;
if (component instanceof type) {
this._componentsByTypeId[typeId] = component;
this._componentDenseIndexByTypeId[typeId] = i;
return component as T;
}
}
return null;
} }
@@ -478,30 +498,18 @@ export class Entity {
return; return;
} }
const typeId = ComponentRegistry.getBitIndex(componentType); const bitIndex = ComponentRegistry.getBitIndex(componentType);
this._componentsByTypeId[typeId] = undefined; // 从本地存储移除
this._localComponents.delete(componentType);
BitMask64Utils.clearBit(this._componentMask, typeId); // 更新位掩码
BitMask64Utils.clearBit(this._componentMask, bitIndex);
const denseIndex = this._componentDenseIndexByTypeId[typeId]; // 使缓存失效
if (denseIndex !== undefined && denseIndex < this.components.length) { this._componentCache = null;
const lastIndex = this.components.length - 1;
if (denseIndex !== lastIndex) {
const lastComponent = this.components[lastIndex];
this.components[denseIndex] = lastComponent;
const lastComponentType = lastComponent.constructor as ComponentType;
const lastTypeId = ComponentRegistry.getBitIndex(lastComponentType);
this._componentDenseIndexByTypeId[lastTypeId] = denseIndex;
}
this.components.pop();
}
this._componentDenseIndexByTypeId[typeId] = -1;
// 从 Scene 存储移除
if (this.scene && this.scene.componentStorageManager) { if (this.scene && this.scene.componentStorageManager) {
this.scene.componentStorageManager.removeComponent(this.id, componentType); this.scene.componentStorageManager.removeComponent(this.id, componentType);
} }
@@ -547,10 +555,15 @@ export class Entity {
public removeAllComponents(): void { public removeAllComponents(): void {
const componentsToRemove = [...this.components]; const componentsToRemove = [...this.components];
this._componentsByTypeId.length = 0; // 清除本地存储
this._componentDenseIndexByTypeId.length = 0; this._localComponents.clear();
// 清除位掩码
BitMask64Utils.clear(this._componentMask); BitMask64Utils.clear(this._componentMask);
// 使缓存失效
this._componentCache = null;
for (const component of componentsToRemove) { for (const component of componentsToRemove) {
const componentType = component.constructor as ComponentType; const componentType = component.constructor as ComponentType;
@@ -561,8 +574,6 @@ export class Entity {
component.onRemovedFromEntity(); component.onRemovedFromEntity();
} }
this.components.length = 0;
// 通知所有相关的QuerySystem组件已全部移除 // 通知所有相关的QuerySystem组件已全部移除
Entity.notifyQuerySystems(this); Entity.notifyQuerySystems(this);
} }
@@ -895,8 +906,7 @@ export class Entity {
childCount: number; childCount: number;
childIds: number[]; childIds: number[];
depth: number; depth: number;
indexMappingSize: number; cacheBuilt: boolean;
denseIndexMappingSize: number;
} { } {
return { return {
name: this.name, name: this.name,
@@ -912,8 +922,7 @@ export class Entity {
childCount: this._children.length, childCount: this._children.length,
childIds: this._children.map(c => c.id), childIds: this._children.map(c => c.id),
depth: this.getDepth(), depth: this.getDepth(),
indexMappingSize: this._componentsByTypeId.filter(c => c !== undefined).length, cacheBuilt: this._componentCache !== null
denseIndexMappingSize: this._componentDenseIndexByTypeId.filter(idx => idx !== -1 && idx !== undefined).length
}; };
} }
} }

View File

@@ -717,7 +717,7 @@ export class EntityDataCollector {
/** /**
* 提取组件详细信息 * 提取组件详细信息
*/ */
public extractComponentDetails(components: Component[]): Array<{ public extractComponentDetails(components: readonly Component[]): Array<{
typeName: string; typeName: string;
properties: Record<string, any>; properties: Record<string, any>;
}> { }> {

View File

@@ -266,7 +266,7 @@ describe('Entity - 组件缓存优化测试', () => {
expect(debugInfo.componentCount).toBe(2); expect(debugInfo.componentCount).toBe(2);
expect(debugInfo.componentTypes).toContain('TestPositionComponent'); expect(debugInfo.componentTypes).toContain('TestPositionComponent');
expect(debugInfo.componentTypes).toContain('TestHealthComponent'); expect(debugInfo.componentTypes).toContain('TestHealthComponent');
expect(debugInfo.indexMappingSize).toBe(2); expect(debugInfo.cacheBuilt).toBe(true);
}); });
}); });
}); });