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:
YHH
2025-12-23 18:00:21 +08:00
committed by GitHub
parent 828ff969e1
commit 58f70a5783
11 changed files with 983 additions and 744 deletions

View File

@@ -29,6 +29,8 @@
--- ---
> **Just need ECS?** The core ECS framework [`@esengine/ecs-framework`](./packages/core/) can be used standalone with Cocos Creator, Laya, or any JS engine. [View ECS Documentation](./packages/core/README.md)
## Overview ## Overview
ESEngine is a cross-platform 2D game engine built from the ground up with modern web technologies. It provides a comprehensive toolset that enables developers to focus on creating games rather than building infrastructure. ESEngine is a cross-platform 2D game engine built from the ground up with modern web technologies. It provides a comprehensive toolset that enables developers to focus on creating games rather than building infrastructure.
@@ -238,13 +240,24 @@ pnpm tauri:dev
``` ```
esengine/ esengine/
├── packages/ # Engine packages (runtime, editor, platform) ├── packages/
├── docs/ # Documentation source │ ├── core/ # ECS Framework (@esengine/ecs-framework)
├── examples/ # Example projects │ ├── math/ # Math library (@esengine/math)
├── scripts/ # Build utilities │ ├── engine-core/ # Engine lifecycle management
└── thirdparty/ # Third-party dependencies │ ├── sprite/ # 2D sprite rendering
│ ├── tilemap/ # Tilemap system
│ ├── physics-rapier2d/ # Physics engine
│ ├── behavior-tree/ # AI behavior trees
│ ├── editor-app/ # Desktop editor (Tauri)
│ └── ... # Other modules
├── docs/ # Documentation source
├── examples/ # Example projects
├── scripts/ # Build utilities
└── thirdparty/ # Third-party dependencies
``` ```
> **Looking for ECS source code?** The ECS framework is in `packages/core/`
## Documentation ## Documentation
- [Getting Started](https://esengine.cn/guide/getting-started.html) - [Getting Started](https://esengine.cn/guide/getting-started.html)

View File

@@ -29,6 +29,8 @@
--- ---
> **只需要 ECS** 核心 ECS 框架 [`@esengine/ecs-framework`](./packages/core/) 可独立使用,支持 Cocos Creator、Laya 或任何 JS 引擎。[查看 ECS 文档](./packages/core/README_CN.md)
## 概述 ## 概述
ESEngine 是一款基于现代 Web 技术从零构建的跨平台 2D 游戏引擎。它提供完整的工具集,让开发者专注于游戏创作而非基础设施搭建。 ESEngine 是一款基于现代 Web 技术从零构建的跨平台 2D 游戏引擎。它提供完整的工具集,让开发者专注于游戏创作而非基础设施搭建。
@@ -238,13 +240,24 @@ pnpm tauri:dev
``` ```
esengine/ esengine/
├── packages/ # 引擎包(运行时、编辑器、平台) ├── packages/
├── docs/ # 文档源码 │ ├── core/ # ECS 框架 (@esengine/ecs-framework)
├── examples/ # 示例项目 │ ├── math/ # 数学库 (@esengine/math)
├── scripts/ # 构建工具 │ ├── engine-core/ # 引擎生命周期管理
└── thirdparty/ # 第三方依赖 │ ├── sprite/ # 2D 精灵渲染
│ ├── tilemap/ # Tilemap 系统
│ ├── physics-rapier2d/ # 物理引擎
│ ├── behavior-tree/ # AI 行为树
│ ├── editor-app/ # 桌面编辑器 (Tauri)
│ └── ... # 其他模块
├── docs/ # 文档源码
├── examples/ # 示例项目
├── scripts/ # 构建工具
└── thirdparty/ # 第三方依赖
``` ```
> **寻找 ECS 源码?** ECS 框架位于 `packages/core/`
## 文档 ## 文档
- [快速入门](https://esengine.cn/guide/getting-started.html) - [快速入门](https://esengine.cn/guide/getting-started.html)

188
packages/core/README.md Normal file
View 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
View 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>

View File

@@ -18,69 +18,72 @@ import { DebugConfigService } from './Utils/Debug/DebugConfigService';
import { createInstance } from './Core/DI/Decorators'; import { createInstance } from './Core/DI/Decorators';
/** /**
* 游戏引擎核心类 * @zh 游戏引擎核心类
* @en Game engine core class
* *
* 职责: * @zh 职责:
* - 提供全局服务Timer、Performance、Pool等 * - 提供全局服务Timer、Performance、Pool等
* - 管理场景生命周期内置SceneManager * - 管理场景生命周期内置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 * @example
* ```typescript * ```typescript
* // 初始化并设置场景 * // @zh 初始化并设置场景 | @en Initialize and set scene
* Core.create({ debug: true }); * Core.create({ debug: true });
* Core.setScene(new GameScene()); * Core.setScene(new GameScene());
* *
* // 游戏循环(自动更新全局服务和场景) * // @zh 游戏循环(自动更新全局服务和场景)| @en Game loop (auto-updates global services and scene)
* function gameLoop(deltaTime: number) { * function gameLoop(deltaTime: number) {
* Core.update(deltaTime); * Core.update(deltaTime);
* } * }
* *
* // 使用定时器 * // @zh 使用定时器 | @en Use timer
* Core.schedule(1.0, false, null, (timer) => { * Core.schedule(1.0, false, null, (timer) => {
* console.log("1秒后执行"); * console.log("Executed after 1 second");
* }); * });
* *
* // 切换场景 * // @zh 切换场景 | @en Switch scene
* Core.loadScene(new MenuScene()); // 延迟切换 * Core.loadScene(new MenuScene()); // @zh 延迟切换 | @en Deferred switch
* Core.setScene(new GameScene()); // 立即切换 * Core.setScene(new GameScene()); // @zh 立即切换 | @en Immediate switch
* *
* // 获取当前场景 * // @zh 获取当前场景 | @en Get current scene
* const currentScene = Core.scene; * const currentScene = Core.scene;
* ``` * ```
*/ */
export class Core { export class Core {
/** /**
* 游戏暂停状态 * @zh 游戏暂停状态当设置为true时游戏循环将暂停执行
* * @en Game paused state, when set to true, game loop will pause execution
* 当设置为true时游戏循环将暂停执行。
*/ */
public static paused = false; public static paused = false;
/** /**
* 全局核心实例 * @zh 全局核心实例可能为null表示Core尚未初始化或已被销毁
* * @en Global core instance, null means Core is not initialized or destroyed
* 可能为null表示Core尚未初始化或已被销毁
*/ */
private static _instance: Core | null = null; private static _instance: Core | null = null;
/** /**
* Core专用日志器 * @zh Core专用日志器
* @en Core logger
*/ */
private static _logger = createLogger('Core'); private static _logger = createLogger('Core');
/** /**
* 调试模式标志 * @zh 调试模式标志,在调试模式下会启用额外的性能监控和错误检查
* * @en Debug mode flag, enables additional performance monitoring and error checking in debug mode
* 在调试模式下会启用额外的性能监控和错误检查。
*/ */
public readonly debug: boolean; public readonly debug: boolean;
/** /**
* 服务容器 * @zh 服务容器,管理所有服务的注册、解析和生命周期
* * @en Service container for managing registration, resolution, and lifecycle of all services
* 管理所有服务的注册、解析和生命周期。
*/ */
private _serviceContainer: ServiceContainer; private _serviceContainer: ServiceContainer;
@@ -90,110 +93,79 @@ export class Core {
private _debugManager?: DebugManager; private _debugManager?: DebugManager;
/** /**
* 场景管理器 * @zh 场景管理器,管理当前场景的生命周期
* * @en Scene manager for managing current scene lifecycle
* 管理当前场景的生命周期。
*/ */
private _sceneManager: SceneManager; private _sceneManager: SceneManager;
/** /**
* World管理器 * @zh World管理器管理多个独立的World实例可选
* * @en World manager for managing multiple independent World instances (optional)
* 管理多个独立的World实例可选
*/ */
private _worldManager: WorldManager; private _worldManager: WorldManager;
/** /**
* 插件管理器 * @zh 插件管理器,管理所有插件的生命周期
* * @en Plugin manager for managing all plugin lifecycles
* 管理所有插件的生命周期。
*/ */
private _pluginManager: PluginManager; private _pluginManager: PluginManager;
/** /**
* 插件服务注册表 * @zh 插件服务注册表,基于 ServiceToken 的类型安全服务注册表
* * @en Plugin service registry, type-safe service registry based on ServiceToken
* 基于 ServiceToken 的类型安全服务注册表。
* Type-safe service registry based on ServiceToken.
*/ */
private _pluginServiceRegistry: PluginServiceRegistry; private _pluginServiceRegistry: PluginServiceRegistry;
/** /**
* Core配置 * @zh Core配置
* @en Core configuration
*/ */
private _config: ICoreConfig; private _config: ICoreConfig;
/** /**
* 创建核心实例 * @zh 创建核心实例
* @en Create core instance
* *
* @param config - Core配置对象 * @param config - @zh Core配置对象 @en Core configuration object
*/ */
private constructor(config: ICoreConfig = {}) { private constructor(config: ICoreConfig = {}) {
Core._instance = this; Core._instance = this;
this._config = { debug: true, ...config };
// 保存配置
this._config = {
debug: true,
...config
};
// 初始化服务容器
this._serviceContainer = new ServiceContainer(); this._serviceContainer = new ServiceContainer();
// 初始化定时器管理器
this._timerManager = new TimerManager(); this._timerManager = new TimerManager();
this._serviceContainer.registerInstance(TimerManager, this._timerManager); this._serviceContainer.registerInstance(TimerManager, this._timerManager);
// 初始化性能监控器
this._performanceMonitor = new PerformanceMonitor(); this._performanceMonitor = new PerformanceMonitor();
this._serviceContainer.registerInstance(PerformanceMonitor, this._performanceMonitor); this._serviceContainer.registerInstance(PerformanceMonitor, this._performanceMonitor);
// 在调试模式下启用性能监控
if (this._config.debug) { if (this._config.debug) {
this._performanceMonitor.enable(); this._performanceMonitor.enable();
} }
// 初始化对象池管理器
this._poolManager = new PoolManager(); this._poolManager = new PoolManager();
this._serviceContainer.registerInstance(PoolManager, this._poolManager); this._serviceContainer.registerInstance(PoolManager, this._poolManager);
// 初始化场景管理器
this._sceneManager = new SceneManager(this._performanceMonitor); this._sceneManager = new SceneManager(this._performanceMonitor);
this._serviceContainer.registerInstance(SceneManager, this._sceneManager); 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._worldManager = new WorldManager({ debug: !!this._config.debug, ...this._config.worldManagerConfig });
this._serviceContainer.registerInstance(WorldManager, this._worldManager); this._serviceContainer.registerInstance(WorldManager, this._worldManager);
// 初始化插件管理器
this._pluginManager = new PluginManager(); this._pluginManager = new PluginManager();
this._pluginManager.initialize(this, this._serviceContainer); this._pluginManager.initialize(this, this._serviceContainer);
this._serviceContainer.registerInstance(PluginManager, this._pluginManager); this._serviceContainer.registerInstance(PluginManager, this._pluginManager);
// 初始化插件服务注册表
// Initialize plugin service registry
this._pluginServiceRegistry = new PluginServiceRegistry(); this._pluginServiceRegistry = new PluginServiceRegistry();
this._serviceContainer.registerInstance(PluginServiceRegistry, this._pluginServiceRegistry); this._serviceContainer.registerInstance(PluginServiceRegistry, this._pluginServiceRegistry);
this.debug = this._config.debug ?? true; this.debug = this._config.debug ?? true;
// 初始化调试管理器
if (this._config.debugConfig?.enabled) { if (this._config.debugConfig?.enabled) {
const configService = new DebugConfigService(); const configService = new DebugConfigService();
configService.setConfig(this._config.debugConfig); configService.setConfig(this._config.debugConfig);
this._serviceContainer.registerInstance(DebugConfigService, configService); 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 = this._serviceContainer.resolve(DebugManager);
this._debugManager.onInitialize(); 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() { public static get Instance() {
return this._instance; return this._instance;
} }
/** /**
* 获取服务容器 * @zh 获取服务容器
* @en Get service container
* *
* 用于注册和解析自定义服务。 * @zh 用于注册和解析自定义服务。
* @en Used for registering and resolving custom services.
* *
* @returns 服务容器实例 * @returns @zh 服务容器实例 @en Service container instance
* @throws 如果Core实例未创建 * @throws @zh 如果Core实例未创建 @en If Core instance is not created
* *
* @example * @example
* ```typescript * ```typescript
* // 注册自定义服务 * // @zh 注册自定义服务 | @en Register custom service
* Core.services.registerSingleton(MyService); * Core.services.registerSingleton(MyService);
* *
* // 解析服务 * // @zh 解析服务 | @en Resolve service
* const myService = Core.services.resolve(MyService); * const myService = Core.services.resolve(MyService);
* ``` * ```
*/ */
@@ -235,28 +210,29 @@ export class Core {
} }
/** /**
* 获取插件服务注册表 * @zh 获取插件服务注册表
* @en Get plugin service registry
* *
* 用于基于 ServiceToken 的类型安全服务注册和获取。 * @zh 用于基于 ServiceToken 的类型安全服务注册和获取。
* For type-safe service registration and retrieval based on ServiceToken. * @en For type-safe service registration and retrieval based on ServiceToken.
* *
* @returns PluginServiceRegistry 实例 * @returns @zh PluginServiceRegistry 实例 @en PluginServiceRegistry instance
* @throws 如果 Core 实例未创建 * @throws @zh 如果 Core 实例未创建 @en If Core instance is not created
* *
* @example * @example
* ```typescript * ```typescript
* import { createServiceToken } from '@esengine/ecs-framework'; * import { createServiceToken } from '@esengine/ecs-framework';
* *
* // 定义服务令牌 * // @zh 定义服务令牌 | @en Define service token
* const MyServiceToken = createServiceToken<IMyService>('myService'); * const MyServiceToken = createServiceToken<IMyService>('myService');
* *
* // 注册服务 * // @zh 注册服务 | @en Register service
* Core.pluginServices.register(MyServiceToken, myServiceInstance); * Core.pluginServices.register(MyServiceToken, myServiceInstance);
* *
* // 获取服务(可选) * // @zh 获取服务(可选)| @en Get service (optional)
* const service = Core.pluginServices.get(MyServiceToken); * const service = Core.pluginServices.get(MyServiceToken);
* *
* // 获取服务(必需,不存在则抛异常) * // @zh 获取服务(必需,不存在则抛异常)| @en Get service (required, throws if not found)
* const service = Core.pluginServices.require(MyServiceToken); * 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实例 * @returns @zh WorldManager实例 @en WorldManager instance
* @throws 如果Core实例未创建 * @throws @zh 如果Core实例未创建 @en If Core instance is not created
* *
* @example * @example
* ```typescript * ```typescript
* // 创建多个游戏房间 * // @zh 创建多个游戏房间 | @en Create multiple game rooms
* const wm = Core.worldManager; * const wm = Core.worldManager;
* const room1 = wm.createWorld('room_001'); * const room1 = wm.createWorld('room_001');
* room1.createScene('game', new GameScene()); * 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模式向后兼容 * @param config - @zh Core配置也可以直接传入boolean表示debug模式向后兼容 @en Core config, can also pass boolean for debug mode (backward compatible)
* @returns Core实例 * @returns @zh Core实例 @en Core instance
* *
* @example * @example
* ```typescript * ```typescript
* // 方式1使用配置对象 * // @zh 方式1使用配置对象 | @en Method 1: Use config object
* Core.create({ * Core.create({
* debug: true, * debug: true,
* debugConfig: { * debugConfig: {
@@ -310,7 +290,7 @@ export class Core {
* } * }
* }); * });
* *
* // 方式2简单模式向后兼容 * // @zh 方式2简单模式向后兼容| @en Method 2: Simple mode (backward compatible)
* Core.create(true); // debug = true * Core.create(true); // debug = true
* ``` * ```
*/ */
@@ -328,16 +308,17 @@ export class Core {
} }
/** /**
* 设置当前场景 * @zh 设置当前场景
* @en Set current scene
* *
* @param scene - 要设置的场景 * @param scene - @zh 要设置的场景 @en The scene to set
* @returns 设置的场景实例 * @returns @zh 设置的场景实例 @en The scene instance that was set
* *
* @example * @example
* ```typescript * ```typescript
* Core.create({ debug: true }); * Core.create({ debug: true });
* *
* // 创建并设置场景 * // @zh 创建并设置场景 | @en Create and set scene
* const gameScene = new GameScene(); * const gameScene = new GameScene();
* Core.setScene(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 { public static get scene(): IScene | null {
if (!this._instance) { 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 * @example
* ```typescript * ```typescript
* // 使用流式API创建实体 * // @zh 使用流式API创建实体 | @en Create entity with fluent API
* const player = Core.ecsAPI?.createEntity('Player') * const player = Core.ecsAPI?.createEntity('Player')
* .addComponent(Position, 100, 100) * .addComponent(Position, 100, 100)
* .addComponent(Velocity, 50, 0); * .addComponent(Velocity, 50, 0);
* *
* // 查询实体 * // @zh 查询实体 | @en Query entities
* const enemies = Core.ecsAPI?.query(Enemy, Transform); * const enemies = Core.ecsAPI?.query(Enemy, Transform);
* *
* // 发射事件 * // @zh 发射事件 | @en Emit event
* Core.ecsAPI?.emit('game:start', { level: 1 }); * 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 * @example
* ```typescript * ```typescript
* // 延迟切换场景(在下一帧生效) * // @zh 延迟切换场景(在下一帧生效)| @en Deferred scene switch (takes effect next frame)
* Core.loadScene(new MenuScene()); * 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 * @example
* ```typescript * ```typescript
* // 初始化 * // @zh 初始化 | @en Initialize
* Core.create({ debug: true }); * Core.create({ debug: true });
* Core.setScene(new GameScene()); * Core.setScene(new GameScene());
* *
* // Laya引擎集成 * // @zh Laya引擎集成 | @en Laya engine integration
* Laya.timer.frameLoop(1, this, () => { * Laya.timer.frameLoop(1, this, () => {
* const deltaTime = Laya.timer.delta / 1000; * const deltaTime = Laya.timer.delta / 1000;
* Core.update(deltaTime); // 自动更新全局服务和场景 * Core.update(deltaTime);
* }); * });
* *
* // Cocos Creator集成 * // @zh Cocos Creator集成 | @en Cocos Creator integration
* update(deltaTime: number) { * 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 timeInSeconds - @zh 延迟时间(秒)@en Delay time in seconds
* @param repeats - 是否重复执行默认为false * @param repeats - @zh 是否重复执行默认为false @en Whether to repeat, defaults to false
* @param context - 回调函数的上下文默认为null * @param context - @zh 回调函数的上下文 @en Context for the callback
* @param onTime - 定时器触发时的回调函数 * @param onTime - @zh 定时器触发时的回调函数 @en Callback when timer fires
* @returns 创建的定时器实例 * @returns @zh 创建的定时器实例 @en The created timer instance
* @throws 如果Core实例未创建或onTime回调未提供 * @throws @zh 如果Core实例未创建或onTime回调未提供 @en If Core instance not created or onTime not provided
* *
* @example * @example
* ```typescript * ```typescript
* // 一次性定时器 * // @zh 一次性定时器 | @en One-time timer
* Core.schedule(1.0, false, null, (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) => { * 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 { public static enableDebug(config: IECSDebugConfig): void {
if (!this._instance) { if (!this._instance) {
@@ -511,7 +499,8 @@ export class Core {
} }
/** /**
* 禁用调试功能 * @zh 禁用调试功能
* @en Disable debug features
*/ */
public static disableDebug(): void { public static disableDebug(): void {
if (!this._instance) return; 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 { public static getDebugData(): unknown {
if (!this._instance?._debugManager) { 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 { public static get isDebugEnabled(): boolean {
return this._instance?._config.debugConfig?.enabled || false; 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 { public static get performanceMonitor(): PerformanceMonitor | null {
return this._instance?._performanceMonitor || null; return this._instance?._performanceMonitor || null;
} }
/** /**
* 安装插件 * @zh 安装插件
* @en Install plugin
* *
* @param plugin - 插件实例 * @param plugin - @zh 插件实例 @en Plugin instance
* @throws 如果Core实例未创建或插件安装失败 * @throws @zh 如果Core实例未创建或插件安装失败 @en If Core instance not created or plugin installation fails
* *
* @example * @example
* ```typescript * ```typescript
* Core.create({ debug: true }); * Core.create({ debug: true });
* *
* // 安装插件 * // @zh 安装插件 | @en Install plugin
* await Core.installPlugin(new MyPlugin()); * await Core.installPlugin(new MyPlugin());
* ``` * ```
*/ */
@@ -581,10 +574,11 @@ export class Core {
} }
/** /**
* 卸载插件 * @zh 卸载插件
* @en Uninstall plugin
* *
* @param name - 插件名称 * @param name - @zh 插件名称 @en Plugin name
* @throws 如果Core实例未创建或插件卸载失败 * @throws @zh 如果Core实例未创建或插件卸载失败 @en If Core instance not created or plugin uninstallation fails
* *
* @example * @example
* ```typescript * ```typescript
@@ -600,10 +594,11 @@ export class Core {
} }
/** /**
* 获取插件实例 * @zh 获取插件实例
* @en Get plugin instance
* *
* @param name - 插件名称 * @param name - @zh 插件名称 @en Plugin name
* @returns 插件实例如果未安装则返回undefined * @returns @zh 插件实例如果未安装则返回undefined @en Plugin instance, or undefined if not installed
* *
* @example * @example
* ```typescript * ```typescript
@@ -622,10 +617,11 @@ export class Core {
} }
/** /**
* 检查插件是否已安装 * @zh 检查插件是否已安装
* @en Check if plugin is installed
* *
* @param name - 插件名称 * @param name - @zh 插件名称 @en Plugin name
* @returns 是否已安装 * @returns @zh 是否已安装 @en Whether installed
* *
* @example * @example
* ```typescript * ```typescript
@@ -643,9 +639,11 @@ export class Core {
} }
/** /**
* 初始化核心系统 * @zh 初始化核心系统
* @en Initialize core system
* *
* 执行核心系统的初始化逻辑。 * @zh 执行核心系统的初始化逻辑。
* @en Execute core system initialization logic.
*/ */
protected initialize() { 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 { private updateInternal(deltaTime: number): void {
if (Core.paused) return; if (Core.paused) return;
// 开始性能监控
const frameStartTime = this._performanceMonitor.startMonitoring('Core.update'); const frameStartTime = this._performanceMonitor.startMonitoring('Core.update');
// 更新时间系统
Time.update(deltaTime); 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'); const servicesStartTime = this._performanceMonitor.startMonitoring('Services.update');
this._serviceContainer.updateAll(deltaTime); this._serviceContainer.updateAll(deltaTime);
this._performanceMonitor.endMonitoring('Services.update', servicesStartTime, this._serviceContainer.getUpdatableCount()); this._performanceMonitor.endMonitoring('Services.update', servicesStartTime, this._serviceContainer.getUpdatableCount());
// 更新对象池管理器
this._poolManager.update(); this._poolManager.update();
// 更新默认场景(通过 SceneManager
this._sceneManager.update(); this._sceneManager.update();
// 更新额外的 WorldManager
this._worldManager.updateAll(); this._worldManager.updateAll();
// 结束性能监控
this._performanceMonitor.endMonitoring('Core.update', frameStartTime); 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 { public static destroy(): void {
if (!this._instance) return; if (!this._instance) return;
// 停止调试管理器 this._instance._debugManager?.stop();
if (this._instance._debugManager) {
this._instance._debugManager.stop();
}
// 清理所有服务
this._instance._serviceContainer.clear(); this._instance._serviceContainer.clear();
Core._logger.info('Core destroyed'); Core._logger.info('Core destroyed');
// 清空实例引用允许重新创建Core实例
this._instance = null; this._instance = null;
} }
} }

View File

@@ -2,13 +2,17 @@ import type { IComponent } from '../Types';
import { Int32 } from './Core/SoAStorage'; import { Int32 } from './Core/SoAStorage';
/** /**
* 游戏组件基类 * @zh 游戏组件基类
* @en Base class for game components
* *
* ECS架构中的组件Component应该是纯数据容器。 * @zh ECS架构中的组件Component应该是纯数据容器。
* 所有游戏逻辑应该在 EntitySystem 中实现,而不是在组件内部。 * 所有游戏逻辑应该在 EntitySystem 中实现,而不是在组件内部。
* @en Components in ECS architecture should be pure data containers.
* All game logic should be implemented in EntitySystem, not inside components.
* *
* @example * @example
* 推荐做法:纯数据组件 * @zh 推荐做法:纯数据组件
* @en Recommended: Pure data component
* ```typescript * ```typescript
* class HealthComponent extends Component { * class HealthComponent extends Component {
* public health: number = 100; * public health: number = 100;
@@ -17,7 +21,8 @@ import { Int32 } from './Core/SoAStorage';
* ``` * ```
* *
* @example * @example
* 推荐做法:在 System 中处理逻辑 * @zh 推荐做法:在 System 中处理逻辑
* @en Recommended: Handle logic in System
* ```typescript * ```typescript
* class HealthSystem extends EntitySystem { * class HealthSystem extends EntitySystem {
* process(entities: Entity[]): void { * process(entities: Entity[]): void {
@@ -33,75 +38,66 @@ import { Int32 } from './Core/SoAStorage';
*/ */
export abstract class Component implements IComponent { export abstract class Component implements IComponent {
/** /**
* 组件ID生成器 * @zh 组件ID生成器用于为每个组件分配唯一的ID
* * @en Component ID generator, used to assign unique IDs to each component
* 用于为每个组件分配唯一的ID。
*
* Component ID generator.
* Used to assign unique IDs to each component.
*/ */
private static idGenerator: number = 0; private static idGenerator: number = 0;
/** /**
* 组件唯一标识符 * @zh 组件唯一标识符,在整个游戏生命周期中唯一
* * @en Unique identifier for the component, unique throughout the game lifecycle
* 在整个游戏生命周期中唯一的数字ID。
*/ */
public readonly id: number; 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 @Int32
public entityId: number | null = null; public entityId: number | null = null;
/** /**
* 最后写入的 epoch * @zh 最后写入的 epoch,用于帧级变更检测
* @en Last write epoch, used for frame-level change detection
* *
* 用于帧级变更检测,记录组件最后一次被修改时的 epoch * @zh 记录组件最后一次被修改时的 epoch0 表示从未被标记为已修改
* 0 表示从未被标记为已修改。 * @en Records the epoch when component was last modified, 0 means never marked as modified
*
* Last write epoch.
* Used for frame-level change detection, records the epoch when component was last modified.
* 0 means never marked as modified.
*/ */
private _lastWriteEpoch: number = 0; private _lastWriteEpoch: number = 0;
/** /**
* 获取最后写入的 epoch * @zh 获取最后写入的 epoch
* * @en Get last write epoch
* Get last write epoch.
*/ */
public get lastWriteEpoch(): number { public get lastWriteEpoch(): number {
return this._lastWriteEpoch; return this._lastWriteEpoch;
} }
/** /**
* 创建组件实例 * @zh 创建组件实例自动分配唯一ID
* * @en Create component instance, automatically assigns unique ID
* 自动分配唯一ID给组件。
*/ */
constructor() { constructor() {
this.id = Component.idGenerator++; this.id = Component.idGenerator++;
} }
/** /**
* 标记组件为已修改 * @zh 标记组件为已修改
* @en Mark component as modified
* *
* 调用此方法会更新组件的 lastWriteEpoch 为当前帧的 epoch。 * @zh 调用此方法会更新组件的 lastWriteEpoch 为当前帧的 epoch。
* 系统可以通过比较 lastWriteEpoch 和上次检查的 epoch 来判断组件是否发生变更。 * 系统可以通过比较 lastWriteEpoch 和上次检查的 epoch 来判断组件是否发生变更。
* * @en Calling this method updates the component's lastWriteEpoch to the current frame's epoch.
* Mark component as modified.
* 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. * 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 * @example
* ```typescript * ```typescript
* // 在修改组件数据后调用 * // @zh 在修改组件数据后调用 | @en Call after modifying component data
* velocity.x = 10; * velocity.x = 10;
* velocity.markDirty(scene.epochManager.current); * 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
* *
* 当组件被添加到实体时调用,可以在此方法中进行初始化操作。 * @zh 当组件被添加到实体时调用,可以在此方法中进行初始化操作。
*
* @remarks
* 这是一个生命周期钩子,用于组件的初始化逻辑。 * 这是一个生命周期钩子,用于组件的初始化逻辑。
* 虽然保留此方法,但建议将复杂的初始化逻辑放在 System 中处理。 * 虽然保留此方法,但建议将复杂的初始化逻辑放在 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 {} public onAddedToEntity(): void {}
/** /**
* 组件从实体移除时的回调 * @zh 组件从实体移除时的回调
* @en Callback when component is removed from an entity
* *
* 当组件从实体中移除时调用,可以在此方法中进行清理操作。 * @zh 当组件从实体中移除时调用,可以在此方法中进行清理操作。
*
* @remarks
* 这是一个生命周期钩子,用于组件的清理逻辑。 * 这是一个生命周期钩子,用于组件的清理逻辑。
* 虽然保留此方法,但建议将复杂的清理逻辑放在 System 中处理。 * 虽然保留此方法,但建议将复杂的清理逻辑放在 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 {} public onRemovedFromEntity(): void {}
/** /**
* 组件反序列化后的回调 * @zh 组件反序列化后的回调
* @en Callback after component deserialization
* *
* 当组件从场景文件加载或快照恢复后调用,可以在此方法中恢复运行时数据。 * @zh 当组件从场景文件加载或快照恢复后调用,可以在此方法中恢复运行时数据。
*
* @remarks
* 这是一个生命周期钩子,用于恢复无法序列化的运行时数据。 * 这是一个生命周期钩子,用于恢复无法序列化的运行时数据。
* 例如:从图片路径重新加载图片尺寸信息,重建缓存等。 * 例如:从图片路径重新加载图片尺寸信息,重建缓存等。
* @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 * @example
* ```typescript * ```typescript
@@ -149,7 +151,8 @@ export abstract class Component implements IComponent {
* *
* public async onDeserialized(): Promise<void> { * public async onDeserialized(): Promise<void> {
* if (this.tilesetImage) { * if (this.tilesetImage) {
* // 重新加载 tileset 图片并恢复运行时数据 * // @zh 重新加载 tileset 图片并恢复运行时数据
* // @en Reload tileset image and restore runtime data
* const img = await loadImage(this.tilesetImage); * const img = await loadImage(this.tilesetImage);
* this.setTilesetInfo(img.width, img.height, ...); * this.setTilesetInfo(img.width, img.height, ...);
* } * }

View File

@@ -9,157 +9,156 @@ import type { IScene } from './IScene';
import { EntityHandle, NULL_HANDLE } from './Core/EntityHandle'; import { EntityHandle, NULL_HANDLE } from './Core/EntityHandle';
/** /**
* 组件活跃状态变化接口 * @zh 组件活跃状态变化接口
* @en Interface for component active state change
*/ */
interface IActiveChangeable { interface IActiveChangeable {
onActiveChanged(): void; 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 { export function compareEntities(a: Entity, b: Entity): number {
/** return a.updateOrder - b.updateOrder || a.id - b.id;
* 比较两个实体
*
* @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;
}
} }
/** /**
* 游戏实体类 * @zh 游戏实体类
* @en Game entity class
* *
* ECS架构中的实体Entity作为组件的容器。 * @zh ECS架构中的实体Entity作为组件的容器。
* 实体本身不包含游戏逻辑,所有功能都通过组件来实现。 * 实体本身不包含游戏逻辑,所有功能都通过组件来实现。
*
* 层级关系通过 HierarchyComponent 和 HierarchySystem 管理, * 层级关系通过 HierarchyComponent 和 HierarchySystem 管理,
* 而非 Entity 内置属性,符合 ECS 组合原则。 * 而非 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 * @example
* ```typescript * ```typescript
* // 创建实体 * // @zh 创建实体 | @en Create entity
* const entity = scene.createEntity("Player"); * const entity = scene.createEntity("Player");
* *
* // 添加组件 * // @zh 添加组件 | @en Add component
* const healthComponent = entity.addComponent(new HealthComponent(100)); * const healthComponent = entity.addComponent(new HealthComponent(100));
* *
* // 获取组件 * // @zh 获取组件 | @en Get component
* const health = entity.getComponent(HealthComponent); * const health = entity.getComponent(HealthComponent);
* *
* // 层级关系使用 HierarchySystem * // @zh 层级关系使用 HierarchySystem | @en Use HierarchySystem for hierarchy
* const hierarchySystem = scene.getSystem(HierarchySystem); * const hierarchySystem = scene.getSystem(HierarchySystem);
* hierarchySystem.setParent(childEntity, parentEntity); * hierarchySystem.setParent(childEntity, parentEntity);
* ``` * ```
*/ */
export class Entity { export class Entity {
/** /**
* Entity专用日志器 * @zh Entity专用日志器
* @en Entity logger
*/ */
private static _logger = createLogger('Entity'); private static _logger = createLogger('Entity');
/** /**
* 实体比较器实例 * @zh 实体名称
*/ * @en Entity name
public static entityComparer: EntityComparer = new EntityComparer();
/**
* 实体名称
*/ */
public name: string; public name: string;
/** /**
* 实体唯一标识符(运行时 ID * @zh 实体唯一标识符运行时ID,用于快速查找
* * @en Unique entity identifier (runtime ID) for fast lookups
* Runtime identifier for fast lookups.
*/ */
public readonly id: number; public readonly id: number;
/** /**
* 持久化唯一标识符GUID * @zh 持久化唯一标识符GUID
* @en Persistent unique identifier (GUID)
* *
* 用于序列化/反序列化时保持实体引用一致性 * @zh 用于序列化/反序列化时保持实体引用一致性,在场景保存和加载时保持不变
* 在场景保存和加载时保持不变。 * @en Used to maintain entity reference consistency during serialization/deserialization, remains stable across save/load cycles
*
* Persistent identifier for serialization.
* Remains stable across save/load cycles.
*/ */
public readonly persistentId: string; public readonly persistentId: string;
/** /**
* 轻量级实体句柄 * @zh 轻量级实体句柄
* @en Lightweight entity handle
* *
* 数值类型的实体标识符,包含索引和代数信息。 * @zh 数值类型的实体标识符,包含索引和代数信息。
* 用于高性能场景下替代对象引用,支持 Archetype 存储等优化。 * 用于高性能场景下替代对象引用,支持 Archetype 存储等优化。
* * @en Numeric identifier containing index and generation.
* Lightweight entity handle.
* Numeric identifier containing index and generation.
* Used for high-performance scenarios instead of object references, * Used for high-performance scenarios instead of object references,
* supports Archetype storage optimizations. * supports Archetype storage optimizations.
*/ */
private _handle: EntityHandle = NULL_HANDLE; private _handle: EntityHandle = NULL_HANDLE;
/** /**
* 所属场景引用 * @zh 所属场景引用
* @en Reference to the owning scene
*/ */
public scene: IScene | null = null; public scene: IScene | null = null;
/** /**
* 销毁状态标志 * @zh 销毁状态标志
* @en Destroyed state flag
*/ */
private _isDestroyed: boolean = false; private _isDestroyed: boolean = false;
/** /**
* 激活状态 * @zh 激活状态
* @en Active state
*/ */
private _active: boolean = true; private _active: boolean = true;
/** /**
* 实体标签 * @zh 实体标签,用于分类和查询
* @en Entity tag for categorization and querying
*/ */
private _tag: number = 0; private _tag: number = 0;
/** /**
* 启用状态 * @zh 启用状态
* @en Enabled state
*/ */
private _enabled: boolean = true; private _enabled: boolean = true;
/** /**
* 更新顺序 * @zh 更新顺序
* @en Update order
*/ */
private _updateOrder: number = 0; private _updateOrder: number = 0;
/** /**
* 组件位掩码用于快速 hasComponent 检查 * @zh 组件位掩码用于快速 hasComponent 检查
* @en Component bitmask for fast hasComponent checks
*/ */
private _componentMask: BitMask64Data = BitMask64Utils.clone(BitMask64Utils.ZERO); private _componentMask: BitMask64Data = BitMask64Utils.clone(BitMask64Utils.ZERO);
/** /**
* 懒加载的组件数组缓存 * @zh 懒加载的组件数组缓存
* @en Lazy-loaded component array cache
*/ */
private _componentCache: Component[] | null = null; private _componentCache: Component[] | null = null;
/** /**
* 生命周期策略 * @zh 生命周期策略,控制实体在场景切换时的行为
* * @en Lifecycle policy controlling entity behavior during scene transitions
* Lifecycle policy for scene transitions.
*/ */
private _lifecyclePolicy: EEntityLifecyclePolicy = EEntityLifecyclePolicy.SceneLocal; private _lifecyclePolicy: EEntityLifecyclePolicy = EEntityLifecyclePolicy.SceneLocal;
/** /**
* 构造函数 * @zh 构造函数
* @en Constructor
* *
* @param name - 实体名称 * @param name - @zh 实体名称 @en Entity name
* @param id - 实体唯一标识符(运行时 ID * @param id - @zh 实体唯一标识符运行时ID@en Unique entity identifier (runtime ID)
* @param persistentId - 持久化标识符(可选,用于反序列化时恢复) * @param persistentId - @zh 持久化标识符(可选,用于反序列化时恢复)@en Persistent identifier (optional, for deserialization)
*/ */
constructor(name: string, id: number, persistentId?: string) { constructor(name: string, id: number, persistentId?: string) {
this.name = name; this.name = name;
@@ -168,44 +167,38 @@ export class Entity {
} }
/** /**
* 获取生命周期策略 * @zh 获取生命周期策略
* * @en Get lifecycle policy
* Get lifecycle policy.
*/ */
public get lifecyclePolicy(): EEntityLifecyclePolicy { public get lifecyclePolicy(): EEntityLifecyclePolicy {
return this._lifecyclePolicy; return this._lifecyclePolicy;
} }
/** /**
* 检查实体是否为持久化实体 * @zh 检查实体是否为持久化实体(跨场景保留)
* * @en Check if entity is persistent (survives scene transitions)
* Check if entity is persistent (survives scene transitions).
*/ */
public get isPersistent(): boolean { public get isPersistent(): boolean {
return this._lifecyclePolicy === EEntityLifecyclePolicy.Persistent; return this._lifecyclePolicy === EEntityLifecyclePolicy.Persistent;
} }
/** /**
* 获取实体句柄 * @zh 获取实体句柄
* @en Get entity handle
* *
* 返回轻量级数值句柄,用于高性能场景。 * @zh 返回轻量级数值句柄,用于高性能场景。如果实体尚未分配句柄,返回 NULL_HANDLE。
* 如果实体尚未分配句柄,返回 NULL_HANDLE。 * @en Returns lightweight numeric handle for high-performance scenarios. Returns NULL_HANDLE if entity has no handle assigned.
*
* Get entity handle.
* Returns lightweight numeric handle for high-performance scenarios.
* Returns NULL_HANDLE if entity has no handle assigned.
*/ */
public get handle(): EntityHandle { public get handle(): EntityHandle {
return this._handle; return this._handle;
} }
/** /**
* 设置实体句柄(内部使用) * @zh 设置实体句柄(内部使用)
* @en Set entity handle (internal use)
* *
* 此方法供 Scene 在创建实体时调用 * @zh 此方法供 Scene 在创建实体时调用
* * @en Called by Scene when creating entities
* Set entity handle (internal use).
* Called by Scene when creating entities.
* *
* @internal * @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). * @returns @zh this支持链式调用 @en Returns this for chaining
* Persistent entities are automatically migrated to the new scene.
*
* @returns this支持链式调用 | Returns this for chaining
* *
* @example * @example
* ```typescript * ```typescript
@@ -236,14 +228,10 @@ export class Entity {
} }
/** /**
* 设置实体为场景本地(随场景销毁) * @zh 设置实体为场景本地(随场景销毁),恢复默认行为
* @en Mark entity as scene-local (destroyed with scene), restores default behavior
* *
* 将实体恢复为默认行为。 * @returns @zh this支持链式调用 @en Returns this for chaining
*
* Mark entity as scene-local (destroyed with scene).
* Restores default behavior.
*
* @returns this支持链式调用 | Returns this for chaining
*/ */
public setSceneLocal(): this { public setSceneLocal(): this {
this._lifecyclePolicy = EEntityLifecyclePolicy.SceneLocal; this._lifecyclePolicy = EEntityLifecyclePolicy.SceneLocal;
@@ -251,18 +239,21 @@ export class Entity {
} }
/** /**
* 获取销毁状态 * @zh 获取销毁状态
* @returns 如果实体已被销毁则返回true * @en Get destroyed state
*
* @returns @zh 如果实体已被销毁则返回true @en Returns true if entity has been destroyed
*/ */
public get isDestroyed(): boolean { public get isDestroyed(): boolean {
return this._isDestroyed; return this._isDestroyed;
} }
/** /**
* 设置销毁状态(内部使用) * @zh 设置销毁状态(内部使用)
* @en Set destroyed state (internal use)
* *
* 此方法供Scene和批量操作使用以提高性能。 * @zh 此方法供Scene和批量操作使用以提高性能。不应在普通业务逻辑中调用应使用destroy()方法
* 不应在普通业务逻辑中调用,应使用destroy()方法。 * @en Used by Scene and batch operations for performance. Should not be called in normal business logic, use destroy() instead
* *
* @internal * @internal
*/ */
@@ -271,8 +262,10 @@ export class Entity {
} }
/** /**
* 获取组件数组(懒加载) * @zh 获取组件数组(懒加载)
* @returns 只读的组件数组 * @en Get component array (lazy-loaded)
*
* @returns @zh 只读的组件数组 @en Readonly component array
*/ */
public get components(): readonly Component[] { public get components(): readonly Component[] {
if (this._componentCache === null) { if (this._componentCache === null) {
@@ -282,7 +275,8 @@ export class Entity {
} }
/** /**
* 从存储重建组件缓存 * @zh 从存储重建组件缓存
* @en Rebuild component cache from storage
*/ */
private _rebuildComponentCache(): void { private _rebuildComponentCache(): void {
const components: Component[] = []; 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 { public get tag(): number {
return this._tag; return this._tag;
} }
/** /**
* 设置实体标签 * @zh 设置实体标签
* @en Set entity tag
* *
* @param value - 新的标签值 * @param value - @zh 新的标签值 @en New tag value
*/ */
public set tag(value: number) { public set tag(value: number) {
this._tag = value; this._tag = value;
} }
/** /**
* 获取启用状态 * @zh 获取启用状态
* @en Get enabled state
* *
* @returns 如果实体已启用则返回true * @returns @zh 如果实体已启用则返回true @en Returns true if entity is enabled
*/ */
public get enabled(): boolean { public get enabled(): boolean {
return this._enabled; return this._enabled;
} }
/** /**
* 设置启用状态 * @zh 设置启用状态
* @en Set enabled state
* *
* @param value - 新的启用状态 * @param value - @zh 新的启用状态 @en New enabled state
*/ */
public set enabled(value: boolean) { public set enabled(value: boolean) {
this._enabled = value; this._enabled = value;
} }
/** /**
* 获取更新顺序 * @zh 获取更新顺序
* @en Get update order
* *
* @returns 实体的更新顺序值 * @returns @zh 实体的更新顺序值 @en Entity's update order value
*/ */
public get updateOrder(): number { public get updateOrder(): number {
return this._updateOrder; return this._updateOrder;
} }
/** /**
* 设置更新顺序 * @zh 设置更新顺序
* @en Set update order
* *
* @param value - 新的更新顺序值 * @param value - @zh 新的更新顺序值 @en New update order value
*/ */
public set updateOrder(value: number) { public set updateOrder(value: number) {
this._updateOrder = value; this._updateOrder = value;
} }
/** /**
* 获取组件位掩码 * @zh 获取组件位掩码
* @en Get component bitmask
* *
* @returns 实体的组件位掩码 * @returns @zh 实体的组件位掩码 @en Entity's component bitmask
*/ */
public get componentMask(): BitMask64Data { public get componentMask(): BitMask64Data {
return this._componentMask; return this._componentMask;
} }
/** /**
* 创建并添加组件 * @zh 创建并添加组件
* @en Create and add component
* *
* @param componentType - 组件类型构造函数 * @param componentType - @zh 组件类型构造函数 @en Component type constructor
* @param args - 组件构造函数参数 * @param args - @zh 组件构造函数参数 @en Component constructor arguments
* @returns 创建的组件实例 * @returns @zh 创建的组件实例 @en Created component instance
* *
* @example * @example
* ```typescript * ```typescript
@@ -418,51 +420,30 @@ export class Entity {
return this.addComponent(component); return this.addComponent(component);
} }
/**
* 内部添加组件方法(不进行重复检查,用于初始化)
*
* @param component - 要添加的组件实例
* @returns 添加的组件实例
*/
private addComponentInternal<T extends Component>(component: T): T { private addComponentInternal<T extends Component>(component: T): T {
const componentType = component.constructor as ComponentType<T>; const componentType = component.constructor as ComponentType<T>;
// 更新位掩码(组件已通过 @ECSComponent 装饰器自动注册)
// Update bitmask (component already registered via @ECSComponent decorator)
const registry = this.scene?.componentRegistry ?? GlobalComponentRegistry; const registry = this.scene?.componentRegistry ?? GlobalComponentRegistry;
const componentMask = registry.getBitMask(componentType); const componentMask = registry.getBitMask(componentType);
BitMask64Utils.orInPlace(this._componentMask, componentMask); BitMask64Utils.orInPlace(this._componentMask, componentMask);
// 使缓存失效
this._componentCache = null; this._componentCache = null;
return component; 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 { private notifyQuerySystems(changedComponentType?: ComponentType): void {
if (this.scene && this.scene.querySystem) { if (!this.scene?.querySystem) return;
this.scene.querySystem.updateEntity(this);
this.scene.clearSystemEntityCaches(); this.scene.querySystem.updateEntity(this);
// 事件驱动:立即通知关心该组件的系统 | Event-driven: notify systems that care about this component this.scene.clearSystemEntityCaches();
if (this.scene.notifyEntityComponentChanged) { this.scene.notifyEntityComponentChanged?.(this, changedComponentType);
this.scene.notifyEntityComponentChanged(this, changedComponentType);
}
}
} }
/** /**
* 添加组件到实体 * @zh 添加组件到实体
* @en Add component to entity
* *
* @param component - 要添加的组件实例 * @param component - @zh 要添加的组件实例 @en Component instance to add
* @returns 添加的组件实例 * @returns @zh 添加的组件实例 @en Added component instance
* @throws {Error} 如果实体已存在该类型的组件 * @throws @zh 如果实体已存在该类型的组件 @en If entity already has this component type
* *
* @example * @example
* ```typescript * ```typescript
@@ -492,20 +473,15 @@ export class Entity {
this.scene.componentStorageManager.addComponent(this.id, component); this.scene.componentStorageManager.addComponent(this.id, component);
component.entityId = this.id; 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) { if (this.scene.isEditorMode) {
this.scene.queueDeferredComponentCallback(() => { this.scene.queueDeferredComponentCallback(() => component.onAddedToEntity());
component.onAddedToEntity();
});
} else { } else {
component.onAddedToEntity(); component.onAddedToEntity();
} }
if (this.scene && this.scene.eventSystem) { if (this.scene.eventSystem) {
this.scene.eventSystem.emitSync('component:added', { this.scene.eventSystem.emitSync('component:added', {
timestamp: Date.now(), timestamp: Date.now(),
source: 'Entity', source: 'Entity',
@@ -523,10 +499,11 @@ export class Entity {
} }
/** /**
* 获取指定类型的组件 * @zh 获取指定类型的组件
* @en Get component of specified type
* *
* @param type - 组件类型构造函数 * @param type - @zh 组件类型构造函数 @en Component type constructor
* @returns 组件实例如果不存在则返回null * @returns @zh 组件实例如果不存在则返回null @en Component instance, or null if not found
* *
* @example * @example
* ```typescript * ```typescript
@@ -553,10 +530,11 @@ export class Entity {
} }
/** /**
* 检查实体是否拥有指定类型的组件 * @zh 检查实体是否拥有指定类型的组件
* @en Check if entity has component of specified type
* *
* @param type - 组件类型构造函数 * @param type - @zh 组件类型构造函数 @en Component type constructor
* @returns 如果实体拥有该组件返回true否则返回false * @returns @zh 如果实体拥有该组件返回true @en Returns true if entity has the component
* *
* @example * @example
* ```typescript * ```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 type - @zh 组件类型构造函数 @en Component type constructor
* @param args - 组件构造函数参数(仅在创建新组件时使用) * @param args - @zh 组件构造函数参数(仅在创建新组件时使用)@en Constructor arguments (only used when creating new component)
* @returns 组件实例 * @returns @zh 组件实例 @en Component instance
* *
* @example * @example
* ```typescript * ```typescript
* // 确保实体拥有Position组件 * // @zh 确保实体拥有Position组件 | @en Ensure entity has Position component
* const position = entity.getOrCreateComponent(Position, 0, 0); * const position = entity.getOrCreateComponent(Position, 0, 0);
* position.x = 100; * 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. * @param components - @zh 要标记的组件 @en Components to mark
* Convenience method that auto-gets epoch from scene and marks components.
* Used for frame-level change detection system.
*
* @param components 要标记的组件 | Components to mark
* *
* @example * @example
* ```typescript * ```typescript
@@ -622,7 +599,7 @@ export class Entity {
* pos.x = 100; * pos.x = 100;
* entity.markDirty(pos); * entity.markDirty(pos);
* *
* // 或者标记多个组件 * // @zh 或者标记多个组件 | @en Or mark multiple components
* entity.markDirty(pos, vel); * 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 { public removeComponent(component: Component): void {
const componentType = component.constructor as ComponentType; const componentType = component.constructor as ComponentType;
@@ -651,29 +629,16 @@ export class Entity {
} }
const bitIndex = registry.getBitIndex(componentType); const bitIndex = registry.getBitIndex(componentType);
// 更新位掩码
BitMask64Utils.clearBit(this._componentMask, bitIndex); BitMask64Utils.clearBit(this._componentMask, bitIndex);
// 使缓存失效
this._componentCache = null; this._componentCache = null;
// 从Scene存储移除 this.scene?.componentStorageManager?.removeComponent(this.id, componentType);
if (this.scene?.componentStorageManager) { this.scene?.referenceTracker?.clearComponentReferences(component);
this.scene.componentStorageManager.removeComponent(this.id, componentType);
}
if (this.scene?.referenceTracker) {
this.scene.referenceTracker.clearComponentReferences(component);
}
if (component.onRemovedFromEntity) {
component.onRemovedFromEntity();
}
component.onRemovedFromEntity?.();
component.entityId = null; component.entityId = null;
if (this.scene && this.scene.eventSystem) { if (this.scene?.eventSystem) {
this.scene.eventSystem.emitSync('component:removed', { this.scene.eventSystem.emitSync('component:removed', {
timestamp: Date.now(), timestamp: Date.now(),
source: 'Entity', source: 'Entity',
@@ -689,10 +654,11 @@ export class Entity {
} }
/** /**
* 移除指定类型的组件 * @zh 移除指定类型的组件
* @en Remove component by type
* *
* @param type - 组件类型 * @param type - @zh 组件类型 @en Component type
* @returns 被移除的组件实例或null * @returns @zh 被移除的组件实例或null @en Removed component instance or null
*/ */
public removeComponentByType<T extends Component>(type: ComponentType<T>): T | null { public removeComponentByType<T extends Component>(type: ComponentType<T>): T | null {
const component = this.getComponent(type); const component = this.getComponent(type);
@@ -704,24 +670,17 @@ export class Entity {
} }
/** /**
* 移除所有组件 * @zh 移除所有组件
* @en Remove all components
*/ */
public removeAllComponents(): void { public removeAllComponents(): void {
const componentsToRemove = [...this.components]; const componentsToRemove = [...this.components];
// 清除位掩码
BitMask64Utils.clear(this._componentMask); BitMask64Utils.clear(this._componentMask);
// 使缓存失效
this._componentCache = null; this._componentCache = null;
for (const component of componentsToRemove) { for (const component of componentsToRemove) {
const componentType = component.constructor as ComponentType; const componentType = component.constructor as ComponentType;
this.scene?.componentStorageManager?.removeComponent(this.id, componentType);
if (this.scene?.componentStorageManager) {
this.scene.componentStorageManager.removeComponent(this.id, componentType);
}
component.onRemovedFromEntity(); component.onRemovedFromEntity();
} }
@@ -729,10 +688,11 @@ export class Entity {
} }
/** /**
* 批量添加组件 * @zh 批量添加组件
* @en Add multiple components
* *
* @param components - 要添加的组件数组 * @param components - @zh 要添加的组件数组 @en Array of components to add
* @returns 添加的组件数组 * @returns @zh 添加的组件数组 @en Array of added components
*/ */
public addComponents<T extends Component>(components: T[]): T[] { public addComponents<T extends Component>(components: T[]): T[] {
const addedComponents: T[] = []; const addedComponents: T[] = [];
@@ -828,10 +788,11 @@ export class Entity {
} }
/** /**
* 销毁实体 * @zh 销毁实体
* @en Destroy entity
* *
* 移除所有组件并标记为已销毁。 * @zh 移除所有组件并标记为已销毁。层级关系的清理由 HierarchySystem 处理。
* 层级关系的清理由 HierarchySystem 处理。 * @en Removes all components and marks as destroyed. Hierarchy cleanup is handled by HierarchySystem.
*/ */
public destroy(): void { public destroy(): void {
if (this._isDestroyed) { if (this._isDestroyed) {
@@ -859,13 +820,14 @@ export class Entity {
} }
/** /**
* 比较实体 * @zh 比较实体优先级
* @en Compare entity priority
* *
* @param other - 另一个实体 * @param other - @zh 另一个实体 @en Another entity
* @returns 比较结果 * @returns @zh 比较结果 @en Comparison result
*/ */
public compareTo(other: Entity): number { public compareTo(other: Entity): number {
return EntityComparer.prototype.compare(this, other); return compareEntities(this, other);
} }
/** /**

View File

@@ -370,21 +370,29 @@ export type ISceneFactory<T extends IScene> = {
} }
/** /**
* 场景配置接口 * @zh 场景配置接口
* Scene configuration interface * @en Scene configuration interface
*/ */
export type ISceneConfig = { export type ISceneConfig = {
/** /**
* 场景名称 * @zh 场景名称
* Scene name * @en Scene name
*/ */
name?: string; name?: string;
/** /**
* 是否从全局注册表继承组件类型 * @zh 是否从全局注册表继承组件类型
* Whether to inherit component types from global registry * @en Whether to inherit component types from global registry
* *
* @default true * @default true
*/ */
inheritGlobalRegistry?: boolean; inheritGlobalRegistry?: boolean;
/**
* @zh 系统最大错误次数,超过此阈值后系统将被自动禁用
* @en Maximum error count for systems, systems exceeding this threshold will be auto-disabled
*
* @default 10
*/
maxSystemErrorCount?: number;
} }

View File

@@ -212,11 +212,10 @@ export class Scene implements IScene {
private _systemErrorCount: Map<EntitySystem, number> = new Map(); 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); const systems = this._filterEntitySystems(allServices);
try { try {
// 使用 SystemScheduler 进行依赖排序
this._systemScheduler.markDirty(); this._systemScheduler.markDirty();
return this._systemScheduler.getAllSortedSystems(systems); return this._systemScheduler.getAllSortedSystems(systems);
} catch (error) { } catch (error) {
if (error instanceof CycleDependencyError) { if (error instanceof CycleDependencyError) {
// 循环依赖错误,记录警告并回退到 updateOrder 排序
this.logger.error( this.logger.error(
`[Scene] 系统存在循环依赖,回退到 updateOrder 排序 | Cycle dependency detected, falling back to updateOrder sort`, `[Scene] 系统存在循环依赖,回退到 updateOrder 排序 | Cycle dependency detected, falling back to updateOrder sort`,
error.involvedNodes error.involvedNodes
@@ -308,7 +305,6 @@ export class Scene implements IScene {
} else { } else {
this.logger.error(`[Scene] 系统排序失败 | System sorting failed`, error); this.logger.error(`[Scene] 系统排序失败 | System sorting failed`, error);
} }
// 回退到简单的 updateOrder 排序
return this._sortSystemsByUpdateOrder(systems); return this._sortSystemsByUpdateOrder(systems);
} }
} }
@@ -378,20 +374,18 @@ export class Scene implements IScene {
} }
/** /**
* 创建场景实例 * @zh 创建场景实例
* Create scene instance * @en Create scene instance
*
* @param config - @zh 场景配置 @en Scene configuration
*/ */
constructor(config?: ISceneConfig) { constructor(config?: ISceneConfig) {
this.entities = new EntityList(this); this.entities = new EntityList(this);
this.identifierPool = new IdentifierPool(); this.identifierPool = new IdentifierPool();
this.componentStorageManager = new ComponentStorageManager(); this.componentStorageManager = new ComponentStorageManager();
// 创建场景级别的组件注册表
// Create scene-level component registry
this.componentRegistry = new ComponentRegistry(); this.componentRegistry = new ComponentRegistry();
// 从全局注册表继承框架组件(默认启用)
// Inherit framework components from global registry (enabled by default)
if (config?.inheritGlobalRegistry !== false) { if (config?.inheritGlobalRegistry !== false) {
this.componentRegistry.cloneFrom(GlobalComponentRegistry); this.componentRegistry.cloneFrom(GlobalComponentRegistry);
} }
@@ -402,6 +396,7 @@ export class Scene implements IScene {
this.handleManager = new EntityHandleManager(); this.handleManager = new EntityHandleManager();
this._services = new ServiceContainer(); this._services = new ServiceContainer();
this.logger = createLogger('Scene'); this.logger = createLogger('Scene');
this._maxErrorCount = config?.maxSystemErrorCount ?? 10;
if (config?.name) { if (config?.name) {
this.name = 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. * In editor mode, this method also executes all deferred component lifecycle callbacks.
*/ */
public begin() { public begin() {
// 标记场景已开始运行
this._didSceneBegin = true; this._didSceneBegin = true;
// 执行所有延迟的组件生命周期回调 | Execute all deferred component lifecycle callbacks
if (this._deferredComponentCallbacks.length > 0) { if (this._deferredComponentCallbacks.length > 0) {
for (const callback of this._deferredComponentCallbacks) { for (const callback of this._deferredComponentCallbacks) {
try { try {
@@ -477,11 +470,9 @@ export class Scene implements IScene {
this.logger.error('Error executing deferred component callback:', error); this.logger.error('Error executing deferred component callback:', error);
} }
} }
// 清空队列 | Clear the queue
this._deferredComponentCallbacks = []; this._deferredComponentCallbacks = [];
} }
// 调用onStart方法
this.onStart(); this.onStart();
} }
@@ -501,38 +492,18 @@ export class Scene implements IScene {
* - 系统清理:在 System.onDestroy() 中处理(实体已被清理) * - 系统清理:在 System.onDestroy() 中处理(实体已被清理)
*/ */
public end() { public end() {
// 标记场景已结束运行
this._didSceneBegin = false; this._didSceneBegin = false;
// 先调用用户的卸载方法,此时用户可以访问实体和系统进行清理
this.unload(); this.unload();
// 移除所有实体
this.entities.removeAllEntities(); this.entities.removeAllEntities();
// 清理查询系统中的实体引用和缓存
this.querySystem.setEntities([]); this.querySystem.setEntities([]);
// 清空组件存储
this.componentStorageManager.clear(); this.componentStorageManager.clear();
// 清空服务容器会调用所有服务的dispose方法包括所有EntitySystem
// 系统的 onDestroy 回调会在这里被触发
this._services.clear(); this._services.clear();
// 清空系统缓存
this._cachedSystems = null; this._cachedSystems = null;
this._systemsOrderDirty = true; this._systemsOrderDirty = true;
// 清空组件索引 | Clear component indices
this._componentIdToSystems.clear(); this._componentIdToSystems.clear();
this._globalNotifySystems.clear(); this._globalNotifySystems.clear();
// 清空句柄映射并重置句柄管理器 | Clear handle mapping and reset handle manager
this._handleToEntity.clear(); this._handleToEntity.clear();
this.handleManager.reset(); this.handleManager.reset();
// 重置 epoch 管理器 | Reset epoch manager
this.epochManager.reset(); this.epochManager.reset();
} }
@@ -540,92 +511,57 @@ export class Scene implements IScene {
* 更新场景 * 更新场景
*/ */
public update() { public update() {
// 递增帧计数(用于变更检测) | Increment epoch (for change detection)
this.epochManager.increment(); this.epochManager.increment();
// 开始性能采样帧
ProfilerSDK.beginFrame(); ProfilerSDK.beginFrame();
const frameHandle = ProfilerSDK.beginSample('Scene.update', ProfileCategory.ECS); const frameHandle = ProfilerSDK.beginSample('Scene.update', ProfileCategory.ECS);
try { try {
ComponentPoolManager.getInstance().update(); ComponentPoolManager.getInstance().update();
this.entities.updateLists(); this.entities.updateLists();
const systems = this.systems; const systems = this.systems;
// Update 阶段 this._runSystemPhase(systems, 'update', 'Systems.update');
const updateHandle = ProfilerSDK.beginSample('Systems.update', ProfileCategory.ECS); this._runSystemPhase(systems, 'lateUpdate', 'Systems.lateUpdate');
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.flushCommandBuffers(systems); this.flushCommandBuffers(systems);
} finally { } finally {
ProfilerSDK.endSample(frameHandle); ProfilerSDK.endSample(frameHandle);
// 结束性能采样帧
ProfilerSDK.endFrame(); ProfilerSDK.endFrame();
} }
} }
/** /**
* 检查系统是否应该运行 * @zh 执行系统的指定阶段
* Check if a system should run * @en Run specified phase for all systems
*
* @param system 要检查的系统 | System to check
* @returns 是否应该运行 | Whether it should run
*/ */
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 { private _shouldSystemRun(system: EntitySystem): boolean {
// 系统必须启用 if (!system.enabled) return false;
// System must be enabled if (!this.isEditorMode) return true;
if (!system.enabled) {
return false;
}
// 非编辑模式下,所有启用的系统都运行
// 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); const metadata = getSystemInstanceMetadata(system);
return metadata?.runInEditMode !== false; return metadata?.runInEditMode !== false;
} }
@@ -685,11 +621,8 @@ export class Scene implements IScene {
public createEntity(name: string) { public createEntity(name: string) {
const entity = new Entity(name, this.identifierPool.checkOut()); const entity = new Entity(name, this.identifierPool.checkOut());
// 分配轻量级句柄 | Assign lightweight handle
const handle = this.handleManager.create(); const handle = this.handleManager.create();
entity.setHandle(handle); entity.setHandle(handle);
// 添加到句柄映射 | Add to handle mapping
this._handleToEntity.set(handle, entity); this._handleToEntity.set(handle, entity);
this.eventSystem.emitSync('entity:created', { entityName: name, entity, scene: this }); 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) * @param changedComponentType 变化的组件类型(可选) | The changed component type (optional)
*/ */
public notifyEntityComponentChanged(entity: Entity, changedComponentType?: ComponentType): void { public notifyEntityComponentChanged(entity: Entity, changedComponentType?: ComponentType): void {
// 已通知的系统集合,避免重复通知 | Set of notified systems to avoid duplicates
const notifiedSystems = new Set<EntitySystem>(); const notifiedSystems = new Set<EntitySystem>();
// 如果提供了组件类型,使用索引优化 | If component type provided, use index optimization
if (changedComponentType && this.componentRegistry.isRegistered(changedComponentType)) { if (changedComponentType && this.componentRegistry.isRegistered(changedComponentType)) {
const componentId = this.componentRegistry.getBitIndex(changedComponentType); const componentId = this.componentRegistry.getBitIndex(changedComponentType);
const interestedSystems = this._componentIdToSystems.get(componentId); 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) { for (const system of this._globalNotifySystems) {
if (!notifiedSystems.has(system)) { if (!notifiedSystems.has(system)) {
system.handleEntityComponentChanged(entity); system.handleEntityComponentChanged(entity);
@@ -746,7 +676,6 @@ export class Scene implements IScene {
} }
} }
// 如果没有提供组件类型,回退到遍历所有系统 | Fallback to all systems if no component type
if (!changedComponentType) { if (!changedComponentType) {
for (const system of this.systems) { for (const system of this.systems) {
if (!notifiedSystems.has(system)) { if (!notifiedSystems.has(system)) {
@@ -772,35 +701,29 @@ export class Scene implements IScene {
return; return;
} }
// nothing 匹配器不需要索引 | Nothing matcher doesn't need indexing
if (matcher.isNothing()) { if (matcher.isNothing()) {
return; return;
} }
const condition = matcher.getCondition(); 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) { if (condition.none.length > 0 || condition.tag !== undefined || condition.name !== undefined) {
this._globalNotifySystems.add(system); this._globalNotifySystems.add(system);
} }
// 空匹配器(匹配所有实体)加入全局通知 | Empty matcher (matches all) goes to global
if (matcher.isEmpty()) { if (matcher.isEmpty()) {
this._globalNotifySystems.add(system); this._globalNotifySystems.add(system);
return; return;
} }
// 索引 all 条件中的组件 | Index components in all condition
for (const componentType of condition.all) { for (const componentType of condition.all) {
this.addSystemToComponentIndex(componentType, system); this.addSystemToComponentIndex(componentType, system);
} }
// 索引 any 条件中的组件 | Index components in any condition
for (const componentType of condition.any) { for (const componentType of condition.any) {
this.addSystemToComponentIndex(componentType, system); this.addSystemToComponentIndex(componentType, system);
} }
// 索引单组件查询 | Index single component query
if (condition.component) { if (condition.component) {
this.addSystemToComponentIndex(condition.component, system); this.addSystemToComponentIndex(condition.component, system);
} }
@@ -834,10 +757,8 @@ export class Scene implements IScene {
* @param system 要移除的系统 | The system to remove * @param system 要移除的系统 | The system to remove
*/ */
private removeSystemFromIndex(system: EntitySystem): void { private removeSystemFromIndex(system: EntitySystem): void {
// 从全局通知列表移除 | Remove from global notify list
this._globalNotifySystems.delete(system); this._globalNotifySystems.delete(system);
// 从所有组件索引中移除 | Remove from all component indices
for (const systems of this._componentIdToSystems.values()) { for (const systems of this._componentIdToSystems.values()) {
systems.delete(system); systems.delete(system);
} }
@@ -852,15 +773,12 @@ export class Scene implements IScene {
this.entities.add(entity); this.entities.add(entity);
entity.scene = this; entity.scene = this;
// 将实体添加到查询系统(可延迟缓存清理)
this.querySystem.addEntity(entity, deferCacheClear); this.querySystem.addEntity(entity, deferCacheClear);
// 清除系统缓存以确保系统能及时发现新实体
if (!deferCacheClear) { if (!deferCacheClear) {
this.clearSystemEntityCaches(); this.clearSystemEntityCaches();
} }
// 触发实体添加事件
this.eventSystem.emitSync('entity:added', { entity, scene: this }); this.eventSystem.emitSync('entity:added', { entity, scene: this });
return entity; return entity;
@@ -875,30 +793,22 @@ export class Scene implements IScene {
public createEntities(count: number, namePrefix: string = 'Entity'): Entity[] { public createEntities(count: number, namePrefix: string = 'Entity'): Entity[] {
const entities: Entity[] = []; const entities: Entity[] = [];
// 批量创建实体对象,不立即添加到系统
for (let i = 0; i < count; i++) { for (let i = 0; i < count; i++) {
const entity = new Entity(`${namePrefix}_${i}`, this.identifierPool.checkOut()); const entity = new Entity(`${namePrefix}_${i}`, this.identifierPool.checkOut());
entity.scene = this; entity.scene = this;
// 分配轻量级句柄 | Assign lightweight handle
const handle = this.handleManager.create(); const handle = this.handleManager.create();
entity.setHandle(handle); entity.setHandle(handle);
// 添加到句柄映射 | Add to handle mapping
this._handleToEntity.set(handle, entity); this._handleToEntity.set(handle, entity);
entities.push(entity); entities.push(entity);
} }
// 批量添加到实体列表
for (const entity of entities) { for (const entity of entities) {
this.entities.add(entity); this.entities.add(entity);
} }
// 批量添加到查询系统(无重复检查,性能最优)
this.querySystem.addEntitiesUnchecked(entities); this.querySystem.addEntitiesUnchecked(entities);
// 批量触发事件(可选,减少事件开销)
this.eventSystem.emitSync('entities:batch_added', { entities, scene: this, count }); this.eventSystem.emitSync('entities:batch_added', { entities, scene: this, count });
return entities; return entities;
@@ -922,7 +832,6 @@ export class Scene implements IScene {
this.entities.remove(entity); this.entities.remove(entity);
this.querySystem.removeEntity(entity); this.querySystem.removeEntity(entity);
// 销毁句柄并从映射中移除 | Destroy handle and remove from mapping
if (isValidHandle(entity.handle)) { if (isValidHandle(entity.handle)) {
this._handleToEntity.delete(entity.handle); this._handleToEntity.delete(entity.handle);
this.handleManager.destroy(entity.handle); this.handleManager.destroy(entity.handle);
@@ -1019,14 +928,9 @@ export class Scene implements IScene {
const persistentEntities = this.findPersistentEntities(); const persistentEntities = this.findPersistentEntities();
for (const entity of persistentEntities) { for (const entity of persistentEntities) {
// 从实体列表移除
this.entities.remove(entity); this.entities.remove(entity);
// 从查询系统移除
this.querySystem.removeEntity(entity); this.querySystem.removeEntity(entity);
entity.scene = null; // detach but preserve component data
// 清除场景引用(但保留组件数据)
entity.scene = null;
} }
return persistentEntities; return persistentEntities;
@@ -1045,23 +949,16 @@ export class Scene implements IScene {
*/ */
public receiveMigratedEntities(entities: Entity[]): void { public receiveMigratedEntities(entities: Entity[]): void {
for (const entity of entities) { for (const entity of entities) {
// 设置新场景引用
entity.scene = this; entity.scene = this;
// 添加到实体列表
this.entities.add(entity); this.entities.add(entity);
// 添加到查询系统
this.querySystem.addEntity(entity); this.querySystem.addEntity(entity);
// 重新注册组件到新场景的存储
for (const component of entity.components) { for (const component of entity.components) {
this.componentStorageManager.addComponent(entity.id, component); this.componentStorageManager.addComponent(entity.id, component);
this.referenceTracker?.registerEntityScene(entity.id, this); this.referenceTracker?.registerEntityScene(entity.id, this);
} }
} }
// 清除系统缓存
if (entities.length > 0) { if (entities.length > 0) {
this.clearSystemEntityCaches(); this.clearSystemEntityCaches();
} }
@@ -1218,10 +1115,7 @@ export class Scene implements IScene {
} }
system.scene = this; system.scene = this;
system.addOrder = this._systemAddCounter++; // for stable sorting
// 分配添加顺序,用于稳定排序 | Assign add order for stable sorting
system.addOrder = this._systemAddCounter++;
system.setPerformanceMonitor(this.performanceMonitor); system.setPerformanceMonitor(this.performanceMonitor);
const metadata = getSystemMetadata(constructor); const metadata = getSystemMetadata(constructor);
@@ -1233,16 +1127,11 @@ export class Scene implements IScene {
} }
this._services.registerInstance(constructor, system); this._services.registerInstance(constructor, system);
// 标记系统列表已变化
this.markSystemsOrderDirty(); this.markSystemsOrderDirty();
// 建立组件类型到系统的索引 | Build component type to system index
this.indexSystemByComponents(system); this.indexSystemByComponents(system);
injectProperties(system, this._services); injectProperties(system, this._services);
// 调试模式下自动包装系统方法以收集性能数据ProfilerSDK 启用时表示调试模式) // Auto-wrap system methods for profiling in debug mode
if (ProfilerSDK.isEnabled()) { if (ProfilerSDK.isEnabled()) {
AutoProfiler.wrapInstance(system, system.systemName, ProfileCategory.ECS); AutoProfiler.wrapInstance(system, system.systemName, ProfileCategory.ECS);
} }
@@ -1318,16 +1207,9 @@ export class Scene implements IScene {
public removeEntityProcessor(processor: EntitySystem): void { public removeEntityProcessor(processor: EntitySystem): void {
const constructor = processor.constructor as ServiceType<EntitySystem>; const constructor = processor.constructor as ServiceType<EntitySystem>;
// 从ServiceContainer移除
this._services.unregister(constructor); this._services.unregister(constructor);
// 标记系统列表已变化
this.markSystemsOrderDirty(); this.markSystemsOrderDirty();
// 从组件类型索引中移除 | Remove from component type index
this.removeSystemFromIndex(processor); this.removeSystemFromIndex(processor);
// 重置System状态
processor.reset(); processor.reset();
} }

View File

@@ -7,93 +7,125 @@ import { ServiceContainer } from '../Core/ServiceContainer';
const logger = createLogger('World'); const logger = createLogger('World');
/** /**
* 全局系统接口 * @zh 全局系统接口
* 全局系统是在World级别运行的系统不依赖特定Scene * @en Global system interface
*
* @zh 全局系统是在World级别运行的系统不依赖特定Scene
* @en Global systems run at World level and don't depend on specific Scene
*/ */
export type IGlobalSystem = { export type IGlobalSystem = {
/** /**
* 系统名称 * @zh 系统名称
* @en System name
*/ */
readonly name: string; readonly name: string;
/** /**
* 初始化系统 * @zh 初始化系统
* @en Initialize system
*/ */
initialize?(): void; initialize?(): void;
/** /**
* 更新系统 * @zh 更新系统
* @en Update system
*/ */
update(deltaTime?: number): void; update(deltaTime?: number): void;
/** /**
* 重置系统 * @zh 重置系统
* @en Reset system
*/ */
reset?(): void; reset?(): void;
/** /**
* 销毁系统 * @zh 销毁系统
* @en Destroy system
*/ */
destroy?(): void; destroy?(): void;
} }
/** /**
* World配置接口 * @zh World配置接口
* @en World configuration interface
*/ */
export type IWorldConfig = { export type IWorldConfig = {
/** /**
* World名称 * @zh World名称
* @en World name
*/ */
name?: string; name?: string;
/** /**
* 是否启用调试模式 * @zh 是否启用调试模式
* @en Enable debug mode
*/ */
debug?: boolean; debug?: boolean;
/** /**
* 最大Scene数量限制 * @zh 最大Scene数量限制
* @en Maximum number of scenes
*/ */
maxScenes?: number; maxScenes?: number;
/** /**
* 是否自动清理空Scene * @zh 是否自动清理空Scene
* @en Auto cleanup empty scenes
*/ */
autoCleanup?: boolean; 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级别的全局服务。 * 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: 应用程序全局服务 * - Core.services: 应用程序全局服务
* - World.services: World级别服务每个World独立 * - World.services: World级别服务每个World独立
* - Scene.services: Scene级别服务每个Scene独立 * - 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 * - 游戏房间每个房间一个World
* - 不同的游戏模式 * - 不同的游戏模式
* - 独立的模拟环境 * - 独立的模拟环境
* @en This design allows creating independent game worlds like:
* - Game rooms (one World per room)
* - Different game modes
* - Independent simulation environments
* *
* @example * @example
* ```typescript * ```typescript
* // 创建游戏房间的World * // @zh 创建游戏房间的World | @en Create World for game room
* const roomWorld = new World({ name: 'Room_001' }); * const roomWorld = new World({ name: 'Room_001' });
* *
* // 注册World级别的服务 * // @zh 注册World级别的服务 | @en Register World-level service
* roomWorld.services.registerSingleton(RoomManager); * roomWorld.services.registerSingleton(RoomManager);
* *
* // 在World中创建Scene * // @zh 在World中创建Scene | @en Create Scene in World
* const gameScene = roomWorld.createScene('game', new Scene()); * const gameScene = roomWorld.createScene('game', new Scene());
* const uiScene = roomWorld.createScene('ui', 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); * const roomManager = roomWorld.services.resolve(RoomManager);
* *
* // 更新整个World * // @zh 更新整个World | @en Update entire World
* roomWorld.update(deltaTime); * roomWorld.update(deltaTime);
* ``` * ```
*/ */
@@ -113,6 +145,7 @@ export class World {
debug: false, debug: false,
maxScenes: 10, maxScenes: 10,
autoCleanup: true, autoCleanup: true,
cleanupThresholdMs: 5 * 60 * 1000,
...config ...config
}; };
@@ -122,15 +155,20 @@ export class World {
} }
/** /**
* World级别的服务容器 * @zh World级别的服务容器用于管理World范围内的全局服务
* 用于管理World范围内的全局服务 * @en World-level service container for managing World-scoped global services
*/ */
public get services(): ServiceContainer { public get services(): ServiceContainer {
return this._services; 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 { public createScene<T extends IScene>(sceneName: string, sceneInstance?: T): T {
if (!sceneName || typeof sceneName !== 'string' || sceneName.trim() === '') { if (!sceneName || typeof sceneName !== 'string' || sceneName.trim() === '') {
@@ -145,34 +183,31 @@ export class World {
throw new Error(`World '${this.name}' 已达到最大Scene数量限制: ${this._config.maxScenes}`); throw new Error(`World '${this.name}' 已达到最大Scene数量限制: ${this._config.maxScenes}`);
} }
// 如果没有提供Scene实例创建默认Scene
const scene = sceneInstance || (new Scene() as unknown as T); const scene = sceneInstance || (new Scene() as unknown as T);
// 如果配置了 debug为 Scene 注册并启用 PerformanceMonitor
if (this._config.debug) { if (this._config.debug) {
const performanceMonitor = new PerformanceMonitor(); const performanceMonitor = new PerformanceMonitor();
performanceMonitor.enable(); performanceMonitor.enable();
scene.services.registerInstance(PerformanceMonitor, performanceMonitor); scene.services.registerInstance(PerformanceMonitor, performanceMonitor);
} }
// 设置Scene的标识 (scene as { id?: string }).id = sceneName;
if ('id' in scene) { if (!scene.name) {
(scene as any).id = sceneName;
}
if ('name' in scene && !scene.name) {
scene.name = sceneName; scene.name = sceneName;
} }
this._scenes.set(sceneName, scene); this._scenes.set(sceneName, scene);
// 初始化Scene
scene.initialize(); scene.initialize();
return scene; 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 { public removeScene(sceneName: string): boolean {
const scene = this._scenes.get(sceneName); const scene = this._scenes.get(sceneName);
@@ -180,12 +215,10 @@ export class World {
return false; return false;
} }
// 如果Scene正在运行先停止它
if (this._activeScenes.has(sceneName)) { if (this._activeScenes.has(sceneName)) {
this.setSceneActive(sceneName, false); this.setSceneActive(sceneName, false);
} }
// 清理Scene资源
scene.end(); scene.end();
this._scenes.delete(sceneName); 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 { public getScene<T extends IScene>(sceneName: string): T | null {
return this._scenes.get(sceneName) as T || null; return this._scenes.get(sceneName) as T || null;
@@ -237,14 +274,10 @@ export class World {
if (active) { if (active) {
this._activeScenes.add(sceneName); this._activeScenes.add(sceneName);
// 启动Scene scene.begin?.();
if (scene.begin) {
scene.begin();
}
logger.debug(`在World '${this.name}' 中激活Scene: ${sceneName}`); logger.debug(`在World '${this.name}' 中激活Scene: ${sceneName}`);
} else { } else {
this._activeScenes.delete(sceneName); this._activeScenes.delete(sceneName);
// 可选择性地停止Scene或者让它继续运行但不更新
logger.debug(`在World '${this.name}' 中停用Scene: ${sceneName}`); logger.debug(`在World '${this.name}' 中停用Scene: ${sceneName}`);
} }
} }
@@ -273,9 +306,7 @@ export class World {
} }
this._globalSystems.push(system); this._globalSystems.push(system);
if (system.initialize) { system.initialize?.();
system.initialize();
}
logger.debug(`在World '${this.name}' 中添加全局System: ${system.name}`); logger.debug(`在World '${this.name}' 中添加全局System: ${system.name}`);
return system; return system;
@@ -291,9 +322,7 @@ export class World {
} }
this._globalSystems.splice(index, 1); this._globalSystems.splice(index, 1);
if (system.reset) { system.reset?.();
system.reset();
}
logger.debug(`从World '${this.name}' 中移除全局System: ${system.name}`); logger.debug(`从World '${this.name}' 中移除全局System: ${system.name}`);
return true; return true;
@@ -321,11 +350,8 @@ export class World {
this._isActive = true; this._isActive = true;
// 启动所有全局System
for (const system of this._globalSystems) { for (const system of this._globalSystems) {
if (system.initialize) { system.initialize?.();
system.initialize();
}
} }
logger.info(`启动World: ${this.name}`); logger.info(`启动World: ${this.name}`);
@@ -339,16 +365,12 @@ export class World {
return; return;
} }
// 停止所有Scene
for (const sceneName of this._activeScenes) { for (const sceneName of this._activeScenes) {
this.setSceneActive(sceneName, false); this.setSceneActive(sceneName, false);
} }
// 重置所有全局System
for (const system of this._globalSystems) { for (const system of this._globalSystems) {
if (system.reset) { system.reset?.();
system.reset();
}
} }
this._isActive = false; this._isActive = false;
@@ -356,41 +378,38 @@ export class World {
} }
/** /**
* 更新World中的全局System * @zh 更新World中的全局System
* 注意此方法由Core.update()调用,不应直接调用 * @en Update global systems in World
*
* @internal Called by Core.update()
*/ */
public updateGlobalSystems(): void { public updateGlobalSystems(): void {
if (!this._isActive) { if (!this._isActive) {
return; return;
} }
// 更新全局System
for (const system of this._globalSystems) { for (const system of this._globalSystems) {
if (system.update) { system.update?.();
system.update();
}
} }
} }
/** /**
* 更新World中的所有激活Scene * @zh 更新World中的所有激活Scene
* 注意此方法由Core.update()调用,不应直接调用 * @en Update all active scenes in World
*
* @internal Called by Core.update()
*/ */
public updateScenes(): void { public updateScenes(): void {
if (!this._isActive) { if (!this._isActive) {
return; return;
} }
// 更新所有激活的Scene
for (const sceneName of this._activeScenes) { for (const sceneName of this._activeScenes) {
const scene = this._scenes.get(sceneName); const scene = this._scenes.get(sceneName);
if (scene && scene.update) { scene?.update?.();
scene.update();
}
} }
// 自动清理(如果启用) if (this._config.autoCleanup) {
if (this._config.autoCleanup && this.shouldAutoCleanup()) {
this.cleanup(); this.cleanup();
} }
} }
@@ -401,28 +420,22 @@ export class World {
public destroy(): void { public destroy(): void {
logger.info(`销毁World: ${this.name}`); logger.info(`销毁World: ${this.name}`);
// 停止World
this.stop(); this.stop();
// 销毁所有Scene for (const sceneName of Array.from(this._scenes.keys())) {
const sceneNames = Array.from(this._scenes.keys());
for (const sceneName of sceneNames) {
this.removeScene(sceneName); this.removeScene(sceneName);
} }
// 清理全局System
for (const system of this._globalSystems) { for (const system of this._globalSystems) {
if (system.destroy) { if (system.destroy) {
system.destroy(); system.destroy();
} else if (system.reset) { } else {
system.reset(); system.reset?.();
} }
} }
this._globalSystems.length = 0; this._globalSystems.length = 0;
// 清空服务容器
this._services.clear(); this._services.clear();
this._scenes.clear(); this._scenes.clear();
this._activeScenes.clear(); this._activeScenes.clear();
} }
@@ -461,58 +474,37 @@ export class World {
} }
}; };
// 统计所有Scene的实体数量
for (const scene of this._scenes.values()) { for (const scene of this._scenes.values()) {
if (scene.entities) { stats.totalEntities += scene.entities?.count ?? 0;
stats.totalEntities += scene.entities.count; stats.totalSystems += scene.systems?.length ?? 0;
}
if (scene.systems) {
stats.totalSystems += scene.systems.length;
}
} }
return stats; return stats;
} }
/** /**
* 检查是否应该执行自动清理 * @zh 检查Scene是否可以被自动清理
* @en Check if a scene is eligible for auto cleanup
*/ */
private shouldAutoCleanup(): boolean { private _isSceneCleanupCandidate(sceneName: string, scene: IScene): boolean {
// 简单的清理策略如果有空Scene且超过5分钟没有实体 const elapsed = Date.now() - this._createdAt;
const currentTime = Date.now(); return !this._activeScenes.has(sceneName) &&
const cleanupThreshold = 5 * 60 * 1000; // 5分钟 scene.entities != null &&
scene.entities.count === 0 &&
for (const [sceneName, scene] of this._scenes) { elapsed > this._config.cleanupThresholdMs!;
if (!this._activeScenes.has(sceneName) &&
scene.entities &&
scene.entities.count === 0 &&
(currentTime - this._createdAt) > cleanupThreshold) {
return true;
}
}
return false;
} }
/** /**
* 执行清理操作 * @zh 执行自动清理操作
* @en Execute auto cleanup operation
*/ */
private cleanup(): void { private cleanup(): void {
const sceneNames = Array.from(this._scenes.keys()); const candidates = [...this._scenes.entries()]
const currentTime = Date.now(); .filter(([name, scene]) => this._isSceneCleanupCandidate(name, scene));
const cleanupThreshold = 5 * 60 * 1000; // 5分钟
for (const sceneName of sceneNames) { for (const [sceneName] of candidates) {
const scene = this._scenes.get(sceneName); this.removeScene(sceneName);
if (scene && logger.debug(`自动清理空Scene: ${sceneName} from World ${this.name}`);
!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}`);
}
} }
} }

View File

@@ -113,6 +113,16 @@ export class PerformanceMonitor implements IService {
constructor() {} 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
}
/** /**
* 启用性能监控 * 启用性能监控
*/ */