diff --git a/packages/core/src/ECS/World.ts b/packages/core/src/ECS/World.ts index 8e11b890..8d044d11 100644 --- a/packages/core/src/ECS/World.ts +++ b/packages/core/src/ECS/World.ts @@ -136,9 +136,13 @@ export class World { /** * 创建并添加Scene到World */ - public createScene(sceneId: string, sceneInstance?: T): T { - if (this._scenes.has(sceneId)) { - throw new Error(`Scene ID '${sceneId}' 已存在于World '${this.name}' 中`); + public createScene(sceneName: string, sceneInstance?: T): T { + if (!sceneName || typeof sceneName !== 'string' || sceneName.trim() === '') { + throw new Error('Scene name不能为空'); + } + + if (this._scenes.has(sceneName)) { + throw new Error(`Scene name '${sceneName}' 已存在于World '${this.name}' 中`); } if (this._scenes.size >= this._config.maxScenes!) { @@ -157,13 +161,13 @@ export class World { // 设置Scene的标识 if ('id' in scene) { - (scene as any).id = sceneId; + (scene as any).id = sceneName; } if ('name' in scene && !scene.name) { - scene.name = sceneId; + scene.name = sceneName; } - this._scenes.set(sceneId, scene); + this._scenes.set(sceneName, scene); // 初始化Scene scene.initialize(); @@ -174,30 +178,30 @@ export class World { /** * 移除Scene */ - public removeScene(sceneId: string): boolean { - const scene = this._scenes.get(sceneId); + public removeScene(sceneName: string): boolean { + const scene = this._scenes.get(sceneName); if (!scene) { return false; } // 如果Scene正在运行,先停止它 - if (this._activeScenes.has(sceneId)) { - this.setSceneActive(sceneId, false); + if (this._activeScenes.has(sceneName)) { + this.setSceneActive(sceneName, false); } // 清理Scene资源 scene.end(); - this._scenes.delete(sceneId); + this._scenes.delete(sceneName); - logger.info(`从World '${this.name}' 中移除Scene: ${sceneId}`); + logger.info(`从World '${this.name}' 中移除Scene: ${sceneName}`); return true; } /** * 获取Scene */ - public getScene(sceneId: string): T | null { - return this._scenes.get(sceneId) as T || null; + public getScene(sceneName: string): T | null { + return this._scenes.get(sceneName) as T || null; } /** @@ -218,9 +222,9 @@ export class World { * 移除所有Scene */ public removeAllScenes(): void { - const sceneIds = Array.from(this._scenes.keys()); - for (const sceneId of sceneIds) { - this.removeScene(sceneId); + const sceneNames = Array.from(this._scenes.keys()); + for (const sceneName of sceneNames) { + this.removeScene(sceneName); } logger.info(`从World '${this.name}' 中移除所有Scene`); } @@ -228,32 +232,32 @@ export class World { /** * 设置Scene激活状态 */ - public setSceneActive(sceneId: string, active: boolean): void { - const scene = this._scenes.get(sceneId); + public setSceneActive(sceneName: string, active: boolean): void { + const scene = this._scenes.get(sceneName); if (!scene) { - logger.warn(`Scene '${sceneId}' 不存在于World '${this.name}' 中`); + logger.warn(`Scene '${sceneName}' 不存在于World '${this.name}' 中`); return; } if (active) { - this._activeScenes.add(sceneId); + this._activeScenes.add(sceneName); // 启动Scene if (scene.begin) { scene.begin(); } - logger.debug(`在World '${this.name}' 中激活Scene: ${sceneId}`); + logger.debug(`在World '${this.name}' 中激活Scene: ${sceneName}`); } else { - this._activeScenes.delete(sceneId); + this._activeScenes.delete(sceneName); // 可选择性地停止Scene,或者让它继续运行但不更新 - logger.debug(`在World '${this.name}' 中停用Scene: ${sceneId}`); + logger.debug(`在World '${this.name}' 中停用Scene: ${sceneName}`); } } /** * 检查Scene是否激活 */ - public isSceneActive(sceneId: string): boolean { - return this._activeScenes.has(sceneId); + public isSceneActive(sceneName: string): boolean { + return this._activeScenes.has(sceneName); } /** @@ -344,8 +348,8 @@ export class World { } // 停止所有Scene - for (const sceneId of this._activeScenes) { - this.setSceneActive(sceneId, false); + for (const sceneName of this._activeScenes) { + this.setSceneActive(sceneName, false); } // 重置所有全局System @@ -386,8 +390,8 @@ export class World { } // 更新所有激活的Scene - for (const sceneId of this._activeScenes) { - const scene = this._scenes.get(sceneId); + for (const sceneName of this._activeScenes) { + const scene = this._scenes.get(sceneName); if (scene && scene.update) { scene.update(); } @@ -409,9 +413,9 @@ export class World { this.stop(); // 销毁所有Scene - const sceneIds = Array.from(this._scenes.keys()); - for (const sceneId of sceneIds) { - this.removeScene(sceneId); + const sceneNames = Array.from(this._scenes.keys()); + for (const sceneName of sceneNames) { + this.removeScene(sceneName); } // 清理全局System @@ -445,10 +449,10 @@ export class World { globalSystemCount: this._globalSystems.length, createdAt: this._createdAt, config: { ...this._config }, - scenes: Array.from(this._scenes.keys()).map((sceneId) => ({ - id: sceneId, - isActive: this._activeScenes.has(sceneId), - name: this._scenes.get(sceneId)?.name || sceneId + scenes: Array.from(this._scenes.keys()).map((sceneName) => ({ + id: sceneName, + isActive: this._activeScenes.has(sceneName), + name: this._scenes.get(sceneName)?.name || sceneName })) }; } @@ -490,8 +494,8 @@ export class World { const currentTime = Date.now(); const cleanupThreshold = 5 * 60 * 1000; // 5分钟 - for (const [sceneId, scene] of this._scenes) { - if (!this._activeScenes.has(sceneId) && + for (const [sceneName, scene] of this._scenes) { + if (!this._activeScenes.has(sceneName) && scene.entities && scene.entities.count === 0 && (currentTime - this._createdAt) > cleanupThreshold) { @@ -506,20 +510,20 @@ export class World { * 执行清理操作 */ private cleanup(): void { - const sceneIds = Array.from(this._scenes.keys()); + const sceneNames = Array.from(this._scenes.keys()); const currentTime = Date.now(); const cleanupThreshold = 5 * 60 * 1000; // 5分钟 - for (const sceneId of sceneIds) { - const scene = this._scenes.get(sceneId); + for (const sceneName of sceneNames) { + const scene = this._scenes.get(sceneName); if (scene && - !this._activeScenes.has(sceneId) && + !this._activeScenes.has(sceneName) && scene.entities && scene.entities.count === 0 && (currentTime - this._createdAt) > cleanupThreshold) { - this.removeScene(sceneId); - logger.debug(`自动清理空Scene: ${sceneId} from World ${this.name}`); + this.removeScene(sceneName); + logger.debug(`自动清理空Scene: ${sceneName} from World ${this.name}`); } } } diff --git a/packages/core/src/ECS/WorldManager.ts b/packages/core/src/ECS/WorldManager.ts index 107e17a9..9fe358a6 100644 --- a/packages/core/src/ECS/WorldManager.ts +++ b/packages/core/src/ECS/WorldManager.ts @@ -92,13 +92,13 @@ export class WorldManager implements IService { /** * 创建新World */ - public createWorld(worldId: string, config?: IWorldConfig): World { - if (!worldId || typeof worldId !== 'string' || worldId.trim() === '') { - throw new Error('World ID不能为空'); + public createWorld(worldName: string, config?: IWorldConfig): World { + if (!worldName || typeof worldName !== 'string' || worldName.trim() === '') { + throw new Error('World name不能为空'); } - if (this._worlds.has(worldId)) { - throw new Error(`World ID '${worldId}' 已存在`); + if (this._worlds.has(worldName)) { + throw new Error(`World name '${worldName}' 已存在`); } if (this._worlds.size >= this._config.maxWorlds!) { @@ -107,14 +107,14 @@ export class WorldManager implements IService { // 优先级:config.debug > WorldManager.debug > 默认 const worldConfig: IWorldConfig = { - name: worldId, + name: worldName, debug: config?.debug ?? this._config.debug ?? false, ...(config?.maxScenes !== undefined && { maxScenes: config.maxScenes }), ...(config?.autoCleanup !== undefined && { autoCleanup: config.autoCleanup }) }; const world = new World(worldConfig); - this._worlds.set(worldId, world); + this._worlds.set(worldName, world); return world; } @@ -122,25 +122,25 @@ export class WorldManager implements IService { /** * 移除World */ - public removeWorld(worldId: string): boolean { - const world = this._worlds.get(worldId); + public removeWorld(worldName: string): boolean { + const world = this._worlds.get(worldName); if (!world) { return false; } // 销毁World world.destroy(); - this._worlds.delete(worldId); + this._worlds.delete(worldName); - logger.info(`移除World: ${worldId}`); + logger.info(`移除World: ${worldName}`); return true; } /** * 获取World */ - public getWorld(worldId: string): World | null { - return this._worlds.get(worldId) || null; + public getWorld(worldName: string): World | null { + return this._worlds.get(worldName) || null; } /** @@ -160,27 +160,27 @@ export class WorldManager implements IService { /** * 设置World激活状态 */ - public setWorldActive(worldId: string, active: boolean): void { - const world = this._worlds.get(worldId); + public setWorldActive(worldName: string, active: boolean): void { + const world = this._worlds.get(worldName); if (!world) { - logger.warn(`World '${worldId}' 不存在`); + logger.warn(`World '${worldName}' 不存在`); return; } if (active) { world.start(); - logger.debug(`激活World: ${worldId}`); + logger.debug(`激活World: ${worldName}`); } else { world.stop(); - logger.debug(`停用World: ${worldId}`); + logger.debug(`停用World: ${worldName}`); } } /** * 检查World是否激活 */ - public isWorldActive(worldId: string): boolean { - const world = this._worlds.get(worldId); + public isWorldActive(worldName: string): boolean { + const world = this._worlds.get(worldName); return world?.isActive ?? false; } @@ -310,14 +310,14 @@ export class WorldManager implements IService { worlds: [] as any[] }; - for (const [worldId, world] of this._worlds) { + for (const [worldName, world] of this._worlds) { const worldStats = world.getStats(); stats.totalScenes += worldStats.totalSystems; // World的getStats可能需要调整 stats.totalEntities += worldStats.totalEntities; stats.totalSystems += worldStats.totalSystems; stats.worlds.push({ - id: worldId, + id: worldName, name: world.name, isActive: world.isActive, sceneCount: world.sceneCount, @@ -334,8 +334,8 @@ export class WorldManager implements IService { public getDetailedStatus() { return { ...this.getStats(), - worlds: Array.from(this._worlds.entries()).map(([worldId, world]) => ({ - id: worldId, + worlds: Array.from(this._worlds.entries()).map(([worldName, world]) => ({ + id: worldName, isActive: world.isActive, status: world.getStatus() })) @@ -350,14 +350,14 @@ export class WorldManager implements IService { public cleanup(): number { const worldsToRemove: string[] = []; - for (const [worldId, world] of this._worlds) { + for (const [worldName, world] of this._worlds) { if (this.shouldCleanupWorld(world)) { - worldsToRemove.push(worldId); + worldsToRemove.push(worldName); } } - for (const worldId of worldsToRemove) { - this.removeWorld(worldId); + for (const worldName of worldsToRemove) { + this.removeWorld(worldName); } if (worldsToRemove.length > 0) { @@ -377,9 +377,9 @@ export class WorldManager implements IService { this.stopAll(); // 销毁所有World - const worldIds = Array.from(this._worlds.keys()); - for (const worldId of worldIds) { - this.removeWorld(worldId); + const worldNames = Array.from(this._worlds.keys()); + for (const worldName of worldNames) { + this.removeWorld(worldName); } this._worlds.clear(); diff --git a/packages/core/src/Plugins/DebugPlugin.ts b/packages/core/src/Plugins/DebugPlugin.ts index 5603a0e7..cc2a8763 100644 --- a/packages/core/src/Plugins/DebugPlugin.ts +++ b/packages/core/src/Plugins/DebugPlugin.ts @@ -275,7 +275,7 @@ export class DebugPlugin implements IPlugin, IService { * @param filter - 查询过滤器 */ public queryEntities(filter: { - sceneId?: string; + sceneName?: string; tag?: number; name?: string; hasComponent?: string; @@ -289,7 +289,7 @@ export class DebugPlugin implements IPlugin, IService { for (const world of worlds) { for (const scene of world.getAllScenes()) { - if (filter.sceneId && scene.name !== filter.sceneId) { + if (filter.sceneName && scene.name !== filter.sceneName) { continue; } diff --git a/packages/core/tests/ECS/World.test.ts b/packages/core/tests/ECS/World.test.ts index 4d86f6be..c8a8b47a 100644 --- a/packages/core/tests/ECS/World.test.ts +++ b/packages/core/tests/ECS/World.test.ts @@ -152,14 +152,24 @@ describe('World', () => { expect(world.sceneCount).toBe(1); }); + test('空的Scene name应该抛出错误', () => { + expect(() => { + world.createScene(''); + }).toThrow('Scene name不能为空'); + + expect(() => { + world.createScene(' '); + }).toThrow('Scene name不能为空'); + }); + test('重复的Scene ID应该抛出错误', () => { world.createScene('duplicate'); - + expect(() => { world.createScene('duplicate'); - }).toThrow("Scene ID 'duplicate' 已存在于World 'TestWorld' 中"); + }).toThrow("Scene name 'duplicate' 已存在于World 'TestWorld' 中"); }); - + test('超出最大Scene数量限制应该抛出错误', () => { const limitedWorld = new World({ maxScenes: 2 }); @@ -488,10 +498,10 @@ describe('World', () => { }); describe('错误处理', () => { - test('Scene ID为空时应该创建默认ID', () => { + test('Scene name为空时应该抛出错误', () => { expect(() => { world.createScene(''); - }).not.toThrow(); + }).toThrow('Scene name不能为空'); }); test('极限情况下的资源管理', () => { diff --git a/packages/core/tests/ECS/WorldManager.test.ts b/packages/core/tests/ECS/WorldManager.test.ts index 44b1275e..6245dcba 100644 --- a/packages/core/tests/ECS/WorldManager.test.ts +++ b/packages/core/tests/ECS/WorldManager.test.ts @@ -109,12 +109,22 @@ describe('WorldManager', () => { expect(world.name).toBe('configured-world'); }); + test('空的World name应该抛出错误', () => { + expect(() => { + worldManager.createWorld(''); + }).toThrow('World name不能为空'); + + expect(() => { + worldManager.createWorld(' '); + }).toThrow('World name不能为空'); + }); + test('重复的World ID应该抛出错误', () => { worldManager.createWorld('duplicate-world'); expect(() => { worldManager.createWorld('duplicate-world'); - }).toThrow("World ID 'duplicate-world' 已存在"); + }).toThrow("World name 'duplicate-world' 已存在"); }); test('超出最大World数量应该抛出错误', () => { diff --git a/packages/core/tests/Plugins/DebugPlugin.test.ts b/packages/core/tests/Plugins/DebugPlugin.test.ts index baf9ecc1..9778ec27 100644 --- a/packages/core/tests/Plugins/DebugPlugin.test.ts +++ b/packages/core/tests/Plugins/DebugPlugin.test.ts @@ -198,6 +198,14 @@ describe('DebugPlugin', () => { const results = debugPlugin.queryEntities({ tag: 999 }); expect(results.length).toBe(0); }); + + it('应该能够按 sceneName 过滤实体', () => { + // 查询特定场景的实体 + const results = debugPlugin.queryEntities({ sceneName: 'test-scene' }); + + // 应该返回 test-scene 中的所有实体(Player, Enemy, Item) + expect(results.length).toBe(3); + }); }); describe('监控功能', () => {