import { Scene } from '../../src/ECS/Scene'; import { EntitySystem } from '../../src/ECS/Systems/EntitySystem'; import { Entity } from '../../src/ECS/Entity'; import { Component } from '../../src/ECS/Component'; import { Matcher } from '../../src/ECS/Utils/Matcher'; import { Injectable, Inject } from '../../src/Core/DI'; import { Core } from '../../src/Core'; import type { IService } from '../../src/Core/ServiceContainer'; import { ECSSystem } from '../../src/ECS/Decorators'; class Transform extends Component { constructor(public x: number = 0, public y: number = 0) { super(); } } class Velocity extends Component { constructor(public vx: number = 0, public vy: number = 0) { super(); } } class Health extends Component { constructor(public value: number = 100) { super(); } } describe('EntitySystem - 依赖注入测试', () => { let scene: Scene; beforeAll(() => { Core.create(); }); beforeEach(() => { scene = new Scene(); }); afterEach(() => { scene.end(); }); afterAll(() => { Core.destroy(); }); describe('基本DI功能', () => { test('应该支持无依赖的System通过类型添加', () => { @Injectable() @ECSSystem('Movement') class MovementSystem extends EntitySystem implements IService { constructor() { super(Matcher.empty().all(Transform, Velocity)); } override dispose() {} } const system = scene.addEntityProcessor(MovementSystem); expect(system).toBeInstanceOf(MovementSystem); expect(scene.systems.length).toBe(1); }); test('应该支持有依赖的System自动注入', () => { @Injectable() @ECSSystem('Collision') class CollisionSystem extends EntitySystem implements IService { public checkCount = 0; constructor() { super(Matcher.empty().all(Transform)); } public checkCollisions() { this.checkCount++; } override dispose() {} } @Injectable() @ECSSystem('Physics') class PhysicsSystem extends EntitySystem implements IService { constructor( @Inject(CollisionSystem) public collision: CollisionSystem ) { super(Matcher.empty().all(Transform, Velocity)); } protected override process(entities: readonly Entity[]): void { this.collision.checkCollisions(); } override dispose() {} } scene.addEntityProcessor(CollisionSystem); const physics = scene.addEntityProcessor(PhysicsSystem); expect(physics).toBeInstanceOf(PhysicsSystem); expect(physics.collision).toBeInstanceOf(CollisionSystem); expect(scene.systems.length).toBe(2); const entity = scene.createEntity('test'); entity.addComponent(new Transform()); entity.addComponent(new Velocity()); scene.update(); expect(physics.collision.checkCount).toBe(1); }); test('应该支持多层级依赖注入', () => { @Injectable() @ECSSystem('A') class SystemA extends EntitySystem implements IService { constructor() { super(Matcher.empty()); } override dispose() {} } @Injectable() @ECSSystem('B') class SystemB extends EntitySystem implements IService { constructor(@Inject(SystemA) public systemA: SystemA) { super(Matcher.empty()); } override dispose() {} } @Injectable() @ECSSystem('C') class SystemC extends EntitySystem implements IService { constructor( @Inject(SystemA) public systemA: SystemA, @Inject(SystemB) public systemB: SystemB ) { super(Matcher.empty()); } override dispose() {} } scene.addEntityProcessor(SystemA); scene.addEntityProcessor(SystemB); const systemC = scene.addEntityProcessor(SystemC); expect(systemC.systemA).toBeInstanceOf(SystemA); expect(systemC.systemB).toBeInstanceOf(SystemB); expect(systemC.systemB.systemA).toBe(systemC.systemA); }); }); describe('批量注册', () => { test('应该支持批量注册System并自动解析依赖', () => { @Injectable() @ECSSystem('Collision') class CollisionSystem extends EntitySystem implements IService { constructor() { super(Matcher.empty().all(Transform)); } override dispose() {} } @Injectable() @ECSSystem('Physics', { updateOrder: 10 }) class PhysicsSystem extends EntitySystem implements IService { constructor(@Inject(CollisionSystem) public collision: CollisionSystem) { super(Matcher.empty().all(Transform, Velocity)); } override dispose() {} } @Injectable() @ECSSystem('Render', { updateOrder: 20 }) class RenderSystem extends EntitySystem implements IService { constructor(@Inject(PhysicsSystem) public physics: PhysicsSystem) { super(Matcher.empty().all(Transform)); } override dispose() {} } const systems = scene.registerSystems([ CollisionSystem, PhysicsSystem, RenderSystem ]); expect(systems.length).toBe(3); expect(scene.systems.length).toBe(3); const [collision, physics, render] = systems; expect(collision).toBeInstanceOf(CollisionSystem); expect(physics).toBeInstanceOf(PhysicsSystem); expect(render).toBeInstanceOf(RenderSystem); expect((physics as any).collision).toBe(collision); expect((render as any).physics).toBe(physics); }); test('批量注册的System应该按updateOrder排序', () => { @Injectable() @ECSSystem('C', { updateOrder: 30 }) class SystemC extends EntitySystem implements IService { constructor() { super(Matcher.empty()); } override dispose() {} } @Injectable() @ECSSystem('A', { updateOrder: 10 }) class SystemA extends EntitySystem implements IService { constructor() { super(Matcher.empty()); } override dispose() {} } @Injectable() @ECSSystem('B', { updateOrder: 20 }) class SystemB extends EntitySystem implements IService { constructor() { super(Matcher.empty()); } override dispose() {} } scene.registerSystems([SystemC, SystemA, SystemB]); const systems = scene.systems; expect(systems[0]).toBeInstanceOf(SystemA); expect(systems[1]).toBeInstanceOf(SystemB); expect(systems[2]).toBeInstanceOf(SystemC); }); }); describe('场景隔离', () => { test('不同Scene的System实例应该相互独立', () => { @Injectable() @ECSSystem('Counter') class CounterSystem extends EntitySystem implements IService { public count = 0; constructor() { super(Matcher.empty()); } protected override process(): void { this.count++; } override dispose() {} } const scene1 = new Scene(); const scene2 = new Scene(); const counter1 = scene1.addEntityProcessor(CounterSystem); const counter2 = scene2.addEntityProcessor(CounterSystem); expect(counter1).not.toBe(counter2); scene1.update(); expect(counter1.count).toBe(1); expect(counter2.count).toBe(0); scene2.update(); expect(counter1.count).toBe(1); expect(counter2.count).toBe(1); scene1.end(); scene2.end(); }); }); describe('getSystem方法', () => { test('应该能通过getSystem获取已注册的System', () => { @Injectable() @ECSSystem('Test') class TestSystem extends EntitySystem implements IService { constructor() { super(Matcher.empty()); } override dispose() {} } scene.addEntityProcessor(TestSystem); const system = scene.getSystem(TestSystem); expect(system).toBeInstanceOf(TestSystem); }); test('获取未注册的System应该返回null', () => { @Injectable() @ECSSystem('Test') class TestSystem extends EntitySystem implements IService { constructor() { super(Matcher.empty()); } override dispose() {} } const system = scene.getSystem(TestSystem); expect(system).toBeNull(); }); }); describe('向后兼容性', () => { test('应该继续支持手动创建实例的方式', () => { class LegacySystem extends EntitySystem { constructor() { super(Matcher.empty().all(Transform)); } } const system = new LegacySystem(); scene.addEntityProcessor(system); expect(scene.systems.length).toBe(1); expect(scene.systems[0]).toBe(system); }); test('混合使用DI和手动创建应该正常工作', () => { @Injectable() @ECSSystem('DI') class DISystem extends EntitySystem implements IService { constructor() { super(Matcher.empty().all(Transform)); } override dispose() {} } class ManualSystem extends EntitySystem { constructor() { super(Matcher.empty().all(Velocity)); } } scene.addEntityProcessor(DISystem); scene.addEntityProcessor(new ManualSystem()); expect(scene.systems.length).toBe(2); }); }); describe('Issue #76 场景验证', () => { test('应该消除硬编码依赖,使用构造函数注入', () => { @Injectable() @ECSSystem('TimeService') class TimeService extends EntitySystem implements IService { public getDeltaTime(): number { return 0.016; } constructor() { super(Matcher.empty()); } override dispose() {} } @Injectable() @ECSSystem('CollisionService') class CollisionService extends EntitySystem implements IService { public detectCollisions(): string[] { return ['collision1', 'collision2']; } constructor() { super(Matcher.empty()); } override dispose() {} } @Injectable() @ECSSystem('Physics') class PhysicsSystem extends EntitySystem implements IService { constructor( @Inject(TimeService) private time: TimeService, @Inject(CollisionService) private collision: CollisionService ) { super(Matcher.empty().all(Transform, Velocity)); } protected override process(entities: readonly Entity[]): void { const dt = this.time.getDeltaTime(); const collisions = this.collision.detectCollisions(); for (const entity of entities) { const transform = entity.getComponent(Transform)!; const velocity = entity.getComponent(Velocity)!; transform.x += velocity.vx * dt; transform.y += velocity.vy * dt; } } override dispose() {} } scene.registerSystems([ TimeService, CollisionService, PhysicsSystem ]); const entity = scene.createEntity('player'); entity.addComponent(new Transform(0, 0)); entity.addComponent(new Velocity(100, 50)); const physics = scene.getSystem(PhysicsSystem); expect(physics).not.toBeNull(); expect((physics as any).time).toBeInstanceOf(TimeService); expect((physics as any).collision).toBeInstanceOf(CollisionService); scene.update(); const transform = entity.getComponent(Transform)!; expect(transform.x).toBeCloseTo(1.6, 1); expect(transform.y).toBeCloseTo(0.8, 1); }); }); });