新增组件/系统装饰器避免混淆

更改Set兼容web/小游戏
This commit is contained in:
YHH
2025-08-14 18:35:03 +08:00
parent 62f250b43c
commit 0b7e623748
19 changed files with 434 additions and 90 deletions

View File

@@ -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('|');
}

View File

@@ -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<T extends Component>(componentType: ComponentType<T>): 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<T extends Component>(componentType: ComponentType<T>): 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<T extends Component> {
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<string, any>();
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());
}

View File

@@ -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<T extends Component>(componentType: ComponentType<T>): 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<T extends Component>(componentType: ComponentType<T>): 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<T extends Component>(componentType: ComponentType<T>): 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;
}

View File

@@ -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;

View File

@@ -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 <T extends new (...args: any[]) => 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 <T extends new (...args: any[]) => 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<T extends Component>(
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<T extends EntitySystem>(
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);
}

View File

@@ -0,0 +1,10 @@
export {
ECSComponent,
ECSSystem,
getComponentTypeName,
getSystemTypeName,
getComponentInstanceTypeName,
getSystemInstanceTypeName,
COMPONENT_TYPE_NAME,
SYSTEM_TYPE_NAME
} from './TypeDecorators';

View File

@@ -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,

View File

@@ -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<string, any>;
} {
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
})),

View File

@@ -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<Entity> = 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<Entity> | 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)) {

View File

@@ -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;

View File

@@ -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);
}
}
}

View File

@@ -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(' & ')}]`;

View File

@@ -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';

View File

@@ -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);
}

View File

@@ -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<string, any>
};

View File

@@ -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<string, number>();
for (const system of entityProcessors.processors) {
const systemTypeName = system.constructor.name;
const systemTypeName = getSystemInstanceTypeName(system);
let systemMemory: number;
if (systemTypeMemoryCache.has(systemTypeName)) {

View File

@@ -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<string, any>;
}> {
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 {

View File

@@ -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),

View File

@@ -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装饰器必须提供有效的类型名称');
});
});
});