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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -113,6 +113,16 @@ export class PerformanceMonitor implements IService {
constructor() {}
/**
* @zh 更新FPS统计可选功能子类可重写
* @en Update FPS statistics (optional, can be overridden by subclasses)
*
* @param _deltaTime - @zh 帧时间间隔(秒)@en Frame delta time in seconds
*/
public updateFPS(_deltaTime: number): void {
// Base implementation does nothing - override in subclass for FPS tracking
}
/**
* 启用性能监控
*/