504 lines
12 KiB
TypeScript
504 lines
12 KiB
TypeScript
|
|
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;
|
|||
|
|
}
|
|||
|
|
}
|