新增world概念(多world管理多scene概念)现在支持多个world多个scene同时更新
This commit is contained in:
466
packages/core/tests/ECS/World.test.ts
Normal file
466
packages/core/tests/ECS/World.test.ts
Normal file
@@ -0,0 +1,466 @@
|
||||
import { World, IWorldConfig, IGlobalSystem } from '../../src/ECS/World';
|
||||
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';
|
||||
|
||||
// 测试用组件
|
||||
class TestComponent extends Component {
|
||||
public value: number = 0;
|
||||
|
||||
constructor(value: number = 0) {
|
||||
super();
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
|
||||
class PlayerComponent extends Component {
|
||||
public playerId: string;
|
||||
|
||||
constructor(playerId: string) {
|
||||
super();
|
||||
this.playerId = playerId;
|
||||
}
|
||||
}
|
||||
|
||||
// 测试用全局系统
|
||||
class TestGlobalSystem implements IGlobalSystem {
|
||||
public readonly name = 'TestGlobalSystem';
|
||||
public updateCount: number = 0;
|
||||
|
||||
public initialize(): void {
|
||||
// 初始化逻辑
|
||||
}
|
||||
|
||||
public update(): void {
|
||||
this.updateCount++;
|
||||
}
|
||||
|
||||
public reset(): void {
|
||||
this.updateCount = 0;
|
||||
}
|
||||
|
||||
public destroy(): void {
|
||||
// 销毁逻辑
|
||||
}
|
||||
}
|
||||
|
||||
class TestSceneSystem extends EntitySystem {
|
||||
public updateCount = 0;
|
||||
|
||||
constructor() {
|
||||
super(Matcher.empty().all(PlayerComponent));
|
||||
}
|
||||
|
||||
protected override process(): void {
|
||||
this.updateCount++;
|
||||
}
|
||||
}
|
||||
|
||||
// 测试用Scene
|
||||
class TestScene extends Scene {
|
||||
public initializeCalled = false;
|
||||
public beginCalled = false;
|
||||
public endCalled = false;
|
||||
|
||||
public override initialize(): void {
|
||||
this.initializeCalled = true;
|
||||
super.initialize();
|
||||
}
|
||||
|
||||
public override begin(): void {
|
||||
this.beginCalled = true;
|
||||
super.begin();
|
||||
}
|
||||
|
||||
public override end(): void {
|
||||
this.endCalled = true;
|
||||
super.end();
|
||||
}
|
||||
}
|
||||
|
||||
describe('World', () => {
|
||||
let world: World;
|
||||
|
||||
beforeEach(() => {
|
||||
world = new World({ name: 'TestWorld' });
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
if (world) {
|
||||
world.destroy();
|
||||
}
|
||||
});
|
||||
|
||||
describe('基础功能', () => {
|
||||
test('创建World时应该设置正确的配置', () => {
|
||||
const config: IWorldConfig = {
|
||||
name: 'GameWorld',
|
||||
debug: true,
|
||||
maxScenes: 5,
|
||||
autoCleanup: false
|
||||
};
|
||||
|
||||
const testWorld = new World(config);
|
||||
|
||||
expect(testWorld.name).toBe('GameWorld');
|
||||
expect(testWorld.sceneCount).toBe(0);
|
||||
expect(testWorld.isActive).toBe(false);
|
||||
expect(testWorld.createdAt).toBeGreaterThan(0);
|
||||
|
||||
testWorld.destroy();
|
||||
});
|
||||
|
||||
test('默认配置应该正确', () => {
|
||||
const defaultWorld = new World();
|
||||
|
||||
expect(defaultWorld.name).toBe('World');
|
||||
expect(defaultWorld.sceneCount).toBe(0);
|
||||
expect(defaultWorld.isActive).toBe(false);
|
||||
|
||||
defaultWorld.destroy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Scene管理', () => {
|
||||
test('创建Scene应该成功', () => {
|
||||
const scene = world.createScene('test-scene');
|
||||
|
||||
expect(scene).toBeDefined();
|
||||
expect(world.sceneCount).toBe(1);
|
||||
expect(world.getSceneIds()).toContain('test-scene');
|
||||
});
|
||||
|
||||
test('创建Scene时传入自定义Scene实例', () => {
|
||||
const customScene = new TestScene();
|
||||
const scene = world.createScene('custom-scene', customScene);
|
||||
|
||||
expect(scene).toBe(customScene);
|
||||
expect(scene.initializeCalled).toBe(true);
|
||||
expect(world.sceneCount).toBe(1);
|
||||
});
|
||||
|
||||
test('重复的Scene ID应该抛出错误', () => {
|
||||
world.createScene('duplicate');
|
||||
|
||||
expect(() => {
|
||||
world.createScene('duplicate');
|
||||
}).toThrow("Scene ID 'duplicate' 已存在于World 'TestWorld' 中");
|
||||
});
|
||||
|
||||
test('超出最大Scene数量限制应该抛出错误', () => {
|
||||
const limitedWorld = new World({ maxScenes: 2 });
|
||||
|
||||
limitedWorld.createScene('scene1');
|
||||
limitedWorld.createScene('scene2');
|
||||
|
||||
expect(() => {
|
||||
limitedWorld.createScene('scene3');
|
||||
}).toThrow("World 'World' 已达到最大Scene数量限制: 2");
|
||||
|
||||
limitedWorld.destroy();
|
||||
});
|
||||
|
||||
test('获取Scene应该正确', () => {
|
||||
const scene = world.createScene('get-test');
|
||||
const retrievedScene = world.getScene('get-test');
|
||||
|
||||
expect(retrievedScene).toBe(scene);
|
||||
});
|
||||
|
||||
test('获取不存在的Scene应该返回null', () => {
|
||||
const scene = world.getScene('non-existent');
|
||||
expect(scene).toBeNull();
|
||||
});
|
||||
|
||||
test('移除Scene应该正确清理', () => {
|
||||
const testScene = new TestScene();
|
||||
world.createScene('remove-test', testScene);
|
||||
world.setSceneActive('remove-test', true);
|
||||
|
||||
const removed = world.removeScene('remove-test');
|
||||
|
||||
expect(removed).toBe(true);
|
||||
expect(world.sceneCount).toBe(0);
|
||||
expect(world.getScene('remove-test')).toBeNull();
|
||||
expect(testScene.endCalled).toBe(true);
|
||||
});
|
||||
|
||||
test('移除不存在的Scene应该返回false', () => {
|
||||
const removed = world.removeScene('non-existent');
|
||||
expect(removed).toBe(false);
|
||||
});
|
||||
|
||||
test('获取所有Scene应该正确', () => {
|
||||
const scene1 = world.createScene('scene1');
|
||||
const scene2 = world.createScene('scene2');
|
||||
|
||||
const allScenes = world.getAllScenes();
|
||||
|
||||
expect(allScenes).toHaveLength(2);
|
||||
expect(allScenes).toContain(scene1);
|
||||
expect(allScenes).toContain(scene2);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Scene激活管理', () => {
|
||||
test('激活Scene应该正确', () => {
|
||||
const testScene = new TestScene();
|
||||
world.createScene('active-test', testScene);
|
||||
|
||||
world.setSceneActive('active-test', true);
|
||||
|
||||
expect(world.isSceneActive('active-test')).toBe(true);
|
||||
expect(world.getActiveSceneCount()).toBe(1);
|
||||
expect(testScene.beginCalled).toBe(true);
|
||||
});
|
||||
|
||||
test('停用Scene应该正确', () => {
|
||||
world.createScene('deactive-test');
|
||||
world.setSceneActive('deactive-test', true);
|
||||
|
||||
world.setSceneActive('deactive-test', false);
|
||||
|
||||
expect(world.isSceneActive('deactive-test')).toBe(false);
|
||||
expect(world.getActiveSceneCount()).toBe(0);
|
||||
});
|
||||
|
||||
test('激活不存在的Scene应该记录警告', () => {
|
||||
const consoleSpy = jest.spyOn(console, 'warn').mockImplementation();
|
||||
|
||||
world.setSceneActive('non-existent', true);
|
||||
|
||||
// 注意:这里需要检查具体的日志实现,可能需要调整
|
||||
consoleSpy.mockRestore();
|
||||
});
|
||||
});
|
||||
|
||||
describe('全局System管理', () => {
|
||||
test('添加全局System应该成功', () => {
|
||||
const globalSystem = new TestGlobalSystem();
|
||||
|
||||
const addedSystem = world.addGlobalSystem(globalSystem);
|
||||
|
||||
expect(addedSystem).toBe(globalSystem);
|
||||
expect(world.getGlobalSystem(TestGlobalSystem)).toBe(globalSystem);
|
||||
});
|
||||
|
||||
test('重复添加相同System应该返回原System', () => {
|
||||
const globalSystem = new TestGlobalSystem();
|
||||
|
||||
const firstAdd = world.addGlobalSystem(globalSystem);
|
||||
const secondAdd = world.addGlobalSystem(globalSystem);
|
||||
|
||||
expect(firstAdd).toBe(secondAdd);
|
||||
expect(firstAdd).toBe(globalSystem);
|
||||
});
|
||||
|
||||
test('移除全局System应该成功', () => {
|
||||
const globalSystem = new TestGlobalSystem();
|
||||
world.addGlobalSystem(globalSystem);
|
||||
|
||||
const removed = world.removeGlobalSystem(globalSystem);
|
||||
|
||||
expect(removed).toBe(true);
|
||||
expect(world.getGlobalSystem(TestGlobalSystem)).toBeNull();
|
||||
});
|
||||
|
||||
test('移除不存在的System应该返回false', () => {
|
||||
const globalSystem = new TestGlobalSystem();
|
||||
|
||||
const removed = world.removeGlobalSystem(globalSystem);
|
||||
|
||||
expect(removed).toBe(false);
|
||||
});
|
||||
|
||||
test('获取不存在的System类型应该返回null', () => {
|
||||
const system = world.getGlobalSystem(TestGlobalSystem);
|
||||
expect(system).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('World生命周期', () => {
|
||||
test('启动World应该正确', () => {
|
||||
const globalSystem = new TestGlobalSystem();
|
||||
world.addGlobalSystem(globalSystem);
|
||||
|
||||
world.start();
|
||||
|
||||
expect(world.isActive).toBe(true);
|
||||
});
|
||||
|
||||
test('重复启动World应该无效果', () => {
|
||||
world.start();
|
||||
const firstActive = world.isActive;
|
||||
|
||||
world.start();
|
||||
|
||||
expect(world.isActive).toBe(firstActive);
|
||||
});
|
||||
|
||||
test('停止World应该停用所有Scene', () => {
|
||||
const testScene = new TestScene();
|
||||
world.createScene('stop-test', testScene);
|
||||
world.setSceneActive('stop-test', true);
|
||||
world.start();
|
||||
|
||||
world.stop();
|
||||
|
||||
expect(world.isActive).toBe(false);
|
||||
expect(world.isSceneActive('stop-test')).toBe(false);
|
||||
});
|
||||
|
||||
test('销毁World应该清理所有资源', () => {
|
||||
const testScene = new TestScene();
|
||||
const globalSystem = new TestGlobalSystem();
|
||||
|
||||
world.createScene('destroy-test', testScene);
|
||||
world.addGlobalSystem(globalSystem);
|
||||
world.start();
|
||||
|
||||
world.destroy();
|
||||
|
||||
expect(world.sceneCount).toBe(0);
|
||||
expect(world.isActive).toBe(false);
|
||||
expect(testScene.endCalled).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('更新逻辑', () => {
|
||||
test('updateGlobalSystems应该更新全局系统', () => {
|
||||
const globalSystem = new TestGlobalSystem();
|
||||
world.addGlobalSystem(globalSystem);
|
||||
world.start();
|
||||
|
||||
// 创建测试Scene
|
||||
const scene = world.createScene('update-test');
|
||||
world.setSceneActive('update-test', true);
|
||||
|
||||
// 直接测试全局系统更新
|
||||
world.updateGlobalSystems();
|
||||
|
||||
// 验证全局System被正确调用
|
||||
expect(globalSystem.updateCount).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
test('未激活的World不应该更新', () => {
|
||||
const globalSystem = new TestGlobalSystem();
|
||||
world.addGlobalSystem(globalSystem);
|
||||
// 不启动World
|
||||
|
||||
world.updateGlobalSystems();
|
||||
|
||||
expect(globalSystem.updateCount).toBe(0);
|
||||
});
|
||||
|
||||
test('updateScenes应该更新激活的Scene', () => {
|
||||
const scene1 = world.createScene('scene1');
|
||||
const scene2 = world.createScene('scene2');
|
||||
|
||||
scene1.addEntityProcessor(new TestSceneSystem());
|
||||
scene2.addEntityProcessor(new TestSceneSystem());
|
||||
|
||||
world.start();
|
||||
world.setSceneActive('scene1', true);
|
||||
// scene2保持未激活
|
||||
|
||||
world.updateScenes();
|
||||
|
||||
// 这里需要根据具体的Scene更新实现来验证
|
||||
// 由于Scene.update()的具体实现可能不同,这里主要测试调用不出错
|
||||
expect(() => world.updateScenes()).not.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe('状态和统计', () => {
|
||||
test('获取World状态应该正确', () => {
|
||||
world.createScene('status-scene1');
|
||||
world.createScene('status-scene2');
|
||||
world.setSceneActive('status-scene1', true);
|
||||
world.addGlobalSystem(new TestGlobalSystem());
|
||||
world.start();
|
||||
|
||||
const status = world.getStatus();
|
||||
|
||||
expect(status.name).toBe('TestWorld');
|
||||
expect(status.isActive).toBe(true);
|
||||
expect(status.sceneCount).toBe(2);
|
||||
expect(status.activeSceneCount).toBe(1);
|
||||
expect(status.globalSystemCount).toBe(1);
|
||||
expect(status.createdAt).toBeGreaterThan(0);
|
||||
expect(status.scenes).toHaveLength(2);
|
||||
|
||||
const activeScene = status.scenes.find(s => s.id === 'status-scene1');
|
||||
expect(activeScene?.isActive).toBe(true);
|
||||
|
||||
const inactiveScene = status.scenes.find(s => s.id === 'status-scene2');
|
||||
expect(inactiveScene?.isActive).toBe(false);
|
||||
});
|
||||
|
||||
test('获取World统计应该包含基本信息', () => {
|
||||
world.addGlobalSystem(new TestGlobalSystem());
|
||||
|
||||
const scene = world.createScene('stats-scene');
|
||||
const entity = scene.createEntity('stats-entity');
|
||||
entity.addComponent(new TestComponent());
|
||||
|
||||
const stats = world.getStats();
|
||||
|
||||
expect(stats).toHaveProperty('totalEntities');
|
||||
expect(stats).toHaveProperty('totalSystems');
|
||||
expect(stats).toHaveProperty('memoryUsage');
|
||||
expect(stats).toHaveProperty('performance');
|
||||
expect(stats.totalSystems).toBeGreaterThanOrEqual(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('自动清理功能', () => {
|
||||
test('自动清理应该移除空闲Scene', async () => {
|
||||
// 创建一个启用自动清理的World
|
||||
const autoCleanWorld = new World({
|
||||
name: 'AutoCleanWorld',
|
||||
autoCleanup: true,
|
||||
maxScenes: 10
|
||||
});
|
||||
|
||||
// 创建一个空Scene
|
||||
autoCleanWorld.createScene('empty-scene');
|
||||
autoCleanWorld.start();
|
||||
|
||||
// 手动触发清理检查
|
||||
autoCleanWorld.updateScenes();
|
||||
|
||||
// 由于清理策略基于时间,这里主要测试不会出错
|
||||
expect(() => autoCleanWorld.updateScenes()).not.toThrow();
|
||||
|
||||
autoCleanWorld.destroy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('错误处理', () => {
|
||||
test('Scene ID为空时应该创建默认ID', () => {
|
||||
expect(() => {
|
||||
world.createScene('');
|
||||
}).not.toThrow();
|
||||
});
|
||||
|
||||
test('极限情况下的资源管理', () => {
|
||||
// 创建大量Scene
|
||||
for (let i = 0; i < 5; i++) {
|
||||
world.createScene(`scene_${i}`);
|
||||
world.setSceneActive(`scene_${i}`, true);
|
||||
}
|
||||
|
||||
// 添加多个全局System
|
||||
for (let i = 0; i < 3; i++) {
|
||||
world.addGlobalSystem(new TestGlobalSystem());
|
||||
}
|
||||
|
||||
world.start();
|
||||
|
||||
// 测试批量清理
|
||||
expect(() => world.destroy()).not.toThrow();
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user