新增组件/系统装饰器避免混淆
更改Set兼容web/小游戏
This commit is contained in:
@@ -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('|');
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
126
packages/core/src/ECS/Decorators/TypeDecorators.ts
Normal file
126
packages/core/src/ECS/Decorators/TypeDecorators.ts
Normal 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);
|
||||
}
|
||||
|
||||
10
packages/core/src/ECS/Decorators/index.ts
Normal file
10
packages/core/src/ECS/Decorators/index.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
export {
|
||||
ECSComponent,
|
||||
ECSSystem,
|
||||
getComponentTypeName,
|
||||
getSystemTypeName,
|
||||
getComponentInstanceTypeName,
|
||||
getSystemInstanceTypeName,
|
||||
COMPONENT_TYPE_NAME,
|
||||
SYSTEM_TYPE_NAME
|
||||
} from './TypeDecorators';
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
})),
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(' & ')}]`;
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
};
|
||||
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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),
|
||||
|
||||
124
packages/core/tests/ECS/Decorators/TypeDecorators.test.ts
Normal file
124
packages/core/tests/ECS/Decorators/TypeDecorators.test.ts
Normal 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装饰器必须提供有效的类型名称');
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user