单一数据源 + 懒加载缓存
This commit is contained in:
@@ -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,9 +41,10 @@ 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);
|
||||||
|
|
||||||
return bitIndex;
|
return 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();
|
||||||
|
|||||||
@@ -95,11 +95,6 @@ export class Entity {
|
|||||||
*/
|
*/
|
||||||
public readonly id: number;
|
public readonly id: number;
|
||||||
|
|
||||||
/**
|
|
||||||
* 组件集合
|
|
||||||
*/
|
|
||||||
public readonly components: Component[] = [];
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 所属场景引用
|
* 所属场景引用
|
||||||
*/
|
*/
|
||||||
@@ -124,36 +119,37 @@ export class Entity {
|
|||||||
* 激活状态
|
* 激活状态
|
||||||
*/
|
*/
|
||||||
private _active: boolean = true;
|
private _active: boolean = true;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 实体标签
|
* 实体标签
|
||||||
*/
|
*/
|
||||||
private _tag: number = 0;
|
private _tag: number = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 启用状态
|
* 启用状态
|
||||||
*/
|
*/
|
||||||
private _enabled: boolean = true;
|
private _enabled: boolean = true;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 更新顺序
|
* 更新顺序
|
||||||
*/
|
*/
|
||||||
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
|
||||||
@@ -316,7 +361,7 @@ export class Entity {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 内部添加组件方法(不进行重复检查,用于初始化)
|
* 内部添加组件方法(不进行重复检查,用于初始化)
|
||||||
*
|
*
|
||||||
* @param component - 要添加的组件实例
|
* @param component - 要添加的组件实例
|
||||||
* @returns 添加的组件实例
|
* @returns 添加的组件实例
|
||||||
*/
|
*/
|
||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -384,50 +428,26 @@ export class Entity {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取指定类型的组件
|
* 获取指定类型的组件
|
||||||
*
|
*
|
||||||
* @param type - 组件类型
|
* @param type - 组件类型
|
||||||
* @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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -468,40 +488,28 @@ export class Entity {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 移除指定的组件
|
* 移除指定的组件
|
||||||
*
|
*
|
||||||
* @param component - 要移除的组件实例
|
* @param component - 要移除的组件实例
|
||||||
*/
|
*/
|
||||||
public removeComponent(component: Component): void {
|
public removeComponent(component: Component): void {
|
||||||
const componentType = component.constructor as ComponentType;
|
const componentType = component.constructor as ComponentType;
|
||||||
|
|
||||||
if (!ComponentRegistry.isRegistered(componentType)) {
|
if (!ComponentRegistry.isRegistered(componentType)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const typeId = ComponentRegistry.getBitIndex(componentType);
|
const bitIndex = ComponentRegistry.getBitIndex(componentType);
|
||||||
|
|
||||||
this._componentsByTypeId[typeId] = undefined;
|
|
||||||
|
|
||||||
BitMask64Utils.clearBit(this._componentMask, typeId);
|
|
||||||
|
|
||||||
const denseIndex = this._componentDenseIndexByTypeId[typeId];
|
|
||||||
if (denseIndex !== undefined && denseIndex < this.components.length) {
|
|
||||||
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;
|
|
||||||
|
|
||||||
|
// 从本地存储移除
|
||||||
|
this._localComponents.delete(componentType);
|
||||||
|
|
||||||
|
// 更新位掩码
|
||||||
|
BitMask64Utils.clearBit(this._componentMask, bitIndex);
|
||||||
|
|
||||||
|
// 使缓存失效
|
||||||
|
this._componentCache = null;
|
||||||
|
|
||||||
|
// 从 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);
|
||||||
}
|
}
|
||||||
@@ -509,7 +517,7 @@ export class Entity {
|
|||||||
if (component.onRemovedFromEntity) {
|
if (component.onRemovedFromEntity) {
|
||||||
component.onRemovedFromEntity();
|
component.onRemovedFromEntity();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Entity.eventBus) {
|
if (Entity.eventBus) {
|
||||||
Entity.eventBus.emitComponentRemoved({
|
Entity.eventBus.emitComponentRemoved({
|
||||||
timestamp: Date.now(),
|
timestamp: Date.now(),
|
||||||
@@ -546,14 +554,19 @@ 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;
|
||||||
|
|
||||||
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);
|
||||||
}
|
}
|
||||||
@@ -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
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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>;
|
||||||
}> {
|
}> {
|
||||||
|
|||||||
@@ -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);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user