refactor(core): 代码结构优化 & 添加独立 ECS 框架文档 (#316)
Core 库优化: - Scene.ts: 提取 _runSystemPhase() 消除 update/lateUpdate 重复代码 - World.ts: 可选链简化、提取 _isSceneCleanupCandidate() 消除重复条件 - Entity.ts: 移除过度设计的 EntityComparer - 清理方法内部冗余注释,保持代码自解释 文档改进: - 为 core 包创建独立 README (中英文) - 明确 ECS 框架可独立用于 Cocos/Laya 等引擎 - 添加 sparse-checkout 命令说明,支持只克隆 core 源码 - 主 README 添加 ECS 独立使用提示
This commit is contained in:
188
packages/core/README.md
Normal file
188
packages/core/README.md
Normal file
@@ -0,0 +1,188 @@
|
||||
<h1 align="center">
|
||||
@esengine/ecs-framework
|
||||
</h1>
|
||||
|
||||
<p align="center">
|
||||
<strong>High-performance ECS Framework for JavaScript Game Engines</strong>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://www.npmjs.com/package/@esengine/ecs-framework"><img src="https://img.shields.io/npm/v/@esengine/ecs-framework?style=flat-square&color=blue" alt="npm"></a>
|
||||
<a href="https://github.com/esengine/esengine/blob/master/LICENSE"><img src="https://img.shields.io/badge/license-MIT-green?style=flat-square" alt="license"></a>
|
||||
<img src="https://img.shields.io/badge/TypeScript-5.0+-blue?style=flat-square&logo=typescript&logoColor=white" alt="TypeScript">
|
||||
<img src="https://img.shields.io/badge/zero-dependencies-brightgreen?style=flat-square" alt="zero dependencies">
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<b>English</b> | <a href="./README_CN.md">中文</a>
|
||||
</p>
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
A standalone, zero-dependency ECS (Entity-Component-System) framework designed for use with **any** JavaScript game engine:
|
||||
|
||||
- **Cocos Creator**
|
||||
- **Laya**
|
||||
- **Egret**
|
||||
- **Phaser**
|
||||
- **Or your own engine**
|
||||
|
||||
This package is the core of [ESEngine](https://github.com/esengine/esengine), but can be used completely independently.
|
||||
|
||||
## Installation
|
||||
|
||||
### npm / pnpm / yarn
|
||||
|
||||
```bash
|
||||
npm install @esengine/ecs-framework
|
||||
```
|
||||
|
||||
### Clone Source Code Only
|
||||
|
||||
If you only want the ECS framework source code (not the full ESEngine):
|
||||
|
||||
```bash
|
||||
# Step 1: Clone repo skeleton without downloading files (requires Git 2.25+)
|
||||
git clone --filter=blob:none --sparse https://github.com/esengine/esengine.git
|
||||
|
||||
# Step 2: Enter directory
|
||||
cd esengine
|
||||
|
||||
# Step 3: Specify which folder to checkout
|
||||
git sparse-checkout set packages/core
|
||||
|
||||
# Now you only have packages/core/ - other folders are not downloaded
|
||||
```
|
||||
|
||||
## Quick Start
|
||||
|
||||
```typescript
|
||||
import {
|
||||
Core, Scene, Entity, Component, EntitySystem,
|
||||
Matcher, Time, ECSComponent, ECSSystem
|
||||
} from '@esengine/ecs-framework';
|
||||
|
||||
// Define components (pure data)
|
||||
@ECSComponent('Position')
|
||||
class Position extends Component {
|
||||
x = 0;
|
||||
y = 0;
|
||||
}
|
||||
|
||||
@ECSComponent('Velocity')
|
||||
class Velocity extends Component {
|
||||
dx = 0;
|
||||
dy = 0;
|
||||
}
|
||||
|
||||
// Define system (logic)
|
||||
@ECSSystem('Movement')
|
||||
class MovementSystem extends EntitySystem {
|
||||
constructor() {
|
||||
super(Matcher.all(Position, Velocity));
|
||||
}
|
||||
|
||||
protected process(entities: readonly Entity[]): void {
|
||||
for (const entity of entities) {
|
||||
const pos = entity.getComponent(Position);
|
||||
const vel = entity.getComponent(Velocity);
|
||||
pos.x += vel.dx * Time.deltaTime;
|
||||
pos.y += vel.dy * Time.deltaTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize
|
||||
Core.create();
|
||||
const scene = new Scene();
|
||||
scene.addSystem(new MovementSystem());
|
||||
|
||||
const player = scene.createEntity('Player');
|
||||
player.addComponent(new Position());
|
||||
player.addComponent(new Velocity());
|
||||
|
||||
Core.setScene(scene);
|
||||
|
||||
// Game loop (integrate with your engine's loop)
|
||||
function update(dt: number) {
|
||||
Core.update(dt);
|
||||
}
|
||||
```
|
||||
|
||||
## Integration Examples
|
||||
|
||||
### With Cocos Creator
|
||||
|
||||
```typescript
|
||||
import { _decorator, Component as CCComponent } from 'cc';
|
||||
import { Core, Scene } from '@esengine/ecs-framework';
|
||||
|
||||
const { ccclass } = _decorator;
|
||||
|
||||
@ccclass('GameManager')
|
||||
export class GameManager extends CCComponent {
|
||||
private scene: Scene;
|
||||
|
||||
onLoad() {
|
||||
Core.create();
|
||||
this.scene = new Scene();
|
||||
// Register your systems...
|
||||
Core.setScene(this.scene);
|
||||
}
|
||||
|
||||
update(dt: number) {
|
||||
Core.update(dt);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### With Laya
|
||||
|
||||
```typescript
|
||||
import { Core, Scene } from '@esengine/ecs-framework';
|
||||
|
||||
export class Main {
|
||||
private scene: Scene;
|
||||
|
||||
constructor() {
|
||||
Core.create();
|
||||
this.scene = new Scene();
|
||||
Core.setScene(this.scene);
|
||||
|
||||
Laya.timer.frameLoop(1, this, this.onUpdate);
|
||||
}
|
||||
|
||||
onUpdate() {
|
||||
Core.update(Laya.timer.delta / 1000);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Features
|
||||
|
||||
| Feature | Description |
|
||||
|---------|-------------|
|
||||
| **Zero Dependencies** | No external runtime dependencies |
|
||||
| **Type-Safe Queries** | Fluent API with full TypeScript support |
|
||||
| **Change Detection** | Epoch-based dirty tracking for optimization |
|
||||
| **Serialization** | Built-in scene serialization and snapshots |
|
||||
| **Service Container** | Dependency injection for systems |
|
||||
| **Performance Monitoring** | Built-in profiling tools |
|
||||
|
||||
## Documentation
|
||||
|
||||
- [API Reference](https://esengine.cn/api/README)
|
||||
- [Architecture Guide](https://esengine.cn/guide/)
|
||||
- [Full ESEngine Documentation](https://esengine.cn/)
|
||||
|
||||
## License
|
||||
|
||||
MIT License - Use freely in commercial and open source projects.
|
||||
|
||||
---
|
||||
|
||||
<p align="center">
|
||||
Part of <a href="https://github.com/esengine/esengine">ESEngine</a> · Can be used standalone
|
||||
</p>
|
||||
188
packages/core/README_CN.md
Normal file
188
packages/core/README_CN.md
Normal file
@@ -0,0 +1,188 @@
|
||||
<h1 align="center">
|
||||
@esengine/ecs-framework
|
||||
</h1>
|
||||
|
||||
<p align="center">
|
||||
<strong>适用于 JavaScript 游戏引擎的高性能 ECS 框架</strong>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://www.npmjs.com/package/@esengine/ecs-framework"><img src="https://img.shields.io/npm/v/@esengine/ecs-framework?style=flat-square&color=blue" alt="npm"></a>
|
||||
<a href="https://github.com/esengine/esengine/blob/master/LICENSE"><img src="https://img.shields.io/badge/license-MIT-green?style=flat-square" alt="license"></a>
|
||||
<img src="https://img.shields.io/badge/TypeScript-5.0+-blue?style=flat-square&logo=typescript&logoColor=white" alt="TypeScript">
|
||||
<img src="https://img.shields.io/badge/zero-dependencies-brightgreen?style=flat-square" alt="零依赖">
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="./README.md">English</a> | <b>中文</b>
|
||||
</p>
|
||||
|
||||
---
|
||||
|
||||
## 概述
|
||||
|
||||
一个独立的、零依赖的 ECS(实体-组件-系统)框架,可与**任何** JavaScript 游戏引擎配合使用:
|
||||
|
||||
- **Cocos Creator**
|
||||
- **Laya**
|
||||
- **Egret**
|
||||
- **Phaser**
|
||||
- **或你自己的引擎**
|
||||
|
||||
这个包是 [ESEngine](https://github.com/esengine/esengine) 的核心,但可以完全独立使用。
|
||||
|
||||
## 安装
|
||||
|
||||
### npm / pnpm / yarn
|
||||
|
||||
```bash
|
||||
npm install @esengine/ecs-framework
|
||||
```
|
||||
|
||||
### 仅克隆源码
|
||||
|
||||
如果你只想要 ECS 框架源码(不需要完整的 ESEngine):
|
||||
|
||||
```bash
|
||||
# 第一步:克隆仓库骨架,不下载文件内容(需要 Git 2.25+)
|
||||
git clone --filter=blob:none --sparse https://github.com/esengine/esengine.git
|
||||
|
||||
# 第二步:进入目录
|
||||
cd esengine
|
||||
|
||||
# 第三步:指定要检出的目录
|
||||
git sparse-checkout set packages/core
|
||||
|
||||
# 完成!现在你只有 packages/core/ 目录,其他文件夹不会下载
|
||||
```
|
||||
|
||||
## 快速开始
|
||||
|
||||
```typescript
|
||||
import {
|
||||
Core, Scene, Entity, Component, EntitySystem,
|
||||
Matcher, Time, ECSComponent, ECSSystem
|
||||
} from '@esengine/ecs-framework';
|
||||
|
||||
// 定义组件(纯数据)
|
||||
@ECSComponent('Position')
|
||||
class Position extends Component {
|
||||
x = 0;
|
||||
y = 0;
|
||||
}
|
||||
|
||||
@ECSComponent('Velocity')
|
||||
class Velocity extends Component {
|
||||
dx = 0;
|
||||
dy = 0;
|
||||
}
|
||||
|
||||
// 定义系统(逻辑)
|
||||
@ECSSystem('Movement')
|
||||
class MovementSystem extends EntitySystem {
|
||||
constructor() {
|
||||
super(Matcher.all(Position, Velocity));
|
||||
}
|
||||
|
||||
protected process(entities: readonly Entity[]): void {
|
||||
for (const entity of entities) {
|
||||
const pos = entity.getComponent(Position);
|
||||
const vel = entity.getComponent(Velocity);
|
||||
pos.x += vel.dx * Time.deltaTime;
|
||||
pos.y += vel.dy * Time.deltaTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化
|
||||
Core.create();
|
||||
const scene = new Scene();
|
||||
scene.addSystem(new MovementSystem());
|
||||
|
||||
const player = scene.createEntity('Player');
|
||||
player.addComponent(new Position());
|
||||
player.addComponent(new Velocity());
|
||||
|
||||
Core.setScene(scene);
|
||||
|
||||
// 游戏循环(与你的引擎循环集成)
|
||||
function update(dt: number) {
|
||||
Core.update(dt);
|
||||
}
|
||||
```
|
||||
|
||||
## 集成示例
|
||||
|
||||
### Cocos Creator
|
||||
|
||||
```typescript
|
||||
import { _decorator, Component as CCComponent } from 'cc';
|
||||
import { Core, Scene } from '@esengine/ecs-framework';
|
||||
|
||||
const { ccclass } = _decorator;
|
||||
|
||||
@ccclass('GameManager')
|
||||
export class GameManager extends CCComponent {
|
||||
private scene: Scene;
|
||||
|
||||
onLoad() {
|
||||
Core.create();
|
||||
this.scene = new Scene();
|
||||
// 注册你的系统...
|
||||
Core.setScene(this.scene);
|
||||
}
|
||||
|
||||
update(dt: number) {
|
||||
Core.update(dt);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Laya
|
||||
|
||||
```typescript
|
||||
import { Core, Scene } from '@esengine/ecs-framework';
|
||||
|
||||
export class Main {
|
||||
private scene: Scene;
|
||||
|
||||
constructor() {
|
||||
Core.create();
|
||||
this.scene = new Scene();
|
||||
Core.setScene(this.scene);
|
||||
|
||||
Laya.timer.frameLoop(1, this, this.onUpdate);
|
||||
}
|
||||
|
||||
onUpdate() {
|
||||
Core.update(Laya.timer.delta / 1000);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 特性
|
||||
|
||||
| 特性 | 描述 |
|
||||
|------|------|
|
||||
| **零依赖** | 无外部运行时依赖 |
|
||||
| **类型安全查询** | 流畅的 API,完整 TypeScript 支持 |
|
||||
| **变更检测** | 基于 Epoch 的脏标记优化 |
|
||||
| **序列化** | 内置场景序列化和快照 |
|
||||
| **服务容器** | 系统的依赖注入 |
|
||||
| **性能监控** | 内置性能分析工具 |
|
||||
|
||||
## 文档
|
||||
|
||||
- [API 参考](https://esengine.cn/api/README)
|
||||
- [架构指南](https://esengine.cn/guide/)
|
||||
- [完整 ESEngine 文档](https://esengine.cn/)
|
||||
|
||||
## 许可证
|
||||
|
||||
MIT 协议 - 可自由用于商业和开源项目。
|
||||
|
||||
---
|
||||
|
||||
<p align="center">
|
||||
<a href="https://github.com/esengine/esengine">ESEngine</a> 的一部分 · 可独立使用
|
||||
</p>
|
||||
@@ -18,69 +18,72 @@ import { DebugConfigService } from './Utils/Debug/DebugConfigService';
|
||||
import { createInstance } from './Core/DI/Decorators';
|
||||
|
||||
/**
|
||||
* 游戏引擎核心类
|
||||
* @zh 游戏引擎核心类
|
||||
* @en Game engine core class
|
||||
*
|
||||
* 职责:
|
||||
* @zh 职责:
|
||||
* - 提供全局服务(Timer、Performance、Pool等)
|
||||
* - 管理场景生命周期(内置SceneManager)
|
||||
* - 管理全局管理器的生命周期
|
||||
* - 提供统一的游戏循环更新入口
|
||||
* @en Responsibilities:
|
||||
* - Provide global services (Timer, Performance, Pool, etc.)
|
||||
* - Manage scene lifecycle (built-in SceneManager)
|
||||
* - Manage global manager lifecycles
|
||||
* - Provide unified game loop update entry
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* // 初始化并设置场景
|
||||
* // @zh 初始化并设置场景 | @en Initialize and set scene
|
||||
* Core.create({ debug: true });
|
||||
* Core.setScene(new GameScene());
|
||||
*
|
||||
* // 游戏循环(自动更新全局服务和场景)
|
||||
* // @zh 游戏循环(自动更新全局服务和场景)| @en Game loop (auto-updates global services and scene)
|
||||
* function gameLoop(deltaTime: number) {
|
||||
* Core.update(deltaTime);
|
||||
* }
|
||||
*
|
||||
* // 使用定时器
|
||||
* // @zh 使用定时器 | @en Use timer
|
||||
* Core.schedule(1.0, false, null, (timer) => {
|
||||
* console.log("1秒后执行");
|
||||
* console.log("Executed after 1 second");
|
||||
* });
|
||||
*
|
||||
* // 切换场景
|
||||
* Core.loadScene(new MenuScene()); // 延迟切换
|
||||
* Core.setScene(new GameScene()); // 立即切换
|
||||
* // @zh 切换场景 | @en Switch scene
|
||||
* Core.loadScene(new MenuScene()); // @zh 延迟切换 | @en Deferred switch
|
||||
* Core.setScene(new GameScene()); // @zh 立即切换 | @en Immediate switch
|
||||
*
|
||||
* // 获取当前场景
|
||||
* // @zh 获取当前场景 | @en Get current scene
|
||||
* const currentScene = Core.scene;
|
||||
* ```
|
||||
*/
|
||||
export class Core {
|
||||
/**
|
||||
* 游戏暂停状态
|
||||
*
|
||||
* 当设置为true时,游戏循环将暂停执行。
|
||||
* @zh 游戏暂停状态,当设置为true时,游戏循环将暂停执行
|
||||
* @en Game paused state, when set to true, game loop will pause execution
|
||||
*/
|
||||
public static paused = false;
|
||||
|
||||
/**
|
||||
* 全局核心实例
|
||||
*
|
||||
* 可能为null表示Core尚未初始化或已被销毁
|
||||
* @zh 全局核心实例,可能为null表示Core尚未初始化或已被销毁
|
||||
* @en Global core instance, null means Core is not initialized or destroyed
|
||||
*/
|
||||
private static _instance: Core | null = null;
|
||||
|
||||
/**
|
||||
* Core专用日志器
|
||||
* @zh Core专用日志器
|
||||
* @en Core logger
|
||||
*/
|
||||
private static _logger = createLogger('Core');
|
||||
|
||||
/**
|
||||
* 调试模式标志
|
||||
*
|
||||
* 在调试模式下会启用额外的性能监控和错误检查。
|
||||
* @zh 调试模式标志,在调试模式下会启用额外的性能监控和错误检查
|
||||
* @en Debug mode flag, enables additional performance monitoring and error checking in debug mode
|
||||
*/
|
||||
public readonly debug: boolean;
|
||||
|
||||
/**
|
||||
* 服务容器
|
||||
*
|
||||
* 管理所有服务的注册、解析和生命周期。
|
||||
* @zh 服务容器,管理所有服务的注册、解析和生命周期
|
||||
* @en Service container for managing registration, resolution, and lifecycle of all services
|
||||
*/
|
||||
private _serviceContainer: ServiceContainer;
|
||||
|
||||
@@ -90,110 +93,79 @@ export class Core {
|
||||
private _debugManager?: DebugManager;
|
||||
|
||||
/**
|
||||
* 场景管理器
|
||||
*
|
||||
* 管理当前场景的生命周期。
|
||||
* @zh 场景管理器,管理当前场景的生命周期
|
||||
* @en Scene manager for managing current scene lifecycle
|
||||
*/
|
||||
private _sceneManager: SceneManager;
|
||||
|
||||
/**
|
||||
* World管理器
|
||||
*
|
||||
* 管理多个独立的World实例(可选)。
|
||||
* @zh World管理器,管理多个独立的World实例(可选)
|
||||
* @en World manager for managing multiple independent World instances (optional)
|
||||
*/
|
||||
private _worldManager: WorldManager;
|
||||
|
||||
/**
|
||||
* 插件管理器
|
||||
*
|
||||
* 管理所有插件的生命周期。
|
||||
* @zh 插件管理器,管理所有插件的生命周期
|
||||
* @en Plugin manager for managing all plugin lifecycles
|
||||
*/
|
||||
private _pluginManager: PluginManager;
|
||||
|
||||
/**
|
||||
* 插件服务注册表
|
||||
*
|
||||
* 基于 ServiceToken 的类型安全服务注册表。
|
||||
* Type-safe service registry based on ServiceToken.
|
||||
* @zh 插件服务注册表,基于 ServiceToken 的类型安全服务注册表
|
||||
* @en Plugin service registry, type-safe service registry based on ServiceToken
|
||||
*/
|
||||
private _pluginServiceRegistry: PluginServiceRegistry;
|
||||
|
||||
/**
|
||||
* Core配置
|
||||
* @zh Core配置
|
||||
* @en Core configuration
|
||||
*/
|
||||
private _config: ICoreConfig;
|
||||
|
||||
/**
|
||||
* 创建核心实例
|
||||
* @zh 创建核心实例
|
||||
* @en Create core instance
|
||||
*
|
||||
* @param config - Core配置对象
|
||||
* @param config - @zh Core配置对象 @en Core configuration object
|
||||
*/
|
||||
private constructor(config: ICoreConfig = {}) {
|
||||
Core._instance = this;
|
||||
|
||||
// 保存配置
|
||||
this._config = {
|
||||
debug: true,
|
||||
...config
|
||||
};
|
||||
|
||||
// 初始化服务容器
|
||||
this._config = { debug: true, ...config };
|
||||
this._serviceContainer = new ServiceContainer();
|
||||
|
||||
// 初始化定时器管理器
|
||||
this._timerManager = new TimerManager();
|
||||
this._serviceContainer.registerInstance(TimerManager, this._timerManager);
|
||||
|
||||
// 初始化性能监控器
|
||||
this._performanceMonitor = new PerformanceMonitor();
|
||||
this._serviceContainer.registerInstance(PerformanceMonitor, this._performanceMonitor);
|
||||
|
||||
// 在调试模式下启用性能监控
|
||||
if (this._config.debug) {
|
||||
this._performanceMonitor.enable();
|
||||
}
|
||||
|
||||
// 初始化对象池管理器
|
||||
this._poolManager = new PoolManager();
|
||||
this._serviceContainer.registerInstance(PoolManager, this._poolManager);
|
||||
|
||||
// 初始化场景管理器
|
||||
this._sceneManager = new SceneManager(this._performanceMonitor);
|
||||
this._serviceContainer.registerInstance(SceneManager, this._sceneManager);
|
||||
this._sceneManager.setSceneChangedCallback(() => this._debugManager?.onSceneChanged());
|
||||
|
||||
// 设置场景切换回调,通知调试管理器
|
||||
this._sceneManager.setSceneChangedCallback(() => {
|
||||
if (this._debugManager) {
|
||||
this._debugManager.onSceneChanged();
|
||||
}
|
||||
});
|
||||
|
||||
// 初始化World管理器
|
||||
this._worldManager = new WorldManager({ debug: !!this._config.debug, ...this._config.worldManagerConfig });
|
||||
this._serviceContainer.registerInstance(WorldManager, this._worldManager);
|
||||
|
||||
// 初始化插件管理器
|
||||
this._pluginManager = new PluginManager();
|
||||
this._pluginManager.initialize(this, this._serviceContainer);
|
||||
this._serviceContainer.registerInstance(PluginManager, this._pluginManager);
|
||||
|
||||
// 初始化插件服务注册表
|
||||
// Initialize plugin service registry
|
||||
this._pluginServiceRegistry = new PluginServiceRegistry();
|
||||
this._serviceContainer.registerInstance(PluginServiceRegistry, this._pluginServiceRegistry);
|
||||
|
||||
this.debug = this._config.debug ?? true;
|
||||
|
||||
// 初始化调试管理器
|
||||
if (this._config.debugConfig?.enabled) {
|
||||
const configService = new DebugConfigService();
|
||||
configService.setConfig(this._config.debugConfig);
|
||||
this._serviceContainer.registerInstance(DebugConfigService, configService);
|
||||
|
||||
this._serviceContainer.registerSingleton(DebugManager, (c) =>
|
||||
createInstance(DebugManager, c)
|
||||
);
|
||||
|
||||
this._serviceContainer.registerSingleton(DebugManager, (c) => createInstance(DebugManager, c));
|
||||
this._debugManager = this._serviceContainer.resolve(DebugManager);
|
||||
this._debugManager.onInitialize();
|
||||
}
|
||||
@@ -202,28 +174,31 @@ export class Core {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取核心实例
|
||||
* @zh 获取核心实例
|
||||
* @en Get core instance
|
||||
*
|
||||
* @returns 全局核心实例
|
||||
* @returns @zh 全局核心实例 @en Global core instance
|
||||
*/
|
||||
public static get Instance() {
|
||||
return this._instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取服务容器
|
||||
* @zh 获取服务容器
|
||||
* @en Get service container
|
||||
*
|
||||
* 用于注册和解析自定义服务。
|
||||
* @zh 用于注册和解析自定义服务。
|
||||
* @en Used for registering and resolving custom services.
|
||||
*
|
||||
* @returns 服务容器实例
|
||||
* @throws 如果Core实例未创建
|
||||
* @returns @zh 服务容器实例 @en Service container instance
|
||||
* @throws @zh 如果Core实例未创建 @en If Core instance is not created
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* // 注册自定义服务
|
||||
* // @zh 注册自定义服务 | @en Register custom service
|
||||
* Core.services.registerSingleton(MyService);
|
||||
*
|
||||
* // 解析服务
|
||||
* // @zh 解析服务 | @en Resolve service
|
||||
* const myService = Core.services.resolve(MyService);
|
||||
* ```
|
||||
*/
|
||||
@@ -235,28 +210,29 @@ export class Core {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取插件服务注册表
|
||||
* @zh 获取插件服务注册表
|
||||
* @en Get plugin service registry
|
||||
*
|
||||
* 用于基于 ServiceToken 的类型安全服务注册和获取。
|
||||
* For type-safe service registration and retrieval based on ServiceToken.
|
||||
* @zh 用于基于 ServiceToken 的类型安全服务注册和获取。
|
||||
* @en For type-safe service registration and retrieval based on ServiceToken.
|
||||
*
|
||||
* @returns PluginServiceRegistry 实例
|
||||
* @throws 如果 Core 实例未创建
|
||||
* @returns @zh PluginServiceRegistry 实例 @en PluginServiceRegistry instance
|
||||
* @throws @zh 如果 Core 实例未创建 @en If Core instance is not created
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* import { createServiceToken } from '@esengine/ecs-framework';
|
||||
*
|
||||
* // 定义服务令牌
|
||||
* // @zh 定义服务令牌 | @en Define service token
|
||||
* const MyServiceToken = createServiceToken<IMyService>('myService');
|
||||
*
|
||||
* // 注册服务
|
||||
* // @zh 注册服务 | @en Register service
|
||||
* Core.pluginServices.register(MyServiceToken, myServiceInstance);
|
||||
*
|
||||
* // 获取服务(可选)
|
||||
* // @zh 获取服务(可选)| @en Get service (optional)
|
||||
* const service = Core.pluginServices.get(MyServiceToken);
|
||||
*
|
||||
* // 获取服务(必需,不存在则抛异常)
|
||||
* // @zh 获取服务(必需,不存在则抛异常)| @en Get service (required, throws if not found)
|
||||
* const service = Core.pluginServices.require(MyServiceToken);
|
||||
* ```
|
||||
*/
|
||||
@@ -268,16 +244,18 @@ export class Core {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取World管理器
|
||||
* @zh 获取World管理器
|
||||
* @en Get World manager
|
||||
*
|
||||
* 用于管理多个独立的World实例(高级用户)。
|
||||
* @zh 用于管理多个独立的World实例(高级用户)。
|
||||
* @en For managing multiple independent World instances (advanced users).
|
||||
*
|
||||
* @returns WorldManager实例
|
||||
* @throws 如果Core实例未创建
|
||||
* @returns @zh WorldManager实例 @en WorldManager instance
|
||||
* @throws @zh 如果Core实例未创建 @en If Core instance is not created
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* // 创建多个游戏房间
|
||||
* // @zh 创建多个游戏房间 | @en Create multiple game rooms
|
||||
* const wm = Core.worldManager;
|
||||
* const room1 = wm.createWorld('room_001');
|
||||
* room1.createScene('game', new GameScene());
|
||||
@@ -292,16 +270,18 @@ export class Core {
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建Core实例
|
||||
* @zh 创建Core实例
|
||||
* @en Create Core instance
|
||||
*
|
||||
* 如果实例已存在,则返回现有实例。
|
||||
* @zh 如果实例已存在,则返回现有实例。
|
||||
* @en If instance already exists, returns the existing instance.
|
||||
*
|
||||
* @param config - Core配置,也可以直接传入boolean表示debug模式(向后兼容)
|
||||
* @returns Core实例
|
||||
* @param config - @zh Core配置,也可以直接传入boolean表示debug模式(向后兼容) @en Core config, can also pass boolean for debug mode (backward compatible)
|
||||
* @returns @zh Core实例 @en Core instance
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* // 方式1:使用配置对象
|
||||
* // @zh 方式1:使用配置对象 | @en Method 1: Use config object
|
||||
* Core.create({
|
||||
* debug: true,
|
||||
* debugConfig: {
|
||||
@@ -310,7 +290,7 @@ export class Core {
|
||||
* }
|
||||
* });
|
||||
*
|
||||
* // 方式2:简单模式(向后兼容)
|
||||
* // @zh 方式2:简单模式(向后兼容)| @en Method 2: Simple mode (backward compatible)
|
||||
* Core.create(true); // debug = true
|
||||
* ```
|
||||
*/
|
||||
@@ -328,16 +308,17 @@ export class Core {
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置当前场景
|
||||
* @zh 设置当前场景
|
||||
* @en Set current scene
|
||||
*
|
||||
* @param scene - 要设置的场景
|
||||
* @returns 设置的场景实例
|
||||
* @param scene - @zh 要设置的场景 @en The scene to set
|
||||
* @returns @zh 设置的场景实例 @en The scene instance that was set
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* Core.create({ debug: true });
|
||||
*
|
||||
* // 创建并设置场景
|
||||
* // @zh 创建并设置场景 | @en Create and set scene
|
||||
* const gameScene = new GameScene();
|
||||
* Core.setScene(gameScene);
|
||||
* ```
|
||||
@@ -352,9 +333,10 @@ export class Core {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前场景
|
||||
* @zh 获取当前场景
|
||||
* @en Get current scene
|
||||
*
|
||||
* @returns 当前场景,如果没有场景则返回null
|
||||
* @returns @zh 当前场景,如果没有场景则返回null @en Current scene, or null if no scene
|
||||
*/
|
||||
public static get scene(): IScene | null {
|
||||
if (!this._instance) {
|
||||
@@ -364,21 +346,22 @@ export class Core {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取ECS流式API
|
||||
* @zh 获取ECS流式API
|
||||
* @en Get ECS fluent API
|
||||
*
|
||||
* @returns ECS API实例,如果当前没有场景则返回null
|
||||
* @returns @zh ECS API实例,如果当前没有场景则返回null @en ECS API instance, or null if no scene
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* // 使用流式API创建实体
|
||||
* // @zh 使用流式API创建实体 | @en Create entity with fluent API
|
||||
* const player = Core.ecsAPI?.createEntity('Player')
|
||||
* .addComponent(Position, 100, 100)
|
||||
* .addComponent(Velocity, 50, 0);
|
||||
*
|
||||
* // 查询实体
|
||||
* // @zh 查询实体 | @en Query entities
|
||||
* const enemies = Core.ecsAPI?.query(Enemy, Transform);
|
||||
*
|
||||
* // 发射事件
|
||||
* // @zh 发射事件 | @en Emit event
|
||||
* Core.ecsAPI?.emit('game:start', { level: 1 });
|
||||
* ```
|
||||
*/
|
||||
@@ -390,13 +373,14 @@ export class Core {
|
||||
}
|
||||
|
||||
/**
|
||||
* 延迟加载场景(下一帧切换)
|
||||
* @zh 延迟加载场景(下一帧切换)
|
||||
* @en Load scene with delay (switch on next frame)
|
||||
*
|
||||
* @param scene - 要加载的场景
|
||||
* @param scene - @zh 要加载的场景 @en The scene to load
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* // 延迟切换场景(在下一帧生效)
|
||||
* // @zh 延迟切换场景(在下一帧生效)| @en Deferred scene switch (takes effect next frame)
|
||||
* Core.loadScene(new MenuScene());
|
||||
* ```
|
||||
*/
|
||||
@@ -410,28 +394,29 @@ export class Core {
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新游戏逻辑
|
||||
* @zh 更新游戏逻辑
|
||||
* @en Update game logic
|
||||
*
|
||||
* 此方法应该在游戏引擎的更新循环中调用。
|
||||
* 会自动更新全局服务和当前场景。
|
||||
* @zh 此方法应该在游戏引擎的更新循环中调用。会自动更新全局服务和当前场景。
|
||||
* @en This method should be called in the game engine's update loop. Automatically updates global services and current scene.
|
||||
*
|
||||
* @param deltaTime - 外部引擎提供的帧时间间隔(秒)
|
||||
* @param deltaTime - @zh 外部引擎提供的帧时间间隔(秒)@en Frame delta time in seconds from external engine
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* // 初始化
|
||||
* // @zh 初始化 | @en Initialize
|
||||
* Core.create({ debug: true });
|
||||
* Core.setScene(new GameScene());
|
||||
*
|
||||
* // Laya引擎集成
|
||||
* // @zh Laya引擎集成 | @en Laya engine integration
|
||||
* Laya.timer.frameLoop(1, this, () => {
|
||||
* const deltaTime = Laya.timer.delta / 1000;
|
||||
* Core.update(deltaTime); // 自动更新全局服务和场景
|
||||
* Core.update(deltaTime);
|
||||
* });
|
||||
*
|
||||
* // Cocos Creator集成
|
||||
* // @zh Cocos Creator集成 | @en Cocos Creator integration
|
||||
* update(deltaTime: number) {
|
||||
* Core.update(deltaTime); // 自动更新全局服务和场景
|
||||
* Core.update(deltaTime);
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
@@ -446,27 +431,29 @@ export class Core {
|
||||
|
||||
|
||||
/**
|
||||
* 调度定时器
|
||||
* @zh 调度定时器
|
||||
* @en Schedule a timer
|
||||
*
|
||||
* 创建一个定时器,在指定时间后执行回调函数。
|
||||
* @zh 创建一个定时器,在指定时间后执行回调函数。
|
||||
* @en Create a timer that executes a callback after the specified time.
|
||||
*
|
||||
* @param timeInSeconds - 延迟时间(秒)
|
||||
* @param repeats - 是否重复执行,默认为false
|
||||
* @param context - 回调函数的上下文,默认为null
|
||||
* @param onTime - 定时器触发时的回调函数
|
||||
* @returns 创建的定时器实例
|
||||
* @throws 如果Core实例未创建或onTime回调未提供
|
||||
* @param timeInSeconds - @zh 延迟时间(秒)@en Delay time in seconds
|
||||
* @param repeats - @zh 是否重复执行,默认为false @en Whether to repeat, defaults to false
|
||||
* @param context - @zh 回调函数的上下文 @en Context for the callback
|
||||
* @param onTime - @zh 定时器触发时的回调函数 @en Callback when timer fires
|
||||
* @returns @zh 创建的定时器实例 @en The created timer instance
|
||||
* @throws @zh 如果Core实例未创建或onTime回调未提供 @en If Core instance not created or onTime not provided
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* // 一次性定时器
|
||||
* // @zh 一次性定时器 | @en One-time timer
|
||||
* Core.schedule(1.0, false, null, (timer) => {
|
||||
* console.log("1秒后执行一次");
|
||||
* console.log("Executed after 1 second");
|
||||
* });
|
||||
*
|
||||
* // 重复定时器
|
||||
* // @zh 重复定时器 | @en Repeating timer
|
||||
* Core.schedule(0.5, true, null, (timer) => {
|
||||
* console.log("每0.5秒执行一次");
|
||||
* console.log("Executed every 0.5 seconds");
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
@@ -481,9 +468,10 @@ export class Core {
|
||||
}
|
||||
|
||||
/**
|
||||
* 启用调试功能
|
||||
* @zh 启用调试功能
|
||||
* @en Enable debug features
|
||||
*
|
||||
* @param config 调试配置
|
||||
* @param config - @zh 调试配置 @en Debug configuration
|
||||
*/
|
||||
public static enableDebug(config: IECSDebugConfig): void {
|
||||
if (!this._instance) {
|
||||
@@ -511,7 +499,8 @@ export class Core {
|
||||
}
|
||||
|
||||
/**
|
||||
* 禁用调试功能
|
||||
* @zh 禁用调试功能
|
||||
* @en Disable debug features
|
||||
*/
|
||||
public static disableDebug(): void {
|
||||
if (!this._instance) return;
|
||||
@@ -528,9 +517,10 @@ export class Core {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取调试数据
|
||||
* @zh 获取调试数据
|
||||
* @en Get debug data
|
||||
*
|
||||
* @returns 当前调试数据,如果调试未启用则返回null
|
||||
* @returns @zh 当前调试数据,如果调试未启用则返回null @en Current debug data, or null if debug is disabled
|
||||
*/
|
||||
public static getDebugData(): unknown {
|
||||
if (!this._instance?._debugManager) {
|
||||
@@ -541,34 +531,37 @@ export class Core {
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查调试是否启用
|
||||
* @zh 检查调试是否启用
|
||||
* @en Check if debug is enabled
|
||||
*
|
||||
* @returns 调试状态
|
||||
* @returns @zh 调试状态 @en Debug status
|
||||
*/
|
||||
public static get isDebugEnabled(): boolean {
|
||||
return this._instance?._config.debugConfig?.enabled || false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取性能监视器实例
|
||||
* @zh 获取性能监视器实例
|
||||
* @en Get performance monitor instance
|
||||
*
|
||||
* @returns 性能监视器,如果Core未初始化则返回null
|
||||
* @returns @zh 性能监视器,如果Core未初始化则返回null @en Performance monitor, or null if Core not initialized
|
||||
*/
|
||||
public static get performanceMonitor(): PerformanceMonitor | null {
|
||||
return this._instance?._performanceMonitor || null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 安装插件
|
||||
* @zh 安装插件
|
||||
* @en Install plugin
|
||||
*
|
||||
* @param plugin - 插件实例
|
||||
* @throws 如果Core实例未创建或插件安装失败
|
||||
* @param plugin - @zh 插件实例 @en Plugin instance
|
||||
* @throws @zh 如果Core实例未创建或插件安装失败 @en If Core instance not created or plugin installation fails
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* Core.create({ debug: true });
|
||||
*
|
||||
* // 安装插件
|
||||
* // @zh 安装插件 | @en Install plugin
|
||||
* await Core.installPlugin(new MyPlugin());
|
||||
* ```
|
||||
*/
|
||||
@@ -581,10 +574,11 @@ export class Core {
|
||||
}
|
||||
|
||||
/**
|
||||
* 卸载插件
|
||||
* @zh 卸载插件
|
||||
* @en Uninstall plugin
|
||||
*
|
||||
* @param name - 插件名称
|
||||
* @throws 如果Core实例未创建或插件卸载失败
|
||||
* @param name - @zh 插件名称 @en Plugin name
|
||||
* @throws @zh 如果Core实例未创建或插件卸载失败 @en If Core instance not created or plugin uninstallation fails
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
@@ -600,10 +594,11 @@ export class Core {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取插件实例
|
||||
* @zh 获取插件实例
|
||||
* @en Get plugin instance
|
||||
*
|
||||
* @param name - 插件名称
|
||||
* @returns 插件实例,如果未安装则返回undefined
|
||||
* @param name - @zh 插件名称 @en Plugin name
|
||||
* @returns @zh 插件实例,如果未安装则返回undefined @en Plugin instance, or undefined if not installed
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
@@ -622,10 +617,11 @@ export class Core {
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查插件是否已安装
|
||||
* @zh 检查插件是否已安装
|
||||
* @en Check if plugin is installed
|
||||
*
|
||||
* @param name - 插件名称
|
||||
* @returns 是否已安装
|
||||
* @param name - @zh 插件名称 @en Plugin name
|
||||
* @returns @zh 是否已安装 @en Whether installed
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
@@ -643,9 +639,11 @@ export class Core {
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化核心系统
|
||||
* @zh 初始化核心系统
|
||||
* @en Initialize core system
|
||||
*
|
||||
* 执行核心系统的初始化逻辑。
|
||||
* @zh 执行核心系统的初始化逻辑。
|
||||
* @en Execute core system initialization logic.
|
||||
*/
|
||||
protected initialize() {
|
||||
// 核心系统初始化
|
||||
@@ -656,61 +654,43 @@ export class Core {
|
||||
}
|
||||
|
||||
/**
|
||||
* 内部更新方法
|
||||
* @zh 内部更新方法
|
||||
* @en Internal update method
|
||||
*
|
||||
* @param deltaTime - 帧时间间隔(秒)
|
||||
* @param deltaTime - @zh 帧时间间隔(秒)@en Frame delta time in seconds
|
||||
*/
|
||||
private updateInternal(deltaTime: number): void {
|
||||
if (Core.paused) return;
|
||||
|
||||
// 开始性能监控
|
||||
const frameStartTime = this._performanceMonitor.startMonitoring('Core.update');
|
||||
|
||||
// 更新时间系统
|
||||
Time.update(deltaTime);
|
||||
this._performanceMonitor.updateFPS?.(Time.deltaTime);
|
||||
|
||||
// 更新FPS监控(如果性能监控器支持)
|
||||
if ('updateFPS' in this._performanceMonitor && typeof this._performanceMonitor.updateFPS === 'function') {
|
||||
this._performanceMonitor.updateFPS(Time.deltaTime);
|
||||
}
|
||||
|
||||
// 更新所有可更新的服务
|
||||
const servicesStartTime = this._performanceMonitor.startMonitoring('Services.update');
|
||||
this._serviceContainer.updateAll(deltaTime);
|
||||
this._performanceMonitor.endMonitoring('Services.update', servicesStartTime, this._serviceContainer.getUpdatableCount());
|
||||
|
||||
// 更新对象池管理器
|
||||
this._poolManager.update();
|
||||
|
||||
// 更新默认场景(通过 SceneManager)
|
||||
this._sceneManager.update();
|
||||
|
||||
// 更新额外的 WorldManager
|
||||
this._worldManager.updateAll();
|
||||
|
||||
// 结束性能监控
|
||||
this._performanceMonitor.endMonitoring('Core.update', frameStartTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* 销毁Core实例
|
||||
* @zh 销毁Core实例
|
||||
* @en Destroy Core instance
|
||||
*
|
||||
* 清理所有资源,通常在应用程序关闭时调用。
|
||||
* @zh 清理所有资源,通常在应用程序关闭时调用。
|
||||
* @en Clean up all resources, typically called when the application closes.
|
||||
*/
|
||||
public static destroy(): void {
|
||||
if (!this._instance) return;
|
||||
|
||||
// 停止调试管理器
|
||||
if (this._instance._debugManager) {
|
||||
this._instance._debugManager.stop();
|
||||
}
|
||||
|
||||
// 清理所有服务
|
||||
this._instance._debugManager?.stop();
|
||||
this._instance._serviceContainer.clear();
|
||||
|
||||
Core._logger.info('Core destroyed');
|
||||
|
||||
// 清空实例引用,允许重新创建Core实例
|
||||
this._instance = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,13 +2,17 @@ import type { IComponent } from '../Types';
|
||||
import { Int32 } from './Core/SoAStorage';
|
||||
|
||||
/**
|
||||
* 游戏组件基类
|
||||
* @zh 游戏组件基类
|
||||
* @en Base class for game components
|
||||
*
|
||||
* ECS架构中的组件(Component)应该是纯数据容器。
|
||||
* @zh ECS架构中的组件(Component)应该是纯数据容器。
|
||||
* 所有游戏逻辑应该在 EntitySystem 中实现,而不是在组件内部。
|
||||
* @en Components in ECS architecture should be pure data containers.
|
||||
* All game logic should be implemented in EntitySystem, not inside components.
|
||||
*
|
||||
* @example
|
||||
* 推荐做法:纯数据组件
|
||||
* @zh 推荐做法:纯数据组件
|
||||
* @en Recommended: Pure data component
|
||||
* ```typescript
|
||||
* class HealthComponent extends Component {
|
||||
* public health: number = 100;
|
||||
@@ -17,7 +21,8 @@ import { Int32 } from './Core/SoAStorage';
|
||||
* ```
|
||||
*
|
||||
* @example
|
||||
* 推荐做法:在 System 中处理逻辑
|
||||
* @zh 推荐做法:在 System 中处理逻辑
|
||||
* @en Recommended: Handle logic in System
|
||||
* ```typescript
|
||||
* class HealthSystem extends EntitySystem {
|
||||
* process(entities: Entity[]): void {
|
||||
@@ -33,75 +38,66 @@ import { Int32 } from './Core/SoAStorage';
|
||||
*/
|
||||
export abstract class Component implements IComponent {
|
||||
/**
|
||||
* 组件ID生成器
|
||||
*
|
||||
* 用于为每个组件分配唯一的ID。
|
||||
*
|
||||
* Component ID generator.
|
||||
* Used to assign unique IDs to each component.
|
||||
* @zh 组件ID生成器,用于为每个组件分配唯一的ID
|
||||
* @en Component ID generator, used to assign unique IDs to each component
|
||||
*/
|
||||
private static idGenerator: number = 0;
|
||||
|
||||
/**
|
||||
* 组件唯一标识符
|
||||
*
|
||||
* 在整个游戏生命周期中唯一的数字ID。
|
||||
* @zh 组件唯一标识符,在整个游戏生命周期中唯一
|
||||
* @en Unique identifier for the component, unique throughout the game lifecycle
|
||||
*/
|
||||
public readonly id: number;
|
||||
|
||||
/**
|
||||
* 所属实体ID
|
||||
* @zh 所属实体ID
|
||||
* @en Owner entity ID
|
||||
*
|
||||
* 存储实体ID而非引用,避免循环引用,符合ECS数据导向设计。
|
||||
* @zh 存储实体ID而非引用,避免循环引用,符合ECS数据导向设计
|
||||
* @en Stores entity ID instead of reference to avoid circular references, following ECS data-oriented design
|
||||
*/
|
||||
@Int32
|
||||
public entityId: number | null = null;
|
||||
|
||||
/**
|
||||
* 最后写入的 epoch
|
||||
* @zh 最后写入的 epoch,用于帧级变更检测
|
||||
* @en Last write epoch, used for frame-level change detection
|
||||
*
|
||||
* 用于帧级变更检测,记录组件最后一次被修改时的 epoch。
|
||||
* 0 表示从未被标记为已修改。
|
||||
*
|
||||
* Last write epoch.
|
||||
* Used for frame-level change detection, records the epoch when component was last modified.
|
||||
* 0 means never marked as modified.
|
||||
* @zh 记录组件最后一次被修改时的 epoch,0 表示从未被标记为已修改
|
||||
* @en Records the epoch when component was last modified, 0 means never marked as modified
|
||||
*/
|
||||
private _lastWriteEpoch: number = 0;
|
||||
|
||||
/**
|
||||
* 获取最后写入的 epoch
|
||||
*
|
||||
* Get last write epoch.
|
||||
* @zh 获取最后写入的 epoch
|
||||
* @en Get last write epoch
|
||||
*/
|
||||
public get lastWriteEpoch(): number {
|
||||
return this._lastWriteEpoch;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建组件实例
|
||||
*
|
||||
* 自动分配唯一ID给组件。
|
||||
* @zh 创建组件实例,自动分配唯一ID
|
||||
* @en Create component instance, automatically assigns unique ID
|
||||
*/
|
||||
constructor() {
|
||||
this.id = Component.idGenerator++;
|
||||
}
|
||||
|
||||
/**
|
||||
* 标记组件为已修改
|
||||
* @zh 标记组件为已修改
|
||||
* @en Mark component as modified
|
||||
*
|
||||
* 调用此方法会更新组件的 lastWriteEpoch 为当前帧的 epoch。
|
||||
* @zh 调用此方法会更新组件的 lastWriteEpoch 为当前帧的 epoch。
|
||||
* 系统可以通过比较 lastWriteEpoch 和上次检查的 epoch 来判断组件是否发生变更。
|
||||
*
|
||||
* Mark component as modified.
|
||||
* Calling this method updates the component's lastWriteEpoch to the current frame's epoch.
|
||||
* @en Calling this method updates the component's lastWriteEpoch to the current frame's epoch.
|
||||
* Systems can compare lastWriteEpoch with their last checked epoch to detect changes.
|
||||
*
|
||||
* @param epoch 当前帧的 epoch | Current frame's epoch
|
||||
* @param epoch - @zh 当前帧的 epoch @en Current frame's epoch
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* // 在修改组件数据后调用
|
||||
* // @zh 在修改组件数据后调用 | @en Call after modifying component data
|
||||
* velocity.x = 10;
|
||||
* velocity.markDirty(scene.epochManager.current);
|
||||
* ```
|
||||
@@ -111,35 +107,41 @@ export abstract class Component implements IComponent {
|
||||
}
|
||||
|
||||
/**
|
||||
* 组件添加到实体时的回调
|
||||
* @zh 组件添加到实体时的回调
|
||||
* @en Callback when component is added to an entity
|
||||
*
|
||||
* 当组件被添加到实体时调用,可以在此方法中进行初始化操作。
|
||||
*
|
||||
* @remarks
|
||||
* @zh 当组件被添加到实体时调用,可以在此方法中进行初始化操作。
|
||||
* 这是一个生命周期钩子,用于组件的初始化逻辑。
|
||||
* 虽然保留此方法,但建议将复杂的初始化逻辑放在 System 中处理。
|
||||
* @en Called when component is added to an entity, can perform initialization here.
|
||||
* This is a lifecycle hook for component initialization logic.
|
||||
* While this method is available, complex initialization logic should be handled in System.
|
||||
*/
|
||||
public onAddedToEntity(): void {}
|
||||
|
||||
/**
|
||||
* 组件从实体移除时的回调
|
||||
* @zh 组件从实体移除时的回调
|
||||
* @en Callback when component is removed from an entity
|
||||
*
|
||||
* 当组件从实体中移除时调用,可以在此方法中进行清理操作。
|
||||
*
|
||||
* @remarks
|
||||
* @zh 当组件从实体中移除时调用,可以在此方法中进行清理操作。
|
||||
* 这是一个生命周期钩子,用于组件的清理逻辑。
|
||||
* 虽然保留此方法,但建议将复杂的清理逻辑放在 System 中处理。
|
||||
* @en Called when component is removed from an entity, can perform cleanup here.
|
||||
* This is a lifecycle hook for component cleanup logic.
|
||||
* While this method is available, complex cleanup logic should be handled in System.
|
||||
*/
|
||||
public onRemovedFromEntity(): void {}
|
||||
|
||||
/**
|
||||
* 组件反序列化后的回调
|
||||
* @zh 组件反序列化后的回调
|
||||
* @en Callback after component deserialization
|
||||
*
|
||||
* 当组件从场景文件加载或快照恢复后调用,可以在此方法中恢复运行时数据。
|
||||
*
|
||||
* @remarks
|
||||
* @zh 当组件从场景文件加载或快照恢复后调用,可以在此方法中恢复运行时数据。
|
||||
* 这是一个生命周期钩子,用于恢复无法序列化的运行时数据。
|
||||
* 例如:从图片路径重新加载图片尺寸信息,重建缓存等。
|
||||
* @en Called after component is loaded from scene file or restored from snapshot.
|
||||
* This is a lifecycle hook for restoring runtime data that cannot be serialized.
|
||||
* For example: reloading image dimensions from image path, rebuilding caches, etc.
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
@@ -149,7 +151,8 @@ export abstract class Component implements IComponent {
|
||||
*
|
||||
* public async onDeserialized(): Promise<void> {
|
||||
* if (this.tilesetImage) {
|
||||
* // 重新加载 tileset 图片并恢复运行时数据
|
||||
* // @zh 重新加载 tileset 图片并恢复运行时数据
|
||||
* // @en Reload tileset image and restore runtime data
|
||||
* const img = await loadImage(this.tilesetImage);
|
||||
* this.setTilesetInfo(img.width, img.height, ...);
|
||||
* }
|
||||
|
||||
@@ -9,157 +9,156 @@ import type { IScene } from './IScene';
|
||||
import { EntityHandle, NULL_HANDLE } from './Core/EntityHandle';
|
||||
|
||||
/**
|
||||
* 组件活跃状态变化接口
|
||||
* @zh 组件活跃状态变化接口
|
||||
* @en Interface for component active state change
|
||||
*/
|
||||
interface IActiveChangeable {
|
||||
onActiveChanged(): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* 实体比较器
|
||||
* @zh 比较两个实体的优先级
|
||||
* @en Compare priority of two entities
|
||||
*
|
||||
* 用于比较两个实体的优先级,首先按更新顺序比较,然后按ID比较。
|
||||
* @param a - @zh 第一个实体 @en First entity
|
||||
* @param b - @zh 第二个实体 @en Second entity
|
||||
* @returns @zh 比较结果:负数表示a优先级更高,正数表示b优先级更高,0表示相等
|
||||
* @en Comparison result: negative means a has higher priority, positive means b has higher priority, 0 means equal
|
||||
*/
|
||||
export class EntityComparer {
|
||||
/**
|
||||
* 比较两个实体
|
||||
*
|
||||
* @param self - 第一个实体
|
||||
* @param other - 第二个实体
|
||||
* @returns 比较结果,负数表示self优先级更高,正数表示other优先级更高,0表示相等
|
||||
*/
|
||||
public compare(self: Entity, other: Entity): number {
|
||||
let compare = self.updateOrder - other.updateOrder;
|
||||
if (compare == 0) compare = self.id - other.id;
|
||||
return compare;
|
||||
}
|
||||
export function compareEntities(a: Entity, b: Entity): number {
|
||||
return a.updateOrder - b.updateOrder || a.id - b.id;
|
||||
}
|
||||
|
||||
/**
|
||||
* 游戏实体类
|
||||
* @zh 游戏实体类
|
||||
* @en Game entity class
|
||||
*
|
||||
* ECS架构中的实体(Entity),作为组件的容器。
|
||||
* @zh ECS架构中的实体(Entity),作为组件的容器。
|
||||
* 实体本身不包含游戏逻辑,所有功能都通过组件来实现。
|
||||
*
|
||||
* 层级关系通过 HierarchyComponent 和 HierarchySystem 管理,
|
||||
* 而非 Entity 内置属性,符合 ECS 组合原则。
|
||||
* @en Entity in ECS architecture, serves as a container for components.
|
||||
* Entity itself contains no game logic, all functionality is implemented through components.
|
||||
* Hierarchy relationships are managed by HierarchyComponent and HierarchySystem,
|
||||
* not built-in Entity properties, following ECS composition principles.
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* // 创建实体
|
||||
* // @zh 创建实体 | @en Create entity
|
||||
* const entity = scene.createEntity("Player");
|
||||
*
|
||||
* // 添加组件
|
||||
* // @zh 添加组件 | @en Add component
|
||||
* const healthComponent = entity.addComponent(new HealthComponent(100));
|
||||
*
|
||||
* // 获取组件
|
||||
* // @zh 获取组件 | @en Get component
|
||||
* const health = entity.getComponent(HealthComponent);
|
||||
*
|
||||
* // 层级关系使用 HierarchySystem
|
||||
* // @zh 层级关系使用 HierarchySystem | @en Use HierarchySystem for hierarchy
|
||||
* const hierarchySystem = scene.getSystem(HierarchySystem);
|
||||
* hierarchySystem.setParent(childEntity, parentEntity);
|
||||
* ```
|
||||
*/
|
||||
export class Entity {
|
||||
/**
|
||||
* Entity专用日志器
|
||||
* @zh Entity专用日志器
|
||||
* @en Entity logger
|
||||
*/
|
||||
private static _logger = createLogger('Entity');
|
||||
|
||||
/**
|
||||
* 实体比较器实例
|
||||
*/
|
||||
public static entityComparer: EntityComparer = new EntityComparer();
|
||||
|
||||
/**
|
||||
* 实体名称
|
||||
* @zh 实体名称
|
||||
* @en Entity name
|
||||
*/
|
||||
public name: string;
|
||||
|
||||
/**
|
||||
* 实体唯一标识符(运行时 ID)
|
||||
*
|
||||
* Runtime identifier for fast lookups.
|
||||
* @zh 实体唯一标识符(运行时ID),用于快速查找
|
||||
* @en Unique entity identifier (runtime ID) for fast lookups
|
||||
*/
|
||||
public readonly id: number;
|
||||
|
||||
/**
|
||||
* 持久化唯一标识符(GUID)
|
||||
* @zh 持久化唯一标识符(GUID)
|
||||
* @en Persistent unique identifier (GUID)
|
||||
*
|
||||
* 用于序列化/反序列化时保持实体引用一致性。
|
||||
* 在场景保存和加载时保持不变。
|
||||
*
|
||||
* Persistent identifier for serialization.
|
||||
* Remains stable across save/load cycles.
|
||||
* @zh 用于序列化/反序列化时保持实体引用一致性,在场景保存和加载时保持不变
|
||||
* @en Used to maintain entity reference consistency during serialization/deserialization, remains stable across save/load cycles
|
||||
*/
|
||||
public readonly persistentId: string;
|
||||
|
||||
/**
|
||||
* 轻量级实体句柄
|
||||
* @zh 轻量级实体句柄
|
||||
* @en Lightweight entity handle
|
||||
*
|
||||
* 数值类型的实体标识符,包含索引和代数信息。
|
||||
* @zh 数值类型的实体标识符,包含索引和代数信息。
|
||||
* 用于高性能场景下替代对象引用,支持 Archetype 存储等优化。
|
||||
*
|
||||
* Lightweight entity handle.
|
||||
* Numeric identifier containing index and generation.
|
||||
* @en Numeric identifier containing index and generation.
|
||||
* Used for high-performance scenarios instead of object references,
|
||||
* supports Archetype storage optimizations.
|
||||
*/
|
||||
private _handle: EntityHandle = NULL_HANDLE;
|
||||
|
||||
/**
|
||||
* 所属场景引用
|
||||
* @zh 所属场景引用
|
||||
* @en Reference to the owning scene
|
||||
*/
|
||||
public scene: IScene | null = null;
|
||||
|
||||
/**
|
||||
* 销毁状态标志
|
||||
* @zh 销毁状态标志
|
||||
* @en Destroyed state flag
|
||||
*/
|
||||
private _isDestroyed: boolean = false;
|
||||
|
||||
/**
|
||||
* 激活状态
|
||||
* @zh 激活状态
|
||||
* @en Active state
|
||||
*/
|
||||
private _active: boolean = true;
|
||||
|
||||
/**
|
||||
* 实体标签
|
||||
* @zh 实体标签,用于分类和查询
|
||||
* @en Entity tag for categorization and querying
|
||||
*/
|
||||
private _tag: number = 0;
|
||||
|
||||
/**
|
||||
* 启用状态
|
||||
* @zh 启用状态
|
||||
* @en Enabled state
|
||||
*/
|
||||
private _enabled: boolean = true;
|
||||
|
||||
/**
|
||||
* 更新顺序
|
||||
* @zh 更新顺序
|
||||
* @en Update order
|
||||
*/
|
||||
private _updateOrder: number = 0;
|
||||
|
||||
/**
|
||||
* 组件位掩码(用于快速 hasComponent 检查)
|
||||
* @zh 组件位掩码,用于快速 hasComponent 检查
|
||||
* @en Component bitmask for fast hasComponent checks
|
||||
*/
|
||||
private _componentMask: BitMask64Data = BitMask64Utils.clone(BitMask64Utils.ZERO);
|
||||
|
||||
/**
|
||||
* 懒加载的组件数组缓存
|
||||
* @zh 懒加载的组件数组缓存
|
||||
* @en Lazy-loaded component array cache
|
||||
*/
|
||||
private _componentCache: Component[] | null = null;
|
||||
|
||||
/**
|
||||
* 生命周期策略
|
||||
*
|
||||
* Lifecycle policy for scene transitions.
|
||||
* @zh 生命周期策略,控制实体在场景切换时的行为
|
||||
* @en Lifecycle policy controlling entity behavior during scene transitions
|
||||
*/
|
||||
private _lifecyclePolicy: EEntityLifecyclePolicy = EEntityLifecyclePolicy.SceneLocal;
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
* @zh 构造函数
|
||||
* @en Constructor
|
||||
*
|
||||
* @param name - 实体名称
|
||||
* @param id - 实体唯一标识符(运行时 ID)
|
||||
* @param persistentId - 持久化标识符(可选,用于反序列化时恢复)
|
||||
* @param name - @zh 实体名称 @en Entity name
|
||||
* @param id - @zh 实体唯一标识符(运行时ID)@en Unique entity identifier (runtime ID)
|
||||
* @param persistentId - @zh 持久化标识符(可选,用于反序列化时恢复)@en Persistent identifier (optional, for deserialization)
|
||||
*/
|
||||
constructor(name: string, id: number, persistentId?: string) {
|
||||
this.name = name;
|
||||
@@ -168,44 +167,38 @@ export class Entity {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取生命周期策略
|
||||
*
|
||||
* Get lifecycle policy.
|
||||
* @zh 获取生命周期策略
|
||||
* @en Get lifecycle policy
|
||||
*/
|
||||
public get lifecyclePolicy(): EEntityLifecyclePolicy {
|
||||
return this._lifecyclePolicy;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查实体是否为持久化实体
|
||||
*
|
||||
* Check if entity is persistent (survives scene transitions).
|
||||
* @zh 检查实体是否为持久化实体(跨场景保留)
|
||||
* @en Check if entity is persistent (survives scene transitions)
|
||||
*/
|
||||
public get isPersistent(): boolean {
|
||||
return this._lifecyclePolicy === EEntityLifecyclePolicy.Persistent;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取实体句柄
|
||||
* @zh 获取实体句柄
|
||||
* @en Get entity handle
|
||||
*
|
||||
* 返回轻量级数值句柄,用于高性能场景。
|
||||
* 如果实体尚未分配句柄,返回 NULL_HANDLE。
|
||||
*
|
||||
* Get entity handle.
|
||||
* Returns lightweight numeric handle for high-performance scenarios.
|
||||
* Returns NULL_HANDLE if entity has no handle assigned.
|
||||
* @zh 返回轻量级数值句柄,用于高性能场景。如果实体尚未分配句柄,返回 NULL_HANDLE。
|
||||
* @en Returns lightweight numeric handle for high-performance scenarios. Returns NULL_HANDLE if entity has no handle assigned.
|
||||
*/
|
||||
public get handle(): EntityHandle {
|
||||
return this._handle;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置实体句柄(内部使用)
|
||||
* @zh 设置实体句柄(内部使用)
|
||||
* @en Set entity handle (internal use)
|
||||
*
|
||||
* 此方法供 Scene 在创建实体时调用。
|
||||
*
|
||||
* Set entity handle (internal use).
|
||||
* Called by Scene when creating entities.
|
||||
* @zh 此方法供 Scene 在创建实体时调用
|
||||
* @en Called by Scene when creating entities
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
@@ -214,14 +207,13 @@ export class Entity {
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置实体为持久化(跨场景保留)
|
||||
* @zh 设置实体为持久化(跨场景保留)
|
||||
* @en Mark entity as persistent (survives scene transitions)
|
||||
*
|
||||
* 标记后的实体在场景切换时不会被销毁,会自动迁移到新场景。
|
||||
* @zh 标记后的实体在场景切换时不会被销毁,会自动迁移到新场景
|
||||
* @en Persistent entities are automatically migrated to the new scene
|
||||
*
|
||||
* Mark entity as persistent (survives scene transitions).
|
||||
* Persistent entities are automatically migrated to the new scene.
|
||||
*
|
||||
* @returns this,支持链式调用 | Returns this for chaining
|
||||
* @returns @zh this,支持链式调用 @en Returns this for chaining
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
@@ -236,14 +228,10 @@ export class Entity {
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置实体为场景本地(随场景销毁)
|
||||
* @zh 设置实体为场景本地(随场景销毁),恢复默认行为
|
||||
* @en Mark entity as scene-local (destroyed with scene), restores default behavior
|
||||
*
|
||||
* 将实体恢复为默认行为。
|
||||
*
|
||||
* Mark entity as scene-local (destroyed with scene).
|
||||
* Restores default behavior.
|
||||
*
|
||||
* @returns this,支持链式调用 | Returns this for chaining
|
||||
* @returns @zh this,支持链式调用 @en Returns this for chaining
|
||||
*/
|
||||
public setSceneLocal(): this {
|
||||
this._lifecyclePolicy = EEntityLifecyclePolicy.SceneLocal;
|
||||
@@ -251,18 +239,21 @@ export class Entity {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取销毁状态
|
||||
* @returns 如果实体已被销毁则返回true
|
||||
* @zh 获取销毁状态
|
||||
* @en Get destroyed state
|
||||
*
|
||||
* @returns @zh 如果实体已被销毁则返回true @en Returns true if entity has been destroyed
|
||||
*/
|
||||
public get isDestroyed(): boolean {
|
||||
return this._isDestroyed;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置销毁状态(内部使用)
|
||||
* @zh 设置销毁状态(内部使用)
|
||||
* @en Set destroyed state (internal use)
|
||||
*
|
||||
* 此方法供Scene和批量操作使用,以提高性能。
|
||||
* 不应在普通业务逻辑中调用,应使用destroy()方法。
|
||||
* @zh 此方法供Scene和批量操作使用,以提高性能。不应在普通业务逻辑中调用,应使用destroy()方法
|
||||
* @en Used by Scene and batch operations for performance. Should not be called in normal business logic, use destroy() instead
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
@@ -271,8 +262,10 @@ export class Entity {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取组件数组(懒加载)
|
||||
* @returns 只读的组件数组
|
||||
* @zh 获取组件数组(懒加载)
|
||||
* @en Get component array (lazy-loaded)
|
||||
*
|
||||
* @returns @zh 只读的组件数组 @en Readonly component array
|
||||
*/
|
||||
public get components(): readonly Component[] {
|
||||
if (this._componentCache === null) {
|
||||
@@ -282,7 +275,8 @@ export class Entity {
|
||||
}
|
||||
|
||||
/**
|
||||
* 从存储重建组件缓存
|
||||
* @zh 从存储重建组件缓存
|
||||
* @en Rebuild component cache from storage
|
||||
*/
|
||||
private _rebuildComponentCache(): void {
|
||||
const components: Component[] = [];
|
||||
@@ -334,74 +328,82 @@ export class Entity {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取实体标签
|
||||
* @zh 获取实体标签
|
||||
* @en Get entity tag
|
||||
*
|
||||
* @returns 实体的数字标签
|
||||
* @returns @zh 实体的数字标签 @en Entity's numeric tag
|
||||
*/
|
||||
public get tag(): number {
|
||||
return this._tag;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置实体标签
|
||||
* @zh 设置实体标签
|
||||
* @en Set entity tag
|
||||
*
|
||||
* @param value - 新的标签值
|
||||
* @param value - @zh 新的标签值 @en New tag value
|
||||
*/
|
||||
public set tag(value: number) {
|
||||
this._tag = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取启用状态
|
||||
* @zh 获取启用状态
|
||||
* @en Get enabled state
|
||||
*
|
||||
* @returns 如果实体已启用则返回true
|
||||
* @returns @zh 如果实体已启用则返回true @en Returns true if entity is enabled
|
||||
*/
|
||||
public get enabled(): boolean {
|
||||
return this._enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置启用状态
|
||||
* @zh 设置启用状态
|
||||
* @en Set enabled state
|
||||
*
|
||||
* @param value - 新的启用状态
|
||||
* @param value - @zh 新的启用状态 @en New enabled state
|
||||
*/
|
||||
public set enabled(value: boolean) {
|
||||
this._enabled = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取更新顺序
|
||||
* @zh 获取更新顺序
|
||||
* @en Get update order
|
||||
*
|
||||
* @returns 实体的更新顺序值
|
||||
* @returns @zh 实体的更新顺序值 @en Entity's update order value
|
||||
*/
|
||||
public get updateOrder(): number {
|
||||
return this._updateOrder;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置更新顺序
|
||||
* @zh 设置更新顺序
|
||||
* @en Set update order
|
||||
*
|
||||
* @param value - 新的更新顺序值
|
||||
* @param value - @zh 新的更新顺序值 @en New update order value
|
||||
*/
|
||||
public set updateOrder(value: number) {
|
||||
this._updateOrder = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取组件位掩码
|
||||
* @zh 获取组件位掩码
|
||||
* @en Get component bitmask
|
||||
*
|
||||
* @returns 实体的组件位掩码
|
||||
* @returns @zh 实体的组件位掩码 @en Entity's component bitmask
|
||||
*/
|
||||
public get componentMask(): BitMask64Data {
|
||||
return this._componentMask;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建并添加组件
|
||||
* @zh 创建并添加组件
|
||||
* @en Create and add component
|
||||
*
|
||||
* @param componentType - 组件类型构造函数
|
||||
* @param args - 组件构造函数参数
|
||||
* @returns 创建的组件实例
|
||||
* @param componentType - @zh 组件类型构造函数 @en Component type constructor
|
||||
* @param args - @zh 组件构造函数参数 @en Component constructor arguments
|
||||
* @returns @zh 创建的组件实例 @en Created component instance
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
@@ -418,51 +420,30 @@ export class Entity {
|
||||
return this.addComponent(component);
|
||||
}
|
||||
|
||||
/**
|
||||
* 内部添加组件方法(不进行重复检查,用于初始化)
|
||||
*
|
||||
* @param component - 要添加的组件实例
|
||||
* @returns 添加的组件实例
|
||||
*/
|
||||
private addComponentInternal<T extends Component>(component: T): T {
|
||||
const componentType = component.constructor as ComponentType<T>;
|
||||
|
||||
// 更新位掩码(组件已通过 @ECSComponent 装饰器自动注册)
|
||||
// Update bitmask (component already registered via @ECSComponent decorator)
|
||||
const registry = this.scene?.componentRegistry ?? GlobalComponentRegistry;
|
||||
const componentMask = registry.getBitMask(componentType);
|
||||
BitMask64Utils.orInPlace(this._componentMask, componentMask);
|
||||
|
||||
// 使缓存失效
|
||||
this._componentCache = null;
|
||||
|
||||
return component;
|
||||
}
|
||||
|
||||
/**
|
||||
* 通知Scene中的QuerySystem实体组件发生变动
|
||||
*
|
||||
* Notify the QuerySystem in Scene that entity components have changed
|
||||
*
|
||||
* @param changedComponentType 变化的组件类型(可选,用于优化通知) | Changed component type (optional, for optimized notification)
|
||||
*/
|
||||
private notifyQuerySystems(changedComponentType?: ComponentType): void {
|
||||
if (this.scene && this.scene.querySystem) {
|
||||
this.scene.querySystem.updateEntity(this);
|
||||
this.scene.clearSystemEntityCaches();
|
||||
// 事件驱动:立即通知关心该组件的系统 | Event-driven: notify systems that care about this component
|
||||
if (this.scene.notifyEntityComponentChanged) {
|
||||
this.scene.notifyEntityComponentChanged(this, changedComponentType);
|
||||
}
|
||||
}
|
||||
if (!this.scene?.querySystem) return;
|
||||
|
||||
this.scene.querySystem.updateEntity(this);
|
||||
this.scene.clearSystemEntityCaches();
|
||||
this.scene.notifyEntityComponentChanged?.(this, changedComponentType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加组件到实体
|
||||
* @zh 添加组件到实体
|
||||
* @en Add component to entity
|
||||
*
|
||||
* @param component - 要添加的组件实例
|
||||
* @returns 添加的组件实例
|
||||
* @throws {Error} 如果实体已存在该类型的组件
|
||||
* @param component - @zh 要添加的组件实例 @en Component instance to add
|
||||
* @returns @zh 添加的组件实例 @en Added component instance
|
||||
* @throws @zh 如果实体已存在该类型的组件 @en If entity already has this component type
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
@@ -492,20 +473,15 @@ export class Entity {
|
||||
this.scene.componentStorageManager.addComponent(this.id, component);
|
||||
|
||||
component.entityId = this.id;
|
||||
if (this.scene.referenceTracker) {
|
||||
this.scene.referenceTracker.registerEntityScene(this.id, this.scene);
|
||||
}
|
||||
this.scene.referenceTracker?.registerEntityScene(this.id, this.scene);
|
||||
|
||||
// 编辑器模式下延迟执行 onAddedToEntity | Defer onAddedToEntity in editor mode
|
||||
if (this.scene.isEditorMode) {
|
||||
this.scene.queueDeferredComponentCallback(() => {
|
||||
component.onAddedToEntity();
|
||||
});
|
||||
this.scene.queueDeferredComponentCallback(() => component.onAddedToEntity());
|
||||
} else {
|
||||
component.onAddedToEntity();
|
||||
}
|
||||
|
||||
if (this.scene && this.scene.eventSystem) {
|
||||
if (this.scene.eventSystem) {
|
||||
this.scene.eventSystem.emitSync('component:added', {
|
||||
timestamp: Date.now(),
|
||||
source: 'Entity',
|
||||
@@ -523,10 +499,11 @@ export class Entity {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定类型的组件
|
||||
* @zh 获取指定类型的组件
|
||||
* @en Get component of specified type
|
||||
*
|
||||
* @param type - 组件类型构造函数
|
||||
* @returns 组件实例,如果不存在则返回null
|
||||
* @param type - @zh 组件类型构造函数 @en Component type constructor
|
||||
* @returns @zh 组件实例,如果不存在则返回null @en Component instance, or null if not found
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
@@ -553,10 +530,11 @@ export class Entity {
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查实体是否拥有指定类型的组件
|
||||
* @zh 检查实体是否拥有指定类型的组件
|
||||
* @en Check if entity has component of specified type
|
||||
*
|
||||
* @param type - 组件类型构造函数
|
||||
* @returns 如果实体拥有该组件返回true,否则返回false
|
||||
* @param type - @zh 组件类型构造函数 @en Component type constructor
|
||||
* @returns @zh 如果实体拥有该组件返回true @en Returns true if entity has the component
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
@@ -577,17 +555,19 @@ export class Entity {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取或创建指定类型的组件
|
||||
* @zh 获取或创建指定类型的组件
|
||||
* @en Get or create component of specified type
|
||||
*
|
||||
* 如果组件已存在则返回现有组件,否则创建新组件并添加到实体
|
||||
* @zh 如果组件已存在则返回现有组件,否则创建新组件并添加到实体
|
||||
* @en Returns existing component if present, otherwise creates and adds new component
|
||||
*
|
||||
* @param type - 组件类型构造函数
|
||||
* @param args - 组件构造函数参数(仅在创建新组件时使用)
|
||||
* @returns 组件实例
|
||||
* @param type - @zh 组件类型构造函数 @en Component type constructor
|
||||
* @param args - @zh 组件构造函数参数(仅在创建新组件时使用)@en Constructor arguments (only used when creating new component)
|
||||
* @returns @zh 组件实例 @en Component instance
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* // 确保实体拥有Position组件
|
||||
* // @zh 确保实体拥有Position组件 | @en Ensure entity has Position component
|
||||
* const position = entity.getOrCreateComponent(Position, 0, 0);
|
||||
* position.x = 100;
|
||||
* ```
|
||||
@@ -605,16 +585,13 @@ export class Entity {
|
||||
}
|
||||
|
||||
/**
|
||||
* 标记组件为已修改
|
||||
* @zh 标记组件为已修改
|
||||
* @en Mark component(s) as modified
|
||||
*
|
||||
* 便捷方法,自动从场景获取当前 epoch 并标记组件。
|
||||
* 用于帧级变更检测系统。
|
||||
* @zh 便捷方法,自动从场景获取当前 epoch 并标记组件。用于帧级变更检测系统。
|
||||
* @en Convenience method that auto-gets epoch from scene and marks components. Used for frame-level change detection system.
|
||||
*
|
||||
* Mark component(s) as modified.
|
||||
* Convenience method that auto-gets epoch from scene and marks components.
|
||||
* Used for frame-level change detection system.
|
||||
*
|
||||
* @param components 要标记的组件 | Components to mark
|
||||
* @param components - @zh 要标记的组件 @en Components to mark
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
@@ -622,7 +599,7 @@ export class Entity {
|
||||
* pos.x = 100;
|
||||
* entity.markDirty(pos);
|
||||
*
|
||||
* // 或者标记多个组件
|
||||
* // @zh 或者标记多个组件 | @en Or mark multiple components
|
||||
* entity.markDirty(pos, vel);
|
||||
* ```
|
||||
*/
|
||||
@@ -638,9 +615,10 @@ export class Entity {
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除指定的组件
|
||||
* @zh 移除指定的组件
|
||||
* @en Remove specified component
|
||||
*
|
||||
* @param component - 要移除的组件实例
|
||||
* @param component - @zh 要移除的组件实例 @en Component instance to remove
|
||||
*/
|
||||
public removeComponent(component: Component): void {
|
||||
const componentType = component.constructor as ComponentType;
|
||||
@@ -651,29 +629,16 @@ export class Entity {
|
||||
}
|
||||
|
||||
const bitIndex = registry.getBitIndex(componentType);
|
||||
|
||||
// 更新位掩码
|
||||
BitMask64Utils.clearBit(this._componentMask, bitIndex);
|
||||
|
||||
// 使缓存失效
|
||||
this._componentCache = null;
|
||||
|
||||
// 从Scene存储移除
|
||||
if (this.scene?.componentStorageManager) {
|
||||
this.scene.componentStorageManager.removeComponent(this.id, componentType);
|
||||
}
|
||||
|
||||
if (this.scene?.referenceTracker) {
|
||||
this.scene.referenceTracker.clearComponentReferences(component);
|
||||
}
|
||||
|
||||
if (component.onRemovedFromEntity) {
|
||||
component.onRemovedFromEntity();
|
||||
}
|
||||
this.scene?.componentStorageManager?.removeComponent(this.id, componentType);
|
||||
this.scene?.referenceTracker?.clearComponentReferences(component);
|
||||
|
||||
component.onRemovedFromEntity?.();
|
||||
component.entityId = null;
|
||||
|
||||
if (this.scene && this.scene.eventSystem) {
|
||||
if (this.scene?.eventSystem) {
|
||||
this.scene.eventSystem.emitSync('component:removed', {
|
||||
timestamp: Date.now(),
|
||||
source: 'Entity',
|
||||
@@ -689,10 +654,11 @@ export class Entity {
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除指定类型的组件
|
||||
* @zh 移除指定类型的组件
|
||||
* @en Remove component by type
|
||||
*
|
||||
* @param type - 组件类型
|
||||
* @returns 被移除的组件实例或null
|
||||
* @param type - @zh 组件类型 @en Component type
|
||||
* @returns @zh 被移除的组件实例或null @en Removed component instance or null
|
||||
*/
|
||||
public removeComponentByType<T extends Component>(type: ComponentType<T>): T | null {
|
||||
const component = this.getComponent(type);
|
||||
@@ -704,24 +670,17 @@ export class Entity {
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除所有组件
|
||||
* @zh 移除所有组件
|
||||
* @en Remove all components
|
||||
*/
|
||||
public removeAllComponents(): void {
|
||||
const componentsToRemove = [...this.components];
|
||||
|
||||
// 清除位掩码
|
||||
BitMask64Utils.clear(this._componentMask);
|
||||
|
||||
// 使缓存失效
|
||||
this._componentCache = null;
|
||||
|
||||
for (const component of componentsToRemove) {
|
||||
const componentType = component.constructor as ComponentType;
|
||||
|
||||
if (this.scene?.componentStorageManager) {
|
||||
this.scene.componentStorageManager.removeComponent(this.id, componentType);
|
||||
}
|
||||
|
||||
this.scene?.componentStorageManager?.removeComponent(this.id, componentType);
|
||||
component.onRemovedFromEntity();
|
||||
}
|
||||
|
||||
@@ -729,10 +688,11 @@ export class Entity {
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量添加组件
|
||||
* @zh 批量添加组件
|
||||
* @en Add multiple components
|
||||
*
|
||||
* @param components - 要添加的组件数组
|
||||
* @returns 添加的组件数组
|
||||
* @param components - @zh 要添加的组件数组 @en Array of components to add
|
||||
* @returns @zh 添加的组件数组 @en Array of added components
|
||||
*/
|
||||
public addComponents<T extends Component>(components: T[]): T[] {
|
||||
const addedComponents: T[] = [];
|
||||
@@ -828,10 +788,11 @@ export class Entity {
|
||||
}
|
||||
|
||||
/**
|
||||
* 销毁实体
|
||||
* @zh 销毁实体
|
||||
* @en Destroy entity
|
||||
*
|
||||
* 移除所有组件并标记为已销毁。
|
||||
* 层级关系的清理由 HierarchySystem 处理。
|
||||
* @zh 移除所有组件并标记为已销毁。层级关系的清理由 HierarchySystem 处理。
|
||||
* @en Removes all components and marks as destroyed. Hierarchy cleanup is handled by HierarchySystem.
|
||||
*/
|
||||
public destroy(): void {
|
||||
if (this._isDestroyed) {
|
||||
@@ -859,13 +820,14 @@ export class Entity {
|
||||
}
|
||||
|
||||
/**
|
||||
* 比较实体
|
||||
* @zh 比较实体优先级
|
||||
* @en Compare entity priority
|
||||
*
|
||||
* @param other - 另一个实体
|
||||
* @returns 比较结果
|
||||
* @param other - @zh 另一个实体 @en Another entity
|
||||
* @returns @zh 比较结果 @en Comparison result
|
||||
*/
|
||||
public compareTo(other: Entity): number {
|
||||
return EntityComparer.prototype.compare(this, other);
|
||||
return compareEntities(this, other);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -370,21 +370,29 @@ export type ISceneFactory<T extends IScene> = {
|
||||
}
|
||||
|
||||
/**
|
||||
* 场景配置接口
|
||||
* Scene configuration interface
|
||||
* @zh 场景配置接口
|
||||
* @en Scene configuration interface
|
||||
*/
|
||||
export type ISceneConfig = {
|
||||
/**
|
||||
* 场景名称
|
||||
* Scene name
|
||||
* @zh 场景名称
|
||||
* @en Scene name
|
||||
*/
|
||||
name?: string;
|
||||
|
||||
/**
|
||||
* 是否从全局注册表继承组件类型
|
||||
* Whether to inherit component types from global registry
|
||||
* @zh 是否从全局注册表继承组件类型
|
||||
* @en Whether to inherit component types from global registry
|
||||
*
|
||||
* @default true
|
||||
*/
|
||||
inheritGlobalRegistry?: boolean;
|
||||
|
||||
/**
|
||||
* @zh 系统最大错误次数,超过此阈值后系统将被自动禁用
|
||||
* @en Maximum error count for systems, systems exceeding this threshold will be auto-disabled
|
||||
*
|
||||
* @default 10
|
||||
*/
|
||||
maxSystemErrorCount?: number;
|
||||
}
|
||||
|
||||
@@ -212,11 +212,10 @@ export class Scene implements IScene {
|
||||
private _systemErrorCount: Map<EntitySystem, number> = new Map();
|
||||
|
||||
/**
|
||||
* 最大允许错误次数
|
||||
*
|
||||
* 系统错误次数超过此阈值后将被自动禁用
|
||||
* @zh 最大允许错误次数,系统错误次数超过此阈值后将被自动禁用
|
||||
* @en Maximum error count, systems exceeding this threshold will be auto-disabled
|
||||
*/
|
||||
private _maxErrorCount: number = 10;
|
||||
private _maxErrorCount: number;
|
||||
|
||||
/**
|
||||
* 系统添加计数器
|
||||
@@ -295,12 +294,10 @@ export class Scene implements IScene {
|
||||
const systems = this._filterEntitySystems(allServices);
|
||||
|
||||
try {
|
||||
// 使用 SystemScheduler 进行依赖排序
|
||||
this._systemScheduler.markDirty();
|
||||
return this._systemScheduler.getAllSortedSystems(systems);
|
||||
} catch (error) {
|
||||
if (error instanceof CycleDependencyError) {
|
||||
// 循环依赖错误,记录警告并回退到 updateOrder 排序
|
||||
this.logger.error(
|
||||
`[Scene] 系统存在循环依赖,回退到 updateOrder 排序 | Cycle dependency detected, falling back to updateOrder sort`,
|
||||
error.involvedNodes
|
||||
@@ -308,7 +305,6 @@ export class Scene implements IScene {
|
||||
} else {
|
||||
this.logger.error(`[Scene] 系统排序失败 | System sorting failed`, error);
|
||||
}
|
||||
// 回退到简单的 updateOrder 排序
|
||||
return this._sortSystemsByUpdateOrder(systems);
|
||||
}
|
||||
}
|
||||
@@ -378,20 +374,18 @@ export class Scene implements IScene {
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建场景实例
|
||||
* Create scene instance
|
||||
* @zh 创建场景实例
|
||||
* @en Create scene instance
|
||||
*
|
||||
* @param config - @zh 场景配置 @en Scene configuration
|
||||
*/
|
||||
constructor(config?: ISceneConfig) {
|
||||
this.entities = new EntityList(this);
|
||||
this.identifierPool = new IdentifierPool();
|
||||
this.componentStorageManager = new ComponentStorageManager();
|
||||
|
||||
// 创建场景级别的组件注册表
|
||||
// Create scene-level component registry
|
||||
this.componentRegistry = new ComponentRegistry();
|
||||
|
||||
// 从全局注册表继承框架组件(默认启用)
|
||||
// Inherit framework components from global registry (enabled by default)
|
||||
if (config?.inheritGlobalRegistry !== false) {
|
||||
this.componentRegistry.cloneFrom(GlobalComponentRegistry);
|
||||
}
|
||||
@@ -402,6 +396,7 @@ export class Scene implements IScene {
|
||||
this.handleManager = new EntityHandleManager();
|
||||
this._services = new ServiceContainer();
|
||||
this.logger = createLogger('Scene');
|
||||
this._maxErrorCount = config?.maxSystemErrorCount ?? 10;
|
||||
|
||||
if (config?.name) {
|
||||
this.name = config.name;
|
||||
@@ -465,10 +460,8 @@ export class Scene implements IScene {
|
||||
* In editor mode, this method also executes all deferred component lifecycle callbacks.
|
||||
*/
|
||||
public begin() {
|
||||
// 标记场景已开始运行
|
||||
this._didSceneBegin = true;
|
||||
|
||||
// 执行所有延迟的组件生命周期回调 | Execute all deferred component lifecycle callbacks
|
||||
if (this._deferredComponentCallbacks.length > 0) {
|
||||
for (const callback of this._deferredComponentCallbacks) {
|
||||
try {
|
||||
@@ -477,11 +470,9 @@ export class Scene implements IScene {
|
||||
this.logger.error('Error executing deferred component callback:', error);
|
||||
}
|
||||
}
|
||||
// 清空队列 | Clear the queue
|
||||
this._deferredComponentCallbacks = [];
|
||||
}
|
||||
|
||||
// 调用onStart方法
|
||||
this.onStart();
|
||||
}
|
||||
|
||||
@@ -501,38 +492,18 @@ export class Scene implements IScene {
|
||||
* - 系统清理:在 System.onDestroy() 中处理(实体已被清理)
|
||||
*/
|
||||
public end() {
|
||||
// 标记场景已结束运行
|
||||
this._didSceneBegin = false;
|
||||
|
||||
// 先调用用户的卸载方法,此时用户可以访问实体和系统进行清理
|
||||
this.unload();
|
||||
|
||||
// 移除所有实体
|
||||
this.entities.removeAllEntities();
|
||||
|
||||
// 清理查询系统中的实体引用和缓存
|
||||
this.querySystem.setEntities([]);
|
||||
|
||||
// 清空组件存储
|
||||
this.componentStorageManager.clear();
|
||||
|
||||
// 清空服务容器(会调用所有服务的dispose方法,包括所有EntitySystem)
|
||||
// 系统的 onDestroy 回调会在这里被触发
|
||||
this._services.clear();
|
||||
|
||||
// 清空系统缓存
|
||||
this._cachedSystems = null;
|
||||
this._systemsOrderDirty = true;
|
||||
|
||||
// 清空组件索引 | Clear component indices
|
||||
this._componentIdToSystems.clear();
|
||||
this._globalNotifySystems.clear();
|
||||
|
||||
// 清空句柄映射并重置句柄管理器 | Clear handle mapping and reset handle manager
|
||||
this._handleToEntity.clear();
|
||||
this.handleManager.reset();
|
||||
|
||||
// 重置 epoch 管理器 | Reset epoch manager
|
||||
this.epochManager.reset();
|
||||
}
|
||||
|
||||
@@ -540,92 +511,57 @@ export class Scene implements IScene {
|
||||
* 更新场景
|
||||
*/
|
||||
public update() {
|
||||
// 递增帧计数(用于变更检测) | Increment epoch (for change detection)
|
||||
this.epochManager.increment();
|
||||
|
||||
// 开始性能采样帧
|
||||
ProfilerSDK.beginFrame();
|
||||
const frameHandle = ProfilerSDK.beginSample('Scene.update', ProfileCategory.ECS);
|
||||
|
||||
try {
|
||||
ComponentPoolManager.getInstance().update();
|
||||
|
||||
this.entities.updateLists();
|
||||
|
||||
const systems = this.systems;
|
||||
|
||||
// Update 阶段
|
||||
const updateHandle = ProfilerSDK.beginSample('Systems.update', ProfileCategory.ECS);
|
||||
try {
|
||||
for (const system of systems) {
|
||||
if (this._shouldSystemRun(system)) {
|
||||
const systemHandle = ProfilerSDK.beginSample(system.systemName, ProfileCategory.ECS);
|
||||
try {
|
||||
system.update();
|
||||
} catch (error) {
|
||||
this._handleSystemError(system, 'update', error);
|
||||
} finally {
|
||||
ProfilerSDK.endSample(systemHandle);
|
||||
}
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
ProfilerSDK.endSample(updateHandle);
|
||||
}
|
||||
|
||||
// LateUpdate 阶段
|
||||
const lateUpdateHandle = ProfilerSDK.beginSample('Systems.lateUpdate', ProfileCategory.ECS);
|
||||
try {
|
||||
for (const system of systems) {
|
||||
if (this._shouldSystemRun(system)) {
|
||||
const systemHandle = ProfilerSDK.beginSample(`${system.systemName}.late`, ProfileCategory.ECS);
|
||||
try {
|
||||
system.lateUpdate();
|
||||
} catch (error) {
|
||||
this._handleSystemError(system, 'lateUpdate', error);
|
||||
} finally {
|
||||
ProfilerSDK.endSample(systemHandle);
|
||||
}
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
ProfilerSDK.endSample(lateUpdateHandle);
|
||||
}
|
||||
|
||||
// 执行所有系统的延迟命令
|
||||
// Flush all systems' deferred commands
|
||||
this._runSystemPhase(systems, 'update', 'Systems.update');
|
||||
this._runSystemPhase(systems, 'lateUpdate', 'Systems.lateUpdate');
|
||||
this.flushCommandBuffers(systems);
|
||||
} finally {
|
||||
ProfilerSDK.endSample(frameHandle);
|
||||
// 结束性能采样帧
|
||||
ProfilerSDK.endFrame();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查系统是否应该运行
|
||||
* Check if a system should run
|
||||
*
|
||||
* @param system 要检查的系统 | System to check
|
||||
* @returns 是否应该运行 | Whether it should run
|
||||
* @zh 执行系统的指定阶段
|
||||
* @en Run specified phase for all systems
|
||||
*/
|
||||
private _runSystemPhase(
|
||||
systems: EntitySystem[],
|
||||
phase: 'update' | 'lateUpdate',
|
||||
profileName: string
|
||||
): void {
|
||||
const phaseHandle = ProfilerSDK.beginSample(profileName, ProfileCategory.ECS);
|
||||
try {
|
||||
for (const system of systems) {
|
||||
if (!this._shouldSystemRun(system)) continue;
|
||||
|
||||
const suffix = phase === 'lateUpdate' ? '.late' : '';
|
||||
const systemHandle = ProfilerSDK.beginSample(`${system.systemName}${suffix}`, ProfileCategory.ECS);
|
||||
try {
|
||||
system[phase]();
|
||||
} catch (error) {
|
||||
this._handleSystemError(system, phase, error);
|
||||
} finally {
|
||||
ProfilerSDK.endSample(systemHandle);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
ProfilerSDK.endSample(phaseHandle);
|
||||
}
|
||||
}
|
||||
|
||||
private _shouldSystemRun(system: EntitySystem): boolean {
|
||||
// 系统必须启用
|
||||
// System must be enabled
|
||||
if (!system.enabled) {
|
||||
return false;
|
||||
}
|
||||
if (!system.enabled) return false;
|
||||
if (!this.isEditorMode) return true;
|
||||
|
||||
// 非编辑模式下,所有启用的系统都运行
|
||||
// In non-edit mode, all enabled systems run
|
||||
if (!this.isEditorMode) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 编辑模式下,默认所有系统都运行
|
||||
// 只有明确标记 runInEditMode: false 的系统不运行
|
||||
// In edit mode, all systems run by default
|
||||
// Only systems explicitly marked runInEditMode: false are skipped
|
||||
const metadata = getSystemInstanceMetadata(system);
|
||||
return metadata?.runInEditMode !== false;
|
||||
}
|
||||
@@ -685,11 +621,8 @@ export class Scene implements IScene {
|
||||
public createEntity(name: string) {
|
||||
const entity = new Entity(name, this.identifierPool.checkOut());
|
||||
|
||||
// 分配轻量级句柄 | Assign lightweight handle
|
||||
const handle = this.handleManager.create();
|
||||
entity.setHandle(handle);
|
||||
|
||||
// 添加到句柄映射 | Add to handle mapping
|
||||
this._handleToEntity.set(handle, entity);
|
||||
|
||||
this.eventSystem.emitSync('entity:created', { entityName: name, entity, scene: this });
|
||||
@@ -722,10 +655,8 @@ export class Scene implements IScene {
|
||||
* @param changedComponentType 变化的组件类型(可选) | The changed component type (optional)
|
||||
*/
|
||||
public notifyEntityComponentChanged(entity: Entity, changedComponentType?: ComponentType): void {
|
||||
// 已通知的系统集合,避免重复通知 | Set of notified systems to avoid duplicates
|
||||
const notifiedSystems = new Set<EntitySystem>();
|
||||
|
||||
// 如果提供了组件类型,使用索引优化 | If component type provided, use index optimization
|
||||
if (changedComponentType && this.componentRegistry.isRegistered(changedComponentType)) {
|
||||
const componentId = this.componentRegistry.getBitIndex(changedComponentType);
|
||||
const interestedSystems = this._componentIdToSystems.get(componentId);
|
||||
@@ -738,7 +669,6 @@ export class Scene implements IScene {
|
||||
}
|
||||
}
|
||||
|
||||
// 通知全局监听系统(none条件、tag/name查询等) | Notify global listener systems
|
||||
for (const system of this._globalNotifySystems) {
|
||||
if (!notifiedSystems.has(system)) {
|
||||
system.handleEntityComponentChanged(entity);
|
||||
@@ -746,7 +676,6 @@ export class Scene implements IScene {
|
||||
}
|
||||
}
|
||||
|
||||
// 如果没有提供组件类型,回退到遍历所有系统 | Fallback to all systems if no component type
|
||||
if (!changedComponentType) {
|
||||
for (const system of this.systems) {
|
||||
if (!notifiedSystems.has(system)) {
|
||||
@@ -772,35 +701,29 @@ export class Scene implements IScene {
|
||||
return;
|
||||
}
|
||||
|
||||
// nothing 匹配器不需要索引 | Nothing matcher doesn't need indexing
|
||||
if (matcher.isNothing()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const condition = matcher.getCondition();
|
||||
|
||||
// 有 none/tag/name 条件的系统加入全局通知 | Systems with none/tag/name go to global
|
||||
if (condition.none.length > 0 || condition.tag !== undefined || condition.name !== undefined) {
|
||||
this._globalNotifySystems.add(system);
|
||||
}
|
||||
|
||||
// 空匹配器(匹配所有实体)加入全局通知 | Empty matcher (matches all) goes to global
|
||||
if (matcher.isEmpty()) {
|
||||
this._globalNotifySystems.add(system);
|
||||
return;
|
||||
}
|
||||
|
||||
// 索引 all 条件中的组件 | Index components in all condition
|
||||
for (const componentType of condition.all) {
|
||||
this.addSystemToComponentIndex(componentType, system);
|
||||
}
|
||||
|
||||
// 索引 any 条件中的组件 | Index components in any condition
|
||||
for (const componentType of condition.any) {
|
||||
this.addSystemToComponentIndex(componentType, system);
|
||||
}
|
||||
|
||||
// 索引单组件查询 | Index single component query
|
||||
if (condition.component) {
|
||||
this.addSystemToComponentIndex(condition.component, system);
|
||||
}
|
||||
@@ -834,10 +757,8 @@ export class Scene implements IScene {
|
||||
* @param system 要移除的系统 | The system to remove
|
||||
*/
|
||||
private removeSystemFromIndex(system: EntitySystem): void {
|
||||
// 从全局通知列表移除 | Remove from global notify list
|
||||
this._globalNotifySystems.delete(system);
|
||||
|
||||
// 从所有组件索引中移除 | Remove from all component indices
|
||||
for (const systems of this._componentIdToSystems.values()) {
|
||||
systems.delete(system);
|
||||
}
|
||||
@@ -852,15 +773,12 @@ export class Scene implements IScene {
|
||||
this.entities.add(entity);
|
||||
entity.scene = this;
|
||||
|
||||
// 将实体添加到查询系统(可延迟缓存清理)
|
||||
this.querySystem.addEntity(entity, deferCacheClear);
|
||||
|
||||
// 清除系统缓存以确保系统能及时发现新实体
|
||||
if (!deferCacheClear) {
|
||||
this.clearSystemEntityCaches();
|
||||
}
|
||||
|
||||
// 触发实体添加事件
|
||||
this.eventSystem.emitSync('entity:added', { entity, scene: this });
|
||||
|
||||
return entity;
|
||||
@@ -875,30 +793,22 @@ export class Scene implements IScene {
|
||||
public createEntities(count: number, namePrefix: string = 'Entity'): Entity[] {
|
||||
const entities: Entity[] = [];
|
||||
|
||||
// 批量创建实体对象,不立即添加到系统
|
||||
for (let i = 0; i < count; i++) {
|
||||
const entity = new Entity(`${namePrefix}_${i}`, this.identifierPool.checkOut());
|
||||
entity.scene = this;
|
||||
|
||||
// 分配轻量级句柄 | Assign lightweight handle
|
||||
const handle = this.handleManager.create();
|
||||
entity.setHandle(handle);
|
||||
|
||||
// 添加到句柄映射 | Add to handle mapping
|
||||
this._handleToEntity.set(handle, entity);
|
||||
|
||||
entities.push(entity);
|
||||
}
|
||||
|
||||
// 批量添加到实体列表
|
||||
for (const entity of entities) {
|
||||
this.entities.add(entity);
|
||||
}
|
||||
|
||||
// 批量添加到查询系统(无重复检查,性能最优)
|
||||
this.querySystem.addEntitiesUnchecked(entities);
|
||||
|
||||
// 批量触发事件(可选,减少事件开销)
|
||||
this.eventSystem.emitSync('entities:batch_added', { entities, scene: this, count });
|
||||
|
||||
return entities;
|
||||
@@ -922,7 +832,6 @@ export class Scene implements IScene {
|
||||
this.entities.remove(entity);
|
||||
this.querySystem.removeEntity(entity);
|
||||
|
||||
// 销毁句柄并从映射中移除 | Destroy handle and remove from mapping
|
||||
if (isValidHandle(entity.handle)) {
|
||||
this._handleToEntity.delete(entity.handle);
|
||||
this.handleManager.destroy(entity.handle);
|
||||
@@ -1019,14 +928,9 @@ export class Scene implements IScene {
|
||||
const persistentEntities = this.findPersistentEntities();
|
||||
|
||||
for (const entity of persistentEntities) {
|
||||
// 从实体列表移除
|
||||
this.entities.remove(entity);
|
||||
|
||||
// 从查询系统移除
|
||||
this.querySystem.removeEntity(entity);
|
||||
|
||||
// 清除场景引用(但保留组件数据)
|
||||
entity.scene = null;
|
||||
entity.scene = null; // detach but preserve component data
|
||||
}
|
||||
|
||||
return persistentEntities;
|
||||
@@ -1045,23 +949,16 @@ export class Scene implements IScene {
|
||||
*/
|
||||
public receiveMigratedEntities(entities: Entity[]): void {
|
||||
for (const entity of entities) {
|
||||
// 设置新场景引用
|
||||
entity.scene = this;
|
||||
|
||||
// 添加到实体列表
|
||||
this.entities.add(entity);
|
||||
|
||||
// 添加到查询系统
|
||||
this.querySystem.addEntity(entity);
|
||||
|
||||
// 重新注册组件到新场景的存储
|
||||
for (const component of entity.components) {
|
||||
this.componentStorageManager.addComponent(entity.id, component);
|
||||
this.referenceTracker?.registerEntityScene(entity.id, this);
|
||||
}
|
||||
}
|
||||
|
||||
// 清除系统缓存
|
||||
if (entities.length > 0) {
|
||||
this.clearSystemEntityCaches();
|
||||
}
|
||||
@@ -1218,10 +1115,7 @@ export class Scene implements IScene {
|
||||
}
|
||||
|
||||
system.scene = this;
|
||||
|
||||
// 分配添加顺序,用于稳定排序 | Assign add order for stable sorting
|
||||
system.addOrder = this._systemAddCounter++;
|
||||
|
||||
system.addOrder = this._systemAddCounter++; // for stable sorting
|
||||
system.setPerformanceMonitor(this.performanceMonitor);
|
||||
|
||||
const metadata = getSystemMetadata(constructor);
|
||||
@@ -1233,16 +1127,11 @@ export class Scene implements IScene {
|
||||
}
|
||||
|
||||
this._services.registerInstance(constructor, system);
|
||||
|
||||
// 标记系统列表已变化
|
||||
this.markSystemsOrderDirty();
|
||||
|
||||
// 建立组件类型到系统的索引 | Build component type to system index
|
||||
this.indexSystemByComponents(system);
|
||||
|
||||
injectProperties(system, this._services);
|
||||
|
||||
// 调试模式下自动包装系统方法以收集性能数据(ProfilerSDK 启用时表示调试模式)
|
||||
// Auto-wrap system methods for profiling in debug mode
|
||||
if (ProfilerSDK.isEnabled()) {
|
||||
AutoProfiler.wrapInstance(system, system.systemName, ProfileCategory.ECS);
|
||||
}
|
||||
@@ -1318,16 +1207,9 @@ export class Scene implements IScene {
|
||||
public removeEntityProcessor(processor: EntitySystem): void {
|
||||
const constructor = processor.constructor as ServiceType<EntitySystem>;
|
||||
|
||||
// 从ServiceContainer移除
|
||||
this._services.unregister(constructor);
|
||||
|
||||
// 标记系统列表已变化
|
||||
this.markSystemsOrderDirty();
|
||||
|
||||
// 从组件类型索引中移除 | Remove from component type index
|
||||
this.removeSystemFromIndex(processor);
|
||||
|
||||
// 重置System状态
|
||||
processor.reset();
|
||||
}
|
||||
|
||||
|
||||
@@ -7,93 +7,125 @@ import { ServiceContainer } from '../Core/ServiceContainer';
|
||||
const logger = createLogger('World');
|
||||
|
||||
/**
|
||||
* 全局系统接口
|
||||
* 全局系统是在World级别运行的系统,不依赖特定Scene
|
||||
* @zh 全局系统接口
|
||||
* @en Global system interface
|
||||
*
|
||||
* @zh 全局系统是在World级别运行的系统,不依赖特定Scene
|
||||
* @en Global systems run at World level and don't depend on specific Scene
|
||||
*/
|
||||
export type IGlobalSystem = {
|
||||
/**
|
||||
* 系统名称
|
||||
* @zh 系统名称
|
||||
* @en System name
|
||||
*/
|
||||
readonly name: string;
|
||||
|
||||
/**
|
||||
* 初始化系统
|
||||
* @zh 初始化系统
|
||||
* @en Initialize system
|
||||
*/
|
||||
initialize?(): void;
|
||||
|
||||
/**
|
||||
* 更新系统
|
||||
* @zh 更新系统
|
||||
* @en Update system
|
||||
*/
|
||||
update(deltaTime?: number): void;
|
||||
|
||||
/**
|
||||
* 重置系统
|
||||
* @zh 重置系统
|
||||
* @en Reset system
|
||||
*/
|
||||
reset?(): void;
|
||||
|
||||
/**
|
||||
* 销毁系统
|
||||
* @zh 销毁系统
|
||||
* @en Destroy system
|
||||
*/
|
||||
destroy?(): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* World配置接口
|
||||
* @zh World配置接口
|
||||
* @en World configuration interface
|
||||
*/
|
||||
export type IWorldConfig = {
|
||||
/**
|
||||
* World名称
|
||||
* @zh World名称
|
||||
* @en World name
|
||||
*/
|
||||
name?: string;
|
||||
|
||||
/**
|
||||
* 是否启用调试模式
|
||||
* @zh 是否启用调试模式
|
||||
* @en Enable debug mode
|
||||
*/
|
||||
debug?: boolean;
|
||||
|
||||
/**
|
||||
* 最大Scene数量限制
|
||||
* @zh 最大Scene数量限制
|
||||
* @en Maximum number of scenes
|
||||
*/
|
||||
maxScenes?: number;
|
||||
|
||||
/**
|
||||
* 是否自动清理空Scene
|
||||
* @zh 是否自动清理空Scene
|
||||
* @en Auto cleanup empty scenes
|
||||
*/
|
||||
autoCleanup?: boolean;
|
||||
|
||||
/**
|
||||
* @zh 自动清理阈值(毫秒),空Scene超过此时间后将被自动清理
|
||||
* @en Auto cleanup threshold (ms), empty scenes exceeding this time will be auto-cleaned
|
||||
*
|
||||
* @default 300000 (5 minutes)
|
||||
*/
|
||||
cleanupThresholdMs?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* World类 - ECS世界管理器
|
||||
* @zh World类 - ECS世界管理器
|
||||
* @en World class - ECS world manager
|
||||
*
|
||||
* World是Scene的容器,每个World可以管理多个Scene。
|
||||
* @zh World是Scene的容器,每个World可以管理多个Scene。
|
||||
* World拥有独立的服务容器,用于管理World级别的全局服务。
|
||||
* @en World is a container for Scenes, each World can manage multiple Scenes.
|
||||
* World has its own service container for managing World-level global services.
|
||||
*
|
||||
* 服务容器层级:
|
||||
* @zh 服务容器层级:
|
||||
* - Core.services: 应用程序全局服务
|
||||
* - World.services: World级别服务(每个World独立)
|
||||
* - Scene.services: Scene级别服务(每个Scene独立)
|
||||
* @en Service container hierarchy:
|
||||
* - Core.services: Application-wide global services
|
||||
* - World.services: World-level services (independent per World)
|
||||
* - Scene.services: Scene-level services (independent per Scene)
|
||||
*
|
||||
* 这种设计允许创建独立的游戏世界,如:
|
||||
* @zh 这种设计允许创建独立的游戏世界,如:
|
||||
* - 游戏房间(每个房间一个World)
|
||||
* - 不同的游戏模式
|
||||
* - 独立的模拟环境
|
||||
* @en This design allows creating independent game worlds like:
|
||||
* - Game rooms (one World per room)
|
||||
* - Different game modes
|
||||
* - Independent simulation environments
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* // 创建游戏房间的World
|
||||
* // @zh 创建游戏房间的World | @en Create World for game room
|
||||
* const roomWorld = new World({ name: 'Room_001' });
|
||||
*
|
||||
* // 注册World级别的服务
|
||||
* // @zh 注册World级别的服务 | @en Register World-level service
|
||||
* roomWorld.services.registerSingleton(RoomManager);
|
||||
*
|
||||
* // 在World中创建Scene
|
||||
* // @zh 在World中创建Scene | @en Create Scene in World
|
||||
* const gameScene = roomWorld.createScene('game', new Scene());
|
||||
* const uiScene = roomWorld.createScene('ui', new Scene());
|
||||
*
|
||||
* // 在Scene中使用World级别的服务
|
||||
* // @zh 在Scene中使用World级别的服务 | @en Use World-level service in Scene
|
||||
* const roomManager = roomWorld.services.resolve(RoomManager);
|
||||
*
|
||||
* // 更新整个World
|
||||
* // @zh 更新整个World | @en Update entire World
|
||||
* roomWorld.update(deltaTime);
|
||||
* ```
|
||||
*/
|
||||
@@ -113,6 +145,7 @@ export class World {
|
||||
debug: false,
|
||||
maxScenes: 10,
|
||||
autoCleanup: true,
|
||||
cleanupThresholdMs: 5 * 60 * 1000,
|
||||
...config
|
||||
};
|
||||
|
||||
@@ -122,15 +155,20 @@ export class World {
|
||||
}
|
||||
|
||||
/**
|
||||
* World级别的服务容器
|
||||
* 用于管理World范围内的全局服务
|
||||
* @zh World级别的服务容器,用于管理World范围内的全局服务
|
||||
* @en World-level service container for managing World-scoped global services
|
||||
*/
|
||||
public get services(): ServiceContainer {
|
||||
return this._services;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建并添加Scene到World
|
||||
* @zh 创建并添加Scene到World
|
||||
* @en Create and add Scene to World
|
||||
*
|
||||
* @param sceneName - @zh Scene名称 @en Scene name
|
||||
* @param sceneInstance - @zh Scene实例(可选)@en Scene instance (optional)
|
||||
* @returns @zh 创建的Scene实例 @en Created Scene instance
|
||||
*/
|
||||
public createScene<T extends IScene>(sceneName: string, sceneInstance?: T): T {
|
||||
if (!sceneName || typeof sceneName !== 'string' || sceneName.trim() === '') {
|
||||
@@ -145,34 +183,31 @@ export class World {
|
||||
throw new Error(`World '${this.name}' 已达到最大Scene数量限制: ${this._config.maxScenes}`);
|
||||
}
|
||||
|
||||
// 如果没有提供Scene实例,创建默认Scene
|
||||
const scene = sceneInstance || (new Scene() as unknown as T);
|
||||
|
||||
// 如果配置了 debug,为 Scene 注册并启用 PerformanceMonitor
|
||||
if (this._config.debug) {
|
||||
const performanceMonitor = new PerformanceMonitor();
|
||||
performanceMonitor.enable();
|
||||
scene.services.registerInstance(PerformanceMonitor, performanceMonitor);
|
||||
}
|
||||
|
||||
// 设置Scene的标识
|
||||
if ('id' in scene) {
|
||||
(scene as any).id = sceneName;
|
||||
}
|
||||
if ('name' in scene && !scene.name) {
|
||||
(scene as { id?: string }).id = sceneName;
|
||||
if (!scene.name) {
|
||||
scene.name = sceneName;
|
||||
}
|
||||
|
||||
this._scenes.set(sceneName, scene);
|
||||
|
||||
// 初始化Scene
|
||||
scene.initialize();
|
||||
|
||||
return scene;
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除Scene
|
||||
* @zh 移除Scene
|
||||
* @en Remove Scene
|
||||
*
|
||||
* @param sceneName - @zh Scene名称 @en Scene name
|
||||
* @returns @zh 是否成功移除 @en Whether removal was successful
|
||||
*/
|
||||
public removeScene(sceneName: string): boolean {
|
||||
const scene = this._scenes.get(sceneName);
|
||||
@@ -180,12 +215,10 @@ export class World {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 如果Scene正在运行,先停止它
|
||||
if (this._activeScenes.has(sceneName)) {
|
||||
this.setSceneActive(sceneName, false);
|
||||
}
|
||||
|
||||
// 清理Scene资源
|
||||
scene.end();
|
||||
this._scenes.delete(sceneName);
|
||||
|
||||
@@ -194,7 +227,11 @@ export class World {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Scene
|
||||
* @zh 获取Scene
|
||||
* @en Get Scene
|
||||
*
|
||||
* @param sceneName - @zh Scene名称 @en Scene name
|
||||
* @returns @zh Scene实例或null @en Scene instance or null
|
||||
*/
|
||||
public getScene<T extends IScene>(sceneName: string): T | null {
|
||||
return this._scenes.get(sceneName) as T || null;
|
||||
@@ -237,14 +274,10 @@ export class World {
|
||||
|
||||
if (active) {
|
||||
this._activeScenes.add(sceneName);
|
||||
// 启动Scene
|
||||
if (scene.begin) {
|
||||
scene.begin();
|
||||
}
|
||||
scene.begin?.();
|
||||
logger.debug(`在World '${this.name}' 中激活Scene: ${sceneName}`);
|
||||
} else {
|
||||
this._activeScenes.delete(sceneName);
|
||||
// 可选择性地停止Scene,或者让它继续运行但不更新
|
||||
logger.debug(`在World '${this.name}' 中停用Scene: ${sceneName}`);
|
||||
}
|
||||
}
|
||||
@@ -273,9 +306,7 @@ export class World {
|
||||
}
|
||||
|
||||
this._globalSystems.push(system);
|
||||
if (system.initialize) {
|
||||
system.initialize();
|
||||
}
|
||||
system.initialize?.();
|
||||
|
||||
logger.debug(`在World '${this.name}' 中添加全局System: ${system.name}`);
|
||||
return system;
|
||||
@@ -291,9 +322,7 @@ export class World {
|
||||
}
|
||||
|
||||
this._globalSystems.splice(index, 1);
|
||||
if (system.reset) {
|
||||
system.reset();
|
||||
}
|
||||
system.reset?.();
|
||||
|
||||
logger.debug(`从World '${this.name}' 中移除全局System: ${system.name}`);
|
||||
return true;
|
||||
@@ -321,11 +350,8 @@ export class World {
|
||||
|
||||
this._isActive = true;
|
||||
|
||||
// 启动所有全局System
|
||||
for (const system of this._globalSystems) {
|
||||
if (system.initialize) {
|
||||
system.initialize();
|
||||
}
|
||||
system.initialize?.();
|
||||
}
|
||||
|
||||
logger.info(`启动World: ${this.name}`);
|
||||
@@ -339,16 +365,12 @@ export class World {
|
||||
return;
|
||||
}
|
||||
|
||||
// 停止所有Scene
|
||||
for (const sceneName of this._activeScenes) {
|
||||
this.setSceneActive(sceneName, false);
|
||||
}
|
||||
|
||||
// 重置所有全局System
|
||||
for (const system of this._globalSystems) {
|
||||
if (system.reset) {
|
||||
system.reset();
|
||||
}
|
||||
system.reset?.();
|
||||
}
|
||||
|
||||
this._isActive = false;
|
||||
@@ -356,41 +378,38 @@ export class World {
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新World中的全局System
|
||||
* 注意:此方法由Core.update()调用,不应直接调用
|
||||
* @zh 更新World中的全局System
|
||||
* @en Update global systems in World
|
||||
*
|
||||
* @internal Called by Core.update()
|
||||
*/
|
||||
public updateGlobalSystems(): void {
|
||||
if (!this._isActive) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 更新全局System
|
||||
for (const system of this._globalSystems) {
|
||||
if (system.update) {
|
||||
system.update();
|
||||
}
|
||||
system.update?.();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新World中的所有激活Scene
|
||||
* 注意:此方法由Core.update()调用,不应直接调用
|
||||
* @zh 更新World中的所有激活Scene
|
||||
* @en Update all active scenes in World
|
||||
*
|
||||
* @internal Called by Core.update()
|
||||
*/
|
||||
public updateScenes(): void {
|
||||
if (!this._isActive) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 更新所有激活的Scene
|
||||
for (const sceneName of this._activeScenes) {
|
||||
const scene = this._scenes.get(sceneName);
|
||||
if (scene && scene.update) {
|
||||
scene.update();
|
||||
}
|
||||
scene?.update?.();
|
||||
}
|
||||
|
||||
// 自动清理(如果启用)
|
||||
if (this._config.autoCleanup && this.shouldAutoCleanup()) {
|
||||
if (this._config.autoCleanup) {
|
||||
this.cleanup();
|
||||
}
|
||||
}
|
||||
@@ -401,28 +420,22 @@ export class World {
|
||||
public destroy(): void {
|
||||
logger.info(`销毁World: ${this.name}`);
|
||||
|
||||
// 停止World
|
||||
this.stop();
|
||||
|
||||
// 销毁所有Scene
|
||||
const sceneNames = Array.from(this._scenes.keys());
|
||||
for (const sceneName of sceneNames) {
|
||||
for (const sceneName of Array.from(this._scenes.keys())) {
|
||||
this.removeScene(sceneName);
|
||||
}
|
||||
|
||||
// 清理全局System
|
||||
for (const system of this._globalSystems) {
|
||||
if (system.destroy) {
|
||||
system.destroy();
|
||||
} else if (system.reset) {
|
||||
system.reset();
|
||||
} else {
|
||||
system.reset?.();
|
||||
}
|
||||
}
|
||||
this._globalSystems.length = 0;
|
||||
|
||||
// 清空服务容器
|
||||
this._services.clear();
|
||||
|
||||
this._scenes.clear();
|
||||
this._activeScenes.clear();
|
||||
}
|
||||
@@ -461,58 +474,37 @@ export class World {
|
||||
}
|
||||
};
|
||||
|
||||
// 统计所有Scene的实体数量
|
||||
for (const scene of this._scenes.values()) {
|
||||
if (scene.entities) {
|
||||
stats.totalEntities += scene.entities.count;
|
||||
}
|
||||
if (scene.systems) {
|
||||
stats.totalSystems += scene.systems.length;
|
||||
}
|
||||
stats.totalEntities += scene.entities?.count ?? 0;
|
||||
stats.totalSystems += scene.systems?.length ?? 0;
|
||||
}
|
||||
|
||||
return stats;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否应该执行自动清理
|
||||
* @zh 检查Scene是否可以被自动清理
|
||||
* @en Check if a scene is eligible for auto cleanup
|
||||
*/
|
||||
private shouldAutoCleanup(): boolean {
|
||||
// 简单的清理策略:如果有空Scene且超过5分钟没有实体
|
||||
const currentTime = Date.now();
|
||||
const cleanupThreshold = 5 * 60 * 1000; // 5分钟
|
||||
|
||||
for (const [sceneName, scene] of this._scenes) {
|
||||
if (!this._activeScenes.has(sceneName) &&
|
||||
scene.entities &&
|
||||
scene.entities.count === 0 &&
|
||||
(currentTime - this._createdAt) > cleanupThreshold) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
private _isSceneCleanupCandidate(sceneName: string, scene: IScene): boolean {
|
||||
const elapsed = Date.now() - this._createdAt;
|
||||
return !this._activeScenes.has(sceneName) &&
|
||||
scene.entities != null &&
|
||||
scene.entities.count === 0 &&
|
||||
elapsed > this._config.cleanupThresholdMs!;
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行清理操作
|
||||
* @zh 执行自动清理操作
|
||||
* @en Execute auto cleanup operation
|
||||
*/
|
||||
private cleanup(): void {
|
||||
const sceneNames = Array.from(this._scenes.keys());
|
||||
const currentTime = Date.now();
|
||||
const cleanupThreshold = 5 * 60 * 1000; // 5分钟
|
||||
const candidates = [...this._scenes.entries()]
|
||||
.filter(([name, scene]) => this._isSceneCleanupCandidate(name, scene));
|
||||
|
||||
for (const sceneName of sceneNames) {
|
||||
const scene = this._scenes.get(sceneName);
|
||||
if (scene &&
|
||||
!this._activeScenes.has(sceneName) &&
|
||||
scene.entities &&
|
||||
scene.entities.count === 0 &&
|
||||
(currentTime - this._createdAt) > cleanupThreshold) {
|
||||
|
||||
this.removeScene(sceneName);
|
||||
logger.debug(`自动清理空Scene: ${sceneName} from World ${this.name}`);
|
||||
}
|
||||
for (const [sceneName] of candidates) {
|
||||
this.removeScene(sceneName);
|
||||
logger.debug(`自动清理空Scene: ${sceneName} from World ${this.name}`);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -113,6 +113,16 @@ export class PerformanceMonitor implements IService {
|
||||
|
||||
constructor() {}
|
||||
|
||||
/**
|
||||
* @zh 更新FPS统计(可选功能,子类可重写)
|
||||
* @en Update FPS statistics (optional, can be overridden by subclasses)
|
||||
*
|
||||
* @param _deltaTime - @zh 帧时间间隔(秒)@en Frame delta time in seconds
|
||||
*/
|
||||
public updateFPS(_deltaTime: number): void {
|
||||
// Base implementation does nothing - override in subclass for FPS tracking
|
||||
}
|
||||
|
||||
/**
|
||||
* 启用性能监控
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user