Files
esengine/packages/core/src/ECS/World.ts

504 lines
12 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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<string, IScene> = new Map();
private readonly _activeScenes: Set<string> = 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<T extends IScene>(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<T extends IScene>(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<T extends IGlobalSystem>(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<T extends IGlobalSystem>(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;
}
}