移除eventhandler装饰器
This commit is contained in:
@@ -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<void> {
|
|
||||||
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<void> {
|
|
||||||
// 模拟云端保存
|
|
||||||
return new Promise(resolve => setTimeout(resolve, 1500));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## 批处理事件
|
## 批处理事件
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -578,37 +578,4 @@ export class TypeSafeEventSystem {
|
|||||||
*/
|
*/
|
||||||
export const GlobalEventSystem = new 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);
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,2 +1,2 @@
|
|||||||
export { EventBus, GlobalEventBus, EventHandler, AsyncEventHandler } from '../EventBus';
|
export { EventBus, GlobalEventBus } from '../EventBus';
|
||||||
export { TypeSafeEventSystem, EventListenerConfig, EventStats } from '../EventSystem';
|
export { TypeSafeEventSystem, EventListenerConfig, EventStats } from '../EventSystem';
|
||||||
@@ -53,9 +53,6 @@ export abstract class EntitySystem implements ISystemBase {
|
|||||||
private _scene: Scene | null;
|
private _scene: Scene | null;
|
||||||
protected logger = createLogger('EntitySystem');
|
protected logger = createLogger('EntitySystem');
|
||||||
|
|
||||||
// 装饰器动态添加的方法(可选)
|
|
||||||
protected initEventListeners?: () => void;
|
|
||||||
protected cleanupEventListeners?: () => void;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 实体ID映射缓存
|
* 实体ID映射缓存
|
||||||
@@ -139,8 +136,6 @@ export abstract class EntitySystem implements ISystemBase {
|
|||||||
this._entityIdMapVersion = -1;
|
this._entityIdMapVersion = -1;
|
||||||
this._entityIdMapSize = 0;
|
this._entityIdMapSize = 0;
|
||||||
|
|
||||||
// 初始化装饰器事件监听器
|
|
||||||
this.initDecoratorEventListeners();
|
|
||||||
|
|
||||||
this._entityCache = {
|
this._entityCache = {
|
||||||
frame: null,
|
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 {
|
public destroy(): void {
|
||||||
// 1. 清理手动添加的事件监听器
|
|
||||||
this.cleanupManualEventListeners();
|
this.cleanupManualEventListeners();
|
||||||
|
|
||||||
// 2. 清理装饰器事件监听器
|
|
||||||
this.cleanupDecoratorEventListeners();
|
|
||||||
|
|
||||||
// 3. 调用用户的销毁回调
|
|
||||||
this.onDestroy();
|
this.onDestroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -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 { IEventListenerConfig, IEventStats } from '../../../src/Types';
|
||||||
import { ECSEventType, EventPriority } from '../../../src/ECS/CoreEvents';
|
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();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -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));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -3,7 +3,7 @@ import { Entity } from '../../../src/ECS/Entity';
|
|||||||
import { Component } from '../../../src/ECS/Component';
|
import { Component } from '../../../src/ECS/Component';
|
||||||
import { Scene } from '../../../src/ECS/Scene';
|
import { Scene } from '../../../src/ECS/Scene';
|
||||||
import { Matcher } from '../../../src/ECS/Utils/Matcher';
|
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';
|
import { TypeSafeEventSystem } from '../../../src/ECS/Core/EventSystem';
|
||||||
|
|
||||||
// 测试组件
|
// 测试组件
|
||||||
@@ -23,18 +23,11 @@ interface TestEvent {
|
|||||||
message: string;
|
message: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface AsyncTestEvent {
|
|
||||||
data: string;
|
|
||||||
timestamp: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 具体的实体系统实现
|
// 具体的实体系统实现
|
||||||
class ConcreteEntitySystem extends EntitySystem {
|
class ConcreteEntitySystem extends EntitySystem {
|
||||||
public processCallCount = 0;
|
public processCallCount = 0;
|
||||||
public eventHandlerCallCount = 0;
|
public eventHandlerCallCount = 0;
|
||||||
public asyncEventHandlerCallCount = 0;
|
|
||||||
public lastEvent: TestEvent | null = null;
|
|
||||||
public lastAsyncEvent: AsyncTestEvent | null = null;
|
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super(Matcher.all(TestComponent));
|
super(Matcher.all(TestComponent));
|
||||||
@@ -44,19 +37,6 @@ class ConcreteEntitySystem extends EntitySystem {
|
|||||||
this.processCallCount++;
|
this.processCallCount++;
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler('test_event')
|
|
||||||
onTestEvent(event: TestEvent): void {
|
|
||||||
this.eventHandlerCallCount++;
|
|
||||||
this.lastEvent = event;
|
|
||||||
}
|
|
||||||
|
|
||||||
@AsyncEventHandler('async_test_event')
|
|
||||||
async onAsyncTestEvent(event: AsyncTestEvent): Promise<void> {
|
|
||||||
this.asyncEventHandlerCallCount++;
|
|
||||||
this.lastAsyncEvent = event;
|
|
||||||
// 模拟异步操作
|
|
||||||
await new Promise(resolve => setTimeout(resolve, 10));
|
|
||||||
}
|
|
||||||
|
|
||||||
// 测试用的手动事件监听器
|
// 测试用的手动事件监听器
|
||||||
public addManualEventListener(): void {
|
public addManualEventListener(): void {
|
||||||
@@ -122,53 +102,6 @@ describe('EntitySystem', () => {
|
|||||||
scene.removeSystem(system);
|
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('手动事件监听器管理', () => {
|
describe('手动事件监听器管理', () => {
|
||||||
it('应该能够手动添加事件监听器', () => {
|
it('应该能够手动添加事件监听器', () => {
|
||||||
@@ -241,23 +174,18 @@ describe('EntitySystem', () => {
|
|||||||
// 添加手动监听器
|
// 添加手动监听器
|
||||||
system.testAddEventListener('destroy_order_test', handler);
|
system.testAddEventListener('destroy_order_test', handler);
|
||||||
|
|
||||||
// 验证装饰器和手动监听器都工作
|
// 验证手动监听器工作
|
||||||
GlobalEventBus.getInstance().emit('test_event', testEvent);
|
|
||||||
scene.eventSystem.emitSync('destroy_order_test', {});
|
scene.eventSystem.emitSync('destroy_order_test', {});
|
||||||
expect(system.eventHandlerCallCount).toBe(1);
|
|
||||||
expect(handler).toHaveBeenCalledTimes(1);
|
expect(handler).toHaveBeenCalledTimes(1);
|
||||||
|
|
||||||
// 直接调用框架的销毁方法
|
// 直接调用框架的销毁方法
|
||||||
system.destroy();
|
system.destroy();
|
||||||
|
|
||||||
// 重置计数器并验证所有监听器都被清理
|
// 重置计数器并验证监听器被清理
|
||||||
system.eventHandlerCallCount = 0;
|
|
||||||
handler.mockClear();
|
handler.mockClear();
|
||||||
|
|
||||||
GlobalEventBus.getInstance().emit('test_event', testEvent);
|
|
||||||
scene.eventSystem.emitSync('destroy_order_test', {});
|
scene.eventSystem.emitSync('destroy_order_test', {});
|
||||||
|
|
||||||
expect(system.eventHandlerCallCount).toBe(0);
|
|
||||||
expect(handler).not.toHaveBeenCalled();
|
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<void>(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);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
Reference in New Issue
Block a user