From 0b7e6237489461f40937ac6496360d06bfd47b25 Mon Sep 17 00:00:00 2001 From: YHH <359807859@qq.com> Date: Thu, 14 Aug 2025 18:35:03 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E7=BB=84=E4=BB=B6/=E7=B3=BB?= =?UTF-8?q?=E7=BB=9F=E8=A3=85=E9=A5=B0=E5=99=A8=E9=81=BF=E5=85=8D=E6=B7=B7?= =?UTF-8?q?=E6=B7=86=20=E6=9B=B4=E6=94=B9Set=E5=85=BC=E5=AE=B9web/?= =?UTF-8?q?=E5=B0=8F=E6=B8=B8=E6=88=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/core/src/ECS/Core/ArchetypeSystem.ts | 5 +- .../core/src/ECS/Core/ComponentStorage.ts | 15 +- .../ComponentStorage/ComponentRegistry.ts | 17 ++- packages/core/src/ECS/Core/QuerySystem.ts | 16 ++- .../core/src/ECS/Decorators/TypeDecorators.ts | 126 ++++++++++++++++ packages/core/src/ECS/Decorators/index.ts | 10 ++ packages/core/src/ECS/Entity.ts | 11 +- packages/core/src/ECS/Scene.ts | 7 +- packages/core/src/ECS/Systems/EntitySystem.ts | 136 +++++++++++++----- .../src/ECS/Utils/ComponentTypeManager.ts | 3 +- .../core/src/ECS/Utils/EntityProcessorList.ts | 3 +- packages/core/src/ECS/Utils/Matcher.ts | 9 +- packages/core/src/ECS/index.ts | 1 + .../src/Utils/Debug/ComponentDataCollector.ts | 7 +- .../src/Utils/Debug/DebugDataFormatter.ts | 3 +- packages/core/src/Utils/Debug/DebugManager.ts | 7 +- .../src/Utils/Debug/EntityDataCollector.ts | 19 +-- .../src/Utils/Debug/SystemDataCollector.ts | 5 +- .../ECS/Decorators/TypeDecorators.test.ts | 124 ++++++++++++++++ 19 files changed, 434 insertions(+), 90 deletions(-) create mode 100644 packages/core/src/ECS/Decorators/TypeDecorators.ts create mode 100644 packages/core/src/ECS/Decorators/index.ts create mode 100644 packages/core/tests/ECS/Decorators/TypeDecorators.test.ts diff --git a/packages/core/src/ECS/Core/ArchetypeSystem.ts b/packages/core/src/ECS/Core/ArchetypeSystem.ts index c1f889eb..62af1d54 100644 --- a/packages/core/src/ECS/Core/ArchetypeSystem.ts +++ b/packages/core/src/ECS/Core/ArchetypeSystem.ts @@ -1,5 +1,6 @@ import { Entity } from '../Entity'; import { ComponentType } from './ComponentStorage'; +import { getComponentTypeName } from '../Decorators'; /** * 原型标识符 @@ -103,7 +104,7 @@ export class ArchetypeSystem { public queryArchetypes(componentTypes: ComponentType[], operation: 'AND' | 'OR' = 'AND'): ArchetypeQueryResult { const startTime = performance.now(); - const cacheKey = `${operation}:${componentTypes.map(t => t.name).sort().join(',')}`; + const cacheKey = `${operation}:${componentTypes.map(t => getComponentTypeName(t)).sort().join(',')}`; // 检查缓存 const cached = this._queryCache.get(cacheKey); @@ -195,7 +196,7 @@ export class ArchetypeSystem { */ private generateArchetypeId(componentTypes: ComponentType[]): ArchetypeId { return componentTypes - .map(type => type.name) + .map(type => getComponentTypeName(type)) .sort() .join('|'); } diff --git a/packages/core/src/ECS/Core/ComponentStorage.ts b/packages/core/src/ECS/Core/ComponentStorage.ts index 0a5b41f7..c9b0be13 100644 --- a/packages/core/src/ECS/Core/ComponentStorage.ts +++ b/packages/core/src/ECS/Core/ComponentStorage.ts @@ -2,6 +2,7 @@ import { Component } from '../Component'; import { IBigIntLike, BigIntFactory } from '../Utils/BigIntCompatibility'; import { SoAStorage, EnableSoA, HighPrecision, Float64, Int32, SerializeMap, SerializeSet, SerializeArray, DeepCopy } from './SoAStorage'; import { createLogger } from '../../Utils/Logger'; +import { getComponentTypeName } from '../Decorators'; // 重新导出装饰器 export { EnableSoA, HighPrecision, Float64, Int32, SerializeMap, SerializeSet, SerializeArray, DeepCopy }; @@ -41,8 +42,8 @@ export class ComponentRegistry { const bitIndex = this.nextBitIndex++; this.componentTypes.set(componentType, bitIndex); - this.componentNameToType.set(componentType.name, componentType); - this.componentNameToId.set(componentType.name, bitIndex); + this.componentNameToType.set(getComponentTypeName(componentType), componentType); + this.componentNameToId.set(getComponentTypeName(componentType), bitIndex); return bitIndex; } @@ -54,7 +55,7 @@ export class ComponentRegistry { public static getBitMask(componentType: ComponentType): IBigIntLike { const bitIndex = this.componentTypes.get(componentType); if (bitIndex === undefined) { - throw new Error(`Component type ${componentType.name} is not registered`); + throw new Error(`Component type ${getComponentTypeName(componentType)} is not registered`); } return BigIntFactory.one().shiftLeft(bitIndex); } @@ -67,7 +68,7 @@ export class ComponentRegistry { public static getBitIndex(componentType: ComponentType): number { const bitIndex = this.componentTypes.get(componentType); if (bitIndex === undefined) { - throw new Error(`Component type ${componentType.name} is not registered`); + throw new Error(`Component type ${getComponentTypeName(componentType)} is not registered`); } return bitIndex; } @@ -229,7 +230,7 @@ export class ComponentStorage { public addComponent(entityId: number, component: T): void { // 检查实体是否已有此组件 if (this.entityToIndex.has(entityId)) { - throw new Error(`Entity ${entityId} already has component ${this.componentType.name}`); + throw new Error(`Entity ${entityId} already has component ${getComponentTypeName(this.componentType)}`); } let index: number; @@ -421,7 +422,7 @@ export class ComponentStorageManager { if (enableSoA) { // 使用SoA优化存储 storage = new SoAStorage(componentType); - ComponentStorageManager._logger.info(`为 ${componentType.name} 启用SoA优化(适用于大规模批量操作)`); + ComponentStorageManager._logger.info(`为 ${getComponentTypeName(componentType)} 启用SoA优化(适用于大规模批量操作)`); } else { // 默认使用原始存储 storage = new ComponentStorage(componentType); @@ -521,7 +522,7 @@ export class ComponentStorageManager { const stats = new Map(); for (const [componentType, storage] of this.storages.entries()) { - const typeName = (componentType as any).name || 'Unknown'; + const typeName = getComponentTypeName(componentType as ComponentType); stats.set(typeName, storage.getStats()); } diff --git a/packages/core/src/ECS/Core/ComponentStorage/ComponentRegistry.ts b/packages/core/src/ECS/Core/ComponentStorage/ComponentRegistry.ts index da5832e7..0b60f5a6 100644 --- a/packages/core/src/ECS/Core/ComponentStorage/ComponentRegistry.ts +++ b/packages/core/src/ECS/Core/ComponentStorage/ComponentRegistry.ts @@ -1,6 +1,7 @@ import { Component } from '../../Component'; import { IBigIntLike, BigIntFactory } from '../../Utils/BigIntCompatibility'; import { createLogger } from '../../../Utils/Logger'; +import { getComponentTypeName } from '../../Decorators'; /** * 组件类型定义 @@ -26,8 +27,11 @@ export class ComponentRegistry { * @returns 分配的位索引 */ public static register(componentType: ComponentType): number { + const typeName = getComponentTypeName(componentType); + if (this.componentTypes.has(componentType)) { - return this.componentTypes.get(componentType)!; + const existingIndex = this.componentTypes.get(componentType)!; + return existingIndex; } if (this.nextBitIndex >= this.maxComponents) { @@ -36,8 +40,9 @@ export class ComponentRegistry { const bitIndex = this.nextBitIndex++; this.componentTypes.set(componentType, bitIndex); - this.componentNameToType.set(componentType.name, componentType); - this.componentNameToId.set(componentType.name, bitIndex); + this.componentNameToType.set(typeName, componentType); + this.componentNameToId.set(typeName, bitIndex); + return bitIndex; } @@ -49,7 +54,8 @@ export class ComponentRegistry { public static getBitMask(componentType: ComponentType): IBigIntLike { const bitIndex = this.componentTypes.get(componentType); if (bitIndex === undefined) { - throw new Error(`Component type ${componentType.name} is not registered`); + const typeName = getComponentTypeName(componentType); + throw new Error(`Component type ${typeName} is not registered`); } return BigIntFactory.one().shiftLeft(bitIndex); } @@ -62,7 +68,8 @@ export class ComponentRegistry { public static getBitIndex(componentType: ComponentType): number { const bitIndex = this.componentTypes.get(componentType); if (bitIndex === undefined) { - throw new Error(`Component type ${componentType.name} is not registered`); + const typeName = getComponentTypeName(componentType); + throw new Error(`Component type ${typeName} is not registered`); } return bitIndex; } diff --git a/packages/core/src/ECS/Core/QuerySystem.ts b/packages/core/src/ECS/Core/QuerySystem.ts index 05e06980..f07f0771 100644 --- a/packages/core/src/ECS/Core/QuerySystem.ts +++ b/packages/core/src/ECS/Core/QuerySystem.ts @@ -3,6 +3,7 @@ import { Component } from '../Component'; import { ComponentRegistry, ComponentType } from './ComponentStorage'; import { IBigIntLike, BigIntFactory } from '../Utils/BigIntCompatibility'; import { createLogger } from '../../Utils/Logger'; +import { getComponentTypeName } from '../Decorators'; import { ComponentPoolManager } from './ComponentPool'; import { ComponentIndexManager, IndexType } from './ComponentIndex'; @@ -164,6 +165,7 @@ export class QuerySystem { this.archetypeSystem.addEntity(entity); this.dirtyTrackingSystem.markDirty(entity, DirtyFlag.COMPONENT_ADDED); + // 只有在非延迟模式下才立即清理缓存 if (!deferCacheClear) { this.clearQueryCache(); @@ -400,7 +402,7 @@ export class QuerySystem { this.queryStats.totalQueries++; // 生成缓存键 - const cacheKey = `all:${componentTypes.map(t => t.name).sort().join(',')}`; + const cacheKey = `all:${componentTypes.map(t => getComponentTypeName(t)).sort().join(',')}`; // 检查缓存 const cached = this.getFromCache(cacheKey); @@ -511,7 +513,7 @@ export class QuerySystem { const startTime = performance.now(); this.queryStats.totalQueries++; - const cacheKey = `any:${componentTypes.map(t => t.name).sort().join(',')}`; + const cacheKey = `any:${componentTypes.map(t => getComponentTypeName(t)).sort().join(',')}`; // 检查缓存 const cached = this.getFromCache(cacheKey); @@ -569,7 +571,7 @@ export class QuerySystem { const startTime = performance.now(); this.queryStats.totalQueries++; - const cacheKey = `none:${componentTypes.map(t => t.name).sort().join(',')}`; + const cacheKey = `none:${componentTypes.map(t => getComponentTypeName(t)).sort().join(',')}`; // 检查缓存 const cached = this.getFromCache(cacheKey); @@ -713,7 +715,7 @@ export class QuerySystem { const startTime = performance.now(); this.queryStats.totalQueries++; - const cacheKey = `component:${componentType.name}`; + const cacheKey = `component:${getComponentTypeName(componentType)}`; // 检查缓存 const cached = this.getFromCache(cacheKey); @@ -880,7 +882,7 @@ export class QuerySystem { mask = mask.or(bitMask); hasValidComponents = true; } catch (error) { - this._logger.warn(`组件类型 ${type.name} 未注册,跳过`); + this._logger.warn(`组件类型 ${getComponentTypeName(type)} 未注册,跳过`); } } @@ -958,7 +960,7 @@ export class QuerySystem { componentIndex: this.componentIndexManager.getStats(), archetypeSystem: this.archetypeSystem.getAllArchetypes().map(a => ({ id: a.id, - componentTypes: a.componentTypes.map(t => t.name), + componentTypes: a.componentTypes.map(t => getComponentTypeName(t)), entityCount: a.entities.length })), dirtyTracking: this.dirtyTrackingSystem.getStats() @@ -1151,7 +1153,7 @@ export class QueryBuilder { const bitMask = ComponentRegistry.getBitMask(type); mask = mask.or(bitMask); } catch (error) { - this._logger.warn(`组件类型 ${type.name} 未注册,跳过`); + this._logger.warn(`组件类型 ${getComponentTypeName(type)} 未注册,跳过`); } } return mask; diff --git a/packages/core/src/ECS/Decorators/TypeDecorators.ts b/packages/core/src/ECS/Decorators/TypeDecorators.ts new file mode 100644 index 00000000..db18e058 --- /dev/null +++ b/packages/core/src/ECS/Decorators/TypeDecorators.ts @@ -0,0 +1,126 @@ +import type { Component } from '../Component'; +import type { EntitySystem } from '../Systems/EntitySystem'; + +/** + * 存储组件类型名称的Symbol键 + */ +export const COMPONENT_TYPE_NAME = Symbol('ComponentTypeName'); + +/** + * 存储系统类型名称的Symbol键 + */ +export const SYSTEM_TYPE_NAME = Symbol('SystemTypeName'); + +/** + * 组件类型装饰器 + * 用于为组件类指定固定的类型名称,避免在代码混淆后失效 + * + * @param typeName 组件类型名称 + * @example + * ```typescript + * @ECSComponent('Position') + * class PositionComponent extends Component { + * x: number = 0; + * y: number = 0; + * } + * ``` + */ +export function ECSComponent(typeName: string) { + return function Component>(target: T): T { + if (!typeName || typeof typeName !== 'string') { + throw new Error('ECSComponent装饰器必须提供有效的类型名称'); + } + + // 在构造函数上存储类型名称 + (target as any)[COMPONENT_TYPE_NAME] = typeName; + + return target; + }; +} + +/** + * 系统类型装饰器 + * 用于为系统类指定固定的类型名称,避免在代码混淆后失效 + * + * @param typeName 系统类型名称 + * @example + * ```typescript + * @ECSSystem('Movement') + * class MovementSystem extends EntitySystem { + * protected process(entities: Entity[]): void { + * // 系统逻辑 + * } + * } + * ``` + */ +export function ECSSystem(typeName: string) { + return function EntitySystem>(target: T): T { + if (!typeName || typeof typeName !== 'string') { + throw new Error('ECSSystem装饰器必须提供有效的类型名称'); + } + + // 在构造函数上存储类型名称 + (target as any)[SYSTEM_TYPE_NAME] = typeName; + + return target; + }; +} + +/** + * 获取组件类型的名称,优先使用装饰器指定的名称 + * + * @param componentType 组件构造函数 + * @returns 组件类型名称 + */ +export function getComponentTypeName( + componentType: new (...args: any[]) => T +): string { + // 优先使用装饰器指定的名称 + const decoratorName = (componentType as any)[COMPONENT_TYPE_NAME]; + if (decoratorName) { + return decoratorName; + } + + // 回退到constructor.name + return componentType.name || 'UnknownComponent'; +} + +/** + * 获取系统类型的名称,优先使用装饰器指定的名称 + * + * @param systemType 系统构造函数 + * @returns 系统类型名称 + */ +export function getSystemTypeName( + systemType: new (...args: any[]) => T +): string { + // 优先使用装饰器指定的名称 + const decoratorName = (systemType as any)[SYSTEM_TYPE_NAME]; + if (decoratorName) { + return decoratorName; + } + + // 回退到constructor.name + return systemType.name || 'UnknownSystem'; +} + +/** + * 从组件实例获取类型名称 + * + * @param component 组件实例 + * @returns 组件类型名称 + */ +export function getComponentInstanceTypeName(component: Component): string { + return getComponentTypeName(component.constructor as new (...args: any[]) => Component); +} + +/** + * 从系统实例获取类型名称 + * + * @param system 系统实例 + * @returns 系统类型名称 + */ +export function getSystemInstanceTypeName(system: EntitySystem): string { + return getSystemTypeName(system.constructor as new (...args: any[]) => EntitySystem); +} + diff --git a/packages/core/src/ECS/Decorators/index.ts b/packages/core/src/ECS/Decorators/index.ts new file mode 100644 index 00000000..e5af3266 --- /dev/null +++ b/packages/core/src/ECS/Decorators/index.ts @@ -0,0 +1,10 @@ +export { + ECSComponent, + ECSSystem, + getComponentTypeName, + getSystemTypeName, + getComponentInstanceTypeName, + getSystemInstanceTypeName, + COMPONENT_TYPE_NAME, + SYSTEM_TYPE_NAME +} from './TypeDecorators'; \ No newline at end of file diff --git a/packages/core/src/ECS/Entity.ts b/packages/core/src/ECS/Entity.ts index 8d077db9..103f249d 100644 --- a/packages/core/src/ECS/Entity.ts +++ b/packages/core/src/ECS/Entity.ts @@ -3,6 +3,7 @@ import { ComponentRegistry, ComponentType } from './Core/ComponentStorage'; import { EventBus } from './Core/EventBus'; import { IBigIntLike, BigIntFactory } from './Utils/BigIntCompatibility'; import { createLogger } from '../Utils/Logger'; +import { getComponentInstanceTypeName, getComponentTypeName } from './Decorators'; // Forward declaration to avoid circular dependency interface IScene { @@ -382,7 +383,7 @@ export class Entity { // 检查是否已有此类型的组件 if (this.hasComponent(componentType)) { - throw new Error(`Entity ${this.name} already has component ${componentType.name}`); + throw new Error(`Entity ${this.name} already has component ${getComponentTypeName(componentType)}`); } // 使用内部方法添加组件 @@ -404,7 +405,7 @@ export class Entity { entityId: this.id, entityName: this.name, entityTag: this.tag?.toString(), - componentType: componentType.name, + componentType: getComponentTypeName(componentType), component: component }); } @@ -558,7 +559,7 @@ export class Entity { entityId: this.id, entityName: this.name, entityTag: this.tag?.toString(), - componentType: componentType.name, + componentType: getComponentTypeName(componentType), component: component }); } @@ -636,7 +637,7 @@ export class Entity { addedComponents.push(this.addComponent(component)); } catch (error) { // 如果某个组件添加失败,继续添加其他组件 - Entity._logger.warn(`添加组件失败 ${component.constructor.name}:`, error); + Entity._logger.warn(`添加组件失败 ${getComponentInstanceTypeName(component)}:`, error); } } @@ -1000,7 +1001,7 @@ export class Entity { activeInHierarchy: this.activeInHierarchy, destroyed: this._isDestroyed, componentCount: this.components.length, - componentTypes: this.components.map(c => c.constructor.name), + componentTypes: this.components.map(c => getComponentInstanceTypeName(c)), componentMask: this._componentMask.toString(2), // 二进制表示 parentId: this._parent?.id || null, childCount: this._children.length, diff --git a/packages/core/src/ECS/Scene.ts b/packages/core/src/ECS/Scene.ts index 91ab3502..c5276228 100644 --- a/packages/core/src/ECS/Scene.ts +++ b/packages/core/src/ECS/Scene.ts @@ -8,6 +8,7 @@ import { QuerySystem } from './Core/QuerySystem'; import { TypeSafeEventSystem } from './Core/EventSystem'; import { EventBus } from './Core/EventBus'; import { IScene, ISceneConfig } from './IScene'; +import { getComponentInstanceTypeName, getSystemInstanceTypeName } from './Decorators'; /** * 游戏场景默认实现类 @@ -392,7 +393,7 @@ export class Scene implements IScene { componentStats: Map; } { return { - name: this.constructor.name, + name: this.name || this.constructor.name, entityCount: this.entities.count, processorCount: this.entityProcessors.count, isRunning: this._didSceneBegin, @@ -400,10 +401,10 @@ export class Scene implements IScene { name: entity.name, id: entity.id, componentCount: entity.components.length, - componentTypes: entity.components.map(c => c.constructor.name) + componentTypes: entity.components.map(c => getComponentInstanceTypeName(c)) })), processors: this.entityProcessors.processors.map(processor => ({ - name: processor.constructor.name, + name: getSystemInstanceTypeName(processor), updateOrder: processor.updateOrder, entityCount: (processor as any)._entities?.length || 0 })), diff --git a/packages/core/src/ECS/Systems/EntitySystem.ts b/packages/core/src/ECS/Systems/EntitySystem.ts index 8f46c238..4fdbf57f 100644 --- a/packages/core/src/ECS/Systems/EntitySystem.ts +++ b/packages/core/src/ECS/Systems/EntitySystem.ts @@ -4,6 +4,7 @@ import { Matcher } from '../Utils/Matcher'; import type { Scene } from '../Scene'; import type { ISystemBase } from '../../Types'; import type { QuerySystem } from '../Core/QuerySystem'; +import { getSystemInstanceTypeName } from '../Decorators'; /** * 实体系统的基类 @@ -36,7 +37,6 @@ export abstract class EntitySystem implements ISystemBase { private _initialized: boolean = false; private _matcher: Matcher; private _trackedEntities: Set = new Set(); - private _lastQueryResult: Entity[] = []; /** * 获取系统处理的实体列表(动态查询) @@ -79,7 +79,7 @@ export abstract class EntitySystem implements ISystemBase { constructor(matcher?: Matcher) { this._matcher = matcher ? matcher : Matcher.empty(); - this._systemName = this.constructor.name; + this._systemName = getSystemInstanceTypeName(this); } private _scene: Scene | null = null; @@ -101,7 +101,7 @@ export abstract class EntitySystem implements ISystemBase { public get matcher(): Matcher { return this._matcher; } - + /** * 设置更新时序 * @param order 更新时序 @@ -123,9 +123,9 @@ export abstract class EntitySystem implements ISystemBase { if (this._initialized) { return; } - + this._initialized = true; - + // 调用用户可重写的初始化方法 this.onInitialize(); } @@ -151,7 +151,6 @@ export abstract class EntitySystem implements ISystemBase { public reset(): void { this._initialized = false; this._trackedEntities.clear(); - this._lastQueryResult = []; } /** @@ -159,14 +158,14 @@ export abstract class EntitySystem implements ISystemBase { */ private queryEntities(): Entity[] { if (!this.scene?.querySystem || !this._matcher) { - this._lastQueryResult = []; return []; } const condition = this._matcher.getCondition(); const querySystem = this.scene.querySystem; let currentEntities: Entity[] = []; - + + // 空条件返回所有实体 if (this._matcher.isEmpty()) { currentEntities = querySystem.getAllEntities(); @@ -180,8 +179,8 @@ export abstract class EntitySystem implements ISystemBase { // 检查实体变化并触发回调 this.updateEntityTracking(currentEntities); - - this._lastQueryResult = currentEntities; + + return currentEntities; } @@ -189,14 +188,15 @@ export abstract class EntitySystem implements ISystemBase { * 检查是否为单一条件查询 */ private isSingleCondition(condition: any): boolean { - const conditionCount = + const conditionCount = (condition.all.length > 0 ? 1 : 0) + (condition.any.length > 0 ? 1 : 0) + (condition.none.length > 0 ? 1 : 0) + (condition.tag !== undefined ? 1 : 0) + (condition.name !== undefined ? 1 : 0) + (condition.component !== undefined ? 1 : 0); - + + return conditionCount === 1; } @@ -208,30 +208,30 @@ export abstract class EntitySystem implements ISystemBase { if (condition.tag !== undefined) { return querySystem.queryByTag(condition.tag).entities; } - + // 按名称查询 if (condition.name !== undefined) { return querySystem.queryByName(condition.name).entities; } - + // 单组件查询 if (condition.component !== undefined) { return querySystem.queryByComponent(condition.component).entities; } - + // 基础组件查询 if (condition.all.length > 0 && condition.any.length === 0 && condition.none.length === 0) { return querySystem.queryAll(...condition.all).entities; } - + if (condition.all.length === 0 && condition.any.length > 0 && condition.none.length === 0) { return querySystem.queryAny(...condition.any).entities; } - + if (condition.all.length === 0 && condition.any.length === 0 && condition.none.length > 0) { return querySystem.queryNone(...condition.none).entities; } - + return []; } @@ -241,6 +241,7 @@ export abstract class EntitySystem implements ISystemBase { private executeComplexQuery(condition: any, querySystem: QuerySystem): Entity[] { let result: Set | null = null; + // 1. 应用标签条件作为基础集合 if (condition.tag !== undefined) { const tagResult = querySystem.queryByTag(condition.tag); @@ -251,9 +252,18 @@ export abstract class EntitySystem implements ISystemBase { if (condition.name !== undefined) { const nameResult = querySystem.queryByName(condition.name); const nameSet = new Set(nameResult.entities); - + if (result) { - result = new Set([...result].filter(e => nameSet.has(e))); + const intersection = []; + for (const entity of result) { + for (const nameEntity of nameSet) { + if (entity === nameEntity || entity.id === nameEntity.id) { + intersection.push(entity); + break; + } + } + } + result = new Set(intersection); } else { result = nameSet; } @@ -263,9 +273,18 @@ export abstract class EntitySystem implements ISystemBase { if (condition.component !== undefined) { const componentResult = querySystem.queryByComponent(condition.component); const componentSet = new Set(componentResult.entities); - + if (result) { - result = new Set([...result].filter(e => componentSet.has(e))); + const intersection = []; + for (const entity of result) { + for (const componentEntity of componentSet) { + if (entity === componentEntity || entity.id === componentEntity.id) { + intersection.push(entity); + break; + } + } + } + result = new Set(intersection); } else { result = componentSet; } @@ -275,9 +294,19 @@ export abstract class EntitySystem implements ISystemBase { if (condition.all.length > 0) { const allResult = querySystem.queryAll(...condition.all); const allSet = new Set(allResult.entities); - + + if (result) { - result = new Set([...result].filter(e => allSet.has(e))); + const intersection = []; + for (const entity of result) { + for (const allEntity of allSet) { + if (entity === allEntity || entity.id === allEntity.id) { + intersection.push(entity); + break; + } + } + } + result = new Set(intersection); } else { result = allSet; } @@ -287,9 +316,22 @@ export abstract class EntitySystem implements ISystemBase { if (condition.any.length > 0) { const anyResult = querySystem.queryAny(...condition.any); const anySet = new Set(anyResult.entities); - + + if (result) { - result = new Set([...result].filter(e => anySet.has(e))); + const intersection = []; + for (const entity of result) { + // 通过id匹配来确保正确的交集计算 + for (const anyEntity of anySet) { + if (entity === anyEntity || entity.id === anyEntity.id) { + intersection.push(entity); + break; + } + } + } + + result = new Set(intersection); + } else { result = anySet; } @@ -301,17 +343,34 @@ export abstract class EntitySystem implements ISystemBase { // 如果没有前置条件,从所有实体开始 result = new Set(querySystem.getAllEntities()); } - + const noneResult = querySystem.queryAny(...condition.none); const noneSet = new Set(noneResult.entities); - result = new Set([...result].filter(e => !noneSet.has(e))); + + const filteredEntities = []; + for (const entity of result) { + let shouldExclude = false; + for (const noneEntity of noneSet) { + if (entity === noneEntity || entity.id === noneEntity.id) { + shouldExclude = true; + break; + } + } + if (!shouldExclude) { + filteredEntities.push(entity); + } + } + result = new Set(filteredEntities); } - return result ? Array.from(result) : []; + const finalResult = result ? Array.from(result) : []; + + + return finalResult; } - + /** * 更新系统 * @@ -322,14 +381,17 @@ export abstract class EntitySystem implements ISystemBase { return; } + const startTime = this._performanceMonitor.startMonitoring(this._systemName); let entityCount = 0; - + try { this.onBegin(); // 动态查询实体并处理 const entities = this.queryEntities(); entityCount = entities.length; + + this.process(entities); } finally { this._performanceMonitor.endMonitoring(this._systemName, startTime, entityCount); @@ -348,7 +410,7 @@ export abstract class EntitySystem implements ISystemBase { const startTime = this._performanceMonitor.startMonitoring(`${this._systemName}_Late`); let entityCount = 0; - + try { // 动态查询实体并处理 const entities = this.queryEntities(); @@ -376,7 +438,7 @@ export abstract class EntitySystem implements ISystemBase { * * @param entities 要处理的实体列表 */ - protected process(entities: Entity[]): void { + protected process(_entities: Entity[]): void { // 子类必须实现此方法 } @@ -387,7 +449,7 @@ export abstract class EntitySystem implements ISystemBase { * * @param entities 要处理的实体列表 */ - protected lateProcess(entities: Entity[]): void { + protected lateProcess(_entities: Entity[]): void { // 子类可以重写此方法 } @@ -447,7 +509,7 @@ export abstract class EntitySystem implements ISystemBase { const entityCount = this.entities.length; const perfData = this.getPerformanceData(); const perfInfo = perfData ? ` (${perfData.executionTime.toFixed(2)}ms)` : ''; - + return `${this._systemName}[${entityCount} entities]${perfInfo}`; } @@ -456,7 +518,7 @@ export abstract class EntitySystem implements ISystemBase { */ private updateEntityTracking(currentEntities: Entity[]): void { const currentSet = new Set(currentEntities); - + // 检查新增的实体 for (const entity of currentEntities) { if (!this._trackedEntities.has(entity)) { @@ -464,7 +526,7 @@ export abstract class EntitySystem implements ISystemBase { this.onAdded(entity); } } - + // 检查移除的实体 for (const entity of this._trackedEntities) { if (!currentSet.has(entity)) { diff --git a/packages/core/src/ECS/Utils/ComponentTypeManager.ts b/packages/core/src/ECS/Utils/ComponentTypeManager.ts index f46d9736..37c70051 100644 --- a/packages/core/src/ECS/Utils/ComponentTypeManager.ts +++ b/packages/core/src/ECS/Utils/ComponentTypeManager.ts @@ -1,5 +1,6 @@ import { Component } from '../Component'; import { Bits } from './Bits'; +import { getComponentTypeName } from '../Decorators'; /** * 组件类型管理器 @@ -34,7 +35,7 @@ export class ComponentTypeManager { if (typeId === undefined) { typeId = this._nextTypeId++; this._componentTypes.set(componentType, typeId); - this._typeNames.set(typeId, componentType.name); + this._typeNames.set(typeId, getComponentTypeName(componentType)); } return typeId; diff --git a/packages/core/src/ECS/Utils/EntityProcessorList.ts b/packages/core/src/ECS/Utils/EntityProcessorList.ts index e2f9bc28..80e2116f 100644 --- a/packages/core/src/ECS/Utils/EntityProcessorList.ts +++ b/packages/core/src/ECS/Utils/EntityProcessorList.ts @@ -1,5 +1,6 @@ import { EntitySystem } from '../Systems/EntitySystem'; import { createLogger } from '../../Utils/Logger'; +import { getSystemInstanceTypeName } from '../Decorators'; /** * 实体处理器列表管理器 @@ -75,7 +76,7 @@ export class EntityProcessorList { try { processor.update(); } catch (error) { - EntityProcessorList._logger.error(`Error in processor ${processor.constructor.name}:`, error); + EntityProcessorList._logger.error(`Error in processor ${getSystemInstanceTypeName(processor)}:`, error); } } } diff --git a/packages/core/src/ECS/Utils/Matcher.ts b/packages/core/src/ECS/Utils/Matcher.ts index 1e840dd5..b4e1c2f4 100644 --- a/packages/core/src/ECS/Utils/Matcher.ts +++ b/packages/core/src/ECS/Utils/Matcher.ts @@ -1,4 +1,5 @@ import { ComponentType } from '../Core/ComponentStorage'; +import { getComponentTypeName } from '../Decorators'; /** * 查询条件类型 @@ -266,15 +267,15 @@ export class Matcher { const parts: string[] = []; if (this.condition.all.length > 0) { - parts.push(`all(${this.condition.all.map(t => t.name).join(', ')})`); + parts.push(`all(${this.condition.all.map(t => getComponentTypeName(t)).join(', ')})`); } if (this.condition.any.length > 0) { - parts.push(`any(${this.condition.any.map(t => t.name).join(', ')})`); + parts.push(`any(${this.condition.any.map(t => getComponentTypeName(t)).join(', ')})`); } if (this.condition.none.length > 0) { - parts.push(`none(${this.condition.none.map(t => t.name).join(', ')})`); + parts.push(`none(${this.condition.none.map(t => getComponentTypeName(t)).join(', ')})`); } if (this.condition.tag !== undefined) { @@ -286,7 +287,7 @@ export class Matcher { } if (this.condition.component !== undefined) { - parts.push(`component(${this.condition.component.name})`); + parts.push(`component(${getComponentTypeName(this.condition.component)})`); } return `Matcher[${parts.join(' & ')}]`; diff --git a/packages/core/src/ECS/index.ts b/packages/core/src/ECS/index.ts index bf4244ef..2f20612d 100644 --- a/packages/core/src/ECS/index.ts +++ b/packages/core/src/ECS/index.ts @@ -3,6 +3,7 @@ export { Component } from './Component'; export { ECSEventType, EventPriority, EVENT_TYPES, EventTypeValidator } from './CoreEvents'; export * from './Systems'; export * from './Utils'; +export * from './Decorators'; export { Scene } from './Scene'; export { IScene, ISceneFactory, ISceneConfig } from './IScene'; export { EntityManager, EntityQueryBuilder } from './Core/EntityManager'; diff --git a/packages/core/src/Utils/Debug/ComponentDataCollector.ts b/packages/core/src/Utils/Debug/ComponentDataCollector.ts index 1e505be2..b7703913 100644 --- a/packages/core/src/Utils/Debug/ComponentDataCollector.ts +++ b/packages/core/src/Utils/Debug/ComponentDataCollector.ts @@ -1,6 +1,7 @@ import { IComponentDebugData } from '../../Types'; import { Core } from '../../Core'; import { ComponentPoolManager } from '../../ECS/Core/ComponentPool'; +import { getComponentInstanceTypeName } from '../../ECS/Decorators'; /** * 组件数据收集器 @@ -36,7 +37,7 @@ export class ComponentDataCollector { entityList.buffer.forEach((entity: any) => { if (entity.components) { entity.components.forEach((component: any) => { - const typeName = component.constructor.name; + const typeName = getComponentInstanceTypeName(component); const stats = componentStats.get(typeName) || { count: 0, entities: 0 }; stats.count++; totalInstances++; @@ -106,7 +107,7 @@ export class ComponentDataCollector { try { for (const entity of entityList.buffer) { if (entity.components) { - const component = entity.components.find((c: any) => c.constructor.name === typeName); + const component = entity.components.find((c: any) => getComponentInstanceTypeName(c) === typeName); if (component) { calculatedSize = this.calculateQuickObjectSize(component); break; @@ -180,7 +181,7 @@ export class ComponentDataCollector { // 找到第一个包含此组件的实体,分析组件大小 for (const entity of entityList.buffer) { if (entity.components) { - const component = entity.components.find((c: any) => c.constructor.name === typeName); + const component = entity.components.find((c: any) => getComponentInstanceTypeName(c) === typeName); if (component) { return this.estimateObjectSize(component); } diff --git a/packages/core/src/Utils/Debug/DebugDataFormatter.ts b/packages/core/src/Utils/Debug/DebugDataFormatter.ts index e668fcb2..61dc4342 100644 --- a/packages/core/src/Utils/Debug/DebugDataFormatter.ts +++ b/packages/core/src/Utils/Debug/DebugDataFormatter.ts @@ -1,4 +1,5 @@ import { Component } from '../../ECS/Component'; +import { getComponentInstanceTypeName } from '../../ECS/Decorators'; /** * 调试数据格式化工具 @@ -56,7 +57,7 @@ export class DebugDataFormatter { }> { return components.map((component: Component) => { const componentDetail = { - typeName: component.constructor.name, + typeName: getComponentInstanceTypeName(component), properties: {} as Record }; diff --git a/packages/core/src/Utils/Debug/DebugManager.ts b/packages/core/src/Utils/Debug/DebugManager.ts index 33792a11..cf0dbb94 100644 --- a/packages/core/src/Utils/Debug/DebugManager.ts +++ b/packages/core/src/Utils/Debug/DebugManager.ts @@ -9,6 +9,7 @@ import { Core } from '../../Core'; import { Component } from '../../ECS/Component'; import { ComponentPoolManager } from '../../ECS/Core/ComponentPool'; import { Pool } from '../../Utils/Pool'; +import { getComponentInstanceTypeName, getSystemInstanceTypeName } from '../../ECS/Decorators'; /** * 调试管理器 @@ -467,7 +468,7 @@ export class DebugManager { if (!entity || entity.destroyed || !entity.components) continue; for (const component of entity.components) { - const typeName = component.constructor.name; + const typeName = getComponentInstanceTypeName(component); componentTypeCounts.set(typeName, (componentTypeCounts.get(typeName) || 0) + 1); } } @@ -486,7 +487,7 @@ export class DebugManager { if (!entity || entity.destroyed || !entity.components) continue; for (const component of entity.components) { - if (component.constructor.name === typeName) { + if (getComponentInstanceTypeName(component) === typeName) { instances.push({ entityId: entity.id, entityName: entity.name || `Entity_${entity.id}`, @@ -550,7 +551,7 @@ export class DebugManager { const systemTypeMemoryCache = new Map(); for (const system of entityProcessors.processors) { - const systemTypeName = system.constructor.name; + const systemTypeName = getSystemInstanceTypeName(system); let systemMemory: number; if (systemTypeMemoryCache.has(systemTypeName)) { diff --git a/packages/core/src/Utils/Debug/EntityDataCollector.ts b/packages/core/src/Utils/Debug/EntityDataCollector.ts index 574090f5..7bbfa8ca 100644 --- a/packages/core/src/Utils/Debug/EntityDataCollector.ts +++ b/packages/core/src/Utils/Debug/EntityDataCollector.ts @@ -3,6 +3,7 @@ import { Core } from '../../Core'; import { Entity } from '../../ECS/Entity'; import { Component } from '../../ECS/Component'; import { ComponentTypeManager } from '../../ECS/Utils/ComponentTypeManager'; +import { getComponentInstanceTypeName, getSystemInstanceTypeName } from '../../ECS/Decorators'; /** * 实体数据收集器 @@ -77,7 +78,7 @@ export class EntityDataCollector { enabled: entity.enabled !== false, activeInHierarchy: entity.activeInHierarchy !== false, componentCount: entity.components.length, - componentTypes: entity.components.map((component: Component) => component.constructor.name), + componentTypes: entity.components.map((component: Component) => getComponentInstanceTypeName(component)), parentId: entity.parent?.id || null, childIds: entity.children?.map((child: Entity) => child.id) || [], depth: entity.getDepth ? entity.getDepth() : 0, @@ -114,7 +115,7 @@ export class EntityDataCollector { parentName: entity.parent?.name || null, components: componentDetails || [], componentCount: entity.components?.length || 0, - componentTypes: entity.components?.map((comp: any) => comp.constructor.name) || [] + componentTypes: entity.components?.map((comp: any) => getComponentInstanceTypeName(comp)) || [] }; } catch (error) { return { @@ -216,7 +217,7 @@ export class EntityDataCollector { if (entityContainer && entityContainer.entities) { entityContainer.entities.forEach((entity: any) => { - const componentTypes = entity.components?.map((comp: any) => comp.constructor.name) || []; + const componentTypes = entity.components?.map((comp: any) => getComponentInstanceTypeName(comp)) || []; const signature = componentTypes.length > 0 ? componentTypes.sort().join(', ') : '无组件'; const existing = distribution.get(signature); @@ -378,7 +379,7 @@ export class EntityDataCollector { if (entityContainer && entityContainer.entities) { entityContainer.entities.forEach((entity: any) => { - const componentTypes = entity.components?.map((comp: any) => comp.constructor.name) || []; + const componentTypes = entity.components?.map((comp: any) => getComponentInstanceTypeName(comp)) || []; const signature = componentTypes.length > 0 ? componentTypes.sort().join(', ') : '无组件'; const existing = distribution.get(signature); @@ -601,7 +602,7 @@ export class EntityDataCollector { enabled: entity.enabled !== false, activeInHierarchy: entity.activeInHierarchy !== false, componentCount: entity.components.length, - componentTypes: entity.components.map((component: Component) => component.constructor.name), + componentTypes: entity.components.map((component: Component) => getComponentInstanceTypeName(component)), parentId: entity.parent?.id || null, children: [] as any[], depth: entity.getDepth ? entity.getDepth() : 0, @@ -690,7 +691,7 @@ export class EntityDataCollector { sceneName: sceneInfo.name, sceneType: sceneInfo.type, componentCount: entity.components.length, - componentTypes: entity.components.map((component: Component) => component.constructor.name), + componentTypes: entity.components.map((component: Component) => getComponentInstanceTypeName(component)), componentMask: entity.componentMask?.toString() || '0', parentId: entity.parent?.id || null, childCount: entity.children?.length || 0, @@ -709,7 +710,7 @@ export class EntityDataCollector { properties: Record; }> { return components.map((component: Component) => { - let typeName = component.constructor.name; + let typeName = getComponentInstanceTypeName(component); if (!typeName || typeName === 'Object' || typeName === 'Function') { try { @@ -739,11 +740,11 @@ export class EntityDataCollector { // 如果没有找到任何属性,添加一些调试信息 if (Object.keys(properties).length === 0) { properties._info = '该组件没有公开属性'; - properties._componentId = component.constructor.name; + properties._componentId = getComponentInstanceTypeName(component); } } catch (error) { properties._error = '属性提取失败'; - properties._componentId = component.constructor.name; + properties._componentId = getComponentInstanceTypeName(component); } return { diff --git a/packages/core/src/Utils/Debug/SystemDataCollector.ts b/packages/core/src/Utils/Debug/SystemDataCollector.ts index a43d5d16..98dc4ab2 100644 --- a/packages/core/src/Utils/Debug/SystemDataCollector.ts +++ b/packages/core/src/Utils/Debug/SystemDataCollector.ts @@ -1,5 +1,6 @@ import { ISystemDebugData } from '../../Types'; import { Core } from '../../Core'; +import { getSystemInstanceTypeName } from '../../ECS/Decorators'; /** * 系统数据收集器 @@ -43,13 +44,13 @@ export class SystemDataCollector { return { totalSystems: systems.length, systemsInfo: systems.map((system: any) => { - const systemName = system.systemName || system.constructor.name; + const systemName = system.systemName || getSystemInstanceTypeName(system); const stats = systemStats.get(systemName); const data = systemData.get(systemName); return { name: systemName, - type: system.constructor.name, + type: getSystemInstanceTypeName(system), entityCount: system.entities?.length || 0, executionTime: stats?.averageTime || data?.executionTime || 0, minExecutionTime: stats?.minTime === Number.MAX_VALUE ? 0 : (stats?.minTime || 0), diff --git a/packages/core/tests/ECS/Decorators/TypeDecorators.test.ts b/packages/core/tests/ECS/Decorators/TypeDecorators.test.ts new file mode 100644 index 00000000..90d7e8e3 --- /dev/null +++ b/packages/core/tests/ECS/Decorators/TypeDecorators.test.ts @@ -0,0 +1,124 @@ +import { Component } from '../../../src/ECS/Component'; +import { EntitySystem } from '../../../src/ECS/Systems/EntitySystem'; +import { + ECSComponent, + ECSSystem, + getComponentTypeName, + getSystemTypeName, + getComponentInstanceTypeName, + getSystemInstanceTypeName +} from '../../../src/ECS/Decorators'; + +describe('TypeDecorators', () => { + describe('@ECSComponent', () => { + test('应该为组件类设置类型名称', () => { + @ECSComponent('TestComponent') + class TestComponent extends Component { + public value: number = 10; + } + + const typeName = getComponentTypeName(TestComponent); + expect(typeName).toBe('TestComponent'); + }); + + test('应该从组件实例获取类型名称', () => { + @ECSComponent('PlayerComponent') + class PlayerComponent extends Component { + public name: string = 'Player'; + } + + const instance = new PlayerComponent(); + const typeName = getComponentInstanceTypeName(instance); + expect(typeName).toBe('PlayerComponent'); + }); + + test('未装饰的组件应该使用constructor.name作为后备', () => { + class UndecoredComponent extends Component { + public data: string = 'test'; + } + + const typeName = getComponentTypeName(UndecoredComponent); + expect(typeName).toBe('UndecoredComponent'); + }); + }); + + describe('@ECSSystem', () => { + test('应该为系统类设置类型名称', () => { + @ECSSystem('TestSystem') + class TestSystem extends EntitySystem { + protected override process(_entities: any[]): void { + // 测试系统 + } + } + + const typeName = getSystemTypeName(TestSystem); + expect(typeName).toBe('TestSystem'); + }); + + test('应该从系统实例获取类型名称', () => { + @ECSSystem('MovementSystem') + class MovementSystem extends EntitySystem { + protected override process(_entities: any[]): void { + // 移动系统 + } + } + + const instance = new MovementSystem(); + const typeName = getSystemInstanceTypeName(instance); + expect(typeName).toBe('MovementSystem'); + }); + + test('未装饰的系统应该使用constructor.name作为后备', () => { + class UndecoredSystem extends EntitySystem { + protected override process(_entities: any[]): void { + // 未装饰的系统 + } + } + + const typeName = getSystemTypeName(UndecoredSystem); + expect(typeName).toBe('UndecoredSystem'); + }); + }); + + describe('混淆场景模拟', () => { + test('装饰器名称在混淆后仍然有效', () => { + // 模拟混淆后的类名 + @ECSComponent('OriginalComponent') + class a extends Component { + public prop: boolean = true; + } + + @ECSSystem('OriginalSystem') + class b extends EntitySystem { + protected override process(_entities: any[]): void { + // 原始系统 + } + } + + // 即使类名被混淆为a和b,装饰器名称依然正确 + expect(getComponentTypeName(a)).toBe('OriginalComponent'); + expect(getSystemTypeName(b)).toBe('OriginalSystem'); + + const componentInstance = new a(); + const systemInstance = new b(); + expect(getComponentInstanceTypeName(componentInstance)).toBe('OriginalComponent'); + expect(getSystemInstanceTypeName(systemInstance)).toBe('OriginalSystem'); + }); + }); + + describe('错误处理', () => { + test('装饰器应该验证类型名称参数', () => { + expect(() => { + @ECSComponent('') + class EmptyNameComponent extends Component {} + }).toThrow('ECSComponent装饰器必须提供有效的类型名称'); + + expect(() => { + @ECSSystem('') + class EmptyNameSystem extends EntitySystem { + protected override process(_entities: any[]): void {} + } + }).toThrow('ECSSystem装饰器必须提供有效的类型名称'); + }); + }); +}); \ No newline at end of file