import { IScene } from './IScene'; import { Scene } from './Scene'; import { createLogger } from '../Utils/Logger'; const logger = createLogger('World'); /** * 全局系统接口 * 全局系统是在World级别运行的系统,不依赖特定Scene */ export interface IGlobalSystem { /** * 系统名称 */ readonly name: string; /** * 初始化系统 */ initialize?(): void; /** * 更新系统 */ update(deltaTime?: number): void; /** * 重置系统 */ reset?(): void; /** * 销毁系统 */ destroy?(): void; } /** * World配置接口 */ export interface IWorldConfig { /** * World名称 */ name?: string; /** * 是否启用调试模式 */ debug?: boolean; /** * 最大Scene数量限制 */ maxScenes?: number; /** * 是否自动清理空Scene */ autoCleanup?: boolean; } /** * World类 - ECS世界管理器 * * World是Scene的容器,每个World可以管理多个Scene。 * 这种设计允许创建独立的游戏世界,如: * - 游戏房间(每个房间一个World) * - 不同的游戏模式 * - 独立的模拟环境 * * @example * ```typescript * // 创建游戏房间的World * const roomWorld = new World({ name: 'Room_001' }); * * // 在World中创建Scene * const gameScene = roomWorld.createScene('game', new Scene()); * const uiScene = roomWorld.createScene('ui', new Scene()); * * // 更新整个World * roomWorld.update(deltaTime); * ``` */ export class World { public readonly name: string; private readonly _config: IWorldConfig; private readonly _scenes: Map = new Map(); private readonly _activeScenes: Set = new Set(); private readonly _globalSystems: IGlobalSystem[] = []; private _isActive: boolean = false; private _createdAt: number; constructor(config: IWorldConfig = {}) { this._config = { name: 'World', debug: false, maxScenes: 10, autoCleanup: true, ...config }; this.name = this._config.name!; this._createdAt = Date.now(); logger.info(`创建World: ${this.name}`); } // ===== Scene管理 ===== /** * 创建并添加Scene到World */ public createScene(sceneId: string, sceneInstance?: T): T { if (this._scenes.has(sceneId)) { throw new Error(`Scene ID '${sceneId}' 已存在于World '${this.name}' 中`); } if (this._scenes.size >= this._config.maxScenes!) { throw new Error(`World '${this.name}' 已达到最大Scene数量限制: ${this._config.maxScenes}`); } // 如果没有提供Scene实例,创建默认Scene const scene = sceneInstance || (new Scene() as unknown as T); // 设置Scene的标识 if ('id' in scene) { (scene as any).id = sceneId; } if ('name' in scene && !scene.name) { scene.name = sceneId; } this._scenes.set(sceneId, scene); // 初始化Scene scene.initialize(); logger.info(`在World '${this.name}' 中创建Scene: ${sceneId}`); return scene; } /** * 移除Scene */ public removeScene(sceneId: string): boolean { const scene = this._scenes.get(sceneId); if (!scene) { return false; } // 如果Scene正在运行,先停止它 if (this._activeScenes.has(sceneId)) { this.setSceneActive(sceneId, false); } // 清理Scene资源 scene.end(); this._scenes.delete(sceneId); logger.info(`从World '${this.name}' 中移除Scene: ${sceneId}`); return true; } /** * 获取Scene */ public getScene(sceneId: string): T | null { return this._scenes.get(sceneId) as T || null; } /** * 获取所有Scene ID */ public getSceneIds(): string[] { return Array.from(this._scenes.keys()); } /** * 获取所有Scene */ public getAllScenes(): IScene[] { return Array.from(this._scenes.values()); } /** * 设置Scene激活状态 */ public setSceneActive(sceneId: string, active: boolean): void { const scene = this._scenes.get(sceneId); if (!scene) { logger.warn(`Scene '${sceneId}' 不存在于World '${this.name}' 中`); return; } if (active) { this._activeScenes.add(sceneId); // 启动Scene if (scene.begin) { scene.begin(); } logger.debug(`在World '${this.name}' 中激活Scene: ${sceneId}`); } else { this._activeScenes.delete(sceneId); // 可选择性地停止Scene,或者让它继续运行但不更新 logger.debug(`在World '${this.name}' 中停用Scene: ${sceneId}`); } } /** * 检查Scene是否激活 */ public isSceneActive(sceneId: string): boolean { return this._activeScenes.has(sceneId); } /** * 获取活跃Scene数量 */ public getActiveSceneCount(): number { return this._activeScenes.size; } // ===== 全局System管理 ===== /** * 添加全局System * 全局System会在所有激活Scene之前更新 */ public addGlobalSystem(system: T): T { if (this._globalSystems.includes(system)) { return system; } this._globalSystems.push(system); if (system.initialize) { system.initialize(); } logger.debug(`在World '${this.name}' 中添加全局System: ${system.name}`); return system; } /** * 移除全局System */ public removeGlobalSystem(system: IGlobalSystem): boolean { const index = this._globalSystems.indexOf(system); if (index === -1) { return false; } this._globalSystems.splice(index, 1); if (system.reset) { system.reset(); } logger.debug(`从World '${this.name}' 中移除全局System: ${system.name}`); return true; } /** * 获取全局System */ public getGlobalSystem(type: new (...args: any[]) => T): T | null { for (const system of this._globalSystems) { if (system instanceof type) { return system as T; } } return null; } // ===== World生命周期 ===== /** * 启动World */ public start(): void { if (this._isActive) { return; } this._isActive = true; // 启动所有全局System for (const system of this._globalSystems) { if (system.initialize) { system.initialize(); } } logger.info(`启动World: ${this.name}`); } /** * 停止World */ public stop(): void { if (!this._isActive) { return; } // 停止所有Scene for (const sceneId of this._activeScenes) { this.setSceneActive(sceneId, false); } // 重置所有全局System for (const system of this._globalSystems) { if (system.reset) { system.reset(); } } this._isActive = false; logger.info(`停止World: ${this.name}`); } /** * 更新World中的全局System * 注意:此方法由Core.update()调用,不应直接调用 */ public updateGlobalSystems(): void { if (!this._isActive) { return; } // 更新全局System for (const system of this._globalSystems) { if (system.update) { system.update(); } } } /** * 更新World中的所有激活Scene * 注意:此方法由Core.update()调用,不应直接调用 */ public updateScenes(): void { if (!this._isActive) { return; } // 更新所有激活的Scene for (const sceneId of this._activeScenes) { const scene = this._scenes.get(sceneId); if (scene && scene.update) { scene.update(); } } // 自动清理(如果启用) if (this._config.autoCleanup && this.shouldAutoCleanup()) { this.cleanup(); } } /** * 销毁World */ public destroy(): void { logger.info(`销毁World: ${this.name}`); // 停止World this.stop(); // 销毁所有Scene const sceneIds = Array.from(this._scenes.keys()); for (const sceneId of sceneIds) { this.removeScene(sceneId); } // 清理全局System for (const system of this._globalSystems) { if (system.destroy) { system.destroy(); } else if (system.reset) { system.reset(); } } this._globalSystems.length = 0; this._scenes.clear(); this._activeScenes.clear(); } // ===== 状态信息 ===== /** * 获取World状态 */ public getStatus() { return { name: this.name, isActive: this._isActive, sceneCount: this._scenes.size, activeSceneCount: this._activeScenes.size, 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 })) }; } /** * 获取World统计信息 */ public getStats() { const stats = { totalEntities: 0, totalSystems: this._globalSystems.length, memoryUsage: 0, performance: { averageUpdateTime: 0, maxUpdateTime: 0 } }; // 统计所有Scene的实体数量 for (const scene of this._scenes.values()) { if (scene.entities) { stats.totalEntities += scene.entities.count; } if (scene.systems) { stats.totalSystems += scene.systems.length; } } return stats; } // ===== 私有方法 ===== /** * 检查是否应该执行自动清理 */ private shouldAutoCleanup(): boolean { // 简单的清理策略:如果有空Scene且超过5分钟没有实体 const currentTime = Date.now(); const cleanupThreshold = 5 * 60 * 1000; // 5分钟 for (const [sceneId, scene] of this._scenes) { if (!this._activeScenes.has(sceneId) && scene.entities && scene.entities.count === 0 && (currentTime - this._createdAt) > cleanupThreshold) { return true; } } return false; } /** * 执行清理操作 */ private cleanup(): void { const sceneIds = 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); if (scene && !this._activeScenes.has(sceneId) && scene.entities && scene.entities.count === 0 && (currentTime - this._createdAt) > cleanupThreshold) { this.removeScene(sceneId); logger.debug(`自动清理空Scene: ${sceneId} from World ${this.name}`); } } } // ===== 访问器 ===== /** * 检查World是否激活 */ public get isActive(): boolean { return this._isActive; } /** * 获取Scene数量 */ public get sceneCount(): number { return this._scenes.size; } /** * 获取创建时间 */ public get createdAt(): number { return this._createdAt; } }