新增组件/系统装饰器避免混淆
更改Set兼容web/小游戏
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
import { Entity } from '../Entity';
|
import { Entity } from '../Entity';
|
||||||
import { ComponentType } from './ComponentStorage';
|
import { ComponentType } from './ComponentStorage';
|
||||||
|
import { getComponentTypeName } from '../Decorators';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 原型标识符
|
* 原型标识符
|
||||||
@@ -103,7 +104,7 @@ export class ArchetypeSystem {
|
|||||||
public queryArchetypes(componentTypes: ComponentType[], operation: 'AND' | 'OR' = 'AND'): ArchetypeQueryResult {
|
public queryArchetypes(componentTypes: ComponentType[], operation: 'AND' | 'OR' = 'AND'): ArchetypeQueryResult {
|
||||||
const startTime = performance.now();
|
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);
|
const cached = this._queryCache.get(cacheKey);
|
||||||
@@ -195,7 +196,7 @@ export class ArchetypeSystem {
|
|||||||
*/
|
*/
|
||||||
private generateArchetypeId(componentTypes: ComponentType[]): ArchetypeId {
|
private generateArchetypeId(componentTypes: ComponentType[]): ArchetypeId {
|
||||||
return componentTypes
|
return componentTypes
|
||||||
.map(type => type.name)
|
.map(type => getComponentTypeName(type))
|
||||||
.sort()
|
.sort()
|
||||||
.join('|');
|
.join('|');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { Component } from '../Component';
|
|||||||
import { IBigIntLike, BigIntFactory } from '../Utils/BigIntCompatibility';
|
import { IBigIntLike, BigIntFactory } from '../Utils/BigIntCompatibility';
|
||||||
import { SoAStorage, EnableSoA, HighPrecision, Float64, Int32, SerializeMap, SerializeSet, SerializeArray, DeepCopy } from './SoAStorage';
|
import { SoAStorage, EnableSoA, HighPrecision, Float64, Int32, SerializeMap, SerializeSet, SerializeArray, DeepCopy } from './SoAStorage';
|
||||||
import { createLogger } from '../../Utils/Logger';
|
import { createLogger } from '../../Utils/Logger';
|
||||||
|
import { getComponentTypeName } from '../Decorators';
|
||||||
|
|
||||||
// 重新导出装饰器
|
// 重新导出装饰器
|
||||||
export { EnableSoA, HighPrecision, Float64, Int32, SerializeMap, SerializeSet, SerializeArray, DeepCopy };
|
export { EnableSoA, HighPrecision, Float64, Int32, SerializeMap, SerializeSet, SerializeArray, DeepCopy };
|
||||||
@@ -41,8 +42,8 @@ export class ComponentRegistry {
|
|||||||
|
|
||||||
const bitIndex = this.nextBitIndex++;
|
const bitIndex = this.nextBitIndex++;
|
||||||
this.componentTypes.set(componentType, bitIndex);
|
this.componentTypes.set(componentType, bitIndex);
|
||||||
this.componentNameToType.set(componentType.name, componentType);
|
this.componentNameToType.set(getComponentTypeName(componentType), componentType);
|
||||||
this.componentNameToId.set(componentType.name, bitIndex);
|
this.componentNameToId.set(getComponentTypeName(componentType), bitIndex);
|
||||||
return bitIndex;
|
return bitIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,7 +55,7 @@ export class ComponentRegistry {
|
|||||||
public static getBitMask<T extends Component>(componentType: ComponentType<T>): IBigIntLike {
|
public static getBitMask<T extends Component>(componentType: ComponentType<T>): IBigIntLike {
|
||||||
const bitIndex = this.componentTypes.get(componentType);
|
const bitIndex = this.componentTypes.get(componentType);
|
||||||
if (bitIndex === undefined) {
|
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);
|
return BigIntFactory.one().shiftLeft(bitIndex);
|
||||||
}
|
}
|
||||||
@@ -67,7 +68,7 @@ export class ComponentRegistry {
|
|||||||
public static getBitIndex<T extends Component>(componentType: ComponentType<T>): number {
|
public static getBitIndex<T extends Component>(componentType: ComponentType<T>): number {
|
||||||
const bitIndex = this.componentTypes.get(componentType);
|
const bitIndex = this.componentTypes.get(componentType);
|
||||||
if (bitIndex === undefined) {
|
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;
|
return bitIndex;
|
||||||
}
|
}
|
||||||
@@ -229,7 +230,7 @@ export class ComponentStorage<T extends Component> {
|
|||||||
public addComponent(entityId: number, component: T): void {
|
public addComponent(entityId: number, component: T): void {
|
||||||
// 检查实体是否已有此组件
|
// 检查实体是否已有此组件
|
||||||
if (this.entityToIndex.has(entityId)) {
|
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;
|
let index: number;
|
||||||
@@ -421,7 +422,7 @@ export class ComponentStorageManager {
|
|||||||
if (enableSoA) {
|
if (enableSoA) {
|
||||||
// 使用SoA优化存储
|
// 使用SoA优化存储
|
||||||
storage = new SoAStorage(componentType);
|
storage = new SoAStorage(componentType);
|
||||||
ComponentStorageManager._logger.info(`为 ${componentType.name} 启用SoA优化(适用于大规模批量操作)`);
|
ComponentStorageManager._logger.info(`为 ${getComponentTypeName(componentType)} 启用SoA优化(适用于大规模批量操作)`);
|
||||||
} else {
|
} else {
|
||||||
// 默认使用原始存储
|
// 默认使用原始存储
|
||||||
storage = new ComponentStorage(componentType);
|
storage = new ComponentStorage(componentType);
|
||||||
@@ -521,7 +522,7 @@ export class ComponentStorageManager {
|
|||||||
const stats = new Map<string, any>();
|
const stats = new Map<string, any>();
|
||||||
|
|
||||||
for (const [componentType, storage] of this.storages.entries()) {
|
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());
|
stats.set(typeName, storage.getStats());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { Component } from '../../Component';
|
import { Component } from '../../Component';
|
||||||
import { IBigIntLike, BigIntFactory } from '../../Utils/BigIntCompatibility';
|
import { IBigIntLike, BigIntFactory } from '../../Utils/BigIntCompatibility';
|
||||||
import { createLogger } from '../../../Utils/Logger';
|
import { createLogger } from '../../../Utils/Logger';
|
||||||
|
import { getComponentTypeName } from '../../Decorators';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 组件类型定义
|
* 组件类型定义
|
||||||
@@ -26,8 +27,11 @@ export class ComponentRegistry {
|
|||||||
* @returns 分配的位索引
|
* @returns 分配的位索引
|
||||||
*/
|
*/
|
||||||
public static register<T extends Component>(componentType: ComponentType<T>): number {
|
public static register<T extends Component>(componentType: ComponentType<T>): number {
|
||||||
|
const typeName = getComponentTypeName(componentType);
|
||||||
|
|
||||||
if (this.componentTypes.has(componentType)) {
|
if (this.componentTypes.has(componentType)) {
|
||||||
return this.componentTypes.get(componentType)!;
|
const existingIndex = this.componentTypes.get(componentType)!;
|
||||||
|
return existingIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.nextBitIndex >= this.maxComponents) {
|
if (this.nextBitIndex >= this.maxComponents) {
|
||||||
@@ -36,8 +40,9 @@ export class ComponentRegistry {
|
|||||||
|
|
||||||
const bitIndex = this.nextBitIndex++;
|
const bitIndex = this.nextBitIndex++;
|
||||||
this.componentTypes.set(componentType, bitIndex);
|
this.componentTypes.set(componentType, bitIndex);
|
||||||
this.componentNameToType.set(componentType.name, componentType);
|
this.componentNameToType.set(typeName, componentType);
|
||||||
this.componentNameToId.set(componentType.name, bitIndex);
|
this.componentNameToId.set(typeName, bitIndex);
|
||||||
|
|
||||||
return bitIndex;
|
return bitIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -49,7 +54,8 @@ export class ComponentRegistry {
|
|||||||
public static getBitMask<T extends Component>(componentType: ComponentType<T>): IBigIntLike {
|
public static getBitMask<T extends Component>(componentType: ComponentType<T>): IBigIntLike {
|
||||||
const bitIndex = this.componentTypes.get(componentType);
|
const bitIndex = this.componentTypes.get(componentType);
|
||||||
if (bitIndex === undefined) {
|
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);
|
return BigIntFactory.one().shiftLeft(bitIndex);
|
||||||
}
|
}
|
||||||
@@ -62,7 +68,8 @@ export class ComponentRegistry {
|
|||||||
public static getBitIndex<T extends Component>(componentType: ComponentType<T>): number {
|
public static getBitIndex<T extends Component>(componentType: ComponentType<T>): number {
|
||||||
const bitIndex = this.componentTypes.get(componentType);
|
const bitIndex = this.componentTypes.get(componentType);
|
||||||
if (bitIndex === undefined) {
|
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;
|
return bitIndex;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { Component } from '../Component';
|
|||||||
import { ComponentRegistry, ComponentType } from './ComponentStorage';
|
import { ComponentRegistry, ComponentType } from './ComponentStorage';
|
||||||
import { IBigIntLike, BigIntFactory } from '../Utils/BigIntCompatibility';
|
import { IBigIntLike, BigIntFactory } from '../Utils/BigIntCompatibility';
|
||||||
import { createLogger } from '../../Utils/Logger';
|
import { createLogger } from '../../Utils/Logger';
|
||||||
|
import { getComponentTypeName } from '../Decorators';
|
||||||
|
|
||||||
import { ComponentPoolManager } from './ComponentPool';
|
import { ComponentPoolManager } from './ComponentPool';
|
||||||
import { ComponentIndexManager, IndexType } from './ComponentIndex';
|
import { ComponentIndexManager, IndexType } from './ComponentIndex';
|
||||||
@@ -164,6 +165,7 @@ export class QuerySystem {
|
|||||||
this.archetypeSystem.addEntity(entity);
|
this.archetypeSystem.addEntity(entity);
|
||||||
this.dirtyTrackingSystem.markDirty(entity, DirtyFlag.COMPONENT_ADDED);
|
this.dirtyTrackingSystem.markDirty(entity, DirtyFlag.COMPONENT_ADDED);
|
||||||
|
|
||||||
|
|
||||||
// 只有在非延迟模式下才立即清理缓存
|
// 只有在非延迟模式下才立即清理缓存
|
||||||
if (!deferCacheClear) {
|
if (!deferCacheClear) {
|
||||||
this.clearQueryCache();
|
this.clearQueryCache();
|
||||||
@@ -400,7 +402,7 @@ export class QuerySystem {
|
|||||||
this.queryStats.totalQueries++;
|
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);
|
const cached = this.getFromCache(cacheKey);
|
||||||
@@ -511,7 +513,7 @@ export class QuerySystem {
|
|||||||
const startTime = performance.now();
|
const startTime = performance.now();
|
||||||
this.queryStats.totalQueries++;
|
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);
|
const cached = this.getFromCache(cacheKey);
|
||||||
@@ -569,7 +571,7 @@ export class QuerySystem {
|
|||||||
const startTime = performance.now();
|
const startTime = performance.now();
|
||||||
this.queryStats.totalQueries++;
|
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);
|
const cached = this.getFromCache(cacheKey);
|
||||||
@@ -713,7 +715,7 @@ export class QuerySystem {
|
|||||||
const startTime = performance.now();
|
const startTime = performance.now();
|
||||||
this.queryStats.totalQueries++;
|
this.queryStats.totalQueries++;
|
||||||
|
|
||||||
const cacheKey = `component:${componentType.name}`;
|
const cacheKey = `component:${getComponentTypeName(componentType)}`;
|
||||||
|
|
||||||
// 检查缓存
|
// 检查缓存
|
||||||
const cached = this.getFromCache(cacheKey);
|
const cached = this.getFromCache(cacheKey);
|
||||||
@@ -880,7 +882,7 @@ export class QuerySystem {
|
|||||||
mask = mask.or(bitMask);
|
mask = mask.or(bitMask);
|
||||||
hasValidComponents = true;
|
hasValidComponents = true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this._logger.warn(`组件类型 ${type.name} 未注册,跳过`);
|
this._logger.warn(`组件类型 ${getComponentTypeName(type)} 未注册,跳过`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -958,7 +960,7 @@ export class QuerySystem {
|
|||||||
componentIndex: this.componentIndexManager.getStats(),
|
componentIndex: this.componentIndexManager.getStats(),
|
||||||
archetypeSystem: this.archetypeSystem.getAllArchetypes().map(a => ({
|
archetypeSystem: this.archetypeSystem.getAllArchetypes().map(a => ({
|
||||||
id: a.id,
|
id: a.id,
|
||||||
componentTypes: a.componentTypes.map(t => t.name),
|
componentTypes: a.componentTypes.map(t => getComponentTypeName(t)),
|
||||||
entityCount: a.entities.length
|
entityCount: a.entities.length
|
||||||
})),
|
})),
|
||||||
dirtyTracking: this.dirtyTrackingSystem.getStats()
|
dirtyTracking: this.dirtyTrackingSystem.getStats()
|
||||||
@@ -1151,7 +1153,7 @@ export class QueryBuilder {
|
|||||||
const bitMask = ComponentRegistry.getBitMask(type);
|
const bitMask = ComponentRegistry.getBitMask(type);
|
||||||
mask = mask.or(bitMask);
|
mask = mask.or(bitMask);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this._logger.warn(`组件类型 ${type.name} 未注册,跳过`);
|
this._logger.warn(`组件类型 ${getComponentTypeName(type)} 未注册,跳过`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return mask;
|
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 { EventBus } from './Core/EventBus';
|
||||||
import { IBigIntLike, BigIntFactory } from './Utils/BigIntCompatibility';
|
import { IBigIntLike, BigIntFactory } from './Utils/BigIntCompatibility';
|
||||||
import { createLogger } from '../Utils/Logger';
|
import { createLogger } from '../Utils/Logger';
|
||||||
|
import { getComponentInstanceTypeName, getComponentTypeName } from './Decorators';
|
||||||
|
|
||||||
// Forward declaration to avoid circular dependency
|
// Forward declaration to avoid circular dependency
|
||||||
interface IScene {
|
interface IScene {
|
||||||
@@ -382,7 +383,7 @@ export class Entity {
|
|||||||
|
|
||||||
// 检查是否已有此类型的组件
|
// 检查是否已有此类型的组件
|
||||||
if (this.hasComponent(componentType)) {
|
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,
|
entityId: this.id,
|
||||||
entityName: this.name,
|
entityName: this.name,
|
||||||
entityTag: this.tag?.toString(),
|
entityTag: this.tag?.toString(),
|
||||||
componentType: componentType.name,
|
componentType: getComponentTypeName(componentType),
|
||||||
component: component
|
component: component
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -558,7 +559,7 @@ export class Entity {
|
|||||||
entityId: this.id,
|
entityId: this.id,
|
||||||
entityName: this.name,
|
entityName: this.name,
|
||||||
entityTag: this.tag?.toString(),
|
entityTag: this.tag?.toString(),
|
||||||
componentType: componentType.name,
|
componentType: getComponentTypeName(componentType),
|
||||||
component: component
|
component: component
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -636,7 +637,7 @@ export class Entity {
|
|||||||
addedComponents.push(this.addComponent(component));
|
addedComponents.push(this.addComponent(component));
|
||||||
} catch (error) {
|
} 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,
|
activeInHierarchy: this.activeInHierarchy,
|
||||||
destroyed: this._isDestroyed,
|
destroyed: this._isDestroyed,
|
||||||
componentCount: this.components.length,
|
componentCount: this.components.length,
|
||||||
componentTypes: this.components.map(c => c.constructor.name),
|
componentTypes: this.components.map(c => getComponentInstanceTypeName(c)),
|
||||||
componentMask: this._componentMask.toString(2), // 二进制表示
|
componentMask: this._componentMask.toString(2), // 二进制表示
|
||||||
parentId: this._parent?.id || null,
|
parentId: this._parent?.id || null,
|
||||||
childCount: this._children.length,
|
childCount: this._children.length,
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import { QuerySystem } from './Core/QuerySystem';
|
|||||||
import { TypeSafeEventSystem } from './Core/EventSystem';
|
import { TypeSafeEventSystem } from './Core/EventSystem';
|
||||||
import { EventBus } from './Core/EventBus';
|
import { EventBus } from './Core/EventBus';
|
||||||
import { IScene, ISceneConfig } from './IScene';
|
import { IScene, ISceneConfig } from './IScene';
|
||||||
|
import { getComponentInstanceTypeName, getSystemInstanceTypeName } from './Decorators';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 游戏场景默认实现类
|
* 游戏场景默认实现类
|
||||||
@@ -392,7 +393,7 @@ export class Scene implements IScene {
|
|||||||
componentStats: Map<string, any>;
|
componentStats: Map<string, any>;
|
||||||
} {
|
} {
|
||||||
return {
|
return {
|
||||||
name: this.constructor.name,
|
name: this.name || this.constructor.name,
|
||||||
entityCount: this.entities.count,
|
entityCount: this.entities.count,
|
||||||
processorCount: this.entityProcessors.count,
|
processorCount: this.entityProcessors.count,
|
||||||
isRunning: this._didSceneBegin,
|
isRunning: this._didSceneBegin,
|
||||||
@@ -400,10 +401,10 @@ export class Scene implements IScene {
|
|||||||
name: entity.name,
|
name: entity.name,
|
||||||
id: entity.id,
|
id: entity.id,
|
||||||
componentCount: entity.components.length,
|
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 => ({
|
processors: this.entityProcessors.processors.map(processor => ({
|
||||||
name: processor.constructor.name,
|
name: getSystemInstanceTypeName(processor),
|
||||||
updateOrder: processor.updateOrder,
|
updateOrder: processor.updateOrder,
|
||||||
entityCount: (processor as any)._entities?.length || 0
|
entityCount: (processor as any)._entities?.length || 0
|
||||||
})),
|
})),
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { Matcher } from '../Utils/Matcher';
|
|||||||
import type { Scene } from '../Scene';
|
import type { Scene } from '../Scene';
|
||||||
import type { ISystemBase } from '../../Types';
|
import type { ISystemBase } from '../../Types';
|
||||||
import type { QuerySystem } from '../Core/QuerySystem';
|
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 _initialized: boolean = false;
|
||||||
private _matcher: Matcher;
|
private _matcher: Matcher;
|
||||||
private _trackedEntities: Set<Entity> = new Set();
|
private _trackedEntities: Set<Entity> = new Set();
|
||||||
private _lastQueryResult: Entity[] = [];
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取系统处理的实体列表(动态查询)
|
* 获取系统处理的实体列表(动态查询)
|
||||||
@@ -79,7 +79,7 @@ export abstract class EntitySystem implements ISystemBase {
|
|||||||
|
|
||||||
constructor(matcher?: Matcher) {
|
constructor(matcher?: Matcher) {
|
||||||
this._matcher = matcher ? matcher : Matcher.empty();
|
this._matcher = matcher ? matcher : Matcher.empty();
|
||||||
this._systemName = this.constructor.name;
|
this._systemName = getSystemInstanceTypeName(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _scene: Scene | null = null;
|
private _scene: Scene | null = null;
|
||||||
@@ -151,7 +151,6 @@ export abstract class EntitySystem implements ISystemBase {
|
|||||||
public reset(): void {
|
public reset(): void {
|
||||||
this._initialized = false;
|
this._initialized = false;
|
||||||
this._trackedEntities.clear();
|
this._trackedEntities.clear();
|
||||||
this._lastQueryResult = [];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -159,7 +158,6 @@ export abstract class EntitySystem implements ISystemBase {
|
|||||||
*/
|
*/
|
||||||
private queryEntities(): Entity[] {
|
private queryEntities(): Entity[] {
|
||||||
if (!this.scene?.querySystem || !this._matcher) {
|
if (!this.scene?.querySystem || !this._matcher) {
|
||||||
this._lastQueryResult = [];
|
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -167,6 +165,7 @@ export abstract class EntitySystem implements ISystemBase {
|
|||||||
const querySystem = this.scene.querySystem;
|
const querySystem = this.scene.querySystem;
|
||||||
let currentEntities: Entity[] = [];
|
let currentEntities: Entity[] = [];
|
||||||
|
|
||||||
|
|
||||||
// 空条件返回所有实体
|
// 空条件返回所有实体
|
||||||
if (this._matcher.isEmpty()) {
|
if (this._matcher.isEmpty()) {
|
||||||
currentEntities = querySystem.getAllEntities();
|
currentEntities = querySystem.getAllEntities();
|
||||||
@@ -181,7 +180,7 @@ export abstract class EntitySystem implements ISystemBase {
|
|||||||
// 检查实体变化并触发回调
|
// 检查实体变化并触发回调
|
||||||
this.updateEntityTracking(currentEntities);
|
this.updateEntityTracking(currentEntities);
|
||||||
|
|
||||||
this._lastQueryResult = currentEntities;
|
|
||||||
return currentEntities;
|
return currentEntities;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -197,6 +196,7 @@ export abstract class EntitySystem implements ISystemBase {
|
|||||||
(condition.name !== undefined ? 1 : 0) +
|
(condition.name !== undefined ? 1 : 0) +
|
||||||
(condition.component !== undefined ? 1 : 0);
|
(condition.component !== undefined ? 1 : 0);
|
||||||
|
|
||||||
|
|
||||||
return conditionCount === 1;
|
return conditionCount === 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -241,6 +241,7 @@ export abstract class EntitySystem implements ISystemBase {
|
|||||||
private executeComplexQuery(condition: any, querySystem: QuerySystem): Entity[] {
|
private executeComplexQuery(condition: any, querySystem: QuerySystem): Entity[] {
|
||||||
let result: Set<Entity> | null = null;
|
let result: Set<Entity> | null = null;
|
||||||
|
|
||||||
|
|
||||||
// 1. 应用标签条件作为基础集合
|
// 1. 应用标签条件作为基础集合
|
||||||
if (condition.tag !== undefined) {
|
if (condition.tag !== undefined) {
|
||||||
const tagResult = querySystem.queryByTag(condition.tag);
|
const tagResult = querySystem.queryByTag(condition.tag);
|
||||||
@@ -253,7 +254,16 @@ export abstract class EntitySystem implements ISystemBase {
|
|||||||
const nameSet = new Set(nameResult.entities);
|
const nameSet = new Set(nameResult.entities);
|
||||||
|
|
||||||
if (result) {
|
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 {
|
} else {
|
||||||
result = nameSet;
|
result = nameSet;
|
||||||
}
|
}
|
||||||
@@ -265,7 +275,16 @@ export abstract class EntitySystem implements ISystemBase {
|
|||||||
const componentSet = new Set(componentResult.entities);
|
const componentSet = new Set(componentResult.entities);
|
||||||
|
|
||||||
if (result) {
|
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 {
|
} else {
|
||||||
result = componentSet;
|
result = componentSet;
|
||||||
}
|
}
|
||||||
@@ -276,8 +295,18 @@ export abstract class EntitySystem implements ISystemBase {
|
|||||||
const allResult = querySystem.queryAll(...condition.all);
|
const allResult = querySystem.queryAll(...condition.all);
|
||||||
const allSet = new Set(allResult.entities);
|
const allSet = new Set(allResult.entities);
|
||||||
|
|
||||||
|
|
||||||
if (result) {
|
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 {
|
} else {
|
||||||
result = allSet;
|
result = allSet;
|
||||||
}
|
}
|
||||||
@@ -288,8 +317,21 @@ export abstract class EntitySystem implements ISystemBase {
|
|||||||
const anyResult = querySystem.queryAny(...condition.any);
|
const anyResult = querySystem.queryAny(...condition.any);
|
||||||
const anySet = new Set(anyResult.entities);
|
const anySet = new Set(anyResult.entities);
|
||||||
|
|
||||||
|
|
||||||
if (result) {
|
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 {
|
} else {
|
||||||
result = anySet;
|
result = anySet;
|
||||||
}
|
}
|
||||||
@@ -304,10 +346,27 @@ export abstract class EntitySystem implements ISystemBase {
|
|||||||
|
|
||||||
const noneResult = querySystem.queryAny(...condition.none);
|
const noneResult = querySystem.queryAny(...condition.none);
|
||||||
const noneSet = new Set(noneResult.entities);
|
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,6 +381,7 @@ export abstract class EntitySystem implements ISystemBase {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const startTime = this._performanceMonitor.startMonitoring(this._systemName);
|
const startTime = this._performanceMonitor.startMonitoring(this._systemName);
|
||||||
let entityCount = 0;
|
let entityCount = 0;
|
||||||
|
|
||||||
@@ -330,6 +390,8 @@ export abstract class EntitySystem implements ISystemBase {
|
|||||||
// 动态查询实体并处理
|
// 动态查询实体并处理
|
||||||
const entities = this.queryEntities();
|
const entities = this.queryEntities();
|
||||||
entityCount = entities.length;
|
entityCount = entities.length;
|
||||||
|
|
||||||
|
|
||||||
this.process(entities);
|
this.process(entities);
|
||||||
} finally {
|
} finally {
|
||||||
this._performanceMonitor.endMonitoring(this._systemName, startTime, entityCount);
|
this._performanceMonitor.endMonitoring(this._systemName, startTime, entityCount);
|
||||||
@@ -376,7 +438,7 @@ export abstract class EntitySystem implements ISystemBase {
|
|||||||
*
|
*
|
||||||
* @param entities 要处理的实体列表
|
* @param entities 要处理的实体列表
|
||||||
*/
|
*/
|
||||||
protected process(entities: Entity[]): void {
|
protected process(_entities: Entity[]): void {
|
||||||
// 子类必须实现此方法
|
// 子类必须实现此方法
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -387,7 +449,7 @@ export abstract class EntitySystem implements ISystemBase {
|
|||||||
*
|
*
|
||||||
* @param entities 要处理的实体列表
|
* @param entities 要处理的实体列表
|
||||||
*/
|
*/
|
||||||
protected lateProcess(entities: Entity[]): void {
|
protected lateProcess(_entities: Entity[]): void {
|
||||||
// 子类可以重写此方法
|
// 子类可以重写此方法
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { Component } from '../Component';
|
import { Component } from '../Component';
|
||||||
import { Bits } from './Bits';
|
import { Bits } from './Bits';
|
||||||
|
import { getComponentTypeName } from '../Decorators';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 组件类型管理器
|
* 组件类型管理器
|
||||||
@@ -34,7 +35,7 @@ export class ComponentTypeManager {
|
|||||||
if (typeId === undefined) {
|
if (typeId === undefined) {
|
||||||
typeId = this._nextTypeId++;
|
typeId = this._nextTypeId++;
|
||||||
this._componentTypes.set(componentType, typeId);
|
this._componentTypes.set(componentType, typeId);
|
||||||
this._typeNames.set(typeId, componentType.name);
|
this._typeNames.set(typeId, getComponentTypeName(componentType));
|
||||||
}
|
}
|
||||||
|
|
||||||
return typeId;
|
return typeId;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { EntitySystem } from '../Systems/EntitySystem';
|
import { EntitySystem } from '../Systems/EntitySystem';
|
||||||
import { createLogger } from '../../Utils/Logger';
|
import { createLogger } from '../../Utils/Logger';
|
||||||
|
import { getSystemInstanceTypeName } from '../Decorators';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 实体处理器列表管理器
|
* 实体处理器列表管理器
|
||||||
@@ -75,7 +76,7 @@ export class EntityProcessorList {
|
|||||||
try {
|
try {
|
||||||
processor.update();
|
processor.update();
|
||||||
} catch (error) {
|
} 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 { ComponentType } from '../Core/ComponentStorage';
|
||||||
|
import { getComponentTypeName } from '../Decorators';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查询条件类型
|
* 查询条件类型
|
||||||
@@ -266,15 +267,15 @@ export class Matcher {
|
|||||||
const parts: string[] = [];
|
const parts: string[] = [];
|
||||||
|
|
||||||
if (this.condition.all.length > 0) {
|
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) {
|
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) {
|
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) {
|
if (this.condition.tag !== undefined) {
|
||||||
@@ -286,7 +287,7 @@ export class Matcher {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (this.condition.component !== undefined) {
|
if (this.condition.component !== undefined) {
|
||||||
parts.push(`component(${this.condition.component.name})`);
|
parts.push(`component(${getComponentTypeName(this.condition.component)})`);
|
||||||
}
|
}
|
||||||
|
|
||||||
return `Matcher[${parts.join(' & ')}]`;
|
return `Matcher[${parts.join(' & ')}]`;
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ export { Component } from './Component';
|
|||||||
export { ECSEventType, EventPriority, EVENT_TYPES, EventTypeValidator } from './CoreEvents';
|
export { ECSEventType, EventPriority, EVENT_TYPES, EventTypeValidator } from './CoreEvents';
|
||||||
export * from './Systems';
|
export * from './Systems';
|
||||||
export * from './Utils';
|
export * from './Utils';
|
||||||
|
export * from './Decorators';
|
||||||
export { Scene } from './Scene';
|
export { Scene } from './Scene';
|
||||||
export { IScene, ISceneFactory, ISceneConfig } from './IScene';
|
export { IScene, ISceneFactory, ISceneConfig } from './IScene';
|
||||||
export { EntityManager, EntityQueryBuilder } from './Core/EntityManager';
|
export { EntityManager, EntityQueryBuilder } from './Core/EntityManager';
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { IComponentDebugData } from '../../Types';
|
import { IComponentDebugData } from '../../Types';
|
||||||
import { Core } from '../../Core';
|
import { Core } from '../../Core';
|
||||||
import { ComponentPoolManager } from '../../ECS/Core/ComponentPool';
|
import { ComponentPoolManager } from '../../ECS/Core/ComponentPool';
|
||||||
|
import { getComponentInstanceTypeName } from '../../ECS/Decorators';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 组件数据收集器
|
* 组件数据收集器
|
||||||
@@ -36,7 +37,7 @@ export class ComponentDataCollector {
|
|||||||
entityList.buffer.forEach((entity: any) => {
|
entityList.buffer.forEach((entity: any) => {
|
||||||
if (entity.components) {
|
if (entity.components) {
|
||||||
entity.components.forEach((component: any) => {
|
entity.components.forEach((component: any) => {
|
||||||
const typeName = component.constructor.name;
|
const typeName = getComponentInstanceTypeName(component);
|
||||||
const stats = componentStats.get(typeName) || { count: 0, entities: 0 };
|
const stats = componentStats.get(typeName) || { count: 0, entities: 0 };
|
||||||
stats.count++;
|
stats.count++;
|
||||||
totalInstances++;
|
totalInstances++;
|
||||||
@@ -106,7 +107,7 @@ export class ComponentDataCollector {
|
|||||||
try {
|
try {
|
||||||
for (const entity of entityList.buffer) {
|
for (const entity of entityList.buffer) {
|
||||||
if (entity.components) {
|
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) {
|
if (component) {
|
||||||
calculatedSize = this.calculateQuickObjectSize(component);
|
calculatedSize = this.calculateQuickObjectSize(component);
|
||||||
break;
|
break;
|
||||||
@@ -180,7 +181,7 @@ export class ComponentDataCollector {
|
|||||||
// 找到第一个包含此组件的实体,分析组件大小
|
// 找到第一个包含此组件的实体,分析组件大小
|
||||||
for (const entity of entityList.buffer) {
|
for (const entity of entityList.buffer) {
|
||||||
if (entity.components) {
|
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) {
|
if (component) {
|
||||||
return this.estimateObjectSize(component);
|
return this.estimateObjectSize(component);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { Component } from '../../ECS/Component';
|
import { Component } from '../../ECS/Component';
|
||||||
|
import { getComponentInstanceTypeName } from '../../ECS/Decorators';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 调试数据格式化工具
|
* 调试数据格式化工具
|
||||||
@@ -56,7 +57,7 @@ export class DebugDataFormatter {
|
|||||||
}> {
|
}> {
|
||||||
return components.map((component: Component) => {
|
return components.map((component: Component) => {
|
||||||
const componentDetail = {
|
const componentDetail = {
|
||||||
typeName: component.constructor.name,
|
typeName: getComponentInstanceTypeName(component),
|
||||||
properties: {} as Record<string, any>
|
properties: {} as Record<string, any>
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import { Core } from '../../Core';
|
|||||||
import { Component } from '../../ECS/Component';
|
import { Component } from '../../ECS/Component';
|
||||||
import { ComponentPoolManager } from '../../ECS/Core/ComponentPool';
|
import { ComponentPoolManager } from '../../ECS/Core/ComponentPool';
|
||||||
import { Pool } from '../../Utils/Pool';
|
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;
|
if (!entity || entity.destroyed || !entity.components) continue;
|
||||||
|
|
||||||
for (const component of entity.components) {
|
for (const component of entity.components) {
|
||||||
const typeName = component.constructor.name;
|
const typeName = getComponentInstanceTypeName(component);
|
||||||
componentTypeCounts.set(typeName, (componentTypeCounts.get(typeName) || 0) + 1);
|
componentTypeCounts.set(typeName, (componentTypeCounts.get(typeName) || 0) + 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -486,7 +487,7 @@ export class DebugManager {
|
|||||||
if (!entity || entity.destroyed || !entity.components) continue;
|
if (!entity || entity.destroyed || !entity.components) continue;
|
||||||
|
|
||||||
for (const component of entity.components) {
|
for (const component of entity.components) {
|
||||||
if (component.constructor.name === typeName) {
|
if (getComponentInstanceTypeName(component) === typeName) {
|
||||||
instances.push({
|
instances.push({
|
||||||
entityId: entity.id,
|
entityId: entity.id,
|
||||||
entityName: entity.name || `Entity_${entity.id}`,
|
entityName: entity.name || `Entity_${entity.id}`,
|
||||||
@@ -550,7 +551,7 @@ export class DebugManager {
|
|||||||
const systemTypeMemoryCache = new Map<string, number>();
|
const systemTypeMemoryCache = new Map<string, number>();
|
||||||
|
|
||||||
for (const system of entityProcessors.processors) {
|
for (const system of entityProcessors.processors) {
|
||||||
const systemTypeName = system.constructor.name;
|
const systemTypeName = getSystemInstanceTypeName(system);
|
||||||
|
|
||||||
let systemMemory: number;
|
let systemMemory: number;
|
||||||
if (systemTypeMemoryCache.has(systemTypeName)) {
|
if (systemTypeMemoryCache.has(systemTypeName)) {
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { Core } from '../../Core';
|
|||||||
import { Entity } from '../../ECS/Entity';
|
import { Entity } from '../../ECS/Entity';
|
||||||
import { Component } from '../../ECS/Component';
|
import { Component } from '../../ECS/Component';
|
||||||
import { ComponentTypeManager } from '../../ECS/Utils/ComponentTypeManager';
|
import { ComponentTypeManager } from '../../ECS/Utils/ComponentTypeManager';
|
||||||
|
import { getComponentInstanceTypeName, getSystemInstanceTypeName } from '../../ECS/Decorators';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 实体数据收集器
|
* 实体数据收集器
|
||||||
@@ -77,7 +78,7 @@ export class EntityDataCollector {
|
|||||||
enabled: entity.enabled !== false,
|
enabled: entity.enabled !== false,
|
||||||
activeInHierarchy: entity.activeInHierarchy !== false,
|
activeInHierarchy: entity.activeInHierarchy !== false,
|
||||||
componentCount: entity.components.length,
|
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,
|
parentId: entity.parent?.id || null,
|
||||||
childIds: entity.children?.map((child: Entity) => child.id) || [],
|
childIds: entity.children?.map((child: Entity) => child.id) || [],
|
||||||
depth: entity.getDepth ? entity.getDepth() : 0,
|
depth: entity.getDepth ? entity.getDepth() : 0,
|
||||||
@@ -114,7 +115,7 @@ export class EntityDataCollector {
|
|||||||
parentName: entity.parent?.name || null,
|
parentName: entity.parent?.name || null,
|
||||||
components: componentDetails || [],
|
components: componentDetails || [],
|
||||||
componentCount: entity.components?.length || 0,
|
componentCount: entity.components?.length || 0,
|
||||||
componentTypes: entity.components?.map((comp: any) => comp.constructor.name) || []
|
componentTypes: entity.components?.map((comp: any) => getComponentInstanceTypeName(comp)) || []
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return {
|
return {
|
||||||
@@ -216,7 +217,7 @@ export class EntityDataCollector {
|
|||||||
|
|
||||||
if (entityContainer && entityContainer.entities) {
|
if (entityContainer && entityContainer.entities) {
|
||||||
entityContainer.entities.forEach((entity: any) => {
|
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 signature = componentTypes.length > 0 ? componentTypes.sort().join(', ') : '无组件';
|
||||||
|
|
||||||
const existing = distribution.get(signature);
|
const existing = distribution.get(signature);
|
||||||
@@ -378,7 +379,7 @@ export class EntityDataCollector {
|
|||||||
|
|
||||||
if (entityContainer && entityContainer.entities) {
|
if (entityContainer && entityContainer.entities) {
|
||||||
entityContainer.entities.forEach((entity: any) => {
|
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 signature = componentTypes.length > 0 ? componentTypes.sort().join(', ') : '无组件';
|
||||||
|
|
||||||
const existing = distribution.get(signature);
|
const existing = distribution.get(signature);
|
||||||
@@ -601,7 +602,7 @@ export class EntityDataCollector {
|
|||||||
enabled: entity.enabled !== false,
|
enabled: entity.enabled !== false,
|
||||||
activeInHierarchy: entity.activeInHierarchy !== false,
|
activeInHierarchy: entity.activeInHierarchy !== false,
|
||||||
componentCount: entity.components.length,
|
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,
|
parentId: entity.parent?.id || null,
|
||||||
children: [] as any[],
|
children: [] as any[],
|
||||||
depth: entity.getDepth ? entity.getDepth() : 0,
|
depth: entity.getDepth ? entity.getDepth() : 0,
|
||||||
@@ -690,7 +691,7 @@ export class EntityDataCollector {
|
|||||||
sceneName: sceneInfo.name,
|
sceneName: sceneInfo.name,
|
||||||
sceneType: sceneInfo.type,
|
sceneType: sceneInfo.type,
|
||||||
componentCount: entity.components.length,
|
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',
|
componentMask: entity.componentMask?.toString() || '0',
|
||||||
parentId: entity.parent?.id || null,
|
parentId: entity.parent?.id || null,
|
||||||
childCount: entity.children?.length || 0,
|
childCount: entity.children?.length || 0,
|
||||||
@@ -709,7 +710,7 @@ export class EntityDataCollector {
|
|||||||
properties: Record<string, any>;
|
properties: Record<string, any>;
|
||||||
}> {
|
}> {
|
||||||
return components.map((component: Component) => {
|
return components.map((component: Component) => {
|
||||||
let typeName = component.constructor.name;
|
let typeName = getComponentInstanceTypeName(component);
|
||||||
|
|
||||||
if (!typeName || typeName === 'Object' || typeName === 'Function') {
|
if (!typeName || typeName === 'Object' || typeName === 'Function') {
|
||||||
try {
|
try {
|
||||||
@@ -739,11 +740,11 @@ export class EntityDataCollector {
|
|||||||
// 如果没有找到任何属性,添加一些调试信息
|
// 如果没有找到任何属性,添加一些调试信息
|
||||||
if (Object.keys(properties).length === 0) {
|
if (Object.keys(properties).length === 0) {
|
||||||
properties._info = '该组件没有公开属性';
|
properties._info = '该组件没有公开属性';
|
||||||
properties._componentId = component.constructor.name;
|
properties._componentId = getComponentInstanceTypeName(component);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
properties._error = '属性提取失败';
|
properties._error = '属性提取失败';
|
||||||
properties._componentId = component.constructor.name;
|
properties._componentId = getComponentInstanceTypeName(component);
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { ISystemDebugData } from '../../Types';
|
import { ISystemDebugData } from '../../Types';
|
||||||
import { Core } from '../../Core';
|
import { Core } from '../../Core';
|
||||||
|
import { getSystemInstanceTypeName } from '../../ECS/Decorators';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 系统数据收集器
|
* 系统数据收集器
|
||||||
@@ -43,13 +44,13 @@ export class SystemDataCollector {
|
|||||||
return {
|
return {
|
||||||
totalSystems: systems.length,
|
totalSystems: systems.length,
|
||||||
systemsInfo: systems.map((system: any) => {
|
systemsInfo: systems.map((system: any) => {
|
||||||
const systemName = system.systemName || system.constructor.name;
|
const systemName = system.systemName || getSystemInstanceTypeName(system);
|
||||||
const stats = systemStats.get(systemName);
|
const stats = systemStats.get(systemName);
|
||||||
const data = systemData.get(systemName);
|
const data = systemData.get(systemName);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
name: systemName,
|
name: systemName,
|
||||||
type: system.constructor.name,
|
type: getSystemInstanceTypeName(system),
|
||||||
entityCount: system.entities?.length || 0,
|
entityCount: system.entities?.length || 0,
|
||||||
executionTime: stats?.averageTime || data?.executionTime || 0,
|
executionTime: stats?.averageTime || data?.executionTime || 0,
|
||||||
minExecutionTime: stats?.minTime === Number.MAX_VALUE ? 0 : (stats?.minTime || 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