diff --git a/docs/guide/event-system.md b/docs/guide/event-system.md index bbed1688..d5b8ca55 100644 --- a/docs/guide/event-system.md +++ b/docs/guide/event-system.md @@ -317,50 +317,6 @@ class GameManager { } ``` -## 事件装饰器 - -使用装饰器自动注册事件监听器: - -```typescript -import { EventHandler, AsyncEventHandler } from '@esengine/ecs-framework'; - -class PlayerController { - constructor() { - // 自动调用事件监听器注册 - this.initEventListeners(); - } - - @EventHandler('player_input') - private onPlayerInput(data: { action: string, value: number }): void { - console.log(`玩家输入: ${data.action} = ${data.value}`); - // 处理玩家输入 - } - - @EventHandler('player_attack', { priority: 100 }) - private onPlayerAttack(data: { damage: number, target: string }): void { - console.log(`玩家攻击 ${data.target},造成 ${data.damage} 伤害`); - // 处理攻击逻辑 - } - - @AsyncEventHandler('save_progress') - private async onSaveProgress(data: { checkpointId: string }): Promise { - console.log(`保存进度到检查点: ${data.checkpointId}`); - // 异步保存进度 - await this.saveToCloud(data.checkpointId); - } - - @EventHandler('game_over', { once: true }) - private onGameOver(): void { - console.log('游戏结束!'); - // 这个方法只会被调用一次 - } - - private async saveToCloud(checkpointId: string): Promise { - // 模拟云端保存 - return new Promise(resolve => setTimeout(resolve, 1500)); - } -} -``` ## 批处理事件 diff --git a/packages/core/src/ECS/Core/EventBus.ts b/packages/core/src/ECS/Core/EventBus.ts index 1eaf1ec5..115c198e 100644 --- a/packages/core/src/ECS/Core/EventBus.ts +++ b/packages/core/src/ECS/Core/EventBus.ts @@ -467,116 +467,4 @@ export class GlobalEventBus { } } -/** - * 事件装饰器工厂 - * 用于自动注册事件监听器,支持自动清理 - */ -export function EventHandler(eventType: string, config: IEventListenerConfig = {}) { - return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) { - const originalMethod = descriptor.value; - - // 存储装饰器信息 - if (!target.constructor._eventHandlers) { - target.constructor._eventHandlers = []; - } - target.constructor._eventHandlers.push({ - eventType, - methodName: propertyKey, - config - }); - - // 在类实例化时自动注册监听器 - const initMethod = target.constructor.prototype.initEventListeners || function() {}; - target.constructor.prototype.initEventListeners = function() { - initMethod.call(this); - - // 初始化监听器追踪数组 - if (!this._decoratorEventListeners) { - this._decoratorEventListeners = []; - } - - const eventBus = GlobalEventBus.getInstance(); - const listenerId = eventBus.on(eventType, originalMethod.bind(this), config); - - // 保存监听器ID用于后续清理 - this._decoratorEventListeners.push({ - eventType, - methodName: propertyKey, - listenerId - }); - }; - - // 添加清理方法 - const cleanupMethod = target.constructor.prototype.cleanupEventListeners || function() {}; - target.constructor.prototype.cleanupEventListeners = function() { - cleanupMethod.call(this); - - if (this._decoratorEventListeners) { - const eventBus = GlobalEventBus.getInstance(); - for (const listener of this._decoratorEventListeners) { - eventBus.off(listener.eventType, listener.listenerId); - } - this._decoratorEventListeners.length = 0; - } - }; - - return descriptor; - }; -} - -/** - * 异步事件装饰器工厂 - * 用于自动注册异步事件监听器,支持自动清理 - */ -export function AsyncEventHandler(eventType: string, config: IEventListenerConfig = {}) { - return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) { - const originalMethod = descriptor.value; - - // 存储装饰器信息 - if (!target.constructor._eventHandlers) { - target.constructor._eventHandlers = []; - } - target.constructor._eventHandlers.push({ - eventType, - methodName: propertyKey, - config, - async: true - }); - - const initMethod = target.constructor.prototype.initEventListeners || function() {}; - target.constructor.prototype.initEventListeners = function() { - initMethod.call(this); - - // 初始化监听器追踪数组 - if (!this._decoratorEventListeners) { - this._decoratorEventListeners = []; - } - - const eventBus = GlobalEventBus.getInstance(); - const listenerId = eventBus.onAsync(eventType, originalMethod.bind(this), config); - - // 保存监听器ID用于后续清理 - this._decoratorEventListeners.push({ - eventType, - methodName: propertyKey, - listenerId - }); - }; - - // 添加清理方法 - const cleanupMethod = target.constructor.prototype.cleanupEventListeners || function() {}; - target.constructor.prototype.cleanupEventListeners = function() { - cleanupMethod.call(this); - - if (this._decoratorEventListeners) { - const eventBus = GlobalEventBus.getInstance(); - for (const listener of this._decoratorEventListeners) { - eventBus.off(listener.eventType, listener.listenerId); - } - this._decoratorEventListeners.length = 0; - } - }; - - return descriptor; - }; -} \ No newline at end of file + \ No newline at end of file diff --git a/packages/core/src/ECS/Core/EventSystem.ts b/packages/core/src/ECS/Core/EventSystem.ts index a8e328ed..0786e4fe 100644 --- a/packages/core/src/ECS/Core/EventSystem.ts +++ b/packages/core/src/ECS/Core/EventSystem.ts @@ -578,37 +578,4 @@ export class TypeSafeEventSystem { */ export const GlobalEventSystem = new TypeSafeEventSystem(); -/** - * 事件装饰器 - 用于自动注册事件监听器 - * @param eventType 事件类型 - * @param config 监听器配置 - */ -export function EventListener(eventType: string, config: EventListenerConfig = {}) { - return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) { - const originalMethod = descriptor.value; - - // 在类实例化时自动注册监听器 - const initMethod = target.constructor.prototype.initEventListeners || function () { }; - target.constructor.prototype.initEventListeners = function () { - initMethod.call(this); - GlobalEventSystem.on(eventType, originalMethod.bind(this), config); - }; - }; -} - -/** - * 异步事件装饰器 - * @param eventType 事件类型 - * @param config 监听器配置 - */ -export function AsyncEventListener(eventType: string, config: EventListenerConfig = {}) { - return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) { - const originalMethod = descriptor.value; - - const initMethod = target.constructor.prototype.initEventListeners || function () { }; - target.constructor.prototype.initEventListeners = function () { - initMethod.call(this); - GlobalEventSystem.onAsync(eventType, originalMethod.bind(this), config); - }; - }; -} \ No newline at end of file + \ No newline at end of file diff --git a/packages/core/src/ECS/Core/Events/index.ts b/packages/core/src/ECS/Core/Events/index.ts index c9dfde58..50d79b23 100644 --- a/packages/core/src/ECS/Core/Events/index.ts +++ b/packages/core/src/ECS/Core/Events/index.ts @@ -1,2 +1,2 @@ -export { EventBus, GlobalEventBus, EventHandler, AsyncEventHandler } from '../EventBus'; +export { EventBus, GlobalEventBus } from '../EventBus'; export { TypeSafeEventSystem, EventListenerConfig, EventStats } from '../EventSystem'; \ No newline at end of file diff --git a/packages/core/src/ECS/Systems/EntitySystem.ts b/packages/core/src/ECS/Systems/EntitySystem.ts index 8354882f..95667593 100644 --- a/packages/core/src/ECS/Systems/EntitySystem.ts +++ b/packages/core/src/ECS/Systems/EntitySystem.ts @@ -53,9 +53,6 @@ export abstract class EntitySystem implements ISystemBase { private _scene: Scene | null; protected logger = createLogger('EntitySystem'); - // 装饰器动态添加的方法(可选) - protected initEventListeners?: () => void; - protected cleanupEventListeners?: () => void; /** * 实体ID映射缓存 @@ -139,8 +136,6 @@ export abstract class EntitySystem implements ISystemBase { this._entityIdMapVersion = -1; this._entityIdMapSize = 0; - // 初始化装饰器事件监听器 - this.initDecoratorEventListeners(); this._entityCache = { frame: null, @@ -752,25 +747,6 @@ export abstract class EntitySystem implements ISystemBase { } } - - /** - * 初始化装饰器事件监听器 - */ - protected initDecoratorEventListeners(): void { - if (this.initEventListeners) { - this.initEventListeners(); - } - } - - /** - * 清理装饰器事件监听器 - */ - protected cleanupDecoratorEventListeners(): void { - if (this.cleanupEventListeners) { - this.cleanupEventListeners(); - } - } - /** * 清理手动添加的事件监听器 */ @@ -792,13 +768,8 @@ export abstract class EntitySystem implements ISystemBase { * 由框架调用,处理系统的完整销毁流程 */ public destroy(): void { - // 1. 清理手动添加的事件监听器 this.cleanupManualEventListeners(); - // 2. 清理装饰器事件监听器 - this.cleanupDecoratorEventListeners(); - - // 3. 调用用户的销毁回调 this.onDestroy(); } diff --git a/packages/core/tests/ECS/Core/DecoratorSystem.test.ts b/packages/core/tests/ECS/Core/DecoratorSystem.test.ts deleted file mode 100644 index 6e7b1e2e..00000000 --- a/packages/core/tests/ECS/Core/DecoratorSystem.test.ts +++ /dev/null @@ -1,433 +0,0 @@ -import { Scene } from '../../../src/ECS/Scene'; -import { Entity } from '../../../src/ECS/Entity'; -import { Component } from '../../../src/ECS/Component'; -import { EntitySystem } from '../../../src/ECS/Systems/EntitySystem'; -import { Matcher } from '../../../src/ECS/Utils/Matcher'; -import { EventBus } from '../../../src/ECS/Core/EventBus'; - -// 测试组件 -class TransformComponent extends Component { - public x: number = 0; - public y: number = 0; - public rotation: number = 0; - - constructor(...args: unknown[]) { - super(); - if (args.length >= 1) this.x = args[0] as number; - if (args.length >= 2) this.y = args[1] as number; - if (args.length >= 3) this.rotation = args[2] as number; - } -} - -class VelocityComponent extends Component { - public vx: number = 0; - public vy: number = 0; - - constructor(...args: unknown[]) { - super(); - if (args.length >= 1) this.vx = args[0] as number; - if (args.length >= 2) this.vy = args[1] as number; - } -} - -class HealthComponent extends Component { - public health: number = 100; - public maxHealth: number = 100; - - constructor(...args: unknown[]) { - super(); - if (args.length >= 1) this.health = args[0] as number; - if (args.length >= 2) this.maxHealth = args[1] as number; - } -} - -// 简单的事件装饰器实现(用于测试) -function EventHandler(eventType: string, priority: number = 0) { - return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) { - const originalMethod = descriptor.value; - - // 在原型上标记事件处理器信息 - if (!target.constructor._eventHandlers) { - target.constructor._eventHandlers = []; - } - target.constructor._eventHandlers.push({ - eventType, - methodName: propertyKey, - priority, - handler: originalMethod - }); - - return descriptor; - }; -} - -// 自动初始化事件监听器的基类 -class EventAwareSystem extends EntitySystem { - private eventListenerIds: string[] = []; - - constructor(matcher: Matcher) { - super(matcher); - } - - public override initialize(): void { - super.initialize(); - this.initializeEventHandlers(); - } - - private initializeEventHandlers(): void { - const eventHandlers = (this.constructor as any)._eventHandlers; - if (!eventHandlers || !this.scene?.eventSystem) { - return; - } - - // 按优先级排序并注册事件处理器 - eventHandlers - .sort((a: any, b: any) => b.priority - a.priority) - .forEach((handlerInfo: any) => { - const listenerId = this.scene!.eventSystem.on( - handlerInfo.eventType, - handlerInfo.handler.bind(this), - { priority: handlerInfo.priority } - ); - this.eventListenerIds.push(listenerId); - }); - } - - public cleanup(): void { - // 清理事件监听器 - if (this.scene?.eventSystem) { - this.eventListenerIds.forEach(id => { - // 注意:这里需要修改EventSystem来支持通过ID移除监听器 - // this.scene!.eventSystem.removeListener(id); - }); - } - this.eventListenerIds = []; - } -} - -// 使用装饰器的测试系统 -class DecoratedMovementSystem extends EventAwareSystem { - public processedEntities: Entity[] = []; - public receivedEvents: any[] = []; - public entityMovedEvents: any[] = []; - - constructor() { - super(Matcher.empty().all(TransformComponent, VelocityComponent)); - } - - protected override process(entities: Entity[]): void { - this.processedEntities = [...entities]; - - for (const entity of entities) { - const transform = entity.getComponent(TransformComponent)!; - const velocity = entity.getComponent(VelocityComponent)!; - - const oldX = transform.x; - const oldY = transform.y; - - // 更新位置 - transform.x += velocity.vx; - transform.y += velocity.vy; - - // 发射实体移动事件 - if (this.scene?.eventSystem) { - this.scene.eventSystem.emit('entity:moved', { - entityId: entity.id, - entityName: entity.name, - oldPosition: { x: oldX, y: oldY }, - newPosition: { x: transform.x, y: transform.y } - }); - } - } - } - - @EventHandler('entity:moved', 10) - onEntityMoved(data: any): void { - this.entityMovedEvents.push(data); - } - - @EventHandler('entity:health_changed', 5) - onHealthChanged(data: any): void { - this.receivedEvents.push({ type: 'health_changed', data }); - } - - @EventHandler('system:initialized', 15) - onSystemInitialized(data: any): void { - this.receivedEvents.push({ type: 'system_initialized', data }); - } -} - -class HealthSystem extends EventAwareSystem { - public processedEntities: Entity[] = []; - public receivedEvents: any[] = []; - - constructor() { - super(Matcher.empty().all(HealthComponent)); - } - - protected override process(entities: Entity[]): void { - this.processedEntities = [...entities]; - - for (const entity of entities) { - const health = entity.getComponent(HealthComponent)!; - - // 模拟健康值变化 - if (health.health > 0) { - const oldHealth = health.health; - health.health = Math.max(0, health.health - 1); - - // 发射健康值变化事件 - if (this.scene?.eventSystem && oldHealth !== health.health) { - this.scene.eventSystem.emit('entity:health_changed', { - entityId: entity.id, - entityName: entity.name, - oldHealth, - newHealth: health.health, - isDead: health.health <= 0 - }); - } - } - } - } - - @EventHandler('entity:health_changed', 8) - onHealthChanged(data: any): void { - this.receivedEvents.push(data); - - // 如果实体死亡,禁用它 - if (data.isDead) { - const entity = this.scene?.findEntityById(data.entityId); - if (entity) { - entity.enabled = false; - } - } - } -} - -describe('装饰器系统测试', () => { - let scene: Scene; - - beforeEach(() => { - scene = new Scene(); - scene.name = "DecoratorTestScene"; - }); - - describe('事件装饰器基础功能', () => { - test('装饰器应该正确注册事件处理器', () => { - const movementSystem = new DecoratedMovementSystem(); - - // 检查装饰器是否正确注册了事件处理器信息 - const eventHandlers = (DecoratedMovementSystem as any)._eventHandlers; - expect(eventHandlers).toBeDefined(); - expect(eventHandlers.length).toBe(3); - - // 检查事件处理器信息 - const entityMovedHandler = eventHandlers.find((h: any) => h.eventType === 'entity:moved'); - expect(entityMovedHandler).toBeDefined(); - expect(entityMovedHandler.priority).toBe(10); - expect(entityMovedHandler.methodName).toBe('onEntityMoved'); - }); - - test('系统初始化时应该自动注册事件监听器', () => { - const entity = scene.createEntity("TestEntity"); - entity.addComponent(new TransformComponent(0, 0)); - entity.addComponent(new VelocityComponent(1, 1)); - - const movementSystem = new DecoratedMovementSystem(); - scene.addEntityProcessor(movementSystem); - - // 验证系统正确初始化 - expect(movementSystem.entities.length).toBe(1); - - // 运行一次更新,应该触发entity:moved事件 - scene.update(); - - // 检查事件是否被正确处理 - expect(movementSystem.entityMovedEvents.length).toBe(1); - expect(movementSystem.entityMovedEvents[0].entityId).toBe(entity.id); - expect(movementSystem.entityMovedEvents[0].newPosition.x).toBe(1); - expect(movementSystem.entityMovedEvents[0].newPosition.y).toBe(1); - }); - }); - - describe('多系统事件交互', () => { - test('多个系统应该能够响应同一事件', () => { - const entity = scene.createEntity("TestEntity"); - entity.addComponent(new TransformComponent(0, 0)); - entity.addComponent(new VelocityComponent(1, 1)); - entity.addComponent(new HealthComponent(10)); - - const movementSystem = new DecoratedMovementSystem(); - const healthSystem = new HealthSystem(); - - scene.addEntityProcessor(movementSystem); - scene.addEntityProcessor(healthSystem); - - // 运行几次更新 - scene.update(); - scene.update(); - scene.update(); - - // 检查健康系统是否处理了健康变化事件 - expect(healthSystem.receivedEvents.length).toBeGreaterThan(0); - - // 检查移动系统是否也接收到了健康变化事件 - const healthChangedEvents = movementSystem.receivedEvents.filter(e => e.type === 'health_changed'); - expect(healthChangedEvents.length).toBeGreaterThan(0); - }); - - test('事件优先级应该正确工作', () => { - const entity = scene.createEntity("TestEntity"); - entity.addComponent(new TransformComponent(0, 0)); - entity.addComponent(new VelocityComponent(1, 1)); - - const movementSystem = new DecoratedMovementSystem(); - scene.addEntityProcessor(movementSystem); - - // 发射系统初始化事件(如果有的话) - if (scene.eventSystem) { - scene.eventSystem.emit('system:initialized', { - systemName: 'DecoratedMovementSystem', - timestamp: Date.now() - }); - } - - // 检查事件是否被接收 - scene.update(); - - // 验证不同优先级的事件都被处理了 - const systemInitEvents = movementSystem.receivedEvents.filter(e => e.type === 'system_initialized'); - expect(systemInitEvents.length).toBeGreaterThanOrEqual(0); - }); - }); - - describe('装饰器系统的时序问题', () => { - test('先添加实体再添加装饰器系统 - 事件应该正常工作', () => { - // 先创建实体 - const entity = scene.createEntity("TestEntity"); - entity.addComponent(new TransformComponent(5, 5)); - entity.addComponent(new VelocityComponent(2, 3)); - - // 然后添加装饰器系统 - const movementSystem = new DecoratedMovementSystem(); - scene.addEntityProcessor(movementSystem); - - // 验证系统正确识别了实体 - expect(movementSystem.entities.length).toBe(1); - - // 运行更新,应该触发事件 - scene.update(); - - // 检查事件装饰器是否正常工作 - expect(movementSystem.entityMovedEvents.length).toBe(1); - expect(movementSystem.entityMovedEvents[0].oldPosition.x).toBe(5); - expect(movementSystem.entityMovedEvents[0].newPosition.x).toBe(7); - }); - - test('动态添加组件后装饰器事件应该正常', () => { - const movementSystem = new DecoratedMovementSystem(); - scene.addEntityProcessor(movementSystem); - - const entity = scene.createEntity("DynamicEntity"); - entity.addComponent(new TransformComponent(0, 0)); - - // 初始状态:系统不匹配 - expect(movementSystem.entities.length).toBe(0); - - // 动态添加速度组件 - entity.addComponent(new VelocityComponent(1, 1)); - - // 系统应该匹配 - expect(movementSystem.entities.length).toBe(1); - - // 运行更新,事件应该正常触发 - scene.update(); - expect(movementSystem.entityMovedEvents.length).toBe(1); - }); - }); - - describe('装饰器系统的清理', () => { - test('系统移除时应该清理事件监听器', () => { - const movementSystem = new DecoratedMovementSystem(); - scene.addEntityProcessor(movementSystem); - - // 验证系统已添加 - expect(scene.entityProcessors.count).toBe(1); - - // 移除系统 - scene.removeEntityProcessor(movementSystem); - expect(scene.entityProcessors.count).toBe(0); - - // 清理事件监听器 - movementSystem.cleanup(); - - // 验证事件监听器已清理(这里主要是检查不抛出异常) - expect(() => { - if (scene.eventSystem) { - scene.eventSystem.emit('entity:moved', { test: true }); - } - }).not.toThrow(); - }); - }); - - describe('边界情况测试', () => { - test('没有装饰器的系统应该正常工作', () => { - class SimpleSystem extends EventAwareSystem { - public processedEntities: Entity[] = []; - - constructor() { - super(Matcher.empty().all(TransformComponent)); - } - - protected override process(entities: Entity[]): void { - this.processedEntities = [...entities]; - } - } - - const entity = scene.createEntity("SimpleEntity"); - entity.addComponent(new TransformComponent(1, 1)); - - const simpleSystem = new SimpleSystem(); - - expect(() => { - scene.addEntityProcessor(simpleSystem); - }).not.toThrow(); - - expect(simpleSystem.entities.length).toBe(1); - - scene.update(); - expect(simpleSystem.processedEntities.length).toBe(1); - }); - - test('空事件数据应该正常处理', () => { - const movementSystem = new DecoratedMovementSystem(); - scene.addEntityProcessor(movementSystem); - - // 发射空事件 - if (scene.eventSystem) { - scene.eventSystem.emit('entity:health_changed', null); - scene.eventSystem.emit('entity:health_changed', undefined); - scene.eventSystem.emit('entity:health_changed', {}); - } - - // 系统应该能够处理空数据而不崩溃 - expect(() => { - scene.update(); - }).not.toThrow(); - }); - }); - - afterEach(() => { - // 清理场景 - scene.destroyAllEntities(); - - // 清理系统并清理它们的事件监听器 - const processors = [...scene.entityProcessors.processors]; - processors.forEach(processor => { - if (processor instanceof EventAwareSystem) { - processor.cleanup(); - } - scene.removeEntityProcessor(processor); - }); - }); -}); \ No newline at end of file diff --git a/packages/core/tests/ECS/Core/EventBus.test.ts b/packages/core/tests/ECS/Core/EventBus.test.ts index 5624834f..f4c55bee 100644 --- a/packages/core/tests/ECS/Core/EventBus.test.ts +++ b/packages/core/tests/ECS/Core/EventBus.test.ts @@ -1,4 +1,4 @@ -import { EventBus, GlobalEventBus, EventHandler, AsyncEventHandler } from '../../../src/ECS/Core/EventBus'; +import { EventBus, GlobalEventBus } from '../../../src/ECS/Core/EventBus'; import { IEventListenerConfig, IEventStats } from '../../../src/Types'; import { ECSEventType, EventPriority } from '../../../src/ECS/CoreEvents'; @@ -477,53 +477,3 @@ describe('GlobalEventBus - 全局事件总线测试', () => { }); }); -describe('事件装饰器测试', () => { - // Mock class for testing decorators - class TestClass { - public receivedEvents: any[] = []; - - @EventHandler('decorator:event') - handleEvent(data: any) { - this.receivedEvents.push(data); - } - - @AsyncEventHandler('async:decorator:event') - async handleAsyncEvent(data: any) { - this.receivedEvents.push(data); - } - } - - test('EventHandler装饰器应该能够自动注册监听器', () => { - const instance = new TestClass(); - - // 手动调用初始化方法来注册装饰器定义的监听器 - if (typeof (instance as any).initEventListeners === 'function') { - (instance as any).initEventListeners(); - } - - const eventBus = GlobalEventBus.getInstance(); - eventBus.emit('decorator:event', { message: 'decorated event' }); - - expect(instance.receivedEvents.length).toBe(1); - expect(instance.receivedEvents[0].message).toBe('decorated event'); - }); - - test('AsyncEventHandler装饰器应该能够自动注册异步监听器', async () => { - const instance = new TestClass(); - - // 手动调用初始化方法来注册装饰器定义的监听器 - if (typeof (instance as any).initEventListeners === 'function') { - (instance as any).initEventListeners(); - } - - const eventBus = GlobalEventBus.getInstance(); - await eventBus.emitAsync('async:decorator:event', { message: 'async decorated event' }); - - expect(instance.receivedEvents.length).toBe(1); - expect(instance.receivedEvents[0].message).toBe('async decorated event'); - }); - - afterEach(() => { - GlobalEventBus.reset(); - }); -}); \ No newline at end of file diff --git a/packages/core/tests/ECS/Core/SystemTimingIssue.test.ts b/packages/core/tests/ECS/Core/SystemTimingIssue.test.ts deleted file mode 100644 index 9c5286ef..00000000 --- a/packages/core/tests/ECS/Core/SystemTimingIssue.test.ts +++ /dev/null @@ -1,375 +0,0 @@ -import { Scene } from '../../../src/ECS/Scene'; -import { Entity } from '../../../src/ECS/Entity'; -import { Component } from '../../../src/ECS/Component'; -import { EntitySystem } from '../../../src/ECS/Systems/EntitySystem'; -import { Matcher } from '../../../src/ECS/Utils/Matcher'; - -// 测试组件 -class PositionComponent extends Component { - public x: number = 0; - public y: number = 0; - - constructor(...args: unknown[]) { - super(); - const [x = 0, y = 0] = args as [number?, number?]; - this.x = x; - this.y = y; - } -} - -class VelocityComponent extends Component { - public vx: number = 0; - public vy: number = 0; - - constructor(...args: unknown[]) { - super(); - const [vx = 0, vy = 0] = args as [number?, number?]; - this.vx = vx; - this.vy = vy; - } -} - -class HealthComponent extends Component { - public health: number = 100; - - constructor(...args: unknown[]) { - super(); - const [health = 100] = args as [number?]; - this.health = health; - } -} - -// 测试系统 -class MovementSystem extends EntitySystem { - public processedEntities: Entity[] = []; - - constructor() { - super(Matcher.empty().all(PositionComponent, VelocityComponent)); - } - - protected override process(entities: Entity[]): void { - this.processedEntities = [...entities]; - // 简单的移动逻辑 - for (const entity of entities) { - const position = entity.getComponent(PositionComponent)!; - const velocity = entity.getComponent(VelocityComponent)!; - position.x += velocity.vx; - position.y += velocity.vy; - } - } -} - -class HealthSystem extends EntitySystem { - public processedEntities: Entity[] = []; - - constructor() { - super(Matcher.empty().all(HealthComponent)); - } - - protected override process(entities: Entity[]): void { - this.processedEntities = [...entities]; - // 简单的健康检查逻辑 - for (const entity of entities) { - const health = entity.getComponent(HealthComponent)!; - if (health.health <= 0) { - // 标记为死亡,但不在这里销毁实体 - entity.enabled = false; - } - } - } -} - -describe('ECS系统时序问题测试', () => { - let scene: Scene; - - beforeEach(() => { - scene = new Scene(); - scene.name = "TimingTestScene"; - }); - - describe('实体添加时序问题', () => { - test(' 先添加实体再添加系统 - 暴露时序问题', () => { - // 第一步:先创建并添加实体 - const player = scene.createEntity("Player"); - player.addComponent(new PositionComponent(10, 20)); - player.addComponent(new VelocityComponent(1, 1)); - player.addComponent(new HealthComponent(100)); - - const enemy = scene.createEntity("Enemy"); - enemy.addComponent(new PositionComponent(50, 60)); - enemy.addComponent(new VelocityComponent(-1, 0)); - enemy.addComponent(new HealthComponent(80)); - - // 验证实体已创建 - expect(scene.entities.count).toBe(2); - - // 第二步:然后添加系统(这里可能出现时序问题) - const movementSystem = new MovementSystem(); - const healthSystem = new HealthSystem(); - - scene.addEntityProcessor(movementSystem); - scene.addEntityProcessor(healthSystem); - - // 验证系统已添加 - expect(scene.entityProcessors.count).toBe(2); - - // 关键测试:检查系统是否正确识别了已存在的实体 - console.log("MovementSystem匹配的实体数量:", movementSystem.entities.length); - console.log("HealthSystem匹配的实体数量:", healthSystem.entities.length); - console.log("Player组件:", player.components.map(c => c.constructor.name)); - console.log("Enemy组件:", enemy.components.map(c => c.constructor.name)); - - // 预期结果:移动系统应该匹配到2个实体(都有Position+Velocity) - expect(movementSystem.entities.length).toBe(2); - // 预期结果:健康系统应该匹配到2个实体(都有Health) - expect(healthSystem.entities.length).toBe(2); - - // 运行一次更新看看 - scene.update(); - - // 检查系统是否处理了实体 - expect(movementSystem.processedEntities.length).toBe(2); - expect(healthSystem.processedEntities.length).toBe(2); - - // 检查移动逻辑是否生效 - const playerPos = player.getComponent(PositionComponent)!; - expect(playerPos.x).toBe(11); // 10 + 1 - expect(playerPos.y).toBe(21); // 20 + 1 - }); - - test(' 先添加系统再添加实体 - 正常工作的情况', () => { - // 第一步:先添加系统 - const movementSystem = new MovementSystem(); - const healthSystem = new HealthSystem(); - - scene.addEntityProcessor(movementSystem); - scene.addEntityProcessor(healthSystem); - - // 验证系统已添加但没有实体 - expect(scene.entityProcessors.count).toBe(2); - expect(movementSystem.entities.length).toBe(0); - expect(healthSystem.entities.length).toBe(0); - - // 第二步:然后创建并添加实体 - const player = scene.createEntity("Player"); - player.addComponent(new PositionComponent(10, 20)); - player.addComponent(new VelocityComponent(1, 1)); - player.addComponent(new HealthComponent(100)); - - const enemy = scene.createEntity("Enemy"); - enemy.addComponent(new PositionComponent(50, 60)); - enemy.addComponent(new VelocityComponent(-1, 0)); - enemy.addComponent(new HealthComponent(80)); - - // 验证实体已创建 - expect(scene.entities.count).toBe(2); - - // 关键测试:检查系统是否正确识别了新添加的实体 - console.log("MovementSystem匹配的实体数量:", movementSystem.entities.length); - console.log("HealthSystem匹配的实体数量:", healthSystem.entities.length); - - // 预期结果:系统应该自动匹配到新实体 - expect(movementSystem.entities.length).toBe(2); - expect(healthSystem.entities.length).toBe(2); - - // 运行一次更新 - scene.update(); - - // 检查系统是否处理了实体 - expect(movementSystem.processedEntities.length).toBe(2); - expect(healthSystem.processedEntities.length).toBe(2); - }); - }); - - describe('组件动态修改时序问题', () => { - test(' 运行时动态添加组件 - 检查系统响应', () => { - // 先设置好系统 - const movementSystem = new MovementSystem(); - const healthSystem = new HealthSystem(); - scene.addEntityProcessor(movementSystem); - scene.addEntityProcessor(healthSystem); - - // 创建只有位置组件的实体 - const entity = scene.createEntity("TestEntity"); - entity.addComponent(new PositionComponent(0, 0)); - - // 初始状态:只有健康系统不匹配,移动系统不匹配 - expect(movementSystem.entities.length).toBe(0); - expect(healthSystem.entities.length).toBe(0); - - // 动态添加速度组件 - entity.addComponent(new VelocityComponent(5, 5)); - - // 关键测试:移动系统是否立即识别到这个实体 - console.log("添加VelocityComponent后MovementSystem实体数:", movementSystem.entities.length); - expect(movementSystem.entities.length).toBe(1); - - // 动态添加健康组件 - entity.addComponent(new HealthComponent(50)); - - // 关键测试:健康系统是否立即识别到这个实体 - console.log("添加HealthComponent后HealthSystem实体数:", healthSystem.entities.length); - expect(healthSystem.entities.length).toBe(1); - - // 运行更新确认处理 - scene.update(); - expect(movementSystem.processedEntities.length).toBe(1); - expect(healthSystem.processedEntities.length).toBe(1); - }); - - test(' 运行时动态移除组件 - 检查系统响应', () => { - // 先设置好系统 - const movementSystem = new MovementSystem(); - scene.addEntityProcessor(movementSystem); - - // 创建完整的可移动实体 - const entity = scene.createEntity("MovableEntity"); - entity.addComponent(new PositionComponent(0, 0)); - entity.addComponent(new VelocityComponent(5, 5)); - - // 确认系统识别了实体 - expect(movementSystem.entities.length).toBe(1); - - // 动态移除速度组件 - const velocityComponent = entity.getComponent(VelocityComponent); - if (velocityComponent) { - entity.removeComponent(velocityComponent); - } - - // 关键测试:移动系统是否立即移除了这个实体 - console.log("移除VelocityComponent后MovementSystem实体数:", movementSystem.entities.length); - expect(movementSystem.entities.length).toBe(0); - - // 重新添加速度组件 - entity.addComponent(new VelocityComponent(3, 3)); - - // 关键测试:移动系统是否重新识别到这个实体 - console.log("重新添加VelocityComponent后MovementSystem实体数:", movementSystem.entities.length); - expect(movementSystem.entities.length).toBe(1); - }); - }); - - describe('系统初始化时序问题', () => { - test(' 系统initialize方法调用时机', () => { - // 创建实体 - const entity1 = scene.createEntity("Entity1"); - entity1.addComponent(new PositionComponent(10, 10)); - entity1.addComponent(new VelocityComponent(1, 1)); - - const entity2 = scene.createEntity("Entity2"); - entity2.addComponent(new PositionComponent(20, 20)); - entity2.addComponent(new VelocityComponent(2, 2)); - - // 创建系统但先不添加到场景 - const movementSystem = new MovementSystem(); - - // 检查系统初始状态 - expect(movementSystem.entities.length).toBe(0); - - // 添加系统到场景 - scene.addEntityProcessor(movementSystem); - - // 关键测试:系统是否在添加时正确初始化并发现已存在的实体 - console.log("系统添加后发现的实体数:", movementSystem.entities.length); - - // 这个测试会暴露initialize方法是否被正确调用 - expect(movementSystem.entities.length).toBe(2); - }); - - test(' 批量实体操作的时序问题', () => { - const movementSystem = new MovementSystem(); - scene.addEntityProcessor(movementSystem); - - // 批量创建实体 - const entities = scene.createEntities(5, "BatchEntity"); - - // 为所有实体添加组件 - entities.forEach((entity, index) => { - entity.addComponent(new PositionComponent(index * 10, index * 10)); - entity.addComponent(new VelocityComponent(index, index)); - }); - - // 关键测试:系统是否识别了所有批量创建的实体 - console.log("批量操作后系统实体数:", movementSystem.entities.length); - expect(movementSystem.entities.length).toBe(5); - - // 运行更新确认处理 - scene.update(); - expect(movementSystem.processedEntities.length).toBe(5); - }); - }); - - describe('事件装饰器时序问题', () => { - // 模拟使用事件装饰器的系统 - class EventDecoratedSystem extends EntitySystem { - public receivedEvents: any[] = []; - - constructor() { - super(Matcher.empty().all(PositionComponent)); - // 模拟装饰器初始化 - this.initEventListeners(); - } - - // 模拟 @EventListener('entity:moved') 装饰器 - private initEventListeners() { - // 这里应该通过装饰器自动完成 - scene.eventSystem?.on('entity:moved', (data) => { - this.onEntityMoved(data); - }); - } - - public onEntityMoved(data: any) { - this.receivedEvents.push(data); - } - - protected override process(entities: Entity[]): void { - // 处理实体移动并发射事件 - for (const entity of entities) { - const pos = entity.getComponent(PositionComponent)!; - // 模拟移动 - pos.x += 1; - - // 发射移动事件 - scene.eventSystem?.emit('entity:moved', { - entityId: entity.id, - position: { x: pos.x, y: pos.y } - }); - } - } - } - - test(' 事件装饰器系统的初始化时序', () => { - // 先创建实体 - const entity = scene.createEntity("EventTestEntity"); - entity.addComponent(new PositionComponent(0, 0)); - - // 然后添加使用事件装饰器的系统 - const eventSystem = new EventDecoratedSystem(); - scene.addEntityProcessor(eventSystem); - - // 验证系统正确识别了实体 - expect(eventSystem.entities.length).toBe(1); - - // 运行更新,应该触发事件 - scene.update(); - - // 关键测试:事件装饰器是否正确工作 - console.log("接收到的事件数量:", eventSystem.receivedEvents.length); - expect(eventSystem.receivedEvents.length).toBe(1); - - // 验证事件数据 - const event = eventSystem.receivedEvents[0]; - expect(event.entityId).toBe(entity.id); - expect(event.position.x).toBe(1); // 移动后的位置 - }); - }); - - afterEach(() => { - // 清理场景 - scene.destroyAllEntities(); - - // 手动清理系统 - const processors = [...scene.entityProcessors.processors]; - processors.forEach(processor => scene.removeEntityProcessor(processor)); - }); -}); \ No newline at end of file diff --git a/packages/core/tests/ECS/Systems/EntitySystem.test.ts b/packages/core/tests/ECS/Systems/EntitySystem.test.ts index 5efdb93c..0c47cbce 100644 --- a/packages/core/tests/ECS/Systems/EntitySystem.test.ts +++ b/packages/core/tests/ECS/Systems/EntitySystem.test.ts @@ -3,7 +3,7 @@ import { Entity } from '../../../src/ECS/Entity'; import { Component } from '../../../src/ECS/Component'; import { Scene } from '../../../src/ECS/Scene'; import { Matcher } from '../../../src/ECS/Utils/Matcher'; -import { EventHandler, AsyncEventHandler, GlobalEventBus } from '../../../src/ECS/Core/EventBus'; +import { GlobalEventBus } from '../../../src/ECS/Core/EventBus'; import { TypeSafeEventSystem } from '../../../src/ECS/Core/EventSystem'; // 测试组件 @@ -23,18 +23,11 @@ interface TestEvent { message: string; } -interface AsyncTestEvent { - data: string; - timestamp: number; -} // 具体的实体系统实现 class ConcreteEntitySystem extends EntitySystem { public processCallCount = 0; public eventHandlerCallCount = 0; - public asyncEventHandlerCallCount = 0; - public lastEvent: TestEvent | null = null; - public lastAsyncEvent: AsyncTestEvent | null = null; constructor() { super(Matcher.all(TestComponent)); @@ -44,19 +37,6 @@ class ConcreteEntitySystem extends EntitySystem { this.processCallCount++; } - @EventHandler('test_event') - onTestEvent(event: TestEvent): void { - this.eventHandlerCallCount++; - this.lastEvent = event; - } - - @AsyncEventHandler('async_test_event') - async onAsyncTestEvent(event: AsyncTestEvent): Promise { - this.asyncEventHandlerCallCount++; - this.lastAsyncEvent = event; - // 模拟异步操作 - await new Promise(resolve => setTimeout(resolve, 10)); - } // 测试用的手动事件监听器 public addManualEventListener(): void { @@ -122,53 +102,6 @@ describe('EntitySystem', () => { scene.removeSystem(system); }); - describe('装饰器事件处理', () => { - it('应该自动注册 @EventHandler 装饰器标记的方法', () => { - const testEvent: TestEvent = { id: 1, message: 'test' }; - - // 发射事件(使用全局事件总线,因为装饰器注册在那里) - GlobalEventBus.getInstance().emit('test_event', testEvent); - - // 验证事件处理器被调用 - expect(system.eventHandlerCallCount).toBe(1); - expect(system.lastEvent).toEqual(testEvent); - }); - - it('应该自动注册 @AsyncEventHandler 装饰器标记的方法', async () => { - const asyncEvent: AsyncTestEvent = { - data: 'async test', - timestamp: Date.now() - }; - - // 发射异步事件(使用全局事件总线) - await GlobalEventBus.getInstance().emitAsync('async_test_event', asyncEvent); - - // 等待异步处理完成 - await new Promise(resolve => setTimeout(resolve, 20)); - - // 验证异步事件处理器被调用 - expect(system.asyncEventHandlerCallCount).toBe(1); - expect(system.lastAsyncEvent).toEqual(asyncEvent); - }); - - it('当系统被销毁时,应该自动清理装饰器事件监听器', () => { - const testEvent: TestEvent = { id: 2, message: 'test after destroy' }; - - // 验证事件监听器工作正常 - GlobalEventBus.getInstance().emit('test_event', testEvent); - expect(system.eventHandlerCallCount).toBe(1); - - // 销毁系统 - scene.removeSystem(system); - - // 重置计数器并再次发射事件 - system.eventHandlerCallCount = 0; - GlobalEventBus.getInstance().emit('test_event', testEvent); - - // 验证事件监听器已被清理,不再响应事件 - expect(system.eventHandlerCallCount).toBe(0); - }); - }); describe('手动事件监听器管理', () => { it('应该能够手动添加事件监听器', () => { @@ -241,23 +174,18 @@ describe('EntitySystem', () => { // 添加手动监听器 system.testAddEventListener('destroy_order_test', handler); - // 验证装饰器和手动监听器都工作 - GlobalEventBus.getInstance().emit('test_event', testEvent); + // 验证手动监听器工作 scene.eventSystem.emitSync('destroy_order_test', {}); - expect(system.eventHandlerCallCount).toBe(1); expect(handler).toHaveBeenCalledTimes(1); // 直接调用框架的销毁方法 system.destroy(); - // 重置计数器并验证所有监听器都被清理 - system.eventHandlerCallCount = 0; + // 重置计数器并验证监听器被清理 handler.mockClear(); - GlobalEventBus.getInstance().emit('test_event', testEvent); scene.eventSystem.emitSync('destroy_order_test', {}); - expect(system.eventHandlerCallCount).toBe(0); expect(handler).not.toHaveBeenCalled(); }); }); @@ -286,41 +214,4 @@ describe('EntitySystem', () => { }); }); - describe('多个事件监听器', () => { - it('应该支持同一个系统监听多个不同的事件', () => { - const event1: TestEvent = { id: 1, message: 'first' }; - const event2: AsyncTestEvent = { data: 'second', timestamp: Date.now() }; - - // 发射两个不同的事件 - GlobalEventBus.getInstance().emit('test_event', event1); - GlobalEventBus.getInstance().emitAsync('async_test_event', event2); - - // 等待异步处理 - return new Promise(resolve => { - setTimeout(() => { - expect(system.eventHandlerCallCount).toBe(1); - expect(system.asyncEventHandlerCallCount).toBe(1); - expect(system.lastEvent).toEqual(event1); - expect(system.lastAsyncEvent).toEqual(event2); - resolve(); - }, 50); - }); - }); - - it('应该支持多个系统监听同一个事件', () => { - const system2 = new ConcreteEntitySystem(); - scene.addSystem(system2); - - const testEvent: TestEvent = { id: 4, message: 'shared event' }; - - // 发射事件 - GlobalEventBus.getInstance().emit('test_event', testEvent); - - // 验证两个系统都接收到了事件 - expect(system.eventHandlerCallCount).toBe(1); - expect(system2.eventHandlerCallCount).toBe(1); - expect(system.lastEvent).toEqual(testEvent); - expect(system2.lastEvent).toEqual(testEvent); - }); - }); }); \ No newline at end of file