refactor: reorganize package structure and decouple framework packages (#338)

* refactor: reorganize package structure and decouple framework packages

## Package Structure Reorganization
- Reorganized 55 packages into categorized subdirectories:
  - packages/framework/ - Generic framework (Laya/Cocos compatible)
  - packages/engine/ - ESEngine core modules
  - packages/rendering/ - Rendering modules (WASM dependent)
  - packages/physics/ - Physics modules
  - packages/streaming/ - World streaming
  - packages/network-ext/ - Network extensions
  - packages/editor/ - Editor framework and plugins
  - packages/rust/ - Rust WASM engine
  - packages/tools/ - Build tools and SDK

## Framework Package Decoupling
- Decoupled behavior-tree and blueprint packages from ESEngine dependencies
- Created abstracted interfaces (IBTAssetManager, IBehaviorTreeAssetContent)
- ESEngine-specific code moved to esengine/ subpath exports
- Framework packages now usable with Cocos/Laya without ESEngine

## CI Configuration
- Updated CI to only type-check and lint framework packages
- Added type-check:framework and lint:framework scripts

## Breaking Changes
- Package import paths changed due to directory reorganization
- ESEngine integrations now use subpath imports (e.g., '@esengine/behavior-tree/esengine')

* fix: update es-engine file path after directory reorganization

* docs: update README to focus on framework over engine

* ci: only build framework packages, remove Rust/WASM dependencies

* fix: remove esengine subpath from behavior-tree and blueprint builds

ESEngine integration code will only be available in full engine builds.
Framework packages are now purely engine-agnostic.

* fix: move network-protocols to framework, build both in CI

* fix: update workflow paths from packages/core to packages/framework/core

* fix: exclude esengine folder from type-check in behavior-tree and blueprint

* fix: update network tsconfig references to new paths

* fix: add test:ci:framework to only test framework packages in CI

* fix: only build core and math npm packages in CI

* fix: exclude test files from CodeQL and fix string escaping security issue
This commit is contained in:
YHH
2025-12-26 14:50:35 +08:00
committed by GitHub
parent a84ff902e4
commit 155411e743
1936 changed files with 4147 additions and 11578 deletions

View File

@@ -0,0 +1,18 @@
{
"presets": [
[
"@babel/preset-env",
{
"targets": {
"browsers": ["> 1%", "last 2 versions", "ie >= 11"]
},
"modules": false,
"loose": true
}
]
],
"plugins": [
"@babel/plugin-transform-optional-chaining",
"@babel/plugin-transform-nullish-coalescing-operator"
]
}

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>

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

@@ -0,0 +1,125 @@
const fs = require('fs');
const path = require('path');
const { execSync } = require('child_process');
console.log('🚀 使用 Rollup 构建npm包...');
async function main() {
try {
// 清理旧的dist目录
if (fs.existsSync('./dist')) {
console.log('🧹 清理旧的构建文件...');
execSync('rimraf ./dist', { stdio: 'inherit' });
}
// 执行Rollup构建
console.log('📦 执行 Rollup 构建...');
execSync('npx rollup -c rollup.config.cjs', { stdio: 'inherit' });
// 生成package.json
console.log('📋 生成 package.json...');
generatePackageJson();
// 复制其他文件
console.log('📁 复制必要文件...');
copyFiles();
// 输出构建结果
showBuildResults();
console.log('✅ 构建完成!');
console.log('\n🚀 发布命令:');
console.log('cd dist && npm publish');
} catch (error) {
console.error('❌ 构建失败:', error.message);
process.exit(1);
}
}
function generatePackageJson() {
const sourcePackage = JSON.parse(fs.readFileSync('./package.json', 'utf8'));
const distPackage = {
name: sourcePackage.name,
version: sourcePackage.version,
description: sourcePackage.description,
main: 'index.cjs',
module: 'index.mjs',
unpkg: 'index.umd.js',
types: 'index.d.ts',
exports: {
'.': {
import: './index.mjs',
require: './index.cjs',
types: './index.d.ts'
}
},
files: [
'index.mjs',
'index.mjs.map',
'index.cjs',
'index.cjs.map',
'index.umd.js',
'index.umd.js.map',
'index.es5.js',
'index.es5.js.map',
'index.d.ts'
],
keywords: [
'ecs',
'entity-component-system',
'game-engine',
'typescript',
'cocos-creator',
'laya',
'rollup'
],
author: sourcePackage.author,
license: sourcePackage.license,
repository: sourcePackage.repository,
bugs: sourcePackage.bugs,
homepage: sourcePackage.homepage,
engines: {
node: '>=16.0.0'
},
sideEffects: false
};
fs.writeFileSync('./dist/package.json', JSON.stringify(distPackage, null, 2));
}
function copyFiles() {
const filesToCopy = [
// 移除不存在的文件以避免警告
];
filesToCopy.forEach(({ src, dest }) => {
if (fs.existsSync(src)) {
fs.copyFileSync(src, dest);
console.log(` ✓ 复制: ${path.basename(dest)}`);
} else {
console.log(` ⚠️ 文件不存在: ${src}`);
}
});
if (filesToCopy.length === 0) {
console.log(' 没有需要复制的文件');
}
}
function showBuildResults() {
const distDir = './dist';
const files = ['index.mjs', 'index.cjs', 'index.umd.js', 'index.es5.js', 'index.d.ts'];
console.log('\n📊 构建结果:');
files.forEach(file => {
const filePath = path.join(distDir, file);
if (fs.existsSync(filePath)) {
const size = fs.statSync(filePath).size;
console.log(` ${file}: ${(size / 1024).toFixed(1)}KB`);
}
});
}
main().catch(console.error);

View File

@@ -0,0 +1,64 @@
/** @type {import('ts-jest').JestConfigWithTsJest} */
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
roots: ['<rootDir>/tests'],
testMatch: ['**/*.test.ts', '**/*.spec.ts'],
testPathIgnorePatterns: ['/node_modules/', '\\.performance\\.test\\.ts$', '/tests/performance/'],
collectCoverage: false,
collectCoverageFrom: [
'src/**/*.ts',
'!src/index.ts',
'!src/**/index.ts',
'!**/*.d.ts',
'!src/**/*.test.ts',
'!src/**/*.spec.ts'
],
coverageDirectory: 'coverage',
coverageReporters: ['text', 'lcov', 'html'],
// 设置覆盖度阈值
coverageThreshold: {
global: {
branches: 6,
functions: 17,
lines: 16,
statements: 15
},
// 核心模块要求更高覆盖率
'./src/ECS/Core/': {
branches: 8,
functions: 20,
lines: 18,
statements: 18
},
// ECS基础模块
'./src/ECS/': {
branches: 7,
functions: 18,
lines: 17,
statements: 16
}
},
verbose: true,
transform: {
'^.+\\.tsx?$': ['ts-jest', {
tsconfig: 'tsconfig.test.json',
useESM: false,
}],
},
moduleNameMapper: {
'^@/(.*)$': '<rootDir>/src/$1',
},
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],
setupFilesAfterEnv: ['<rootDir>/tests/setup.ts'],
testTimeout: 0,
// 清除模块缓存
clearMocks: true,
restoreMocks: true,
// 忽略某些模块
modulePathIgnorePatterns: [
'<rootDir>/bin/',
'<rootDir>/dist/',
'<rootDir>/node_modules/'
]
};

View File

@@ -0,0 +1,28 @@
/** @type {import('ts-jest').JestConfigWithTsJest} */
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
roots: ['<rootDir>/tests'],
testMatch: ['**/performance/**/*.test.ts', '**/*.performance.test.ts'],
collectCoverage: false,
verbose: true,
transform: {
'^.+\\.tsx?$': ['ts-jest', {
tsconfig: 'tsconfig.json',
useESM: false,
}],
},
moduleNameMapper: {
'^@/(.*)$': '<rootDir>/src/$1',
},
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],
setupFilesAfterEnv: ['<rootDir>/tests/setup.ts'],
testTimeout: 0,
clearMocks: true,
restoreMocks: true,
modulePathIgnorePatterns: [
'<rootDir>/bin/',
'<rootDir>/dist/',
'<rootDir>/node_modules/'
]
};

View File

@@ -0,0 +1,24 @@
{
"id": "core",
"name": "@esengine/ecs-framework",
"globalKey": "ecsFramework",
"displayName": "Core ECS",
"outputPath": "dist/index.mjs",
"description": "Core Entity-Component-System framework | 核心 ECS 框架",
"version": "1.0.0",
"category": "Core",
"icon": "Box",
"tags": ["ecs", "entity", "component", "system"],
"isCore": true,
"defaultEnabled": true,
"isEngineModule": true,
"canContainContent": false,
"platforms": ["web", "desktop", "mobile"],
"dependencies": [],
"exports": {
"components": ["Component", "Transform"],
"systems": ["System"],
"other": ["World", "Entity", "EntityManager", "SystemManager"]
},
"requiresWasm": false
}

View File

@@ -0,0 +1,83 @@
{
"name": "@esengine/ecs-framework",
"version": "2.4.2",
"description": "用于Laya、Cocos Creator等JavaScript游戏引擎的高性能ECS框架",
"main": "dist/index.cjs",
"module": "dist/index.mjs",
"types": "dist/index.d.ts",
"unpkg": "dist/index.umd.js",
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.mjs",
"require": "./dist/index.cjs"
}
},
"files": [
"dist/**/*"
],
"keywords": [
"ecs",
"entity-component-system",
"game-engine",
"typescript",
"laya",
"cocos",
"egret"
],
"scripts": {
"clean": "rimraf bin dist tsconfig.tsbuildinfo",
"build:ts": "tsc",
"prebuild": "npm run clean",
"build": "npm run build:ts && node build-rollup.cjs",
"build:watch": "tsc --watch",
"rebuild": "npm run clean && npm run build",
"build:npm": "npm run build",
"test": "jest --config jest.config.cjs",
"test:watch": "jest --watch --config jest.config.cjs",
"test:performance": "jest --config jest.performance.config.cjs",
"test:coverage": "jest --coverage --config jest.config.cjs",
"test:ci": "jest --ci --coverage --config jest.config.cjs",
"test:clear": "jest --clearCache",
"type-check": "npx tsc --noEmit",
"lint": "eslint \"src/**/*.{ts,tsx}\"",
"lint:fix": "eslint \"src/**/*.{ts,tsx}\" --fix"
},
"author": "yhh",
"license": "MIT",
"devDependencies": {
"@babel/core": "^7.28.3",
"@babel/plugin-transform-nullish-coalescing-operator": "^7.27.1",
"@babel/plugin-transform-optional-chaining": "^7.27.1",
"@babel/preset-env": "^7.28.3",
"@rollup/plugin-babel": "^6.0.4",
"@rollup/plugin-commonjs": "^28.0.3",
"@rollup/plugin-node-resolve": "^16.0.1",
"@rollup/plugin-terser": "^0.4.4",
"@jest/globals": "^29.7.0",
"@types/jest": "^29.5.14",
"@types/node": "^20.19.17",
"@eslint/js": "^9.37.0",
"eslint": "^9.37.0",
"typescript-eslint": "^8.46.1",
"jest": "^29.7.0",
"jest-environment-jsdom": "^29.7.0",
"rimraf": "^5.0.0",
"rollup": "^4.42.0",
"rollup-plugin-dts": "^6.2.1",
"ts-jest": "^29.4.0",
"typescript": "^5.8.3"
},
"publishConfig": {
"access": "public",
"registry": "https://registry.npmjs.org/"
},
"repository": {
"type": "git",
"url": "https://github.com/esengine/esengine.git",
"directory": "packages/core"
},
"dependencies": {
"tslib": "^2.8.1"
}
}

5550
packages/framework/core/pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,218 @@
const resolve = require('@rollup/plugin-node-resolve');
const commonjs = require('@rollup/plugin-commonjs');
const terser = require('@rollup/plugin-terser');
const babel = require('@rollup/plugin-babel');
const dts = require('rollup-plugin-dts').default;
const { readFileSync } = require('fs');
const pkg = JSON.parse(readFileSync('./package.json', 'utf8'));
const banner = `/**
* @esengine/ecs-framework v${pkg.version}
* 高性能ECS框架 - 适用于Cocos Creator和Laya等JavaScript游戏引擎
*
* @author ${pkg.author}
* @license ${pkg.license}
*/`;
const external = [];
const modernPlugins = [
resolve({
browser: true,
preferBuiltins: false
}),
commonjs({
include: /node_modules/
})
];
const legacyPlugins = [
resolve({
browser: true,
preferBuiltins: false
}),
commonjs({
include: /node_modules/
}),
babel({
babelHelpers: 'bundled',
exclude: 'node_modules/**',
extensions: ['.js', '.ts']
})
];
module.exports = [
// ES模块构建
{
input: 'bin/index.js',
output: {
file: 'dist/index.mjs',
format: 'es',
banner,
sourcemap: true,
exports: 'named'
},
plugins: [
...modernPlugins,
terser({
format: {
comments: /^!/
}
})
],
external,
onwarn(warning, warn) {
// 忽略 msgpack-lite 的循环依赖警告
if (warning.code === 'CIRCULAR_DEPENDENCY' && warning.ids && warning.ids.some(id => id.includes('msgpack-lite'))) {
return;
}
warn(warning);
},
treeshake: {
moduleSideEffects: (id) => id.includes('reflect-metadata'),
propertyReadSideEffects: false,
unknownGlobalSideEffects: false
}
},
// CommonJS构建
{
input: 'bin/index.js',
output: {
file: 'dist/index.cjs',
format: 'cjs',
banner,
sourcemap: true,
exports: 'named'
},
plugins: [
...modernPlugins,
terser({
format: {
comments: /^!/
}
})
],
external,
onwarn(warning, warn) {
if (warning.code === 'CIRCULAR_DEPENDENCY' && warning.ids && warning.ids.some(id => id.includes('msgpack-lite'))) {
return;
}
warn(warning);
},
treeshake: {
moduleSideEffects: (id) => id.includes('reflect-metadata')
}
},
// UMD构建
{
input: 'bin/index.js',
output: {
file: 'dist/index.umd.js',
format: 'umd',
name: 'ECS',
banner,
sourcemap: true,
exports: 'named'
},
plugins: [
...legacyPlugins,
terser({
format: {
comments: /^!/
}
})
],
external: [],
onwarn(warning, warn) {
if (warning.code === 'CIRCULAR_DEPENDENCY' && warning.ids && warning.ids.some(id => id.includes('msgpack-lite'))) {
return;
}
warn(warning);
},
treeshake: {
moduleSideEffects: (id) => id.includes('reflect-metadata')
}
},
// ES5兼容构建 - 适用于只支持ES5语法的JavaScript环境
{
input: 'bin/index.js',
output: {
file: 'dist/index.es5.js',
format: 'cjs',
banner: banner + '\n// ES5 Compatible Build for legacy JavaScript environments',
sourcemap: true,
exports: 'named'
},
plugins: [
resolve({
browser: true,
preferBuiltins: false
}),
commonjs({
include: /node_modules/
}),
babel({
babelHelpers: 'bundled',
exclude: 'node_modules/**',
extensions: ['.js', '.ts'],
presets: [
['@babel/preset-env', {
targets: {
browsers: ['ie >= 9', 'chrome >= 30', 'firefox >= 30', 'safari >= 8']
},
modules: false,
loose: true,
forceAllTransforms: true
}]
],
plugins: [
'@babel/plugin-transform-optional-chaining',
'@babel/plugin-transform-nullish-coalescing-operator'
]
}),
terser({
ecma: 5,
format: {
comments: /^!/
},
compress: {
drop_console: false,
drop_debugger: false
}
})
],
external: [],
onwarn(warning, warn) {
if (warning.code === 'CIRCULAR_DEPENDENCY' && warning.ids && warning.ids.some(id => id.includes('msgpack-lite'))) {
return;
}
warn(warning);
},
treeshake: {
moduleSideEffects: (id) => id.includes('reflect-metadata')
}
},
// 类型定义构建
{
input: 'bin/index.d.ts',
output: {
file: 'dist/index.d.ts',
format: 'es',
banner: `/**
* @esengine/ecs-framework v${pkg.version}
* TypeScript definitions
*/`
},
plugins: [
dts({
respectExternal: true
})
],
external: []
}
];

View File

@@ -0,0 +1,696 @@
import { TimerManager } from './Utils/Timers/TimerManager';
import { ITimer } from './Utils/Timers/ITimer';
import { Timer } from './Utils/Timers/Timer';
import { Time } from './Utils/Time';
import { PerformanceMonitor } from './Utils/PerformanceMonitor';
import { PoolManager } from './Utils/Pool/PoolManager';
import { DebugManager } from './Utils/Debug';
import { ICoreConfig, IECSDebugConfig } from './Types';
import { createLogger } from './Utils/Logger';
import { SceneManager } from './ECS/SceneManager';
import { IScene } from './ECS/IScene';
import { ServiceContainer } from './Core/ServiceContainer';
import { PluginManager } from './Core/PluginManager';
import { PluginServiceRegistry } from './Core/PluginServiceRegistry';
import { IPlugin } from './Core/Plugin';
import { WorldManager } from './ECS/WorldManager';
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("Executed after 1 second");
* });
*
* // @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 {
/**
* @zh 游戏暂停状态当设置为true时游戏循环将暂停执行
* @en Game paused state, when set to true, game loop will pause execution
*/
public static paused = false;
/**
* @zh 全局核心实例可能为null表示Core尚未初始化或已被销毁
* @en Global core instance, null means Core is not initialized or destroyed
*/
private static _instance: Core | null = null;
/**
* @zh Core专用日志器
* @en Core logger
*/
private static readonly _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;
private _timerManager: TimerManager;
private _performanceMonitor: PerformanceMonitor;
private _poolManager: PoolManager;
private _debugManager?: DebugManager;
/**
* @zh 场景管理器,管理当前场景的生命周期
* @en Scene manager for managing current scene lifecycle
*/
private _sceneManager: SceneManager;
/**
* @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;
/**
* @zh 插件服务注册表,基于 ServiceToken 的类型安全服务注册表
* @en Plugin service registry, type-safe service registry based on ServiceToken
*/
private _pluginServiceRegistry: PluginServiceRegistry;
/**
* @zh Core配置
* @en Core configuration
*/
private _config: ICoreConfig;
/**
* @zh 创建核心实例
* @en Create core instance
*
* @param config - @zh Core配置对象 @en Core configuration object
*/
private constructor(config: ICoreConfig = {}) {
Core._instance = this;
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._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);
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._debugManager = this._serviceContainer.resolve(DebugManager);
this._debugManager.onInitialize();
}
this.initialize();
}
/**
* @zh 获取核心实例
* @en Get core instance
*
* @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 @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);
* ```
*/
public static get services(): ServiceContainer {
if (!this._instance) {
throw new Error('Core instance not created, call Core.create() first | Core实例未创建请先调用Core.create()');
}
return this._instance._serviceContainer;
}
/**
* @zh 获取插件服务注册表
* @en Get plugin service registry
*
* @zh 用于基于 ServiceToken 的类型安全服务注册和获取。
* @en For type-safe service registration and retrieval based on ServiceToken.
*
* @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);
* ```
*/
public static get pluginServices(): PluginServiceRegistry {
if (!this._instance) {
throw new Error('Core instance not created, call Core.create() first | Core实例未创建请先调用Core.create()');
}
return this._instance._pluginServiceRegistry;
}
/**
* @zh 获取World管理器
* @en Get World manager
*
* @zh 用于管理多个独立的World实例高级用户
* @en For managing multiple independent World instances (advanced users).
*
* @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());
* room1.start();
* ```
*/
public static get worldManager(): WorldManager {
if (!this._instance) {
throw new Error('Core instance not created, call Core.create() first | Core实例未创建请先调用Core.create()');
}
return this._instance._worldManager;
}
/**
* @zh 创建Core实例
* @en Create Core instance
*
* @zh 如果实例已存在,则返回现有实例。
* @en If instance already exists, returns the existing instance.
*
* @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
* // @zh 方式1使用配置对象 | @en Method 1: Use config object
* Core.create({
* debug: true,
* debugConfig: {
* enabled: true,
* websocketUrl: 'ws://localhost:9229'
* }
* });
*
* // @zh 方式2简单模式向后兼容| @en Method 2: Simple mode (backward compatible)
* Core.create(true); // debug = true
* ```
*/
public static create(config: ICoreConfig | boolean = true): Core {
if (this._instance == null) {
// 向后兼容如果传入boolean转换为配置对象
const coreConfig: ICoreConfig = typeof config === 'boolean'
? { debug: config }
: config;
this._instance = new Core(coreConfig);
} else {
this._logger.warn('Core实例已创建返回现有实例');
}
return this._instance;
}
/**
* @zh 设置当前场景
* @en Set current scene
*
* @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);
* ```
*/
public static setScene<T extends IScene>(scene: T): T {
if (!this._instance) {
Core._logger.warn('Core实例未创建请先调用Core.create()');
throw new Error('Core实例未创建');
}
return this._instance._sceneManager.setScene(scene);
}
/**
* @zh 获取当前场景
* @en Get current scene
*
* @returns @zh 当前场景如果没有场景则返回null @en Current scene, or null if no scene
*/
public static get scene(): IScene | null {
if (!this._instance) {
return null;
}
return this._instance._sceneManager.currentScene;
}
/**
* @zh 获取ECS流式API
* @en Get ECS fluent API
*
* @returns @zh ECS API实例如果当前没有场景则返回null @en ECS API instance, or null if no scene
*
* @example
* ```typescript
* // @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 });
* ```
*/
public static get ecsAPI() {
if (!this._instance) {
return null;
}
return this._instance._sceneManager.api;
}
/**
* @zh 延迟加载场景(下一帧切换)
* @en Load scene with delay (switch on next frame)
*
* @param scene - @zh 要加载的场景 @en The scene to load
*
* @example
* ```typescript
* // @zh 延迟切换场景(在下一帧生效)| @en Deferred scene switch (takes effect next frame)
* Core.loadScene(new MenuScene());
* ```
*/
public static loadScene<T extends IScene>(scene: T): void {
if (!this._instance) {
Core._logger.warn('Core实例未创建请先调用Core.create()');
return;
}
this._instance._sceneManager.loadScene(scene);
}
/**
* @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 - @zh 外部引擎提供的帧时间间隔(秒)@en Frame delta time in seconds from external engine
*
* @example
* ```typescript
* // @zh 初始化 | @en Initialize
* Core.create({ debug: true });
* Core.setScene(new GameScene());
*
* // @zh Laya引擎集成 | @en Laya engine integration
* Laya.timer.frameLoop(1, this, () => {
* const deltaTime = Laya.timer.delta / 1000;
* Core.update(deltaTime);
* });
*
* // @zh Cocos Creator集成 | @en Cocos Creator integration
* update(deltaTime: number) {
* Core.update(deltaTime);
* }
* ```
*/
public static update(deltaTime: number): void {
if (!this._instance) {
Core._logger.warn('Core实例未创建请先调用Core.create()');
return;
}
this._instance.updateInternal(deltaTime);
}
/**
* @zh 调度定时器
* @en Schedule a timer
*
* @zh 创建一个定时器,在指定时间后执行回调函数。
* @en Create a timer that executes a callback after the specified time.
*
* @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("Executed after 1 second");
* });
*
* // @zh 重复定时器 | @en Repeating timer
* Core.schedule(0.5, true, null, (timer) => {
* console.log("Executed every 0.5 seconds");
* });
* ```
*/
public static schedule<TContext = unknown>(timeInSeconds: number, repeats: boolean = false, context?: TContext, onTime?: (timer: ITimer<TContext>) => void): Timer<TContext> {
if (!this._instance) {
throw new Error('Core实例未创建请先调用Core.create()');
}
if (!onTime) {
throw new Error('onTime callback is required');
}
return this._instance._timerManager.schedule(timeInSeconds, repeats, context as TContext, onTime);
}
/**
* @zh 启用调试功能
* @en Enable debug features
*
* @param config - @zh 调试配置 @en Debug configuration
*/
public static enableDebug(config: IECSDebugConfig): void {
if (!this._instance) {
Core._logger.warn('Core实例未创建请先调用Core.create()');
return;
}
if (this._instance._debugManager) {
this._instance._debugManager.updateConfig(config);
} else {
const configService = new DebugConfigService();
configService.setConfig(config);
this._instance._serviceContainer.registerInstance(DebugConfigService, configService);
this._instance._serviceContainer.registerSingleton(DebugManager, (c) =>
createInstance(DebugManager, c)
);
this._instance._debugManager = this._instance._serviceContainer.resolve(DebugManager);
this._instance._debugManager.onInitialize();
}
// 更新Core配置
this._instance._config.debugConfig = config;
}
/**
* @zh 禁用调试功能
* @en Disable debug features
*/
public static disableDebug(): void {
if (!this._instance) return;
if (this._instance._debugManager) {
this._instance._debugManager.stop();
delete this._instance._debugManager;
}
// 更新Core配置
if (this._instance._config.debugConfig) {
this._instance._config.debugConfig.enabled = false;
}
}
/**
* @zh 获取调试数据
* @en Get debug data
*
* @returns @zh 当前调试数据如果调试未启用则返回null @en Current debug data, or null if debug is disabled
*/
public static getDebugData(): unknown {
if (!this._instance?._debugManager) {
return null;
}
return this._instance._debugManager.getDebugData();
}
/**
* @zh 检查调试是否启用
* @en Check if debug is enabled
*
* @returns @zh 调试状态 @en Debug status
*/
public static get isDebugEnabled(): boolean {
return this._instance?._config.debugConfig?.enabled || false;
}
/**
* @zh 获取性能监视器实例
* @en Get performance monitor instance
*
* @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 - @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());
* ```
*/
public static async installPlugin(plugin: IPlugin): Promise<void> {
if (!this._instance) {
throw new Error('Core实例未创建请先调用Core.create()');
}
await this._instance._pluginManager.install(plugin);
}
/**
* @zh 卸载插件
* @en Uninstall plugin
*
* @param name - @zh 插件名称 @en Plugin name
* @throws @zh 如果Core实例未创建或插件卸载失败 @en If Core instance not created or plugin uninstallation fails
*
* @example
* ```typescript
* await Core.uninstallPlugin('my-plugin');
* ```
*/
public static async uninstallPlugin(name: string): Promise<void> {
if (!this._instance) {
throw new Error('Core实例未创建请先调用Core.create()');
}
await this._instance._pluginManager.uninstall(name);
}
/**
* @zh 获取插件实例
* @en Get plugin instance
*
* @param name - @zh 插件名称 @en Plugin name
* @returns @zh 插件实例如果未安装则返回undefined @en Plugin instance, or undefined if not installed
*
* @example
* ```typescript
* const myPlugin = Core.getPlugin('my-plugin');
* if (myPlugin) {
* console.log(myPlugin.version);
* }
* ```
*/
public static getPlugin(name: string): IPlugin | undefined {
if (!this._instance) {
return undefined;
}
return this._instance._pluginManager.getPlugin(name);
}
/**
* @zh 检查插件是否已安装
* @en Check if plugin is installed
*
* @param name - @zh 插件名称 @en Plugin name
* @returns @zh 是否已安装 @en Whether installed
*
* @example
* ```typescript
* if (Core.isPluginInstalled('my-plugin')) {
* console.log('Plugin is installed');
* }
* ```
*/
public static isPluginInstalled(name: string): boolean {
if (!this._instance) {
return false;
}
return this._instance._pluginManager.isInstalled(name);
}
/**
* @zh 初始化核心系统
* @en Initialize core system
*
* @zh 执行核心系统的初始化逻辑。
* @en Execute core system initialization logic.
*/
protected initialize() {
// 核心系统初始化
Core._logger.info('Core initialized', {
debug: this.debug,
debugEnabled: this._config.debugConfig?.enabled || false
});
}
/**
* @zh 内部更新方法
* @en Internal update method
*
* @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);
const servicesStartTime = this._performanceMonitor.startMonitoring('Services.update');
this._serviceContainer.updateAll(deltaTime);
this._performanceMonitor.endMonitoring('Services.update', servicesStartTime, this._serviceContainer.getUpdatableCount());
this._poolManager.update();
this._sceneManager.update();
this._worldManager.updateAll();
this._performanceMonitor.endMonitoring('Core.update', frameStartTime);
}
/**
* @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;
this._instance._debugManager?.stop();
this._instance._serviceContainer.clear();
Core._logger.info('Core destroyed');
this._instance = null;
}
}

View File

@@ -0,0 +1,330 @@
/**
* 依赖注入装饰器
*
* 提供 @Injectable、@InjectProperty 和 @Updatable 装饰器,用于标记可注入的类和依赖注入点
*/
import type { ServiceContainer } from '../ServiceContainer';
import type { IService, ServiceType } from '../ServiceContainer';
/**
* 依赖注入元数据存储
*/
type Constructor = abstract new (...args: unknown[]) => unknown;
const injectableMetadata = new WeakMap<Constructor, InjectableMetadata>();
const updatableMetadata = new WeakMap<Constructor, UpdatableMetadata>();
/**
* 可注入元数据接口
*/
export type InjectableMetadata = {
/**
* 是否可注入
*/
injectable: boolean;
/**
* 依赖列表
*/
dependencies: Array<ServiceType<IService> | string | symbol>;
/**
* 属性注入映射
* key: 属性名, value: 服务类型
*/
properties?: Map<string | symbol, ServiceType<IService>>;
}
/**
* 可更新元数据接口
*/
export type UpdatableMetadata = {
/**
* 是否可更新
*/
updatable: boolean;
/**
* 更新优先级数值越小越先执行默认0
*/
priority: number;
}
/**
* @Injectable() 装饰器
*
* 标记类为可注入的服务使其可以通过ServiceContainer进行依赖注入
*
* @example
* ```typescript
* @Injectable()
* class TimeService implements IService {
* constructor() {}
* dispose() {}
* }
*
* @Injectable()
* class PhysicsSystem extends EntitySystem {
* @InjectProperty(TimeService)
* private timeService!: TimeService;
*
* constructor() {
* super(Matcher.empty());
* }
* }
* ```
*/
export function Injectable(): ClassDecorator {
return function (target: Function): void {
const existing = injectableMetadata.get(target as Constructor);
injectableMetadata.set(target as Constructor, {
injectable: true,
dependencies: [],
...(existing?.properties && { properties: existing.properties })
});
} as ClassDecorator;
}
/**
* @Updatable() 装饰器
*
* 标记服务类为可更新的使其在每帧自动被ServiceContainer调用update方法。
* 使用此装饰器的类必须实现IUpdatable接口包含update方法
*
* @param priority - 更新优先级数值越小越先执行默认0
* @throws 如果类没有实现update方法将在运行时抛出错误
*
* @example
* ```typescript
* @Injectable()
* @Updatable()
* class TimerManager implements IService, IUpdatable {
* update(deltaTime?: number) {
* // 每帧更新逻辑
* }
* dispose() {}
* }
*
* // 指定优先级
* @Injectable()
* @Updatable(10)
* class PhysicsManager implements IService, IUpdatable {
* update() { }
* dispose() {}
* }
* ```
*/
export function Updatable(priority: number = 0): ClassDecorator {
return function (target: Function): void {
// 验证类原型上是否有update方法
const prototype = (target as Constructor & { prototype: { update?: unknown } }).prototype;
if (!prototype || typeof prototype.update !== 'function') {
throw new Error(
`@Updatable() decorator requires class ${target.name} to implement IUpdatable interface with update() method. ` +
'Please add \'implements IUpdatable\' and define update(deltaTime?: number): void method.'
);
}
// 标记为可更新
updatableMetadata.set(target as Constructor, {
updatable: true,
priority
});
} as ClassDecorator;
}
/**
* @InjectProperty() 装饰器
*
* 通过属性装饰器注入依赖
*
* 注入时机在构造函数执行后、onInitialize() 调用前完成
*
* @param serviceType 服务类型
*
* @example
* ```typescript
* @Injectable()
* class PhysicsSystem extends EntitySystem {
* @InjectProperty(TimeService)
* private timeService!: TimeService;
*
* @InjectProperty(CollisionService)
* private collision!: CollisionService;
*
* constructor() {
* super(Matcher.empty());
* }
*
* public onInitialize(): void {
* // 此时属性已注入完成,可以安全使用
* console.log(this.timeService.getDeltaTime());
* }
* }
* ```
*/
export function InjectProperty(serviceType: ServiceType<IService>): PropertyDecorator {
return function (target: object, propertyKey: string | symbol) {
let metadata = injectableMetadata.get((target as { constructor: Constructor }).constructor);
if (!metadata) {
metadata = {
injectable: true,
dependencies: []
};
injectableMetadata.set((target as { constructor: Constructor }).constructor, metadata);
}
if (!metadata.properties) {
metadata.properties = new Map();
}
metadata.properties.set(propertyKey, serviceType);
};
}
/**
* 检查类是否标记为可注入
*
* @param target 目标类
* @returns 是否可注入
*/
export function isInjectable(target: Constructor): boolean {
const metadata = injectableMetadata.get(target);
return metadata?.injectable ?? false;
}
/**
* 获取类的依赖注入元数据
*
* @param target 目标类
* @returns 依赖注入元数据
*/
export function getInjectableMetadata(target: Constructor): InjectableMetadata | undefined {
return injectableMetadata.get(target);
}
/**
* 获取属性注入元数据
*
* @param target 目标类
* @returns 属性名到服务类型的映射
*/
export function getPropertyInjectMetadata(target: Constructor): Map<string | symbol, ServiceType<IService>> {
const metadata = injectableMetadata.get(target);
return metadata?.properties || new Map();
}
/**
* 创建实例并自动注入依赖
*
* @param constructor 构造函数
* @param container 服务容器
* @returns 创建的实例
*
* @example
* ```typescript
* const instance = createInstance(MySystem, container);
* ```
*/
export function createInstance<T>(
constructor: new (...args: any[]) => T,
container: ServiceContainer
): T {
// 创建实例(无参数注入)
const instance = new constructor();
// 注入属性依赖
injectProperties(instance as object, container);
return instance;
}
/**
* 为实例注入属性依赖
*
* @param instance 目标实例
* @param container 服务容器
*/
export function injectProperties<T extends object>(instance: T, container: ServiceContainer): void {
const constructor = (instance as { constructor: Constructor }).constructor;
const metadata = getInjectableMetadata(constructor);
if (!metadata?.properties || metadata.properties.size === 0) {
return;
}
for (const [propertyKey, serviceType] of metadata.properties) {
const service = container.resolve(serviceType);
if (service !== null) {
(instance as Record<string | symbol, unknown>)[propertyKey] = service;
}
}
}
/**
* 检查类是否标记为可更新
*
* @param target 目标类
* @returns 是否可更新
*/
export function isUpdatable(target: Constructor): boolean {
const metadata = updatableMetadata.get(target);
return metadata?.updatable ?? false;
}
/**
* 获取类的可更新元数据
*
* @param target 目标类
* @returns 可更新元数据
*/
export function getUpdatableMetadata(target: Constructor): UpdatableMetadata | undefined {
return updatableMetadata.get(target);
}
/**
* 注册可注入的服务到容器
*
* 自动检测@Injectable装饰器并注册服务
*
* @param container 服务容器
* @param serviceType 服务类型
* @param singleton 是否注册为单例默认true
*
* @example
* ```typescript
* @Injectable()
* class MyService implements IService {
* dispose() {}
* }
*
* // 自动注册
* registerInjectable(Core.services, MyService);
* ```
*/
export function registerInjectable<T extends IService>(
container: ServiceContainer,
serviceType: ServiceType<T>,
singleton: boolean = true
): void {
if (!isInjectable(serviceType)) {
throw new Error(
`${serviceType.name} is not marked as @Injectable(). ` +
'Please add @Injectable() decorator to the class.'
);
}
// 创建工厂函数使用createInstance自动解析依赖
const factory = (c: ServiceContainer) => createInstance(serviceType, c);
// 注册到容器
if (singleton) {
container.registerSingleton(serviceType, factory);
} else {
container.registerTransient(serviceType, factory);
}
}

View File

@@ -0,0 +1,21 @@
/**
* 依赖注入模块
*
* 提供装饰器和工具函数,用于实现依赖注入模式
*/
export {
Injectable,
InjectProperty,
Updatable,
isInjectable,
getInjectableMetadata,
getPropertyInjectMetadata,
isUpdatable,
getUpdatableMetadata,
createInstance,
injectProperties,
registerInjectable
} from './Decorators';
export type { InjectableMetadata, UpdatableMetadata } from './Decorators';

View File

@@ -0,0 +1,124 @@
import type { Core } from '../Core';
import type { ServiceContainer } from './ServiceContainer';
/**
* 插件状态
*/
export enum PluginState {
/**
* 未安装
*/
NotInstalled = 'not_installed',
/**
* 已安装
*/
Installed = 'installed',
/**
* 安装失败
*/
Failed = 'failed'
}
/**
* 插件接口
*
* 所有插件都必须实现此接口。
* 插件提供了一种扩展框架功能的标准方式。
*
* @example
* ```typescript
* class MyPlugin implements IPlugin {
* readonly name = 'my-plugin';
* readonly version = '1.0.0';
* readonly dependencies = ['other-plugin'];
*
* async install(core: Core, services: ServiceContainer) {
* // 注册服务
* services.registerSingleton(MyService);
*
* // 添加系统
* const world = core.getWorld();
* if (world) {
* world.addSystem(new MySystem());
* }
* }
*
* async uninstall() {
* // 清理资源
* }
* }
* ```
*/
export type IPlugin = {
/**
* 插件唯一名称
*
* 用于依赖解析和插件管理。
*/
readonly name: string;
/**
* 插件版本
*
* 遵循语义化版本规范 (semver)。
*/
readonly version: string;
/**
* 依赖的其他插件名称列表
*
* 这些插件必须在当前插件之前安装。
*/
readonly dependencies?: readonly string[];
/**
* 安装插件
*
* 在此方法中初始化插件,注册服务、系统等。
* 可以是同步或异步的。
*
* @param core - Core实例用于访问World等
* @param services - 服务容器,用于注册服务
*/
install(core: Core, services: ServiceContainer): void | Promise<void>;
/**
* 卸载插件
*
* 清理插件占用的资源。
* 可以是同步或异步的。
*/
uninstall(): void | Promise<void>;
}
/**
* 插件元数据
*/
export type IPluginMetadata = {
/**
* 插件名称
*/
name: string;
/**
* 插件版本
*/
version: string;
/**
* 插件状态
*/
state: PluginState;
/**
* 安装时间戳
*/
installedAt?: number;
/**
* 错误信息(如果安装失败)
*/
error?: string;
}

View File

@@ -0,0 +1,266 @@
import { IPlugin, IPluginMetadata, PluginState } from './Plugin';
import type { IService } from './ServiceContainer';
import type { Core } from '../Core';
import type { ServiceContainer } from './ServiceContainer';
import { createLogger } from '../Utils/Logger';
const logger = createLogger('PluginManager');
/**
* 插件管理器
*
* 负责插件的注册、安装、卸载和生命周期管理。
* 支持依赖检查和异步加载。
*
* @example
* ```typescript
* const core = Core.create();
* const pluginManager = core.getService(PluginManager);
*
* // 注册插件
* await pluginManager.install(new MyPlugin());
*
* // 查询插件
* const plugin = pluginManager.getPlugin('my-plugin');
*
* // 卸载插件
* await pluginManager.uninstall('my-plugin');
* ```
*/
export class PluginManager implements IService {
/**
* 已安装的插件
*/
private _plugins: Map<string, IPlugin> = new Map();
/**
* 插件元数据
*/
private _metadata: Map<string, IPluginMetadata> = new Map();
/**
* Core实例引用
*/
private _core: Core | null = null;
/**
* 服务容器引用
*/
private _services: ServiceContainer | null = null;
/**
* 初始化插件管理器
*
* @param core - Core实例
* @param services - 服务容器
*/
public initialize(core: Core, services: ServiceContainer): void {
this._core = core;
this._services = services;
logger.info('PluginManager initialized');
}
/**
* 安装插件
*
* 会自动检查依赖并按正确顺序安装。
*
* @param plugin - 插件实例
* @throws 如果依赖检查失败或安装失败
*/
public async install(plugin: IPlugin): Promise<void> {
if (!this._core || !this._services) {
throw new Error('PluginManager not initialized. Call initialize() first.');
}
// 检查是否已安装
if (this._plugins.has(plugin.name)) {
logger.warn(`Plugin ${plugin.name} is already installed`);
return;
}
// 检查依赖
if (plugin.dependencies && plugin.dependencies.length > 0) {
this._checkDependencies(plugin);
}
// 创建元数据
const metadata: IPluginMetadata = {
name: plugin.name,
version: plugin.version,
state: PluginState.NotInstalled,
installedAt: Date.now()
};
this._metadata.set(plugin.name, metadata);
try {
// 调用插件的安装方法
logger.info(`Installing plugin: ${plugin.name} v${plugin.version}`);
await plugin.install(this._core, this._services);
// 标记为已安装
this._plugins.set(plugin.name, plugin);
metadata.state = PluginState.Installed;
logger.info(`Plugin ${plugin.name} installed successfully`);
} catch (error) {
// 安装失败
metadata.state = PluginState.Failed;
metadata.error = error instanceof Error ? error.message : String(error);
logger.error(`Failed to install plugin ${plugin.name}:`, error);
throw error;
}
}
/**
* 卸载插件
*
* @param name - 插件名称
* @throws 如果插件未安装或卸载失败
*/
public async uninstall(name: string): Promise<void> {
const plugin = this._plugins.get(name);
if (!plugin) {
throw new Error(`Plugin ${name} is not installed`);
}
// 检查是否有其他插件依赖此插件
this._checkDependents(name);
try {
logger.info(`Uninstalling plugin: ${name}`);
await plugin.uninstall();
// 从注册表中移除
this._plugins.delete(name);
this._metadata.delete(name);
logger.info(`Plugin ${name} uninstalled successfully`);
} catch (error) {
logger.error(`Failed to uninstall plugin ${name}:`, error);
throw error;
}
}
/**
* 获取插件实例
*
* @param name - 插件名称
* @returns 插件实例如果未安装则返回undefined
*/
public getPlugin(name: string): IPlugin | undefined {
return this._plugins.get(name);
}
/**
* 获取插件元数据
*
* @param name - 插件名称
* @returns 插件元数据如果未安装则返回undefined
*/
public getMetadata(name: string): IPluginMetadata | undefined {
return this._metadata.get(name);
}
/**
* 获取所有已安装的插件
*
* @returns 插件列表
*/
public getAllPlugins(): IPlugin[] {
return Array.from(this._plugins.values());
}
/**
* 获取所有插件元数据
*
* @returns 元数据列表
*/
public getAllMetadata(): IPluginMetadata[] {
return Array.from(this._metadata.values());
}
/**
* 检查插件是否已安装
*
* @param name - 插件名称
* @returns 是否已安装
*/
public isInstalled(name: string): boolean {
return this._plugins.has(name);
}
/**
* 检查插件依赖
*
* @param plugin - 插件实例
* @throws 如果依赖未满足
*/
private _checkDependencies(plugin: IPlugin): void {
if (!plugin.dependencies) {
return;
}
const missingDeps: string[] = [];
for (const dep of plugin.dependencies) {
if (!this._plugins.has(dep)) {
missingDeps.push(dep);
}
}
if (missingDeps.length > 0) {
throw new Error(
`Plugin ${plugin.name} has unmet dependencies: ${missingDeps.join(', ')}`
);
}
}
/**
* 检查是否有其他插件依赖指定插件
*
* @param name - 插件名称
* @throws 如果有其他插件依赖此插件
*/
private _checkDependents(name: string): void {
const dependents: string[] = [];
for (const plugin of this._plugins.values()) {
if (plugin.dependencies && plugin.dependencies.includes(name)) {
dependents.push(plugin.name);
}
}
if (dependents.length > 0) {
throw new Error(
`Cannot uninstall plugin ${name}: it is required by ${dependents.join(', ')}`
);
}
}
/**
* 释放资源
*/
public dispose(): void {
// 卸载所有插件(逆序,先卸载依赖项)
const plugins = Array.from(this._plugins.values()).reverse();
for (const plugin of plugins) {
try {
logger.info(`Disposing plugin: ${plugin.name}`);
plugin.uninstall();
} catch (error) {
logger.error(`Error disposing plugin ${plugin.name}:`, error);
}
}
this._plugins.clear();
this._metadata.clear();
this._core = null;
this._services = null;
logger.info('PluginManager disposed');
}
}

View File

@@ -0,0 +1,144 @@
/**
* 插件服务注册表
* Plugin Service Registry
*
* 基于 ServiceToken 的类型安全服务注册表。
* Type-safe service registry based on ServiceToken.
*
* 设计原则 | Design principles:
* 1. 类型安全 - 使用 ServiceToken 携带类型信息
* 2. 显式依赖 - 通过导入 token 明确表达依赖关系
* 3. 可选依赖 - get 返回 undefinedrequire 抛异常
* 4. 单一职责 - 只负责服务注册和查询,不涉及生命周期管理
* 5. 谁定义接口,谁导出 Token - 各模块定义自己的接口和 Token
*/
// ============================================================================
// 服务令牌 | Service Token
// ============================================================================
/**
* 服务令牌接口
* Service token interface
*
* 用于类型安全的服务注册和获取。
* For type-safe service registration and retrieval.
*
* 注意__phantom 是必需属性,确保 TypeScript 在跨包类型解析时保留泛型类型信息。
* Note: __phantom is a required property to ensure TypeScript preserves generic
* type information across packages.
*/
export type ServiceToken<T> = {
readonly id: symbol;
readonly name: string;
/**
* Phantom type 标记(强制类型推断)
* Phantom type marker (enforces type inference)
*/
readonly __phantom: T;
}
/**
* 创建服务令牌
* Create a service token
*
* 使用 Symbol.for() 确保相同名称的令牌在不同模块中引用同一个 Symbol。
* Uses Symbol.for() to ensure tokens with the same name reference the same Symbol across modules.
*
* 这解决了跨包场景下服务注册和获取使用不同 Symbol 的问题。
* This fixes the issue where service registration and retrieval use different Symbols across packages.
*
* @param name 令牌名称 | Token name
* @returns 服务令牌 | Service token
*/
export function createServiceToken<T>(name: string): ServiceToken<T> {
// 使用 Symbol.for() 从全局 Symbol 注册表获取或创建 Symbol
// 这确保相同名称在任何地方都返回同一个 Symbol
// Use Symbol.for() to get or create Symbol from global Symbol registry
// This ensures the same name returns the same Symbol everywhere
const tokenKey = `@esengine/service:${name}`;
return {
id: Symbol.for(tokenKey),
name
} as ServiceToken<T>;
}
// ============================================================================
// 插件服务注册表 | Plugin Service Registry
// ============================================================================
/**
* 插件服务注册表
* Plugin service registry
*
* 用于跨插件共享服务的类型安全注册表。
* Type-safe registry for sharing services between plugins.
*/
export class PluginServiceRegistry {
private _services = new Map<symbol, unknown>();
/**
* 注册服务
* Register a service
*/
register<T>(token: ServiceToken<T>, service: T): void {
this._services.set(token.id, service);
}
/**
* 获取服务(可选)
* Get a service (optional)
*/
get<T>(token: ServiceToken<T>): T | undefined {
return this._services.get(token.id) as T | undefined;
}
/**
* 获取服务(必需)
* Get a service (required)
*
* @throws 如果服务未注册 | If service is not registered
*/
require<T>(token: ServiceToken<T>): T {
const service = this._services.get(token.id);
if (service === undefined) {
throw new Error(`Service not found: ${token.name}`);
}
return service as T;
}
/**
* 检查服务是否已注册
* Check if a service is registered
*/
has<T>(token: ServiceToken<T>): boolean {
return this._services.has(token.id);
}
/**
* 注销服务
* Unregister a service
*/
unregister<T>(token: ServiceToken<T>): boolean {
return this._services.delete(token.id);
}
/**
* 清空所有服务
* Clear all services
*/
clear(): void {
this._services.clear();
}
/**
* 释放资源
* Dispose resources
*
* 实现 IService 接口,在服务容器清理时调用。
* Implements IService interface, called when service container is cleaned up.
*/
dispose(): void {
this.clear();
}
}

View File

@@ -0,0 +1,287 @@
/**
* 运行时模式服务
* Runtime Mode Service
*
* 提供统一的运行时模式查询接口,使第三方模块能够感知当前运行环境。
* Provides unified runtime mode query interface for third-party modules to be aware of current runtime environment.
*
* 模式定义 | Mode Definitions:
* - Editor 模式编辑器环境显示网格、Gizmos、坐标轴等
* - Playing 模式游戏运行中Play 按钮已按下)
* - Preview 模式:预览模式(场景预览但不是完整的游戏运行)
*
* @example
* ```typescript
* import { RuntimeModeToken, type IRuntimeMode } from '@esengine/ecs-framework';
*
* // 获取服务
* const runtimeMode = context.services.get(RuntimeModeToken);
*
* // 检查当前模式
* if (runtimeMode?.isEditor) {
* // 编辑器特定逻辑
* }
*
* // 监听模式变化
* const unsubscribe = runtimeMode?.onModeChanged((mode) => {
* console.log('Mode changed:', mode.isPlaying ? 'Playing' : 'Stopped');
* });
* ```
*/
import { createServiceToken } from './PluginServiceRegistry';
import { createLogger } from '../Utils/Logger';
// ============================================================================
// 接口定义 | Interface Definitions
// ============================================================================
/**
* 运行时模式接口
* Runtime mode interface
*/
export type IRuntimeMode = {
/**
* 是否为编辑器模式
* Whether in editor mode
*
* 编辑器模式下会显示网格、Gizmos、坐标轴指示器等辅助元素。
* In editor mode, grid, gizmos, axis indicator and other helper elements are shown.
*/
readonly isEditor: boolean;
/**
* 是否正在播放(游戏运行中)
* Whether playing (game is running)
*
* 当用户点击 Play 按钮后为 true点击 Stop 后为 false。
* True after user clicks Play button, false after clicking Stop.
*/
readonly isPlaying: boolean;
/**
* 是否为预览模式
* Whether in preview mode
*
* 预览模式是编辑器中的场景预览,不是完整的游戏运行。
* Preview mode is scene preview in editor, not full game runtime.
*/
readonly isPreview: boolean;
/**
* 是否为独立运行时(非编辑器环境)
* Whether in standalone runtime (non-editor environment)
*
* Web 构建、移动端等独立运行环境中为 true。
* True in standalone runtime environments like web build, mobile, etc.
*/
readonly isStandalone: boolean;
/**
* 订阅模式变化事件
* Subscribe to mode change events
*
* @param callback 模式变化回调
* @returns 取消订阅函数
*/
onModeChanged(callback: (mode: IRuntimeMode) => void): () => void;
}
// ============================================================================
// 服务令牌 | Service Token
// ============================================================================
/**
* 运行时模式服务令牌
* Runtime mode service token
*/
export const RuntimeModeToken = createServiceToken<IRuntimeMode>('runtimeMode');
// ============================================================================
// 默认实现 | Default Implementation
// ============================================================================
/**
* 模式变化回调类型
* Mode change callback type
*/
type ModeChangeCallback = (mode: IRuntimeMode) => void;
/**
* 运行时模式服务配置
* Runtime mode service configuration
*/
export type RuntimeModeConfig = {
/** 是否为编辑器模式 | Whether in editor mode */
isEditor?: boolean;
/** 是否正在播放 | Whether playing */
isPlaying?: boolean;
/** 是否为预览模式 | Whether in preview mode */
isPreview?: boolean;
}
/**
* 运行时模式服务默认实现
* Default runtime mode service implementation
*/
export class RuntimeModeService implements IRuntimeMode {
private _isEditor: boolean;
private _isPlaying: boolean;
private _isPreview: boolean;
private _callbacks: Set<ModeChangeCallback> = new Set();
/**
* 创建运行时模式服务
* Create runtime mode service
*
* @param config 初始配置
*/
constructor(config: RuntimeModeConfig = {}) {
this._isEditor = config.isEditor ?? false;
this._isPlaying = config.isPlaying ?? false;
this._isPreview = config.isPreview ?? false;
}
// ========== IRuntimeMode 实现 ==========
get isEditor(): boolean {
return this._isEditor;
}
get isPlaying(): boolean {
return this._isPlaying;
}
get isPreview(): boolean {
return this._isPreview;
}
get isStandalone(): boolean {
return !this._isEditor;
}
onModeChanged(callback: ModeChangeCallback): () => void {
this._callbacks.add(callback);
return () => {
this._callbacks.delete(callback);
};
}
// ========== 设置方法(供运行时内部使用)==========
/**
* 设置编辑器模式
* Set editor mode
*
* @internal
*/
setEditorMode(isEditor: boolean): void {
if (this._isEditor !== isEditor) {
this._isEditor = isEditor;
this._notifyChange();
}
}
/**
* 设置播放状态
* Set playing state
*
* @internal
*/
setPlaying(isPlaying: boolean): void {
if (this._isPlaying !== isPlaying) {
this._isPlaying = isPlaying;
this._notifyChange();
}
}
/**
* 设置预览模式
* Set preview mode
*
* @internal
*/
setPreview(isPreview: boolean): void {
if (this._isPreview !== isPreview) {
this._isPreview = isPreview;
this._notifyChange();
}
}
/**
* 批量更新模式
* Batch update mode
*
* @internal
*/
updateMode(config: RuntimeModeConfig): void {
let changed = false;
if (config.isEditor !== undefined && this._isEditor !== config.isEditor) {
this._isEditor = config.isEditor;
changed = true;
}
if (config.isPlaying !== undefined && this._isPlaying !== config.isPlaying) {
this._isPlaying = config.isPlaying;
changed = true;
}
if (config.isPreview !== undefined && this._isPreview !== config.isPreview) {
this._isPreview = config.isPreview;
changed = true;
}
if (changed) {
this._notifyChange();
}
}
private static readonly _logger = createLogger('RuntimeModeService');
/**
* @zh 通知模式变化
* @en Notify mode change
*/
private _notifyChange(): void {
for (const callback of this._callbacks) {
try {
callback(this);
} catch (error) {
RuntimeModeService._logger.error('Callback error:', error);
}
}
}
/**
* 释放资源
* Dispose resources
*/
dispose(): void {
this._callbacks.clear();
}
}
/**
* 创建编辑器模式服务
* Create editor mode service
*/
export function createEditorModeService(): RuntimeModeService {
return new RuntimeModeService({
isEditor: true,
isPlaying: false,
isPreview: false
});
}
/**
* 创建独立运行时模式服务
* Create standalone runtime mode service
*/
export function createStandaloneModeService(): RuntimeModeService {
return new RuntimeModeService({
isEditor: false,
isPlaying: true,
isPreview: false
});
}

View File

@@ -0,0 +1,453 @@
import { createLogger } from '../Utils/Logger';
import { isUpdatable as checkUpdatable, getUpdatableMetadata } from './DI';
const logger = createLogger('ServiceContainer');
/**
* 服务基础接口
* 所有通过 ServiceContainer 管理的服务都应该实现此接口
*/
export type IService = {
/**
* 释放服务占用的资源
* 当服务被注销或容器被清空时调用
*/
dispose(): void;
}
/**
* 服务类型
*
* 支持任意构造函数签名,以便与依赖注入装饰器配合使用
* 使用 any[] 以允许任意参数类型的构造函数
*/
export type ServiceType<T extends IService> = new (...args: any[]) => T;
/**
* 服务标识符
*
* 支持类构造函数或 Symbol 作为服务标识符
*/
export type ServiceIdentifier<T extends IService = IService> = ServiceType<T> | symbol;
/**
* 服务生命周期
*/
export enum ServiceLifetime {
/**
* 单例模式 - 整个应用生命周期内只有一个实例
*/
Singleton = 'singleton',
/**
* 瞬时模式 - 每次请求都创建新实例
*/
Transient = 'transient'
}
/**
* 服务注册信息
*/
interface ServiceRegistration<T extends IService> {
/**
* 服务标识符
*/
identifier: ServiceIdentifier<T>;
/**
* 服务类型(用于构造实例)
*/
type?: ServiceType<T>;
/**
* 服务实例(单例模式)
*/
instance?: T;
/**
* 工厂函数
*/
factory?: (container: ServiceContainer) => T;
/**
* 生命周期
*/
lifetime: ServiceLifetime;
}
/**
* 服务容器
*
* 负责管理所有服务的注册、解析和生命周期。
* 支持依赖注入和服务定位模式。
*
* 特点:
* - 单例和瞬时两种生命周期
* - 支持工厂函数注册
* - 支持实例注册
* - 类型安全的服务解析
*
* @example
* ```typescript
* const container = new ServiceContainer();
*
* // 注册单例服务
* container.registerSingleton(TimerManager);
*
* // 注册工厂函数
* container.registerSingleton(Logger, (c) => createLogger('App'));
*
* // 注册实例
* container.registerInstance(Config, new Config());
*
* // 解析服务
* const timer = container.resolve(TimerManager);
* ```
*/
export class ServiceContainer {
/**
* 服务注册表
*/
private _services: Map<ServiceIdentifier, ServiceRegistration<IService>> = new Map();
/**
* 正在解析的服务栈(用于循环依赖检测)
*/
private _resolving: Set<ServiceIdentifier> = new Set();
/**
* 可更新的服务列表
*
* 自动收集所有使用@Updatable装饰器标记的服务供Core统一更新
* 按优先级排序(数值越小越先执行)
*/
private _updatableServices: Array<{ instance: IService; priority: number }> = [];
/**
* 注册单例服务
*
* @param type - 服务类型
* @param factory - 可选的工厂函数
*
* @example
* ```typescript
* // 直接注册类型
* container.registerSingleton(TimerManager);
*
* // 使用工厂函数
* container.registerSingleton(Logger, (c) => {
* return createLogger('App');
* });
* ```
*/
public registerSingleton<T extends IService>(
type: ServiceType<T>,
factory?: (container: ServiceContainer) => T
): void {
if (this._services.has(type as ServiceIdentifier)) {
logger.warn(`Service ${type.name} is already registered`);
return;
}
this._services.set(type as ServiceIdentifier, {
identifier: type as ServiceIdentifier,
type: type as ServiceType<IService>,
...(factory && { factory: factory as (container: ServiceContainer) => IService }),
lifetime: ServiceLifetime.Singleton
});
logger.debug(`Registered singleton service: ${type.name}`);
}
/**
* 注册瞬时服务
*
* 每次解析都会创建新实例。
*
* @param type - 服务类型
* @param factory - 可选的工厂函数
*
* @example
* ```typescript
* // 每次解析都创建新实例
* container.registerTransient(Command);
* ```
*/
public registerTransient<T extends IService>(
type: ServiceType<T>,
factory?: (container: ServiceContainer) => T
): void {
if (this._services.has(type as ServiceIdentifier)) {
logger.warn(`Service ${type.name} is already registered`);
return;
}
this._services.set(type as ServiceIdentifier, {
identifier: type as ServiceIdentifier,
type: type as ServiceType<IService>,
...(factory && { factory: factory as (container: ServiceContainer) => IService }),
lifetime: ServiceLifetime.Transient
});
logger.debug(`Registered transient service: ${type.name}`);
}
/**
* 注册服务实例
*
* 直接注册已创建的实例,自动视为单例。
*
* @param identifier - 服务标识符(构造函数或 Symbol
* @param instance - 服务实例
*
* @example
* ```typescript
* const config = new Config();
* container.registerInstance(Config, config);
*
* // 使用 Symbol 作为标识符(适用于接口)
* const IFileSystem = Symbol('IFileSystem');
* container.registerInstance(IFileSystem, new TauriFileSystem());
* ```
*/
public registerInstance<T extends IService>(identifier: ServiceIdentifier<T>, instance: T): void {
if (this._services.has(identifier)) {
const name = typeof identifier === 'symbol' ? identifier.description : identifier.name;
logger.warn(`Service ${name} is already registered`);
return;
}
this._services.set(identifier, {
identifier,
instance: instance as IService,
lifetime: ServiceLifetime.Singleton
});
// 如果使用了@Updatable装饰器添加到可更新列表
if (typeof identifier !== 'symbol' && checkUpdatable(identifier)) {
const metadata = getUpdatableMetadata(identifier);
const priority = metadata?.priority ?? 0;
this._updatableServices.push({ instance, priority });
// 按优先级排序(数值越小越先执行)
this._updatableServices.sort((a, b) => a.priority - b.priority);
logger.debug(`Service ${identifier.name} is updatable (priority: ${priority}), added to update list`);
}
const name = typeof identifier === 'symbol' ? identifier.description : identifier.name;
logger.debug(`Registered service instance: ${name}`);
}
/**
* 解析服务
*
* @param identifier - 服务标识符(构造函数或 Symbol
* @returns 服务实例
* @throws 如果服务未注册或存在循环依赖
*
* @example
* ```typescript
* const timer = container.resolve(TimerManager);
*
* // 使用 Symbol
* const fileSystem = container.resolve(IFileSystem);
* ```
*/
public resolve<T extends IService>(identifier: ServiceIdentifier<T>): T {
const registration = this._services.get(identifier);
const name = typeof identifier === 'symbol' ? identifier.description : identifier.name;
if (!registration) {
throw new Error(`Service ${name} is not registered`);
}
// 检测循环依赖
if (this._resolving.has(identifier)) {
const chain = Array.from(this._resolving).map((t) =>
typeof t === 'symbol' ? t.description : t.name
).join(' -> ');
throw new Error(`Circular dependency detected: ${chain} -> ${name}`);
}
// 如果是单例且已经有实例,直接返回
if (registration.lifetime === ServiceLifetime.Singleton && registration.instance) {
return registration.instance as T;
}
// 添加到解析栈
this._resolving.add(identifier);
try {
// 创建实例
let instance: IService;
if (registration.factory) {
// 使用工厂函数
instance = registration.factory(this);
} else if (registration.type) {
// 直接构造
instance = new (registration.type)();
} else {
throw new Error(`Service ${name} has no factory or type to construct`);
}
// 如果是单例,缓存实例
if (registration.lifetime === ServiceLifetime.Singleton) {
registration.instance = instance;
// 如果使用了@Updatable装饰器添加到可更新列表
if (registration.type && checkUpdatable(registration.type)) {
const metadata = getUpdatableMetadata(registration.type);
const priority = metadata?.priority ?? 0;
this._updatableServices.push({ instance, priority });
// 按优先级排序(数值越小越先执行)
this._updatableServices.sort((a, b) => a.priority - b.priority);
logger.debug(`Service ${name} is updatable (priority: ${priority}), added to update list`);
}
}
return instance as T;
} finally {
// 从解析栈移除
this._resolving.delete(identifier);
}
}
/**
* 尝试解析服务
*
* 如果服务未注册返回null而不是抛出异常。
*
* @param identifier - 服务标识符(构造函数或 Symbol
* @returns 服务实例或null
*
* @example
* ```typescript
* const timer = container.tryResolve(TimerManager);
* if (timer) {
* timer.schedule(...);
* }
* ```
*/
public tryResolve<T extends IService>(identifier: ServiceIdentifier<T>): T | null {
try {
return this.resolve(identifier);
} catch {
return null;
}
}
/**
* 检查服务是否已注册
*
* @param identifier - 服务标识符(构造函数或 Symbol
* @returns 是否已注册
*/
public isRegistered<T extends IService>(identifier: ServiceIdentifier<T>): boolean {
return this._services.has(identifier);
}
/**
* 注销服务
*
* @param identifier - 服务标识符(构造函数或 Symbol
* @returns 是否成功注销
*/
public unregister<T extends IService>(identifier: ServiceIdentifier<T>): boolean {
const registration = this._services.get(identifier);
if (!registration) {
return false;
}
// 如果有单例实例,调用 dispose
if (registration.instance) {
// 从可更新列表中移除
const index = this._updatableServices.findIndex((item) => item.instance === registration.instance);
if (index !== -1) {
this._updatableServices.splice(index, 1);
}
registration.instance.dispose();
}
this._services.delete(identifier);
const name = typeof identifier === 'symbol' ? identifier.description : identifier.name;
logger.debug(`Unregistered service: ${name}`);
return true;
}
/**
* 清空所有服务
*/
public clear(): void {
// 清理所有单例实例
for (const [, registration] of this._services) {
if (registration.instance) {
registration.instance.dispose();
}
}
this._services.clear();
this._updatableServices = [];
logger.debug('Cleared all services');
}
/**
* 获取所有已注册的服务标识符
*
* @returns 服务标识符数组
*/
public getRegisteredServices(): ServiceIdentifier[] {
return Array.from(this._services.keys());
}
/**
* 更新所有使用@Updatable装饰器标记的服务
*
* 此方法会按优先级顺序遍历所有可更新的服务并调用它们的update方法。
* 所有服务在注册时已经由@Updatable装饰器验证过必须实现IUpdatable接口。
* 通常在Core的主更新循环中调用。
*
* @param deltaTime - 可选的帧时间间隔(秒)
*
* @example
* ```typescript
* // 在Core的update方法中
* this._serviceContainer.updateAll(deltaTime);
* ```
*/
public updateAll(deltaTime?: number): void {
for (const { instance } of this._updatableServices) {
(instance as IService & { update: (deltaTime?: number) => void }).update(deltaTime);
}
}
/**
* 获取所有可更新的服务数量
*
* @returns 可更新服务的数量
*/
public getUpdatableCount(): number {
return this._updatableServices.length;
}
/**
* 获取所有已实例化的服务实例
*
* @returns 所有服务实例的数组
*/
public getAll(): IService[] {
const instances: IService[] = [];
for (const descriptor of this._services.values()) {
if (descriptor.instance) {
instances.push(descriptor.instance);
}
}
return instances;
}
}

View File

@@ -0,0 +1,164 @@
import type { IComponent } from '../Types';
import { Int32 } from './Core/SoAStorage';
/**
* @zh 游戏组件基类
* @en Base class for game components
*
* @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;
* public maxHealth: number = 100;
* }
* ```
*
* @example
* @zh 推荐做法:在 System 中处理逻辑
* @en Recommended: Handle logic in System
* ```typescript
* class HealthSystem extends EntitySystem {
* process(entities: Entity[]): void {
* for (const entity of entities) {
* const health = entity.getComponent(HealthComponent);
* if (health && health.health <= 0) {
* entity.destroy();
* }
* }
* }
* }
* ```
*/
export abstract class Component implements IComponent {
/**
* @zh 组件ID生成器用于为每个组件分配唯一的ID
* @en Component ID generator, used to assign unique IDs to each component
*/
private static _idGenerator: number = 0;
/**
* @zh 组件唯一标识符,在整个游戏生命周期中唯一
* @en Unique identifier for the component, unique throughout the game lifecycle
*/
public readonly id: number;
/**
* @zh 所属实体ID
* @en Owner entity ID
*
* @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;
/**
* @zh 最后写入的 epoch用于帧级变更检测
* @en Last write epoch, used for frame-level change detection
*
* @zh 记录组件最后一次被修改时的 epoch0 表示从未被标记为已修改
* @en Records the epoch when component was last modified, 0 means never marked as modified
*/
private _lastWriteEpoch: number = 0;
/**
* @zh 获取最后写入的 epoch
* @en Get last write epoch
*/
public get lastWriteEpoch(): number {
return this._lastWriteEpoch;
}
/**
* @zh 创建组件实例自动分配唯一ID
* @en Create component instance, automatically assigns unique ID
*/
constructor() {
this.id = Component._idGenerator++;
}
/**
* @zh 标记组件为已修改
* @en Mark component as modified
*
* @zh 调用此方法会更新组件的 lastWriteEpoch 为当前帧的 epoch。
* 系统可以通过比较 lastWriteEpoch 和上次检查的 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 - @zh 当前帧的 epoch @en Current frame's epoch
*
* @example
* ```typescript
* // @zh 在修改组件数据后调用 | @en Call after modifying component data
* velocity.x = 10;
* velocity.markDirty(scene.epochManager.current);
* ```
*/
public markDirty(epoch: number): void {
this._lastWriteEpoch = epoch;
}
/**
* @zh 组件添加到实体时的回调
* @en Callback when component is added to an entity
*
* @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
*
* @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
*
* @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
* class TilemapComponent extends Component {
* public tilesetImage: string = '';
* private _tilesetData: TilesetData | undefined;
*
* public async onDeserialized(): Promise<void> {
* if (this.tilesetImage) {
* // @zh 重新加载 tileset 图片并恢复运行时数据
* // @en Reload tileset image and restore runtime data
* const img = await loadImage(this.tilesetImage);
* this.setTilesetInfo(img.width, img.height, ...);
* }
* }
* }
* ```
*/
public onDeserialized(): void | Promise<void> {}
}

View File

@@ -0,0 +1,56 @@
import { Component } from '../Component';
import { ECSComponent } from '../Decorators';
import { Serializable, Serialize } from '../Serialization/SerializationDecorators';
/**
* 层级关系组件 - 用于建立实体间的父子关系
*
* 只有需要层级关系的实体才添加此组件,遵循 ECS 组合原则。
* 层级操作应通过 HierarchySystem 进行,而非直接修改此组件。
*
* @example
* ```typescript
* // 通过 HierarchySystem 设置父子关系
* const hierarchySystem = scene.getSystem(HierarchySystem);
* hierarchySystem.setParent(childEntity, parentEntity);
*
* // 查询层级信息
* const parent = hierarchySystem.getParent(entity);
* const children = hierarchySystem.getChildren(entity);
* ```
*/
@ECSComponent('Hierarchy', { editor: { hideInInspector: true } })
@Serializable({ version: 1, typeId: 'Hierarchy' })
export class HierarchyComponent extends Component {
/**
* 父实体 ID
* null 表示根实体(无父级)
*/
@Serialize()
public parentId: number | null = null;
/**
* 子实体 ID 列表
* 顺序即为子级的排列顺序
*/
@Serialize()
public childIds: number[] = [];
/**
* 在层级中的深度
* 根实体深度为 0由 HierarchySystem 维护
*/
public depth: number = 0;
/**
* 层级中是否激活
* 考虑所有祖先的激活状态,由 HierarchySystem 维护
*/
public bActiveInHierarchy: boolean = true;
/**
* 层级缓存是否脏
* 用于优化缓存更新
*/
public bCacheDirty: boolean = true;
}

View File

@@ -0,0 +1,211 @@
/**
* 预制体实例组件 - 用于追踪预制体实例
* Prefab instance component - for tracking prefab instances
*
* 当实体从预制体实例化时,会自动添加此组件以追踪其源预制体。
* When an entity is instantiated from a prefab, this component is automatically added to track its source.
*/
import { Component } from '../Component';
import { ECSComponent } from '../Decorators';
import { Serializable, Serialize } from '../Serialization/SerializationDecorators';
/**
* 预制体实例组件
* Prefab instance component
*
* 标记实体为预制体实例,并存储与源预制体的关联信息。
* Marks an entity as a prefab instance and stores association with source prefab.
*
* @example
* ```typescript
* // 检查实体是否为预制体实例 | Check if entity is a prefab instance
* const prefabComp = entity.getComponent(PrefabInstanceComponent);
* if (prefabComp) {
* console.log(`Instance of prefab: ${prefabComp.sourcePrefabGuid}`);
* }
* ```
*/
@ECSComponent('PrefabInstance', { editor: { hideInInspector: true } })
@Serializable({ version: 1, typeId: 'PrefabInstance' })
export class PrefabInstanceComponent extends Component {
/**
* 源预制体的资产 GUID
* Source prefab asset GUID
*/
@Serialize()
public sourcePrefabGuid: string = '';
/**
* 源预制体的资产路径(用于显示和调试)
* Source prefab asset path (for display and debugging)
*/
@Serialize()
public sourcePrefabPath: string = '';
/**
* 是否为预制体层级的根实体
* Whether this is the root entity of the prefab hierarchy
*/
@Serialize()
public isRoot: boolean = false;
/**
* 根预制体实例的实体 ID用于子实体追溯到根实例
* Entity ID of the root prefab instance (for child entities to trace back to root)
*/
@Serialize()
public rootInstanceEntityId: number | null = null;
/**
* 属性覆盖记录
* Property override records
*
* 记录哪些属性被用户修改过格式componentType.propertyPath
* Records which properties have been modified by user, format: componentType.propertyPath
*/
@Serialize()
public modifiedProperties: string[] = [];
/**
* 实例化时间戳
* Instantiation timestamp
*/
@Serialize()
public instantiatedAt: number = 0;
/**
* 属性原始值存储
* Original property values storage
*
* 存储被修改属性的原始值,用于还原操作。
* Stores original values of modified properties for revert operations.
* 格式:{ "ComponentType.propertyPath": originalValue }
* Format: { "ComponentType.propertyPath": originalValue }
*/
@Serialize()
public originalValues: Record<string, unknown> = {};
constructor(
sourcePrefabGuid: string = '',
sourcePrefabPath: string = '',
isRoot: boolean = false
) {
super();
this.sourcePrefabGuid = sourcePrefabGuid;
this.sourcePrefabPath = sourcePrefabPath;
this.isRoot = isRoot;
this.instantiatedAt = Date.now();
}
/**
* 标记属性为已修改
* Mark a property as modified
*
* @param componentType - 组件类型名称 | Component type name
* @param propertyPath - 属性路径 | Property path
*/
public markPropertyModified(componentType: string, propertyPath: string): void {
const key = `${componentType}.${propertyPath}`;
if (!this.modifiedProperties.includes(key)) {
this.modifiedProperties.push(key);
}
}
/**
* 检查属性是否已被修改
* Check if a property has been modified
*
* @param componentType - 组件类型名称 | Component type name
* @param propertyPath - 属性路径 | Property path
* @returns 是否已修改 | Whether it has been modified
*/
public isPropertyModified(componentType: string, propertyPath: string): boolean {
const key = `${componentType}.${propertyPath}`;
return this.modifiedProperties.includes(key);
}
/**
* 清除属性修改标记
* Clear property modification mark
*
* @param componentType - 组件类型名称 | Component type name
* @param propertyPath - 属性路径 | Property path
*/
public clearPropertyModified(componentType: string, propertyPath: string): void {
const key = `${componentType}.${propertyPath}`;
const index = this.modifiedProperties.indexOf(key);
if (index !== -1) {
this.modifiedProperties.splice(index, 1);
}
}
/**
* 清除所有属性修改标记
* Clear all property modification marks
*/
public clearAllModifications(): void {
this.modifiedProperties = [];
this.originalValues = {};
}
/**
* 存储属性的原始值
* Store original value of a property
*
* 只有在第一次修改时才存储,后续修改不覆盖。
* Only stores on first modification, subsequent modifications don't overwrite.
*
* @param componentType - 组件类型名称 | Component type name
* @param propertyPath - 属性路径 | Property path
* @param value - 原始值 | Original value
*/
public storeOriginalValue(componentType: string, propertyPath: string, value: unknown): void {
const key = `${componentType}.${propertyPath}`;
// 只在第一次修改时存储原始值 | Only store on first modification
if (!(key in this.originalValues)) {
// 深拷贝值以防止引用问题 | Deep clone to prevent reference issues
this.originalValues[key] = this.deepClone(value);
}
}
/**
* 获取属性的原始值
* Get original value of a property
*
* @param key - 属性键格式componentType.propertyPath| Property key (format: componentType.propertyPath)
* @returns 原始值,如果不存在则返回 undefined | Original value or undefined if not found
*/
public getOriginalValue(key: string): unknown {
return this.originalValues[key];
}
/**
* 检查是否有属性的原始值
* Check if original value exists for a property
*
* @param componentType - 组件类型名称 | Component type name
* @param propertyPath - 属性路径 | Property path
* @returns 是否存在原始值 | Whether original value exists
*/
public hasOriginalValue(componentType: string, propertyPath: string): boolean {
const key = `${componentType}.${propertyPath}`;
return key in this.originalValues;
}
/**
* 深拷贝值
* Deep clone value
*/
private deepClone(value: unknown): unknown {
if (value === null || value === undefined) return value;
if (typeof value === 'object') {
try {
return JSON.parse(JSON.stringify(value));
} catch {
return value;
}
}
return value;
}
}

View File

@@ -0,0 +1,2 @@
export { HierarchyComponent } from './HierarchyComponent';
export { PrefabInstanceComponent } from './PrefabInstanceComponent';

View File

@@ -0,0 +1,307 @@
import { Entity } from '../Entity';
import { ComponentType, GlobalComponentRegistry } from './ComponentStorage';
import { BitMask64Data, BitMask64Utils } from '../Utils';
import { BitMaskHashMap } from '../Utils/BitMaskHashMap';
/**
* 原型标识符
*/
export type ArchetypeId = BitMask64Data;
/**
* 原型数据结构
*/
export type Archetype = {
/** 原型唯一标识符 */
id: ArchetypeId;
/** 包含的组件类型 */
componentTypes: ComponentType[];
/** 属于该原型的实体集合 */
entities: Set<Entity>;
}
/**
* 原型查询结果
*/
export type ArchetypeQueryResult = {
/** 匹配的原型列表 */
archetypes: Archetype[];
/** 所有匹配实体的总数 */
totalEntities: number;
}
/**
* Archetype系统
*
* 根据实体的组件组合将实体分组到不同的原型中,提供高效的查询性能。
*/
export class ArchetypeSystem {
/** 所有原型的映射表 */
private _archetypes = new BitMaskHashMap<Archetype>();
/** 实体到原型的映射 */
private _entityToArchetype = new Map<Entity, Archetype>();
/** 组件类型到原型的映射 */
private _componentToArchetypes = new Map<ComponentType, Set<Archetype>>();
/** 实体组件类型缓存 */
private _entityComponentTypesCache = new Map<Entity, ComponentType[]>();
/** 所有原型 */
private _allArchetypes: Archetype[] = [];
/**
* 添加实体到原型系统
*/
public addEntity(entity: Entity): void {
const componentTypes = this.getEntityComponentTypes(entity);
const archetypeId = this.generateArchetypeId(componentTypes);
let archetype = this._archetypes.get(archetypeId);
if (!archetype) {
archetype = this.createArchetype(componentTypes);
}
archetype.entities.add(entity);
this._entityToArchetype.set(entity, archetype);
}
/**
* 从原型系统中移除实体
*/
public removeEntity(entity: Entity): void {
const archetype = this._entityToArchetype.get(entity);
if (!archetype) return;
archetype.entities.delete(entity);
// 清理实体相关缓存
this._entityComponentTypesCache.delete(entity);
this._entityToArchetype.delete(entity);
}
/**
* 更新实体的原型归属
*
* 当实体的组件组合发生变化时调用此方法,将实体从旧原型移动到新原型。
* 如果新的组件组合对应的原型不存在,将自动创建新原型。
*
* @param entity 要更新的实体
*/
public updateEntity(entity: Entity): void {
const currentArchetype = this._entityToArchetype.get(entity);
// 清理实体组件类型缓存,强制重新计算
this._entityComponentTypesCache.delete(entity);
const newComponentTypes = this.getEntityComponentTypes(entity);
const newArchetypeId = this.generateArchetypeId(newComponentTypes);
// 如果实体已在正确的原型中,无需更新
if (currentArchetype && currentArchetype.id === newArchetypeId) {
return;
}
// 从旧原型中移除实体
if (currentArchetype) {
currentArchetype.entities.delete(entity);
}
// 获取或创建新原型
let newArchetype = this._archetypes.get(newArchetypeId);
if (!newArchetype) {
newArchetype = this.createArchetype(newComponentTypes);
}
// 将实体添加到新原型
newArchetype.entities.add(entity);
this._entityToArchetype.set(entity, newArchetype);
}
/**
* 查询包含指定组件组合的原型
*
* @param componentTypes 要查询的组件类型列表
* @param operation 查询操作类型:'AND'(包含所有)或 'OR'(包含任意)
* @returns 匹配的原型列表及实体总数
*/
public queryArchetypes(componentTypes: ComponentType[], operation: 'AND' | 'OR' = 'AND'): ArchetypeQueryResult {
const matchingArchetypes: Archetype[] = [];
let totalEntities = 0;
if (operation === 'AND') {
if (componentTypes.length === 0) {
for (const archetype of this._allArchetypes) {
matchingArchetypes.push(archetype);
totalEntities += archetype.entities.size;
}
return { archetypes: matchingArchetypes, totalEntities };
}
if (componentTypes.length === 1) {
const archetypes = this._componentToArchetypes.get(componentTypes[0]!);
if (archetypes) {
for (const archetype of archetypes) {
matchingArchetypes.push(archetype);
totalEntities += archetype.entities.size;
}
}
return { archetypes: matchingArchetypes, totalEntities };
}
let smallestSet: Set<Archetype> | undefined;
let smallestSize = Infinity;
for (const componentType of componentTypes) {
const archetypes = this._componentToArchetypes.get(componentType);
if (!archetypes || archetypes.size === 0) {
return { archetypes: [], totalEntities: 0 };
}
if (archetypes.size < smallestSize) {
smallestSize = archetypes.size;
smallestSet = archetypes;
}
}
const queryMask = this.generateArchetypeId(componentTypes);
if (smallestSet) {
for (const archetype of smallestSet) {
if (BitMask64Utils.hasAll(archetype.id, queryMask)) {
matchingArchetypes.push(archetype);
totalEntities += archetype.entities.size;
}
}
}
} else {
const foundArchetypes = new Set<Archetype>();
for (const componentType of componentTypes) {
const archetypes = this._componentToArchetypes.get(componentType);
if (archetypes) {
for (const archetype of archetypes) {
foundArchetypes.add(archetype);
}
}
}
for (const archetype of foundArchetypes) {
matchingArchetypes.push(archetype);
totalEntities += archetype.entities.size;
}
}
return {
archetypes: matchingArchetypes,
totalEntities
};
}
/**
* 获取实体所属的原型
*/
public getEntityArchetype(entity: Entity): Archetype | undefined {
return this._entityToArchetype.get(entity);
}
/**
* 获取所有原型
*/
public getAllArchetypes(): Archetype[] {
return this._allArchetypes.slice();
}
/**
* 获取包含指定组件类型的所有实体
*/
public getEntitiesByComponent(componentType: ComponentType): Entity[] {
const archetypes = this._componentToArchetypes.get(componentType);
if (!archetypes || archetypes.size === 0) {
return [];
}
const entities: Entity[] = [];
for (const archetype of archetypes) {
for (const entity of archetype.entities) {
entities.push(entity);
}
}
return entities;
}
/**
* 清空所有数据
*/
public clear(): void {
this._archetypes.clear();
this._entityToArchetype.clear();
this._componentToArchetypes.clear();
this._entityComponentTypesCache.clear();
this._allArchetypes = [];
}
/**
* 更新所有原型数组
*/
private updateAllArchetypeArrays(): void {
this._allArchetypes = [];
for (const archetype of this._archetypes.values()) {
this._allArchetypes.push(archetype);
}
}
/**
* 获取实体的组件类型列表
*/
private getEntityComponentTypes(entity: Entity): ComponentType[] {
let componentTypes = this._entityComponentTypesCache.get(entity);
if (!componentTypes) {
componentTypes = entity.components.map((component) => component.constructor as ComponentType);
this._entityComponentTypesCache.set(entity, componentTypes);
}
return componentTypes;
}
/**
* 生成原型ID
* 使用ComponentRegistry确保与Entity.componentMask使用相同的bitIndex
*/
private generateArchetypeId(componentTypes: ComponentType[]): ArchetypeId {
const mask = BitMask64Utils.clone(BitMask64Utils.ZERO);
for (const type of componentTypes) {
const bitMask = GlobalComponentRegistry.getBitMask(type);
BitMask64Utils.orInPlace(mask, bitMask);
}
return mask;
}
/**
* 创建新原型
*/
private createArchetype(componentTypes: ComponentType[]): Archetype {
const id = this.generateArchetypeId(componentTypes);
const archetype: Archetype = {
id,
componentTypes: [...componentTypes],
entities: new Set<Entity>()
};
this._archetypes.set(id,archetype);
this.updateAllArchetypeArrays();
for (const componentType of componentTypes) {
let archetypes = this._componentToArchetypes.get(componentType);
if (!archetypes) {
archetypes = new Set();
this._componentToArchetypes.set(componentType, archetypes);
}
archetypes.add(archetype);
}
return archetype;
}
}

View File

@@ -0,0 +1,580 @@
import { Entity } from '../Entity';
import { Component } from '../Component';
import { ComponentType, GlobalComponentRegistry } from './ComponentStorage';
import { getComponentTypeName } from '../Decorators';
import { IScene } from '../IScene';
import { createLogger } from '../../Utils/Logger';
const logger = createLogger('CommandBuffer');
/**
* 延迟命令类型
* Deferred command type
*/
export enum CommandType {
/** 添加组件 | Add component */
ADD_COMPONENT = 'add_component',
/** 移除组件 | Remove component */
REMOVE_COMPONENT = 'remove_component',
/** 销毁实体 | Destroy entity */
DESTROY_ENTITY = 'destroy_entity',
/** 设置实体激活状态 | Set entity active state */
SET_ENTITY_ACTIVE = 'set_entity_active'
}
/**
* 延迟命令接口
* Deferred command interface
*/
export type DeferredCommand = {
/** 命令类型 | Command type */
type: CommandType;
/** 目标实体 | Target entity */
entity: Entity;
/** 组件实例(用于 ADD_COMPONENT| Component instance (for ADD_COMPONENT) */
component?: Component;
/** 组件类型(用于 REMOVE_COMPONENT| Component type (for REMOVE_COMPONENT) */
componentType?: ComponentType;
/** 布尔值(用于启用/激活状态)| Boolean value (for enabled/active state) */
value?: boolean;
}
/**
* 每个实体的待处理操作
* Pending operations per entity
*
* 使用 last-write-wins 语义进行去重。
* Uses last-write-wins semantics for deduplication.
*/
interface PendingPerEntity {
/** 是否销毁实体(如果为 true忽略其他操作| Destroy entity (if true, ignores other operations) */
bDestroy?: boolean;
/** 最终的激活状态 | Final active state */
active?: boolean;
/** 要添加的组件typeId -> Component| Components to add (typeId -> Component) */
adds?: Map<number, Component>;
/** 要移除的组件类型 ID 集合 | Component type IDs to remove */
removes?: Set<number>;
}
/**
* 命令缓冲区 - 用于延迟执行实体操作
* Command Buffer - for deferred entity operations
*
* 在系统的 process() 方法中使用 CommandBuffer 可以避免迭代过程中修改实体列表,
* 从而提高性能(无需每帧拷贝数组)并保证迭代安全。
*
* 特点:
* - **去重**: 同一实体的多次同类操作会被合并last-write-wins
* - **有序执行**: removes -> adds -> active -> destroy
* - **冲突处理**: addComponent 会取消同类型的 removeComponent反之亦然
* - **销毁优先**: destroyEntity 会取消该实体的所有其他操作
*
* Features:
* - **Deduplication**: Multiple operations on same entity are merged (last-write-wins)
* - **Ordered execution**: removes -> adds -> active -> destroy
* - **Conflict handling**: addComponent cancels same-type removeComponent and vice versa
* - **Destroy priority**: destroyEntity cancels all other operations for that entity
*
* @example
* ```typescript
* class DamageSystem extends EntitySystem {
* protected process(entities: readonly Entity[]): void {
* for (const entity of entities) {
* const health = entity.getComponent(Health);
* if (health.value <= 0) {
* // 延迟到帧末执行,不影响当前迭代
* // Deferred to end of frame, doesn't affect current iteration
* this.commands.addComponent(entity, new DeadMarker());
* this.commands.destroyEntity(entity);
* }
* }
* }
* }
* ```
*/
export class CommandBuffer {
/** 每个实体的待处理操作 | Pending operations per entity */
private _pending: Map<Entity, PendingPerEntity> = new Map();
/** 旧式命令队列(用于兼容)| Legacy command queue (for compatibility) */
private _commands: DeferredCommand[] = [];
/** 关联的场景 | Associated scene */
private _scene: IScene | null = null;
/** 是否启用调试日志 | Enable debug logging */
private _debug: boolean = false;
/** 是否使用去重模式 | Whether to use deduplication mode */
private _useDeduplication: boolean = true;
/**
* 创建命令缓冲区
* Create command buffer
*
* @param scene - 关联的场景 | Associated scene
* @param debug - 是否启用调试 | Enable debug
*/
constructor(scene?: IScene, debug: boolean = false) {
this._scene = scene ?? null;
this._debug = debug;
}
/**
* 设置关联的场景
* Set associated scene
*/
public setScene(scene: IScene | null): void {
this._scene = scene;
}
/**
* 获取关联的场景
* Get associated scene
*/
public get scene(): IScene | null {
return this._scene;
}
/**
* 设置是否使用去重模式
* Set whether to use deduplication mode
*
* @param enabled - 是否启用 | Whether to enable
*/
public setDeduplication(enabled: boolean): void {
this._useDeduplication = enabled;
}
/**
* 获取待执行的命令数量
* Get pending command count
*
* 返回实际的操作数量,而不是实体数量。
* Returns actual operation count, not entity count.
*/
public get pendingCount(): number {
if (this._useDeduplication) {
let count = 0;
for (const ops of this._pending.values()) {
if (ops.bDestroy) count++;
if (ops.active !== undefined) count++;
if (ops.adds) count += ops.adds.size;
if (ops.removes) count += ops.removes.size;
}
return count;
}
return this._commands.length;
}
/**
* 检查是否有待执行的命令
* Check if there are pending commands
*/
public get hasPending(): boolean {
if (this._useDeduplication) {
return this._pending.size > 0;
}
return this._commands.length > 0;
}
/**
* 获取或创建实体的待处理操作
* Get or create pending operations for entity
*/
private getPending(entity: Entity): PendingPerEntity {
let pending = this._pending.get(entity);
if (!pending) {
pending = {};
this._pending.set(entity, pending);
}
return pending;
}
/**
* 获取组件类型 ID位索引
* Get component type ID (bit index)
*/
private getTypeId(componentOrType: Component | ComponentType): number {
if (typeof componentOrType === 'function') {
// ComponentType
return GlobalComponentRegistry.getBitIndex(componentOrType);
} else {
// Component instance
return GlobalComponentRegistry.getBitIndex(componentOrType.constructor as ComponentType);
}
}
/**
* 延迟添加组件
* Deferred add component
*
* 如果之前有同类型的 removeComponent会被取消。
* If there was a removeComponent for the same type, it will be canceled.
*
* @param entity - 目标实体 | Target entity
* @param component - 要添加的组件 | Component to add
*/
public addComponent(entity: Entity, component: Component): void {
if (this._useDeduplication) {
const pending = this.getPending(entity);
// 如果实体已标记销毁,忽略
if (pending.bDestroy) {
if (this._debug) {
logger.debug(`CommandBuffer: 忽略添加组件,实体 ${entity.name} 已标记销毁`);
}
return;
}
const typeId = this.getTypeId(component);
// 取消同类型的 remove
pending.removes?.delete(typeId);
// 添加(覆盖同类型的之前的 add
if (!pending.adds) {
pending.adds = new Map();
}
pending.adds.set(typeId, component);
if (this._debug) {
const typeName = getComponentTypeName(component.constructor as ComponentType);
logger.debug(`CommandBuffer: 延迟添加组件 ${typeName} 到实体 ${entity.name}`);
}
} else {
// 旧模式
this._commands.push({
type: CommandType.ADD_COMPONENT,
entity,
component
});
}
}
/**
* 延迟移除组件
* Deferred remove component
*
* 如果之前有同类型的 addComponent会被取消。
* If there was an addComponent for the same type, it will be canceled.
*
* @param entity - 目标实体 | Target entity
* @param componentType - 要移除的组件类型 | Component type to remove
*/
public removeComponent<T extends Component>(entity: Entity, componentType: ComponentType<T>): void {
if (this._useDeduplication) {
const pending = this.getPending(entity);
// 如果实体已标记销毁,忽略
if (pending.bDestroy) {
if (this._debug) {
logger.debug(`CommandBuffer: 忽略移除组件,实体 ${entity.name} 已标记销毁`);
}
return;
}
const typeId = this.getTypeId(componentType);
// 取消同类型的 add
pending.adds?.delete(typeId);
// 添加到移除列表
if (!pending.removes) {
pending.removes = new Set();
}
pending.removes.add(typeId);
if (this._debug) {
logger.debug(`CommandBuffer: 延迟移除组件 ${componentType.name} 从实体 ${entity.name}`);
}
} else {
// 旧模式
this._commands.push({
type: CommandType.REMOVE_COMPONENT,
entity,
componentType
});
}
}
/**
* 延迟销毁实体
* Deferred destroy entity
*
* 会取消该实体的所有其他待处理操作。
* Cancels all other pending operations for this entity.
*
* @param entity - 要销毁的实体 | Entity to destroy
*/
public destroyEntity(entity: Entity): void {
if (this._useDeduplication) {
const pending = this.getPending(entity);
// 清除所有其他操作
pending.adds?.clear();
pending.removes?.clear();
delete pending.active;
// 标记销毁
pending.bDestroy = true;
if (this._debug) {
logger.debug(`CommandBuffer: 延迟销毁实体 ${entity.name}`);
}
} else {
// 旧模式
this._commands.push({
type: CommandType.DESTROY_ENTITY,
entity
});
}
}
/**
* 延迟设置实体激活状态
* Deferred set entity active state
*
* @param entity - 目标实体 | Target entity
* @param active - 激活状态 | Active state
*/
public setEntityActive(entity: Entity, active: boolean): void {
if (this._useDeduplication) {
const pending = this.getPending(entity);
// 如果实体已标记销毁,忽略
if (pending.bDestroy) {
if (this._debug) {
logger.debug(`CommandBuffer: 忽略设置激活状态,实体 ${entity.name} 已标记销毁`);
}
return;
}
// 设置(覆盖之前的设置)
pending.active = active;
if (this._debug) {
logger.debug(`CommandBuffer: 延迟设置实体 ${entity.name} 激活状态为 ${active}`);
}
} else {
// 旧模式
this._commands.push({
type: CommandType.SET_ENTITY_ACTIVE,
entity,
value: active
});
}
}
/**
* 执行所有待处理的命令
* Execute all pending commands
*
* 执行顺序removes -> adds -> active -> destroy
* Execution order: removes -> adds -> active -> destroy
*
* 通常在帧末由 Scene 自动调用。
* Usually called automatically by Scene at end of frame.
*
* @returns 执行的命令数量 | Number of commands executed
*/
public flush(): number {
if (this._useDeduplication) {
return this.flushDeduplication();
} else {
return this.flushLegacy();
}
}
/**
* 使用去重模式刷新
* Flush using deduplication mode
*/
private flushDeduplication(): number {
if (this._pending.size === 0) {
return 0;
}
const entityCount = this._pending.size;
let commandCount = 0;
if (this._debug) {
logger.debug(`CommandBuffer: 开始执行 ${entityCount} 个实体的延迟命令`);
}
// 复制并清空,防止执行过程中有新命令加入
const pending = this._pending;
this._pending = new Map();
// 分阶段执行
// Phase 1: Removes
for (const [entity, ops] of pending) {
if (ops.bDestroy || !entity.scene) continue;
if (ops.removes && ops.removes.size > 0) {
for (const typeId of ops.removes) {
try {
const componentType = GlobalComponentRegistry.getTypeByBitIndex(typeId);
if (componentType) {
entity.removeComponentByType(componentType);
commandCount++;
}
} catch (error) {
logger.error(`CommandBuffer: 移除组件失败`, { entity: entity.name, typeId, error });
}
}
}
}
// Phase 2: Adds
for (const [entity, ops] of pending) {
if (ops.bDestroy || !entity.scene) continue;
if (ops.adds && ops.adds.size > 0) {
for (const component of ops.adds.values()) {
try {
entity.addComponent(component);
commandCount++;
} catch (error) {
const typeName = getComponentTypeName(component.constructor as ComponentType);
logger.error(`CommandBuffer: 添加组件失败`, {
entity: entity.name,
component: typeName,
error
});
}
}
}
}
// Phase 3: Active state
for (const [entity, ops] of pending) {
if (ops.bDestroy || !entity.scene) continue;
if (ops.active !== undefined) {
try {
entity.active = ops.active;
commandCount++;
} catch (error) {
logger.error(`CommandBuffer: 设置激活状态失败`, { entity: entity.name, error });
}
}
}
// Phase 4: Destroy
for (const [entity, ops] of pending) {
if (!ops.bDestroy || !entity.scene) continue;
try {
entity.destroy();
commandCount++;
} catch (error) {
logger.error(`CommandBuffer: 销毁实体失败`, { entity: entity.name, error });
}
}
if (this._debug) {
logger.debug(`CommandBuffer: 完成执行 ${commandCount} 个延迟命令`);
}
return commandCount;
}
/**
* 使用旧模式刷新
* Flush using legacy mode
*/
private flushLegacy(): number {
if (this._commands.length === 0) {
return 0;
}
const count = this._commands.length;
if (this._debug) {
logger.debug(`CommandBuffer: 开始执行 ${count} 个延迟命令`);
}
// 复制命令列表并清空,防止执行过程中有新命令加入
const commands = this._commands;
this._commands = [];
for (const cmd of commands) {
this.executeCommand(cmd);
}
if (this._debug) {
logger.debug(`CommandBuffer: 完成执行 ${count} 个延迟命令`);
}
return count;
}
/**
* 执行单个命令(旧模式)
* Execute single command (legacy mode)
*/
private executeCommand(cmd: DeferredCommand): void {
// 检查实体是否仍然有效
if (!cmd.entity.scene) {
if (this._debug) {
logger.debug(`CommandBuffer: 跳过命令,实体 ${cmd.entity.name} 已无效`);
}
return;
}
try {
switch (cmd.type) {
case CommandType.ADD_COMPONENT:
if (cmd.component) {
cmd.entity.addComponent(cmd.component);
}
break;
case CommandType.REMOVE_COMPONENT:
if (cmd.componentType) {
cmd.entity.removeComponentByType(cmd.componentType);
}
break;
case CommandType.DESTROY_ENTITY:
cmd.entity.destroy();
break;
case CommandType.SET_ENTITY_ACTIVE:
if (cmd.value !== undefined) {
cmd.entity.active = cmd.value;
}
break;
}
} catch (error) {
logger.error(`CommandBuffer: 执行命令失败`, { command: cmd, error });
}
}
/**
* 清空所有待处理的命令(不执行)
* Clear all pending commands (without executing)
*/
public clear(): void {
if (this._debug) {
const count = this._useDeduplication ? this._pending.size : this._commands.length;
if (count > 0) {
logger.debug(`CommandBuffer: 清空 ${count} 个未执行的命令`);
}
}
this._pending.clear();
this._commands.length = 0;
}
/**
* 销毁命令缓冲区
* Dispose command buffer
*/
public dispose(): void {
this.clear();
this._scene = null;
}
}

View File

@@ -0,0 +1,372 @@
import { Component } from '../Component';
/**
* 组件对象池,用于复用组件实例以减少内存分配
*/
export class ComponentPool<T extends Component> {
private pool: T[] = [];
private createFn: () => T;
private resetFn?: (component: T) => void;
private maxSize: number;
private minSize: number;
private stats = {
totalCreated: 0,
totalAcquired: 0,
totalReleased: 0
};
constructor(
createFn: () => T,
resetFn?: (component: T) => void,
maxSize: number = 1000,
minSize: number = 10
) {
this.createFn = createFn;
if (resetFn) {
this.resetFn = resetFn;
}
this.maxSize = maxSize;
this.minSize = Math.max(1, minSize);
}
/**
* 获取一个组件实例
*/
acquire(): T {
this.stats.totalAcquired++;
if (this.pool.length > 0) {
return this.pool.pop()!;
}
this.stats.totalCreated++;
return this.createFn();
}
/**
* 释放一个组件实例回池中
*/
release(component: T): void {
this.stats.totalReleased++;
if (this.pool.length >= this.maxSize) {
return;
}
if (this.resetFn) {
this.resetFn(component);
}
this.pool.push(component);
}
/**
* 预填充对象池
*/
prewarm(count: number): void {
const targetCount = Math.min(count, this.maxSize);
for (let i = this.pool.length; i < targetCount; i++) {
const component = this.createFn();
if (this.resetFn) {
this.resetFn(component);
}
this.pool.push(component);
this.stats.totalCreated++;
}
}
/**
* 自动收缩池大小
*/
shrink(): void {
while (this.pool.length > this.minSize) {
this.pool.pop();
}
}
/**
* 清空对象池
*/
clear(): void {
this.pool.length = 0;
}
/**
* 获取池中可用对象数量
*/
getAvailableCount(): number {
return this.pool.length;
}
/**
* 获取池的最大容量
*/
getMaxSize(): number {
return this.maxSize;
}
/**
* 获取统计信息
*/
getStats() {
const hitRate = this.stats.totalAcquired === 0
? 0
: (this.stats.totalAcquired - this.stats.totalCreated) / this.stats.totalAcquired;
return {
totalCreated: this.stats.totalCreated,
totalAcquired: this.stats.totalAcquired,
totalReleased: this.stats.totalReleased,
hitRate: hitRate,
currentSize: this.pool.length,
maxSize: this.maxSize,
minSize: this.minSize,
utilizationRate: this.pool.length / this.maxSize
};
}
}
/**
* 组件使用追踪
*/
interface ComponentUsageTracker {
createCount: number;
releaseCount: number;
lastAccessTime: number;
}
/**
* 全局组件池管理器
*/
export class ComponentPoolManager {
private static _instance: ComponentPoolManager;
private _pools = new Map<string, ComponentPool<Component>>();
private _usageTracker = new Map<string, ComponentUsageTracker>();
private _autoCleanupInterval = 60000;
private _lastCleanupTime = 0;
private constructor() {}
static getInstance(): ComponentPoolManager {
if (!ComponentPoolManager._instance) {
ComponentPoolManager._instance = new ComponentPoolManager();
}
return ComponentPoolManager._instance;
}
/**
* @zh 注册组件池
* @en Register component pool
*/
registerPool<T extends Component>(
componentName: string,
createFn: () => T,
resetFn?: (component: T) => void,
maxSize?: number,
minSize?: number
): void {
this._pools.set(componentName, new ComponentPool(createFn, resetFn, maxSize, minSize) as unknown as ComponentPool<Component>);
this._usageTracker.set(componentName, {
createCount: 0,
releaseCount: 0,
lastAccessTime: Date.now()
});
}
/**
* @zh 获取组件实例
* @en Acquire component instance
*/
acquireComponent<T extends Component>(componentName: string): T | null {
const pool = this._pools.get(componentName);
this._trackUsage(componentName, 'create');
return pool ? (pool.acquire() as T) : null;
}
/**
* @zh 释放组件实例
* @en Release component instance
*/
releaseComponent<T extends Component>(componentName: string, component: T): void {
const pool = this._pools.get(componentName);
this._trackUsage(componentName, 'release');
if (pool) {
pool.release(component);
}
}
/**
* @zh 追踪使用情况
* @en Track usage
*/
private _trackUsage(componentName: string, action: 'create' | 'release'): void {
let tracker = this._usageTracker.get(componentName);
if (!tracker) {
tracker = {
createCount: 0,
releaseCount: 0,
lastAccessTime: Date.now()
};
this._usageTracker.set(componentName, tracker);
}
if (action === 'create') {
tracker.createCount++;
} else {
tracker.releaseCount++;
}
tracker.lastAccessTime = Date.now();
}
/**
* @zh 自动清理(定期调用)
* @en Auto cleanup (called periodically)
*/
public update(): void {
const now = Date.now();
if (now - this._lastCleanupTime < this._autoCleanupInterval) {
return;
}
for (const [name, tracker] of this._usageTracker.entries()) {
const inactive = now - tracker.lastAccessTime > 120000;
if (inactive) {
const pool = this._pools.get(name);
if (pool) {
pool.shrink();
}
}
}
this._lastCleanupTime = now;
}
/**
* @zh 获取热点组件列表
* @en Get hot components list
*/
public getHotComponents(threshold: number = 100): string[] {
return Array.from(this._usageTracker.entries())
.filter(([_, tracker]) => tracker.createCount > threshold)
.map(([name]) => name);
}
/**
* @zh 预热所有池
* @en Prewarm all pools
*/
prewarmAll(count: number = 100): void {
for (const pool of this._pools.values()) {
pool.prewarm(count);
}
}
/**
* @zh 清空所有池
* @en Clear all pools
*/
clearAll(): void {
for (const pool of this._pools.values()) {
pool.clear();
}
}
/**
* @zh 重置管理器
* @en Reset manager
*/
reset(): void {
this._pools.clear();
this._usageTracker.clear();
}
/**
* @zh 获取全局统计信息
* @en Get global stats
*/
getGlobalStats(): Array<{
componentName: string;
poolStats: ReturnType<ComponentPool<Component>['getStats']>;
usage: ComponentUsageTracker | undefined;
}> {
const stats: Array<{
componentName: string;
poolStats: ReturnType<ComponentPool<Component>['getStats']>;
usage: ComponentUsageTracker | undefined;
}> = [];
for (const [name, pool] of this._pools.entries()) {
stats.push({
componentName: name,
poolStats: pool.getStats(),
usage: this._usageTracker.get(name)
});
}
return stats;
}
/**
* @zh 获取池统计信息
* @en Get pool stats
*/
getPoolStats(): Map<string, { available: number; maxSize: number }> {
const stats = new Map();
for (const [name, pool] of this._pools) {
stats.set(name, {
available: pool.getAvailableCount(),
maxSize: pool.getMaxSize()
});
}
return stats;
}
/**
* @zh 获取池利用率信息
* @en Get pool utilization info
*/
getPoolUtilization(): Map<string, { used: number; total: number; utilization: number }> {
const utilization = new Map();
for (const [name, pool] of this._pools) {
const available = pool.getAvailableCount();
const maxSize = pool.getMaxSize();
const used = maxSize - available;
const utilRate = maxSize > 0 ? (used / maxSize * 100) : 0;
utilization.set(name, {
used: used,
total: maxSize,
utilization: utilRate
});
}
return utilization;
}
/**
* @zh 获取指定组件的池利用率
* @en Get component pool utilization
*/
getComponentUtilization(componentName: string): number {
const pool = this._pools.get(componentName);
if (!pool) return 0;
const available = pool.getAvailableCount();
const maxSize = pool.getMaxSize();
const used = maxSize - available;
return maxSize > 0 ? (used / maxSize * 100) : 0;
}
}

View File

@@ -0,0 +1,383 @@
import { Component } from '../Component';
import { BitMask64Utils, BitMask64Data } from '../Utils/BigIntCompatibility';
import { SoAStorage } from './SoAStorage';
import type { SupportedTypedArray } from './SoAStorage';
import { createLogger } from '../../Utils/Logger';
import { getComponentTypeName, ComponentType } from '../Decorators';
import { ComponentRegistry, GlobalComponentRegistry } from './ComponentStorage/ComponentRegistry';
import type { IComponentRegistry } from './ComponentStorage/IComponentRegistry';
// 导出核心类型
// Export core types
export { ComponentRegistry, GlobalComponentRegistry };
export type { IComponentRegistry };
export type { ComponentType };
/**
* 高性能组件存储器
*/
export class ComponentStorage<T extends Component> {
private dense: T[] = [];
private entityIds: number[] = [];
private entityToIndex = new Map<number, number>();
private componentType: ComponentType<T>;
constructor(componentType: ComponentType<T>) {
this.componentType = componentType;
}
/**
* 添加组件
* @param entityId 实体ID
* @param component 组件实例
*/
public addComponent(entityId: number, component: T): void {
// 检查实体是否已有此组件
if (this.entityToIndex.has(entityId)) {
throw new Error(`Entity ${entityId} already has component ${getComponentTypeName(this.componentType)}`);
}
// 末尾插入到致密数组
const index = this.dense.length;
this.dense.push(component);
this.entityIds.push(entityId);
this.entityToIndex.set(entityId, index);
}
/**
* 获取组件
* @param entityId 实体ID
* @returns 组件实例或null
*/
public getComponent(entityId: number): T | null {
const index = this.entityToIndex.get(entityId);
return index !== undefined ? this.dense[index]! : null;
}
/**
* 检查实体是否有此组件
* @param entityId 实体ID
* @returns 是否有组件
*/
public hasComponent(entityId: number): boolean {
return this.entityToIndex.has(entityId);
}
/**
* 移除组件
* @param entityId 实体ID
* @returns 被移除的组件或null
*/
public removeComponent(entityId: number): T | null {
const index = this.entityToIndex.get(entityId);
if (index === undefined) {
return null;
}
const component = this.dense[index]!;
const lastIndex = this.dense.length - 1;
if (index !== lastIndex) {
// 将末尾元素交换到要删除的位置
const lastComponent = this.dense[lastIndex]!;
const lastEntityId = this.entityIds[lastIndex]!;
this.dense[index] = lastComponent;
this.entityIds[index] = lastEntityId;
// 更新被交换元素的映射
this.entityToIndex.set(lastEntityId, index);
}
// 移除末尾元素
this.dense.pop();
this.entityIds.pop();
this.entityToIndex.delete(entityId);
return component;
}
/**
* 高效遍历所有组件
* @param callback 回调函数
*/
public forEach(callback: (component: T, entityId: number, index: number) => void): void {
for (let i = 0; i < this.dense.length; i++) {
callback(this.dense[i]!, this.entityIds[i]!, i);
}
}
/**
* 获取所有组件
* @returns 组件数组
*/
public getDenseArray(): { components: T[]; entityIds: number[] } {
return {
components: [...this.dense],
entityIds: [...this.entityIds]
};
}
/**
* 清空所有组件
*/
public clear(): void {
this.dense.length = 0;
this.entityIds.length = 0;
this.entityToIndex.clear();
}
/**
* 获取组件数量
*/
public get size(): number {
return this.dense.length;
}
/**
* 获取组件类型
*/
public get type(): ComponentType<T> {
return this.componentType;
}
/**
* 获取存储统计信息
*/
public getStats(): {
totalSlots: number;
usedSlots: number;
freeSlots: number;
fragmentation: number;
} {
const totalSlots = this.dense.length;
const usedSlots = this.dense.length;
const freeSlots = 0; // 永远无空洞
const fragmentation = 0; // 永远无碎片
return {
totalSlots,
usedSlots,
freeSlots,
fragmentation
};
}
}
/**
* 组件存储管理器
* 管理所有组件类型的存储器
*/
export class ComponentStorageManager {
private static readonly _logger = createLogger('ComponentStorage');
private storages = new Map<Function, ComponentStorage<Component> | SoAStorage<Component>>();
/**
* 检查组件类型是否启用SoA存储
* @param componentType 组件类型
* @returns 是否为SoA存储
*/
public isSoAStorage<T extends Component>(componentType: ComponentType<T>): boolean {
const storage = this.storages.get(componentType);
return storage instanceof SoAStorage;
}
/**
* 获取SoA存储器类型安全
* @param componentType 组件类型
* @returns SoA存储器或null
*/
public getSoAStorage<T extends Component>(componentType: ComponentType<T>): SoAStorage<T> | null {
const storage = this.getStorage(componentType);
return storage instanceof SoAStorage ? storage : null;
}
/**
* 直接获取SoA字段数组类型安全
* @param componentType 组件类型
* @param fieldName 字段名
* @returns TypedArray或null
*/
public getFieldArray<T extends Component>(
componentType: ComponentType<T>,
fieldName: string
): SupportedTypedArray | null {
const soaStorage = this.getSoAStorage(componentType);
return soaStorage ? soaStorage.getFieldArray(fieldName) : null;
}
/**
* 直接获取SoA字段数组类型安全带字段名检查
* @param componentType 组件类型
* @param fieldName 字段名(类型检查)
* @returns TypedArray或null
*/
public getTypedFieldArray<T extends Component, K extends keyof T>(
componentType: ComponentType<T>,
fieldName: K
): SupportedTypedArray | null {
const soaStorage = this.getSoAStorage(componentType);
return soaStorage ? soaStorage.getTypedFieldArray(fieldName) : null;
}
/**
* 获取SoA存储的活跃索引
* @param componentType 组件类型
* @returns 活跃索引数组或空数组
*/
public getActiveIndices<T extends Component>(componentType: ComponentType<T>): number[] {
const soaStorage = this.getSoAStorage(componentType);
return soaStorage ? soaStorage.getActiveIndices() : [];
}
/**
* 获取实体在SoA存储中的索引
* @param componentType 组件类型
* @param entityId 实体ID
* @returns 存储索引或undefined
*/
public getEntityIndex<T extends Component>(componentType: ComponentType<T>, entityId: number): number | undefined {
const soaStorage = this.getSoAStorage(componentType);
return soaStorage ? soaStorage.getEntityIndex(entityId) : undefined;
}
/**
* 根据索引获取实体ID
* @param componentType 组件类型
* @param index 存储索引
* @returns 实体ID或undefined
*/
public getEntityIdByIndex<T extends Component>(componentType: ComponentType<T>, index: number): number | undefined {
const soaStorage = this.getSoAStorage(componentType);
return soaStorage ? soaStorage.getEntityIdByIndex(index) : undefined;
}
/**
* 获取或创建组件存储器(默认原始存储)
* @param componentType 组件类型
* @returns 组件存储器
*/
public getStorage<T extends Component>(componentType: ComponentType<T>): ComponentStorage<T> | SoAStorage<T> {
let storage = this.storages.get(componentType);
if (!storage) {
// 检查是否启用SoA优化
const enableSoA = (componentType as unknown as { __enableSoA?: boolean }).__enableSoA;
if (enableSoA) {
// 使用SoA优化存储
storage = new SoAStorage(componentType);
ComponentStorageManager._logger.info(`${getComponentTypeName(componentType)} 启用SoA优化适用于大规模批量操作`);
} else {
// 默认使用原始存储
storage = new ComponentStorage(componentType);
}
this.storages.set(componentType, storage);
}
return storage as ComponentStorage<T> | SoAStorage<T>;
}
/**
* 添加组件
* @param entityId 实体ID
* @param component 组件实例
*/
public addComponent<T extends Component>(entityId: number, component: T): void {
const componentType = component.constructor as ComponentType<T>;
const storage = this.getStorage(componentType);
storage.addComponent(entityId, component);
}
/**
* 获取组件
* @param entityId 实体ID
* @param componentType 组件类型
* @returns 组件实例或null
*/
public getComponent<T extends Component>(entityId: number, componentType: ComponentType<T>): T | null {
const storage = this.storages.get(componentType);
return storage ? (storage as ComponentStorage<T> | SoAStorage<T>).getComponent(entityId) : null;
}
/**
* 检查实体是否有组件
* @param entityId 实体ID
* @param componentType 组件类型
* @returns 是否有组件
*/
public hasComponent<T extends Component>(entityId: number, componentType: ComponentType<T>): boolean {
const storage = this.storages.get(componentType);
return storage ? storage.hasComponent(entityId) : false;
}
/**
* 移除组件
* @param entityId 实体ID
* @param componentType 组件类型
* @returns 被移除的组件或null
*/
public removeComponent<T extends Component>(entityId: number, componentType: ComponentType<T>): T | null {
const storage = this.storages.get(componentType);
return storage ? (storage as ComponentStorage<T> | SoAStorage<T>).removeComponent(entityId) : null;
}
/**
* 移除实体的所有组件
* @param entityId 实体ID
*/
public removeAllComponents(entityId: number): void {
for (const storage of this.storages.values()) {
storage.removeComponent(entityId);
}
}
/**
* 获取实体的组件位掩码
* Get component bitmask for entity
*
* @param entityId 实体ID | Entity ID
* @param registry 组件注册表(可选,默认使用全局注册表)| Component registry (optional, defaults to global)
* @returns 组件位掩码 | Component bitmask
*/
public getComponentMask(entityId: number, registry: IComponentRegistry = GlobalComponentRegistry): BitMask64Data {
const mask = BitMask64Utils.clone(BitMask64Utils.ZERO);
for (const [componentType, storage] of this.storages.entries()) {
if (storage.hasComponent(entityId)) {
const componentMask = registry.getBitMask(componentType as ComponentType);
BitMask64Utils.orInPlace(mask, componentMask);
}
}
return mask;
}
/**
* 获取所有存储器的统计信息
*/
public getAllStats(): Map<string, { totalSlots: number; usedSlots: number; freeSlots: number; fragmentation: number }> {
const stats = new Map<string, { totalSlots: number; usedSlots: number; freeSlots: number; fragmentation: number }>();
for (const [componentType, storage] of this.storages.entries()) {
const typeName = getComponentTypeName(componentType as ComponentType);
stats.set(typeName, storage.getStats());
}
return stats;
}
/**
* 清空所有存储器
*/
public clear(): void {
for (const storage of this.storages.values()) {
storage.clear();
}
this.storages.clear();
}
}

View File

@@ -0,0 +1,359 @@
/**
* Component Registry Implementation.
* 组件注册表实现。
*
* Manages component type bitmask allocation.
* Each Scene has its own registry instance for isolation.
* 管理组件类型的位掩码分配。
* 每个 Scene 都有自己的注册表实例以实现隔离。
*/
import { Component } from '../../Component';
import { BitMask64Utils, BitMask64Data } from '../../Utils/BigIntCompatibility';
import { createLogger } from '../../../Utils/Logger';
import {
ComponentType,
getComponentTypeName,
hasECSComponentDecorator
} from './ComponentTypeUtils';
import type { IComponentRegistry } from './IComponentRegistry';
const logger = createLogger('ComponentRegistry');
/**
* Component Registry.
* 组件注册表。
*
* Instance-based registry for component type management.
* Each Scene should have its own registry.
* 基于实例的组件类型管理注册表。
* 每个 Scene 应有自己的注册表。
*/
export class ComponentRegistry implements IComponentRegistry {
private _componentTypes = new Map<Function, number>();
private _bitIndexToType = new Map<number, Function>();
private _componentNameToType = new Map<string, Function>();
private _componentNameToId = new Map<string, number>();
private _maskCache = new Map<string, BitMask64Data>();
private _nextBitIndex = 0;
private _hotReloadEnabled = false;
private _warnedComponents = new Set<Function>();
/**
* Register component type and allocate bitmask.
* 注册组件类型并分配位掩码。
*
* @param componentType - Component constructor | 组件构造函数
* @returns Allocated bit index | 分配的位索引
*/
public register<T extends Component>(componentType: ComponentType<T>): number {
const typeName = getComponentTypeName(componentType);
// Check if @ECSComponent decorator is used
// 检查是否使用了 @ECSComponent 装饰器
if (!hasECSComponentDecorator(componentType) && !this._warnedComponents.has(componentType)) {
this._warnedComponents.add(componentType);
logger.warn(
`Component "${typeName}" is missing @ECSComponent decorator. ` +
`This may cause issues with serialization and code minification. ` +
`Please add: @ECSComponent('${typeName}') | ` +
`组件 "${typeName}" 缺少 @ECSComponent 装饰器,可能导致序列化和代码压缩问题`
);
}
if (this._componentTypes.has(componentType)) {
return this._componentTypes.get(componentType)!;
}
// Hot reload: check if same-named component exists
// 热更新:检查是否有同名组件
if (this._hotReloadEnabled && this._componentNameToType.has(typeName)) {
const existingType = this._componentNameToType.get(typeName);
if (existingType !== componentType) {
// Reuse old bitIndex, replace class mapping
// 复用旧的 bitIndex替换类映射
const existingIndex = this._componentTypes.get(existingType!)!;
this._componentTypes.delete(existingType!);
this._componentTypes.set(componentType, existingIndex);
this._bitIndexToType.set(existingIndex, componentType);
this._componentNameToType.set(typeName, componentType);
logger.debug(`Hot reload: replaced component "${typeName}"`);
return existingIndex;
}
}
const bitIndex = this._nextBitIndex++;
this._componentTypes.set(componentType, bitIndex);
this._bitIndexToType.set(bitIndex, componentType);
this._componentNameToType.set(typeName, componentType);
this._componentNameToId.set(typeName, bitIndex);
return bitIndex;
}
/**
* Get component type's bitmask.
* 获取组件类型的位掩码。
*/
public getBitMask<T extends Component>(componentType: ComponentType<T>): BitMask64Data {
const bitIndex = this._componentTypes.get(componentType);
if (bitIndex === undefined) {
const typeName = getComponentTypeName(componentType);
throw new Error(`Component type ${typeName} is not registered`);
}
return BitMask64Utils.create(bitIndex);
}
/**
* Get component type's bit index.
* 获取组件类型的位索引。
*/
public getBitIndex<T extends Component>(componentType: ComponentType<T>): number {
const bitIndex = this._componentTypes.get(componentType);
if (bitIndex === undefined) {
const typeName = getComponentTypeName(componentType);
throw new Error(`Component type ${typeName} is not registered`);
}
return bitIndex;
}
/**
* Check if component type is registered.
* 检查组件类型是否已注册。
*/
public isRegistered<T extends Component>(componentType: ComponentType<T>): boolean {
return this._componentTypes.has(componentType);
}
/**
* Get component type by bit index.
* 通过位索引获取组件类型。
*/
public getTypeByBitIndex(bitIndex: number): ComponentType | null {
return (this._bitIndexToType.get(bitIndex) as ComponentType) || null;
}
/**
* Get registered component count.
* 获取已注册的组件数量。
*/
public getRegisteredCount(): number {
return this._nextBitIndex;
}
/**
* Get component type by name.
* 通过名称获取组件类型。
*/
public getComponentType(componentName: string): Function | null {
return this._componentNameToType.get(componentName) || null;
}
/**
* Get all registered component types.
* 获取所有已注册的组件类型。
*/
public getAllRegisteredTypes(): Map<Function, number> {
return new Map(this._componentTypes);
}
/**
* Get all component names.
* 获取所有组件名称。
*/
public getAllComponentNames(): Map<string, Function> {
return new Map(this._componentNameToType);
}
/**
* Get component type ID by name.
* 通过名称获取组件类型 ID。
*/
public getComponentId(componentName: string): number | undefined {
return this._componentNameToId.get(componentName);
}
/**
* Register component type by name.
* 通过名称注册组件类型。
*/
public registerComponentByName(componentName: string): number {
if (this._componentNameToId.has(componentName)) {
return this._componentNameToId.get(componentName)!;
}
const bitIndex = this._nextBitIndex++;
this._componentNameToId.set(componentName, bitIndex);
return bitIndex;
}
/**
* Create single component mask.
* 创建单个组件的掩码。
*/
public createSingleComponentMask(componentName: string): BitMask64Data {
const cacheKey = `single:${componentName}`;
if (this._maskCache.has(cacheKey)) {
return this._maskCache.get(cacheKey)!;
}
const componentId = this.getComponentId(componentName);
if (componentId === undefined) {
throw new Error(`Component type ${componentName} is not registered`);
}
const mask = BitMask64Utils.create(componentId);
this._maskCache.set(cacheKey, mask);
return mask;
}
/**
* Create component mask for multiple components.
* 创建多个组件的掩码。
*/
public createComponentMask(componentNames: string[]): BitMask64Data {
const sortedNames = [...componentNames].sort();
const cacheKey = `multi:${sortedNames.join(',')}`;
if (this._maskCache.has(cacheKey)) {
return this._maskCache.get(cacheKey)!;
}
const mask = BitMask64Utils.clone(BitMask64Utils.ZERO);
for (const name of componentNames) {
const componentId = this.getComponentId(name);
if (componentId !== undefined) {
const componentMask = BitMask64Utils.create(componentId);
BitMask64Utils.orInPlace(mask, componentMask);
}
}
this._maskCache.set(cacheKey, mask);
return mask;
}
/**
* Clear mask cache.
* 清除掩码缓存。
*/
public clearMaskCache(): void {
this._maskCache.clear();
}
/**
* Enable hot reload mode.
* 启用热更新模式。
*/
public enableHotReload(): void {
this._hotReloadEnabled = true;
}
/**
* Disable hot reload mode.
* 禁用热更新模式。
*/
public disableHotReload(): void {
this._hotReloadEnabled = false;
}
/**
* Check if hot reload mode is enabled.
* 检查热更新模式是否启用。
*/
public isHotReloadEnabled(): boolean {
return this._hotReloadEnabled;
}
/**
* Unregister component type.
* 注销组件类型。
*/
public unregister(componentName: string): void {
const componentType = this._componentNameToType.get(componentName);
if (!componentType) {
return;
}
const bitIndex = this._componentTypes.get(componentType);
// Remove type mappings
// 移除类型映射
this._componentTypes.delete(componentType);
if (bitIndex !== undefined) {
this._bitIndexToType.delete(bitIndex);
}
this._componentNameToType.delete(componentName);
this._componentNameToId.delete(componentName);
// Clear related mask cache
// 清除相关的掩码缓存
this.clearMaskCache();
logger.debug(`Component unregistered: ${componentName}`);
}
/**
* Get all registered component info.
* 获取所有已注册的组件信息。
*/
public getRegisteredComponents(): Array<{ name: string; type: Function; bitIndex: number }> {
const result: Array<{ name: string; type: Function; bitIndex: number }> = [];
for (const [name, type] of this._componentNameToType) {
const bitIndex = this._componentTypes.get(type);
if (bitIndex !== undefined) {
result.push({ name, type, bitIndex });
}
}
return result;
}
/**
* Reset registry.
* 重置注册表。
*/
public reset(): void {
this._componentTypes.clear();
this._bitIndexToType.clear();
this._componentNameToType.clear();
this._componentNameToId.clear();
this._maskCache.clear();
this._warnedComponents.clear();
this._nextBitIndex = 0;
this._hotReloadEnabled = false;
}
/**
* Clone component types from another registry.
* 从另一个注册表克隆组件类型。
*
* Used to inherit framework components when creating a new Scene.
* 用于在创建新 Scene 时继承框架组件。
*/
public cloneFrom(source: IComponentRegistry): void {
const types = source.getAllRegisteredTypes();
for (const [type, index] of types) {
this._componentTypes.set(type, index);
this._bitIndexToType.set(index, type);
const typeName = getComponentTypeName(type as ComponentType);
this._componentNameToType.set(typeName, type);
this._componentNameToId.set(typeName, index);
}
this._nextBitIndex = source.getRegisteredCount();
this._hotReloadEnabled = source.isHotReloadEnabled();
}
}
/**
* Global Component Registry.
* 全局组件注册表。
*
* Used by framework components and decorators.
* Scene instances clone from this registry on creation.
* 用于框架组件和装饰器。
* Scene 实例在创建时从此注册表克隆。
*/
export const GlobalComponentRegistry = new ComponentRegistry();

View File

@@ -0,0 +1,213 @@
/**
* Component Type Utilities
* 组件类型工具函数
*
* This module contains low-level utilities for component type handling.
* It has NO dependencies on other ECS modules to avoid circular imports.
*
* 此模块包含组件类型处理的底层工具函数。
* 它不依赖其他 ECS 模块,以避免循环导入。
*/
import type { Component } from '../../Component';
/**
* 组件类型定义
* Component type definition
*
* 注意:构造函数参数使用 any[] 是必要的,因为组件可以有各种不同签名的构造函数
* Note: Constructor args use any[] because components can have various constructor signatures
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export type ComponentType<T extends Component = Component> = new (...args: any[]) => T;
/**
* 存储组件类型名称的 Symbol 键
* Symbol key for storing component type name
*/
export const COMPONENT_TYPE_NAME = Symbol('ComponentTypeName');
/**
* 存储组件依赖的 Symbol 键
* Symbol key for storing component dependencies
*/
export const COMPONENT_DEPENDENCIES = Symbol('ComponentDependencies');
/**
* 存储组件编辑器选项的 Symbol 键
* Symbol key for storing component editor options
*/
export const COMPONENT_EDITOR_OPTIONS = Symbol('ComponentEditorOptions');
/**
* 组件编辑器选项
* Component editor options
*/
export type ComponentEditorOptions = {
/**
* 是否在 Inspector 中隐藏此组件
* Whether to hide this component in Inspector
*
* @default false
*/
hideInInspector?: boolean;
/**
* 组件分类(用于 Inspector 中的分组显示)
* Component category (for grouping in Inspector)
*/
category?: string;
/**
* 组件图标(用于 Inspector 中的显示)
* Component icon (for display in Inspector)
*/
icon?: string;
}
/**
* 组件构造函数上的元数据接口
* Metadata interface stored on component constructors
*
* 使用 Symbol 索引签名来类型安全地访问装饰器存储的元数据
* Uses Symbol index signature to safely access decorator-stored metadata
*/
export type ComponentTypeMetadata = {
readonly [COMPONENT_TYPE_NAME]?: string;
readonly [COMPONENT_DEPENDENCIES]?: string[];
readonly [COMPONENT_EDITOR_OPTIONS]?: ComponentEditorOptions;
}
/**
* 可写的组件类型元数据(用于装饰器设置)
* Writable component type metadata (for decorator setting)
*/
export type WritableComponentTypeMetadata = {
[COMPONENT_TYPE_NAME]?: string;
[COMPONENT_DEPENDENCIES]?: string[];
[COMPONENT_EDITOR_OPTIONS]?: ComponentEditorOptions;
}
/**
* 获取组件构造函数的元数据
* Get metadata from component constructor
*
* @param componentType 组件构造函数
* @returns 元数据对象
*/
export function getComponentTypeMetadata(componentType: ComponentType): ComponentTypeMetadata {
return componentType as unknown as ComponentTypeMetadata;
}
/**
* 获取可写的组件构造函数元数据(用于装饰器)
* Get writable metadata from component constructor (for decorators)
*
* @param componentType 组件构造函数
* @returns 可写的元数据对象
*/
export function getWritableComponentTypeMetadata(componentType: ComponentType): WritableComponentTypeMetadata {
return componentType as unknown as WritableComponentTypeMetadata;
}
/**
* 检查组件是否使用了 @ECSComponent 装饰器
* Check if component has @ECSComponent decorator
*
* @param componentType 组件构造函数
* @returns 是否有装饰器
*/
export function hasECSComponentDecorator(componentType: ComponentType): boolean {
const metadata = getComponentTypeMetadata(componentType);
return metadata[COMPONENT_TYPE_NAME] !== undefined;
}
/**
* 获取组件类型的名称,优先使用装饰器指定的名称
* Get component type name, preferring decorator-specified name
*
* @param componentType 组件构造函数
* @returns 组件类型名称
*/
export function getComponentTypeName(componentType: ComponentType): string {
// 优先使用装饰器指定的名称
// Prefer decorator-specified name
const metadata = getComponentTypeMetadata(componentType);
const decoratorName = metadata[COMPONENT_TYPE_NAME];
if (decoratorName) {
return decoratorName;
}
// 回退到 constructor.name
// Fallback to constructor.name
return componentType.name || 'UnknownComponent';
}
/**
* 从组件实例获取类型名称
* Get type name from component instance
*
* @param component 组件实例
* @returns 组件类型名称
*/
export function getComponentInstanceTypeName(component: Component): string {
return getComponentTypeName(component.constructor as ComponentType);
}
/**
* 获取组件的依赖列表
* Get component dependencies
*
* @param componentType 组件构造函数
* @returns 依赖的组件名称列表
*/
export function getComponentDependencies(componentType: ComponentType): string[] | undefined {
const metadata = getComponentTypeMetadata(componentType);
return metadata[COMPONENT_DEPENDENCIES];
}
/**
* 获取组件的编辑器选项
* Get component editor options
*
* @param componentType 组件构造函数
* @returns 编辑器选项
*/
export function getComponentEditorOptions(componentType: ComponentType): ComponentEditorOptions | undefined {
const metadata = getComponentTypeMetadata(componentType);
return metadata[COMPONENT_EDITOR_OPTIONS];
}
/**
* 从组件实例获取编辑器选项
* Get editor options from component instance
*
* @param component 组件实例
* @returns 编辑器选项
*/
export function getComponentInstanceEditorOptions(component: Component): ComponentEditorOptions | undefined {
return getComponentEditorOptions(component.constructor as ComponentType);
}
/**
* 检查组件是否应该在 Inspector 中隐藏
* Check if component should be hidden in Inspector
*
* @param componentType 组件构造函数
* @returns 是否隐藏
*/
export function isComponentHiddenInInspector(componentType: ComponentType): boolean {
const options = getComponentEditorOptions(componentType);
return options?.hideInInspector ?? false;
}
/**
* 从组件实例检查是否应该在 Inspector 中隐藏
* Check if component instance should be hidden in Inspector
*
* @param component 组件实例
* @returns 是否隐藏
*/
export function isComponentInstanceHiddenInInspector(component: Component): boolean {
return isComponentHiddenInInspector(component.constructor as ComponentType);
}

View File

@@ -0,0 +1,192 @@
/**
* Component Registry Interface.
* 组件注册表接口。
*
* Defines the contract for component type registration and lookup.
* Each Scene has its own ComponentRegistry instance for isolation.
* 定义组件类型注册和查找的契约。
* 每个 Scene 都有自己的 ComponentRegistry 实例以实现隔离。
*/
import type { Component } from '../../Component';
import type { BitMask64Data } from '../../Utils/BigIntCompatibility';
import type { ComponentType } from './ComponentTypeUtils';
/**
* Component Registry Interface.
* 组件注册表接口。
*/
export type IComponentRegistry = {
/**
* Register component type and allocate bitmask.
* 注册组件类型并分配位掩码。
*
* @param componentType - Component constructor | 组件构造函数
* @returns Allocated bit index | 分配的位索引
*/
register<T extends Component>(componentType: ComponentType<T>): number;
/**
* Get component type's bitmask.
* 获取组件类型的位掩码。
*
* @param componentType - Component constructor | 组件构造函数
* @returns Bitmask | 位掩码
*/
getBitMask<T extends Component>(componentType: ComponentType<T>): BitMask64Data;
/**
* Get component type's bit index.
* 获取组件类型的位索引。
*
* @param componentType - Component constructor | 组件构造函数
* @returns Bit index | 位索引
*/
getBitIndex<T extends Component>(componentType: ComponentType<T>): number;
/**
* Check if component type is registered.
* 检查组件类型是否已注册。
*
* @param componentType - Component constructor | 组件构造函数
* @returns Whether registered | 是否已注册
*/
isRegistered<T extends Component>(componentType: ComponentType<T>): boolean;
/**
* Get component type by bit index.
* 通过位索引获取组件类型。
*
* @param bitIndex - Bit index | 位索引
* @returns Component constructor or null | 组件构造函数或 null
*/
getTypeByBitIndex(bitIndex: number): ComponentType | null;
/**
* Get component type by name.
* 通过名称获取组件类型。
*
* @param componentName - Component name | 组件名称
* @returns Component constructor or null | 组件构造函数或 null
*/
getComponentType(componentName: string): Function | null;
/**
* Get component type ID by name.
* 通过名称获取组件类型 ID。
*
* @param componentName - Component name | 组件名称
* @returns Component type ID or undefined | 组件类型 ID 或 undefined
*/
getComponentId(componentName: string): number | undefined;
/**
* Get all registered component types.
* 获取所有已注册的组件类型。
*
* @returns Map of component type to bit index | 组件类型到位索引的映射
*/
getAllRegisteredTypes(): Map<Function, number>;
/**
* Get all component names.
* 获取所有组件名称。
*
* @returns Map of name to component type | 名称到组件类型的映射
*/
getAllComponentNames(): Map<string, Function>;
/**
* Get registered component count.
* 获取已注册的组件数量。
*
* @returns Count | 数量
*/
getRegisteredCount(): number;
/**
* Register component type by name.
* 通过名称注册组件类型。
*
* @param componentName - Component name | 组件名称
* @returns Allocated component ID | 分配的组件 ID
*/
registerComponentByName(componentName: string): number;
/**
* Create single component mask.
* 创建单个组件的掩码。
*
* @param componentName - Component name | 组件名称
* @returns Component mask | 组件掩码
*/
createSingleComponentMask(componentName: string): BitMask64Data;
/**
* Create component mask for multiple components.
* 创建多个组件的掩码。
*
* @param componentNames - Component names | 组件名称数组
* @returns Combined mask | 组合掩码
*/
createComponentMask(componentNames: string[]): BitMask64Data;
/**
* Unregister component type.
* 注销组件类型。
*
* @param componentName - Component name | 组件名称
*/
unregister(componentName: string): void;
/**
* Enable hot reload mode.
* 启用热更新模式。
*/
enableHotReload(): void;
/**
* Disable hot reload mode.
* 禁用热更新模式。
*/
disableHotReload(): void;
/**
* Check if hot reload mode is enabled.
* 检查热更新模式是否启用。
*
* @returns Whether enabled | 是否启用
*/
isHotReloadEnabled(): boolean;
/**
* Clear mask cache.
* 清除掩码缓存。
*/
clearMaskCache(): void;
/**
* Reset registry.
* 重置注册表。
*/
reset(): void;
/**
* Get all registered component info.
* 获取所有已注册的组件信息。
*
* @returns Array of component info | 组件信息数组
*/
getRegisteredComponents(): Array<{ name: string; type: Function; bitIndex: number }>;
/**
* Clone component types from another registry.
* 从另一个注册表克隆组件类型。
*
* Used to inherit framework components when creating a new Scene.
* 用于在创建新 Scene 时继承框架组件。
*
* @param source - Source registry | 源注册表
*/
cloneFrom(source: IComponentRegistry): void;
}

View File

@@ -0,0 +1,136 @@
/**
* 轻量级实体句柄
*
* 使用数值表示实体,包含索引和代数信息。
* 28位索引 + 20位代数 = 48位在 JavaScript 安全整数范围内。
*
* Lightweight entity handle.
* Uses numeric value to represent entity with index and generation.
* 28-bit index + 20-bit generation = 48-bit, within JavaScript safe integer range.
*
* @example
* ```typescript
* const handle = makeHandle(42, 1);
* console.log(indexOf(handle)); // 42
* console.log(genOf(handle)); // 1
* console.log(isValidHandle(handle)); // true
* ```
*/
/**
* 实体句柄类型
*
* 使用 branded type 提供类型安全。
*
* Entity handle type.
* Uses branded type for type safety.
*/
export type EntityHandle = number & { readonly __brand: 'EntityHandle' };
/**
* 索引位数 | Index bits
*/
export const INDEX_BITS = 28;
/**
* 代数位数 | Generation bits
*/
export const GEN_BITS = 20;
/**
* 索引掩码 | Index mask
*/
export const INDEX_MASK = (1 << INDEX_BITS) - 1; // 0x0FFFFFFF
/**
* 代数掩码 | Generation mask
*/
export const GEN_MASK = (1 << GEN_BITS) - 1; // 0x000FFFFF
/**
* 最大实体数量 | Maximum entity count
*/
export const MAX_ENTITIES = 1 << INDEX_BITS; // 268,435,456
/**
* 最大代数值 | Maximum generation value
*/
export const MAX_GENERATION = 1 << GEN_BITS; // 1,048,576
/**
* 空句柄常量 | Null handle constant
*/
export const NULL_HANDLE = 0 as EntityHandle;
/**
* 创建实体句柄
* Create entity handle
*
* @param index 实体索引 | Entity index
* @param generation 实体代数 | Entity generation
* @returns 实体句柄 | Entity handle
*/
export function makeHandle(index: number, generation: number): EntityHandle {
// handle = generation * 2^28 + index
// 使用乘法而不是位移,因为位移只支持 32 位
return ((generation & GEN_MASK) * MAX_ENTITIES + (index & INDEX_MASK)) as EntityHandle;
}
/**
* 从句柄提取索引
* Extract index from handle
*
* @param handle 实体句柄 | Entity handle
* @returns 实体索引 | Entity index
*/
export function indexOf(handle: EntityHandle): number {
return handle & INDEX_MASK;
}
/**
* 从句柄提取代数
* Extract generation from handle
*
* @param handle 实体句柄 | Entity handle
* @returns 实体代数 | Entity generation
*/
export function genOf(handle: EntityHandle): number {
return Math.floor(handle / MAX_ENTITIES) & GEN_MASK;
}
/**
* 检查句柄是否有效(非空)
* Check if handle is valid (non-null)
*
* @param handle 实体句柄 | Entity handle
* @returns 是否有效 | Whether valid
*/
export function isValidHandle(handle: EntityHandle): boolean {
return handle !== NULL_HANDLE;
}
/**
* 比较两个句柄是否相等
* Compare two handles for equality
*
* @param a 第一个句柄 | First handle
* @param b 第二个句柄 | Second handle
* @returns 是否相等 | Whether equal
*/
export function handleEquals(a: EntityHandle, b: EntityHandle): boolean {
return a === b;
}
/**
* 将句柄转换为字符串(用于调试)
* Convert handle to string (for debugging)
*
* @param handle 实体句柄 | Entity handle
* @returns 字符串表示 | String representation
*/
export function handleToString(handle: EntityHandle): string {
if (handle === NULL_HANDLE) {
return 'Entity(NULL)';
}
return `Entity(idx=${indexOf(handle)}, gen=${genOf(handle)})`;
}

View File

@@ -0,0 +1,331 @@
/**
* 实体句柄管理器
*
* 管理轻量级实体句柄的生命周期,包括创建、销毁和状态查询。
* 使用 TypedArray 实现高效的内存布局。
*
* Entity handle manager.
* Manages lifecycle of lightweight entity handles including creation, destruction and state queries.
* Uses TypedArray for efficient memory layout.
*
* @example
* ```typescript
* const manager = new EntityHandleManager();
* const handle = manager.create();
* console.log(manager.isAlive(handle)); // true
*
* manager.destroy(handle);
* console.log(manager.isAlive(handle)); // false
*
* // 索引会被复用,但代数会增加
* const newHandle = manager.create();
* console.log(indexOf(handle) === indexOf(newHandle)); // true
* console.log(genOf(newHandle) > genOf(handle)); // true
* ```
*/
import {
EntityHandle,
makeHandle,
indexOf,
genOf,
MAX_ENTITIES,
MAX_GENERATION
} from './EntityHandle';
/**
* 初始容量 | Initial capacity
*/
const INITIAL_CAPACITY = 1024;
/**
* 实体句柄管理器
*
* Entity handle manager.
*/
export class EntityHandleManager {
/**
* 代数数组(每个索引的当前代数)
* Generation array (current generation for each index)
*/
private _generations: Uint32Array;
/**
* 存活状态数组0=死亡1=存活)
* Alive state array (0=dead, 1=alive)
*/
private _alive: Uint8Array;
/**
* 启用状态数组0=禁用1=启用)
* Enabled state array (0=disabled, 1=enabled)
*/
private _enabled: Uint8Array;
/**
* 空闲索引列表(用于复用已销毁的索引)
* Free index list (for reusing destroyed indices)
*/
private _freeList: number[] = [];
/**
* 下一个新索引
* Next new index
*/
private _nextIndex: number = 1; // 从 1 开始0 保留给 NULL_HANDLE
/**
* 当前存活实体数量
* Current alive entity count
*/
private _aliveCount: number = 0;
/**
* 当前容量
* Current capacity
*/
private _capacity: number;
/**
* 创建实体句柄管理器
* Create entity handle manager
*
* @param initialCapacity 初始容量 | Initial capacity
*/
constructor(initialCapacity: number = INITIAL_CAPACITY) {
this._capacity = initialCapacity;
this._generations = new Uint32Array(initialCapacity);
this._alive = new Uint8Array(initialCapacity);
this._enabled = new Uint8Array(initialCapacity);
// 索引 0 保留给 NULL_HANDLE
this._alive[0] = 0;
this._enabled[0] = 0;
}
/**
* 获取存活实体数量
* Get alive entity count
*/
public get aliveCount(): number {
return this._aliveCount;
}
/**
* 获取当前容量
* Get current capacity
*/
public get capacity(): number {
return this._capacity;
}
/**
* 创建新实体句柄
* Create new entity handle
*
* @returns 新实体句柄 | New entity handle
*/
public create(): EntityHandle {
let index: number;
// 优先从空闲列表中获取索引
if (this._freeList.length > 0) {
index = this._freeList.pop()!;
} else {
// 分配新索引
index = this._nextIndex++;
// 检查是否需要扩容
if (index >= this._capacity) {
this.grow(index);
}
}
// 获取当前代数(可能因之前销毁而增加)
const generation = this._generations[index]!;
// 标记为存活和启用
this._alive[index] = 1;
this._enabled[index] = 1;
this._aliveCount++;
return makeHandle(index, generation);
}
/**
* 销毁实体句柄
* Destroy entity handle
*
* @param handle 要销毁的句柄 | Handle to destroy
* @returns 是否成功销毁 | Whether destruction succeeded
*/
public destroy(handle: EntityHandle): boolean {
const index = indexOf(handle);
const generation = genOf(handle);
// 验证句柄有效性
if (index >= this._capacity || index === 0) {
return false;
}
if (this._generations[index] !== generation) {
return false;
}
if (this._alive[index] !== 1) {
return false;
}
// 标记为死亡
this._alive[index] = 0;
this._enabled[index] = 0;
this._aliveCount--;
// 增加代数(防止 ABA 问题)
const newGen = (generation + 1) % MAX_GENERATION;
this._generations[index] = newGen;
// 将索引加入空闲列表
this._freeList.push(index);
return true;
}
/**
* 检查句柄是否存活
* Check if handle is alive
*
* @param handle 实体句柄 | Entity handle
* @returns 是否存活 | Whether alive
*/
public isAlive(handle: EntityHandle): boolean {
const index = indexOf(handle);
const generation = genOf(handle);
if (index >= this._capacity || index === 0) {
return false;
}
return this._alive[index] === 1 && this._generations[index] === generation;
}
/**
* 检查句柄是否启用
* Check if handle is enabled
*
* @param handle 实体句柄 | Entity handle
* @returns 是否启用 | Whether enabled
*/
public isEnabled(handle: EntityHandle): boolean {
if (!this.isAlive(handle)) {
return false;
}
const index = indexOf(handle);
return this._enabled[index] === 1;
}
/**
* 设置句柄启用状态
* Set handle enabled state
*
* @param handle 实体句柄 | Entity handle
* @param enabled 启用状态 | Enabled state
* @returns 是否成功设置 | Whether setting succeeded
*/
public setEnabled(handle: EntityHandle, enabled: boolean): boolean {
if (!this.isAlive(handle)) {
return false;
}
const index = indexOf(handle);
this._enabled[index] = enabled ? 1 : 0;
return true;
}
/**
* 验证句柄是否有效(存活且代数匹配)
* Validate if handle is valid (alive and generation matches)
*
* @param handle 实体句柄 | Entity handle
* @returns 是否有效 | Whether valid
*/
public validate(handle: EntityHandle): boolean {
return this.isAlive(handle);
}
/**
* 扩容数组
* Grow arrays
*/
private grow(minIndex: number): void {
let newSize = this._capacity;
// 按 2 倍扩容
while (newSize <= minIndex) {
newSize <<= 1;
}
// 检查是否超过最大容量
if (newSize > MAX_ENTITIES) {
newSize = MAX_ENTITIES;
if (minIndex >= newSize) {
throw new Error(`EntityHandleManager: 超过最大实体数量 ${MAX_ENTITIES}`);
}
}
// 创建新数组并复制数据
const newGenerations = new Uint32Array(newSize);
const newAlive = new Uint8Array(newSize);
const newEnabled = new Uint8Array(newSize);
newGenerations.set(this._generations);
newAlive.set(this._alive);
newEnabled.set(this._enabled);
this._generations = newGenerations;
this._alive = newAlive;
this._enabled = newEnabled;
this._capacity = newSize;
}
/**
* 重置管理器状态
* Reset manager state
*/
public reset(): void {
this._generations.fill(0);
this._alive.fill(0);
this._enabled.fill(0);
this._freeList.length = 0;
this._nextIndex = 1;
this._aliveCount = 0;
}
/**
* 遍历所有存活的句柄
* Iterate all alive handles
*
* @param callback 回调函数 | Callback function
*/
public forEach(callback: (handle: EntityHandle) => void): void {
for (let i = 1; i < this._nextIndex; i++) {
if (this._alive[i] === 1) {
const handle = makeHandle(i, this._generations[i]!);
callback(handle);
}
}
}
/**
* 获取所有存活的句柄
* Get all alive handles
*
* @returns 存活句柄数组 | Alive handles array
*/
public getAllAlive(): EntityHandle[] {
const result: EntityHandle[] = [];
this.forEach((handle) => result.push(handle));
return result;
}
}

View File

@@ -0,0 +1,26 @@
/**
* 实体生命周期策略
*
* 定义实体在场景切换时的行为。
*
* Entity lifecycle policy.
* Defines entity behavior during scene transitions.
*/
export const enum EEntityLifecyclePolicy {
/**
* 默认策略 - 随场景销毁
*
* Default policy - destroyed with scene.
*/
SceneLocal = 0,
/**
* 持久化策略 - 跨场景保留
*
* 实体在场景切换时自动迁移到新场景。
*
* Persistent policy - survives scene transitions.
* Entity is automatically migrated to the new scene.
*/
Persistent = 1
}

View File

@@ -0,0 +1,103 @@
/**
* 帧级变更检测管理器
*
* 基于 epoch帧计数的组件变更追踪系统。
* 每帧递增 epoch组件写入时记录当前 epoch
* 系统可以查询自上次检查以来发生变更的组件。
*
* Frame-level change detection manager.
* Epoch-based component change tracking system.
* Epoch increments each frame, components record current epoch on write,
* systems can query components changed since last check.
*
* @example
* ```typescript
* // 在 System 中使用
* class MovementSystem extends EntitySystem {
* private _lastEpoch = 0;
*
* process(): void {
* const epoch = this.scene.epochManager;
*
* // 只处理 Velocity 变化的实体
* for (const entity of this.entities) {
* const vel = entity.getComponent(Velocity);
* if (vel && vel.lastWriteEpoch > this._lastEpoch) {
* // 处理变更
* }
* }
*
* this._lastEpoch = epoch.current;
* }
* }
* ```
*/
/**
* Epoch 管理器
*
* Epoch manager.
*/
export class EpochManager {
/**
* 当前 epoch 值
*
* 从 1 开始0 表示"从未写入"。
*
* Current epoch value.
* Starts from 1, 0 means "never written".
*/
private _current: number = 1;
/**
* 获取当前 epoch
*
* Get current epoch.
*/
public get current(): number {
return this._current;
}
/**
* 递增 epoch
*
* 应在每帧开始时调用。
*
* Increment epoch.
* Should be called at the start of each frame.
*/
public increment(): void {
this._current++;
// 处理溢出(虽然 2^53 帧基本不可能达到)
// Handle overflow (though 2^53 frames is practically unreachable)
if (this._current >= Number.MAX_SAFE_INTEGER) {
this._current = 1;
}
}
/**
* 重置 epoch
*
* 在场景重置时调用。
*
* Reset epoch.
* Called when scene is reset.
*/
public reset(): void {
this._current = 1;
}
/**
* 检查给定 epoch 是否在指定 epoch 之后发生变更
*
* Check if given epoch is after the specified epoch (changed since).
*
* @param writeEpoch 写入时的 epoch | Epoch when written
* @param sinceEpoch 检查点 epoch | Checkpoint epoch
* @returns 是否在检查点之后发生变更 | Whether changed after checkpoint
*/
public isChangedSince(writeEpoch: number, sinceEpoch: number): boolean {
return writeEpoch > sinceEpoch;
}
}

View File

@@ -0,0 +1,475 @@
import {
IEventBus,
IEventListenerConfig,
IEventStats,
IEventData,
IEntityEventData,
IComponentEventData,
ISystemEventData,
ISceneEventData,
IPerformanceEventData
} from '../../Types';
import { createLogger } from '../../Utils/Logger';
import {
TypeSafeEventSystem,
EventListenerConfig,
EventStats
} from './EventSystem';
import {
ECSEventType,
EventPriority,
EventTypeValidator
} from '../CoreEvents';
/**
* 增强的事件总线实现
* 基于TypeSafeEventSystem提供类型安全的事件发布订阅机制
*/
export class EventBus implements IEventBus {
private static readonly _logger = createLogger('EventBus');
private eventSystem: TypeSafeEventSystem;
private eventIdCounter = 0;
private isDebugMode = false;
constructor(debugMode: boolean = false) {
this.eventSystem = new TypeSafeEventSystem();
this.isDebugMode = debugMode;
}
/**
* 发射事件
* @param eventType 事件类型
* @param data 事件数据
* @param enhance 是否增强事件数据添加timestamp、eventId等默认false提升性能
*/
public emit<T>(eventType: string, data: T, enhance: boolean = false): void {
this.validateEventType(eventType);
const finalData = enhance ? this.enhanceEventData(eventType, data) : data;
if (this.isDebugMode) {
EventBus._logger.info(`发射事件: ${eventType}`, finalData);
}
this.eventSystem.emitSync(eventType, finalData);
}
/**
* 异步发射事件
* @param eventType 事件类型
* @param data 事件数据
* @param enhance 是否增强事件数据添加timestamp、eventId等默认false提升性能
*/
public async emitAsync<T>(eventType: string, data: T, enhance: boolean = false): Promise<void> {
this.validateEventType(eventType);
const finalData = enhance ? this.enhanceEventData(eventType, data) : data;
if (this.isDebugMode) {
EventBus._logger.info(`发射异步事件: ${eventType}`, finalData);
}
await this.eventSystem.emit(eventType, finalData);
}
/**
* 监听事件
* @param eventType 事件类型
* @param handler 事件处理器
* @param config 监听器配置
* @returns 监听器ID
*/
public on<T>(
eventType: string,
handler: (data: T) => void,
config: IEventListenerConfig = {}
): string {
this.validateEventType(eventType);
const eventConfig: EventListenerConfig = {
once: config.once || false,
priority: config.priority || EventPriority.NORMAL,
async: config.async || false
};
if (config.thisArg) {
eventConfig.thisArg = config.thisArg as object;
}
if (this.isDebugMode) {
EventBus._logger.info(`添加监听器: ${eventType}`, eventConfig);
}
return this.eventSystem.on(eventType, handler, eventConfig);
}
/**
* 监听事件(一次性)
* @param eventType 事件类型
* @param handler 事件处理器
* @param config 监听器配置
* @returns 监听器ID
*/
public once<T>(
eventType: string,
handler: (data: T) => void,
config: IEventListenerConfig = {}
): string {
return this.on(eventType, handler, { ...config, once: true });
}
/**
* 异步监听事件
* @param eventType 事件类型
* @param handler 异步事件处理器
* @param config 监听器配置
* @returns 监听器ID
*/
public onAsync<T>(
eventType: string,
handler: (data: T) => Promise<void>,
config: IEventListenerConfig = {}
): string {
return this.on(eventType, handler, { ...config, async: true });
}
/**
* 移除事件监听器
* @param eventType 事件类型
* @param listenerId 监听器ID
*/
public off(eventType: string, listenerId: string): boolean {
if (this.isDebugMode) {
EventBus._logger.info(`移除监听器: ${listenerId} 事件: ${eventType}`);
}
return this.eventSystem.off(eventType, listenerId);
}
/**
* 移除指定事件类型的所有监听器
* @param eventType 事件类型
*/
public offAll(eventType: string): void {
if (this.isDebugMode) {
EventBus._logger.info(`移除所有监听器: ${eventType}`);
}
this.eventSystem.offAll(eventType);
}
/**
* 检查是否有指定事件的监听器
* @param eventType 事件类型
*/
public hasListeners(eventType: string): boolean {
return this.eventSystem.hasListeners(eventType);
}
/**
* 获取事件统计信息
* @param eventType 事件类型(可选)
*/
public getStats(eventType?: string): IEventStats | Map<string, IEventStats> {
const stats = this.eventSystem.getStats(eventType);
if (stats instanceof Map) {
// 转换Map中的每个EventStats为IEventStats
const result = new Map<string, IEventStats>();
stats.forEach((stat, key) => {
result.set(key, this.convertEventStats(stat));
});
return result;
} else {
return this.convertEventStats(stats);
}
}
/**
* 清空所有监听器
*/
public clear(): void {
if (this.isDebugMode) {
EventBus._logger.info('清空所有监听器');
}
this.eventSystem.clear();
}
/**
* 启用或禁用事件系统
* @param enabled 是否启用
*/
public setEnabled(enabled: boolean): void {
this.eventSystem.setEnabled(enabled);
}
/**
* 设置调试模式
* @param debug 是否启用调试
*/
public setDebugMode(debug: boolean): void {
this.isDebugMode = debug;
}
/**
* 设置最大监听器数量
* @param max 最大数量
*/
public setMaxListeners(max: number): void {
this.eventSystem.setMaxListeners(max);
}
/**
* 获取监听器数量
* @param eventType 事件类型
*/
public getListenerCount(eventType: string): number {
return this.eventSystem.getListenerCount(eventType);
}
/**
* 设置事件批处理配置
* @param eventType 事件类型
* @param batchSize 批处理大小
* @param delay 延迟时间(毫秒)
*/
public setBatchConfig(eventType: string, batchSize: number, delay: number): void {
this.eventSystem.setBatchConfig(eventType, {
batchSize,
delay,
enabled: true
});
}
/**
* 刷新指定事件的批处理队列
* @param eventType 事件类型
*/
public flushBatch(eventType: string): void {
this.eventSystem.flushBatch(eventType);
}
/**
* 重置事件统计
* @param eventType 事件类型(可选)
*/
public resetStats(eventType?: string): void {
this.eventSystem.resetStats(eventType);
}
// 便捷方法发射预定义的ECS事件
/**
* 发射实体创建事件
* @param entityData 实体事件数据
*/
public emitEntityCreated(entityData: IEntityEventData): void {
this.emit(ECSEventType.ENTITY_CREATED, entityData);
}
/**
* 发射实体销毁事件
* @param entityData 实体事件数据
*/
public emitEntityDestroyed(entityData: IEntityEventData): void {
this.emit(ECSEventType.ENTITY_DESTROYED, entityData);
}
/**
* 发射组件添加事件
* @param componentData 组件事件数据
*/
public emitComponentAdded(componentData: IComponentEventData): void {
this.emit(ECSEventType.COMPONENT_ADDED, componentData);
}
/**
* 发射组件移除事件
* @param componentData 组件事件数据
*/
public emitComponentRemoved(componentData: IComponentEventData): void {
this.emit(ECSEventType.COMPONENT_REMOVED, componentData);
}
/**
* 发射系统添加事件
* @param systemData 系统事件数据
*/
public emitSystemAdded(systemData: ISystemEventData): void {
this.emit(ECSEventType.SYSTEM_ADDED, systemData);
}
/**
* 发射系统移除事件
* @param systemData 系统事件数据
*/
public emitSystemRemoved(systemData: ISystemEventData): void {
this.emit(ECSEventType.SYSTEM_REMOVED, systemData);
}
/**
* 发射场景变化事件
* @param sceneData 场景事件数据
*/
public emitSceneChanged(sceneData: ISceneEventData): void {
this.emit(ECSEventType.SCENE_ACTIVATED, sceneData);
}
/**
* 发射性能警告事件
* @param performanceData 性能事件数据
*/
public emitPerformanceWarning(performanceData: IPerformanceEventData): void {
this.emit(ECSEventType.PERFORMANCE_WARNING, performanceData);
}
// 便捷方法监听预定义的ECS事件
/**
* 监听实体创建事件
* @param handler 事件处理器
* @param config 监听器配置
*/
public onEntityCreated(
handler: (data: IEntityEventData) => void,
config?: IEventListenerConfig
): string {
return this.on(ECSEventType.ENTITY_CREATED, handler, config);
}
/**
* 监听组件添加事件
* @param handler 事件处理器
* @param config 监听器配置
*/
public onComponentAdded(
handler: (data: IComponentEventData) => void,
config?: IEventListenerConfig
): string {
return this.on(ECSEventType.COMPONENT_ADDED, handler, config);
}
/**
* 监听系统错误事件
* @param handler 事件处理器
* @param config 监听器配置
*/
public onSystemError(
handler: (data: ISystemEventData) => void,
config?: IEventListenerConfig
): string {
return this.on(ECSEventType.SYSTEM_ERROR, handler, config);
}
/**
* 监听性能警告事件
* @param handler 事件处理器
* @param config 监听器配置
*/
public onPerformanceWarning(
handler: (data: IPerformanceEventData) => void,
config?: IEventListenerConfig
): string {
return this.on(ECSEventType.PERFORMANCE_WARNING, handler, config);
}
// 私有方法
/**
* 验证事件类型仅在debug模式下执行提升性能
* @param eventType 事件类型
*/
private validateEventType(eventType: string): void {
// 只在debug模式下进行验证提升生产环境性能
if (this.isDebugMode) {
if (!EventTypeValidator.isValid(eventType)) {
EventBus._logger.warn(`未知事件类型: ${eventType}`);
// 在调试模式下添加自定义事件类型
EventTypeValidator.addCustomType(eventType);
}
}
}
/**
* 增强事件数据
* @param eventType 事件类型
* @param data 原始数据
*/
private enhanceEventData<T>(eventType: string, data: T): T & IEventData {
// 处理null或undefined数据
if (data === null || data === undefined) {
return {
timestamp: Date.now(),
eventId: `${eventType}_${++this.eventIdCounter}`,
source: 'EventBus'
} as T & IEventData;
}
const enhanced = data as T & IEventData;
// 如果数据还没有基础事件属性,添加它们
if (!enhanced.timestamp) {
enhanced.timestamp = Date.now();
}
if (!enhanced.eventId) {
enhanced.eventId = `${eventType}_${++this.eventIdCounter}`;
}
if (!enhanced.source) {
enhanced.source = 'EventBus';
}
return enhanced;
}
/**
* 转换EventStats为IEventStats
* @param stats EventStats实例
*/
private convertEventStats(stats: EventStats): IEventStats {
return {
eventType: stats.eventType,
listenerCount: stats.listenerCount,
triggerCount: stats.triggerCount,
totalExecutionTime: stats.totalExecutionTime,
averageExecutionTime: stats.averageExecutionTime,
lastTriggerTime: stats.lastTriggerTime
};
}
}
/**
* 全局事件总线实例
* 提供全局访问的事件总线
*/
export class GlobalEventBus {
private static _instance: EventBus;
/**
* @zh 获取全局事件总线实例
* @en Get global event bus instance
*
* @param debugMode - @zh 是否启用调试模式 @en Whether to enable debug mode
*/
public static getInstance(debugMode: boolean = false): EventBus {
if (!this._instance) {
this._instance = new EventBus(debugMode);
}
return this._instance;
}
/**
* @zh 重置全局事件总线实例
* @en Reset global event bus instance
*
* @param debugMode - @zh 是否启用调试模式 @en Whether to enable debug mode
*/
public static reset(debugMode: boolean = false): EventBus {
if (this._instance) {
this._instance.clear();
}
this._instance = new EventBus(debugMode);
return this._instance;
}
}

View File

@@ -0,0 +1,577 @@
import { createLogger } from '../../Utils/Logger';
/**
* 事件处理器函数类型
*/
export type EventHandler<T> = (event: T) => void;
/**
* 异步事件处理器函数类型
*/
export type AsyncEventHandler<T> = (event: T) => Promise<void>;
/**
* 事件监听器配置
*/
export type EventListenerConfig = {
/** 是否只执行一次 */
once?: boolean;
/** 优先级(数字越大优先级越高) */
priority?: number;
/** 是否异步执行 */
async?: boolean;
/** 事件处理函数的 this 绑定对象 */
thisArg?: object;
}
/**
* 内部事件监听器
*
* 注意handler 使用 any 是必要的类型擦除
* 原因需要在同一数组中存储处理不同事件类型T的监听器
* 类型安全保证:公共 API (on<T>/emit<T>) 在编译时保证类型匹配
*/
interface InternalEventListener {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
handler: EventHandler<any> | AsyncEventHandler<any>;
config: EventListenerConfig;
id: string;
}
/**
* 事件统计信息
*/
export type EventStats = {
/** 事件类型 */
eventType: string;
/** 监听器数量 */
listenerCount: number;
/** 触发次数 */
triggerCount: number;
/** 总执行时间(毫秒) */
totalExecutionTime: number;
/** 平均执行时间(毫秒) */
averageExecutionTime: number;
/** 最后触发时间 */
lastTriggerTime: number;
}
/**
* 事件批处理配置
*/
export type EventBatchConfig = {
/** 批处理大小 */
batchSize: number;
/** 批处理延迟(毫秒) */
delay: number;
/** 是否启用批处理 */
enabled: boolean;
}
/**
* 类型安全的高性能事件系统
* 支持同步/异步事件、优先级、批处理等功能
*/
export class TypeSafeEventSystem {
private static readonly _logger = createLogger('EventSystem');
private listeners = new Map<string, InternalEventListener[]>();
private stats = new Map<string, EventStats>();
// eslint-disable-next-line @typescript-eslint/no-explicit-any
private batchQueue = new Map<string, any[]>();
private batchTimers = new Map<string, ReturnType<typeof setTimeout>>();
private batchConfigs = new Map<string, EventBatchConfig>();
private nextListenerId = 0;
private isEnabled = true;
private maxListeners = 100; // 每个事件类型的最大监听器数量
/**
* 添加事件监听器
* @param eventType 事件类型
* @param handler 事件处理器(同步或异步,根据 config.async 决定)
* @param config 监听器配置
* @returns 监听器ID用于移除
*/
public on<T>(
eventType: string,
handler: EventHandler<T> | AsyncEventHandler<T>,
config: EventListenerConfig = {}
): string {
return this.addListener(eventType, handler, config);
}
/**
* 添加一次性事件监听器
* @param eventType 事件类型
* @param handler 事件处理器
* @param config 监听器配置
* @returns 监听器ID
*/
public once<T>(eventType: string, handler: EventHandler<T>, config: EventListenerConfig = {}): string {
return this.addListener(eventType, handler, { ...config, once: true });
}
/**
* 添加异步事件监听器
* @param eventType 事件类型
* @param handler 异步事件处理器
* @param config 监听器配置
* @returns 监听器ID
*/
public onAsync<T>(eventType: string, handler: AsyncEventHandler<T>, config: EventListenerConfig = {}): string {
return this.addListener(eventType, handler, { ...config, async: true });
}
/**
* 移除事件监听器
* @param eventType 事件类型
* @param listenerId 监听器ID
* @returns 是否成功移除
*/
public off(eventType: string, listenerId: string): boolean {
const listeners = this.listeners.get(eventType);
if (!listeners) return false;
const index = listeners.findIndex((l) => l.id === listenerId);
if (index === -1) return false;
listeners.splice(index, 1);
// 如果没有监听器了,清理相关数据
if (listeners.length === 0) {
this.listeners.delete(eventType);
this.stats.delete(eventType);
}
return true;
}
/**
* 移除指定事件类型的所有监听器
* @param eventType 事件类型
*/
public offAll(eventType: string): void {
this.listeners.delete(eventType);
this.stats.delete(eventType);
this.clearBatch(eventType);
}
/**
* 触发事件
* @param eventType 事件类型
* @param event 事件数据
* @returns Promise如果有异步监听器
*/
public async emit<T>(eventType: string, event: T): Promise<void> {
if (!this.isEnabled) return;
// 检查是否启用了批处理
const batchConfig = this.batchConfigs.get(eventType);
if (batchConfig?.enabled) {
this.addToBatch(eventType, event);
return;
}
await this.executeEvent(eventType, event);
}
/**
* 同步触发事件(忽略异步监听器)
* @param eventType 事件类型
* @param event 事件数据
*/
public emitSync<T>(eventType: string, event: T): void {
if (!this.isEnabled) return;
const listeners = this.listeners.get(eventType);
if (!listeners || listeners.length === 0) return;
const startTime = performance.now();
const toRemove: string[] = [];
// 按优先级排序
const sortedListeners = this.sortListenersByPriority(listeners);
for (const listener of sortedListeners) {
if (listener.config.async) continue; // 跳过异步监听器
try {
if (listener.config.thisArg) {
(listener.handler as EventHandler<T>).call(listener.config.thisArg, event);
} else {
(listener.handler as EventHandler<T>)(event);
}
if (listener.config.once) {
toRemove.push(listener.id);
}
} catch (error) {
TypeSafeEventSystem._logger.error(`事件处理器执行错误 ${eventType}:`, error);
}
}
// 移除一次性监听器
this.removeListeners(eventType, toRemove);
// 更新统计信息
this.updateStats(eventType, performance.now() - startTime);
}
/**
* 设置事件批处理配置
* @param eventType 事件类型
* @param config 批处理配置
*/
public setBatchConfig(eventType: string, config: EventBatchConfig): void {
this.batchConfigs.set(eventType, config);
}
/**
* 立即处理指定事件类型的批处理队列
* @param eventType 事件类型
*/
public flushBatch(eventType: string): void {
const batch = this.batchQueue.get(eventType);
if (!batch || batch.length === 0) return;
// 清除定时器
const timer = this.batchTimers.get(eventType);
if (timer) {
clearTimeout(timer);
this.batchTimers.delete(eventType);
}
// 处理批处理事件
this.processBatch(eventType, batch);
// 清空队列
this.batchQueue.delete(eventType);
}
/**
* 获取事件统计信息
* @param eventType 事件类型(可选)
* @returns 统计信息
*/
public getStats(eventType?: string): EventStats | Map<string, EventStats> {
if (eventType) {
return this.stats.get(eventType) || this.createEmptyStats(eventType);
}
return new Map(this.stats);
}
/**
* 重置统计信息
* @param eventType 事件类型(可选,不指定则重置所有)
*/
public resetStats(eventType?: string): void {
if (eventType) {
this.stats.delete(eventType);
} else {
this.stats.clear();
}
}
/**
* 启用/禁用事件系统
* @param enabled 是否启用
*/
public setEnabled(enabled: boolean): void {
this.isEnabled = enabled;
}
/**
* 检查是否有指定事件类型的监听器
* @param eventType 事件类型
* @returns 是否有监听器
*/
public hasListeners(eventType: string): boolean {
const listeners = this.listeners.get(eventType);
return listeners ? listeners.length > 0 : false;
}
/**
* 获取指定事件类型的监听器数量
* @param eventType 事件类型
* @returns 监听器数量
*/
public getListenerCount(eventType: string): number {
const listeners = this.listeners.get(eventType);
return listeners ? listeners.length : 0;
}
/**
* 清空所有事件监听器和数据
*/
public clear(): void {
this.listeners.clear();
this.stats.clear();
this.clearAllBatches();
}
/**
* 设置每个事件类型的最大监听器数量
* @param max 最大数量
*/
public setMaxListeners(max: number): void {
this.maxListeners = max;
}
/**
* 添加监听器的内部实现
* @param eventType 事件类型
* @param handler 事件处理器
* @param config 配置
* @returns 监听器ID
*/
private addListener<T>(
eventType: string,
handler: EventHandler<T> | AsyncEventHandler<T>,
config: EventListenerConfig
): string {
let listeners = this.listeners.get(eventType);
if (!listeners) {
listeners = [];
this.listeners.set(eventType, listeners);
}
// 检查监听器数量限制
if (listeners.length >= this.maxListeners) {
TypeSafeEventSystem._logger.warn(`事件类型 ${eventType} 的监听器数量超过最大限制 (${this.maxListeners})`);
return '';
}
const listenerId = `listener_${this.nextListenerId++}`;
const listener: InternalEventListener = {
handler,
config: {
priority: 0,
...config
},
id: listenerId
};
listeners.push(listener);
// 初始化统计信息
if (!this.stats.has(eventType)) {
this.stats.set(eventType, this.createEmptyStats(eventType));
}
return listenerId;
}
/**
* 执行事件的内部实现
* @param eventType 事件类型
* @param event 事件数据
*/
private async executeEvent<T>(eventType: string, event: T): Promise<void> {
const listeners = this.listeners.get(eventType);
if (!listeners || listeners.length === 0) return;
const startTime = performance.now();
const toRemove: string[] = [];
// 按优先级排序
const sortedListeners = this.sortListenersByPriority(listeners);
// 分离同步和异步监听器
const syncListeners = sortedListeners.filter((l) => !l.config.async);
const asyncListeners = sortedListeners.filter((l) => l.config.async);
// 执行同步监听器
for (const listener of syncListeners) {
try {
if (listener.config.thisArg) {
(listener.handler as EventHandler<T>).call(listener.config.thisArg, event);
} else {
(listener.handler as EventHandler<T>)(event);
}
if (listener.config.once) {
toRemove.push(listener.id);
}
} catch (error) {
TypeSafeEventSystem._logger.error(`同步事件处理器执行错误 ${eventType}:`, error);
}
}
// 执行异步监听器
const asyncPromises = asyncListeners.map(async (listener) => {
try {
if (listener.config.thisArg) {
await (listener.handler as AsyncEventHandler<T>).call(listener.config.thisArg, event);
} else {
await (listener.handler as AsyncEventHandler<T>)(event);
}
if (listener.config.once) {
toRemove.push(listener.id);
}
} catch (error) {
TypeSafeEventSystem._logger.error(`异步事件处理器执行错误 ${eventType}:`, error);
}
});
// 等待所有异步监听器完成
await Promise.all(asyncPromises);
// 移除一次性监听器
this.removeListeners(eventType, toRemove);
// 更新统计信息
this.updateStats(eventType, performance.now() - startTime);
}
/**
* 按优先级排序监听器
* @param listeners 监听器数组
* @returns 排序后的监听器数组
*/
private sortListenersByPriority(listeners: InternalEventListener[]): InternalEventListener[] {
return listeners.slice().sort((a, b) => (b.config.priority || 0) - (a.config.priority || 0));
}
/**
* 移除指定的监听器
* @param eventType 事件类型
* @param listenerIds 要移除的监听器ID数组
*/
private removeListeners(eventType: string, listenerIds: string[]): void {
if (listenerIds.length === 0) return;
const listeners = this.listeners.get(eventType);
if (!listeners) return;
for (const id of listenerIds) {
const index = listeners.findIndex((l) => l.id === id);
if (index !== -1) {
listeners.splice(index, 1);
}
}
// 如果没有监听器了,清理相关数据
if (listeners.length === 0) {
this.listeners.delete(eventType);
this.stats.delete(eventType);
}
}
/**
* 添加事件到批处理队列
* @param eventType 事件类型
* @param event 事件数据
*/
private addToBatch<T>(eventType: string, event: T): void {
let batch = this.batchQueue.get(eventType);
if (!batch) {
batch = [];
this.batchQueue.set(eventType, batch);
}
batch.push(event);
const config = this.batchConfigs.get(eventType)!;
// 如果达到批处理大小,立即处理
if (batch.length >= config.batchSize) {
this.flushBatch(eventType);
return;
}
// 设置延迟处理定时器
if (!this.batchTimers.has(eventType)) {
const timer = setTimeout(() => {
this.flushBatch(eventType);
}, config.delay);
this.batchTimers.set(eventType, timer);
}
}
/**
* 处理批处理事件
* @param eventType 事件类型
* @param batch 批处理事件数组
*/
private async processBatch<T>(eventType: string, batch: T[]): Promise<void> {
// 创建批处理事件对象
const batchEvent = {
type: eventType,
events: batch,
count: batch.length,
timestamp: Date.now()
};
// 触发批处理事件
await this.executeEvent(`${eventType}:batch`, batchEvent);
}
/**
* 清除指定事件类型的批处理
* @param eventType 事件类型
*/
private clearBatch(eventType: string): void {
this.batchQueue.delete(eventType);
const timer = this.batchTimers.get(eventType);
if (timer) {
clearTimeout(timer);
this.batchTimers.delete(eventType);
}
}
/**
* 清除所有批处理
*/
private clearAllBatches(): void {
this.batchQueue.clear();
for (const timer of this.batchTimers.values()) {
clearTimeout(timer);
}
this.batchTimers.clear();
this.batchConfigs.clear();
}
/**
* 更新事件统计信息
* @param eventType 事件类型
* @param executionTime 执行时间
*/
private updateStats(eventType: string, executionTime: number): void {
let stats = this.stats.get(eventType);
if (!stats) {
stats = this.createEmptyStats(eventType);
this.stats.set(eventType, stats);
}
stats.triggerCount++;
stats.totalExecutionTime += executionTime;
stats.averageExecutionTime = stats.totalExecutionTime / stats.triggerCount;
stats.lastTriggerTime = Date.now();
stats.listenerCount = this.getListenerCount(eventType);
}
/**
* 创建空的统计信息
* @param eventType 事件类型
* @returns 空的统计信息
*/
private createEmptyStats(eventType: string): EventStats {
return {
eventType,
listenerCount: 0,
triggerCount: 0,
totalExecutionTime: 0,
averageExecutionTime: 0,
lastTriggerTime: 0
};
}
}
/**
* 全局事件系统实例
*/
export const GlobalEventSystem = new TypeSafeEventSystem();

View File

@@ -0,0 +1,3 @@
export { EventBus, GlobalEventBus } from '../EventBus';
export { TypeSafeEventSystem } from '../EventSystem';
export type { EventListenerConfig, EventStats } from '../EventSystem';

View File

@@ -0,0 +1,11 @@
// 重新导出拆分后的模块以保持向后兼容性
export {
EntityBuilder,
SceneBuilder,
ComponentBuilder,
EntityBatchOperator,
ECSFluentAPI,
createECSAPI,
initializeECS,
ECS
} from './FluentAPI/index';

View File

@@ -0,0 +1,55 @@
import { Component } from '../../Component';
/**
* 组件构建器 - 提供流式API创建组件
*/
export class ComponentBuilder<T extends Component> {
private component: T;
constructor(componentClass: new (...args: unknown[]) => T, ...args: unknown[]) {
this.component = new componentClass(...args);
}
/**
* 设置组件属性
* @param property 属性名
* @param value 属性值
* @returns 组件构建器
*/
public set<K extends keyof T>(property: K, value: T[K]): ComponentBuilder<T> {
this.component[property] = value;
return this;
}
/**
* 使用配置函数设置组件
* @param configurator 配置函数
* @returns 组件构建器
*/
public configure(configurator: (component: T) => void): ComponentBuilder<T> {
configurator(this.component);
return this;
}
/**
* 条件性设置属性
* @param condition 条件
* @param property 属性名
* @param value 属性值
* @returns 组件构建器
*/
public setIf<K extends keyof T>(condition: boolean, property: K, value: T[K]): ComponentBuilder<T> {
if (condition) {
this.component[property] = value;
}
return this;
}
/**
* 构建并返回组件
* @returns 构建的组件
*/
public build(): T {
return this.component;
}
}

View File

@@ -0,0 +1,210 @@
import { Entity } from '../../Entity';
import { Component } from '../../Component';
import { IScene } from '../../IScene';
import { ComponentType } from '../ComponentStorage';
import { QuerySystem, QueryBuilder } from '../QuerySystem';
import { TypeSafeEventSystem } from '../EventSystem';
import { EntityBuilder } from './EntityBuilder';
import { SceneBuilder } from './SceneBuilder';
import { ComponentBuilder } from './ComponentBuilder';
import { EntityBatchOperator } from './EntityBatchOperator';
/**
* ECS流式API主入口
* 提供统一的流式接口
*/
export class ECSFluentAPI {
private scene: IScene;
private querySystem: QuerySystem;
private eventSystem: TypeSafeEventSystem;
constructor(scene: IScene, querySystem: QuerySystem, eventSystem: TypeSafeEventSystem) {
this.scene = scene;
this.querySystem = querySystem;
this.eventSystem = eventSystem;
}
/**
* 创建实体构建器
* @returns 实体构建器
*/
public createEntity(): EntityBuilder {
return new EntityBuilder(this.scene, this.scene.componentStorageManager);
}
/**
* 创建场景构建器
* @returns 场景构建器
*/
public createScene(): SceneBuilder {
return new SceneBuilder();
}
/**
* 创建组件构建器
* @param componentClass 组件类
* @param args 构造参数
* @returns 组件构建器
*/
public createComponent<T extends Component>(
componentClass: new (...args: unknown[]) => T,
...args: unknown[]
): ComponentBuilder<T> {
return new ComponentBuilder(componentClass, ...args);
}
/**
* 创建查询构建器
* @returns 查询构建器
*/
public query(): QueryBuilder {
return new QueryBuilder(this.querySystem);
}
/**
* 查找实体
* @param componentTypes 组件类型
* @returns 实体数组
*/
public find(...componentTypes: ComponentType[]): readonly Entity[] {
return this.querySystem.queryAll(...componentTypes).entities;
}
/**
* 查找第一个匹配的实体
* @param componentTypes 组件类型
* @returns 实体或null
*/
public findFirst(...componentTypes: ComponentType[]): Entity | null {
const result = this.querySystem.queryAll(...componentTypes);
return result.entities.length > 0 ? result.entities[0]! : null;
}
/**
* 按名称查找实体
* @param name 实体名称
* @returns 实体或null
*/
public findByName(name: string): Entity | null {
return this.scene.findEntity(name);
}
/**
* 按标签查找实体
* @param tag 标签
* @returns 实体数组
*/
public findByTag(tag: number): Entity[] {
return this.scene.findEntitiesByTag(tag);
}
/**
* 触发事件
* @param eventType 事件类型
* @param event 事件数据
*/
public emit<T>(eventType: string, event: T): void {
this.eventSystem.emitSync(eventType, event);
}
/**
* 异步触发事件
* @param eventType 事件类型
* @param event 事件数据
*/
public async emitAsync<T>(eventType: string, event: T): Promise<void> {
await this.eventSystem.emit(eventType, event);
}
/**
* 监听事件
* @param eventType 事件类型
* @param handler 事件处理器
* @returns 监听器ID
*/
public on<T>(eventType: string, handler: (event: T) => void): string {
return this.eventSystem.on(eventType, handler);
}
/**
* 一次性监听事件
* @param eventType 事件类型
* @param handler 事件处理器
* @returns 监听器ID
*/
public once<T>(eventType: string, handler: (event: T) => void): string {
return this.eventSystem.once(eventType, handler);
}
/**
* 移除事件监听器
* @param eventType 事件类型
* @param listenerId 监听器ID
*/
public off(eventType: string, listenerId: string): void {
this.eventSystem.off(eventType, listenerId);
}
/**
* 批量操作实体
* @param entities 实体数组
* @returns 批量操作器
*/
public batch(entities: Entity[]): EntityBatchOperator {
return new EntityBatchOperator(entities);
}
/**
* 获取场景统计信息
* @returns 统计信息
*/
public getStats(): {
entityCount: number;
systemCount: number;
componentStats: Map<string, unknown>;
queryStats: unknown;
eventStats: Map<string, unknown>;
} {
return {
entityCount: this.scene.entities.count,
systemCount: this.scene.systems.length,
componentStats: this.scene.componentStorageManager.getAllStats(),
queryStats: this.querySystem.getStats(),
eventStats: this.eventSystem.getStats() as Map<string, unknown>
};
}
}
/**
* 创建ECS流式API实例
* @param scene 场景
* @param querySystem 查询系统
* @param eventSystem 事件系统
* @returns ECS流式API实例
*/
export function createECSAPI(
scene: IScene,
querySystem: QuerySystem,
eventSystem: TypeSafeEventSystem
): ECSFluentAPI {
return new ECSFluentAPI(scene, querySystem, eventSystem);
}
/**
* 全局ECS流式API实例需要在使用前初始化
*/
export let ECS: ECSFluentAPI;
/**
* 初始化全局ECS API
* @param scene 场景
* @param querySystem 查询系统
* @param eventSystem 事件系统
*/
export function initializeECS(
scene: IScene,
querySystem: QuerySystem,
eventSystem: TypeSafeEventSystem
): void {
ECS = createECSAPI(scene, querySystem, eventSystem);
}

View File

@@ -0,0 +1,98 @@
import { Entity } from '../../Entity';
import { Component } from '../../Component';
import { ComponentType } from '../ComponentStorage';
/**
* 实体批量操作器
* 提供对多个实体的批量操作
*/
export class EntityBatchOperator {
private entities: Entity[];
constructor(entities: Entity[]) {
this.entities = entities;
}
/**
* 批量添加组件
* @param component 组件实例
* @returns 批量操作器
*/
public addComponent<T extends Component>(component: T): EntityBatchOperator {
for (const entity of this.entities) {
entity.addComponent(component);
}
return this;
}
/**
* 批量移除组件
* @param componentType 组件类型
* @returns 批量操作器
*/
public removeComponent<T extends Component>(componentType: ComponentType<T>): EntityBatchOperator {
for (const entity of this.entities) {
entity.removeComponentByType(componentType);
}
return this;
}
/**
* 批量设置活跃状态
* @param active 是否活跃
* @returns 批量操作器
*/
public setActive(active: boolean): EntityBatchOperator {
for (const entity of this.entities) {
entity.active = active;
}
return this;
}
/**
* 批量设置标签
* @param tag 标签
* @returns 批量操作器
*/
public setTag(tag: number): EntityBatchOperator {
for (const entity of this.entities) {
entity.tag = tag;
}
return this;
}
/**
* 批量执行操作
* @param operation 操作函数
* @returns 批量操作器
*/
public forEach(operation: (entity: Entity, index: number) => void): EntityBatchOperator {
this.entities.forEach(operation);
return this;
}
/**
* 过滤实体
* @param predicate 过滤条件
* @returns 新的批量操作器
*/
public filter(predicate: (entity: Entity) => boolean): EntityBatchOperator {
return new EntityBatchOperator(this.entities.filter(predicate));
}
/**
* 获取实体数组
* @returns 实体数组
*/
public toArray(): Entity[] {
return this.entities.slice();
}
/**
* 获取实体数量
* @returns 实体数量
*/
public count(): number {
return this.entities.length;
}
}

View File

@@ -0,0 +1,207 @@
import { Entity } from '../../Entity';
import { Component } from '../../Component';
import { IScene } from '../../IScene';
import { ComponentType, ComponentStorageManager } from '../ComponentStorage';
import { HierarchySystem } from '../../Systems/HierarchySystem';
/**
* 实体构建器 - 提供流式API创建和配置实体
*/
export class EntityBuilder {
private entity: Entity;
private scene: IScene;
private storageManager: ComponentStorageManager;
constructor(scene: IScene, storageManager: ComponentStorageManager) {
this.scene = scene;
this.storageManager = storageManager;
const id = scene.identifierPool.checkOut();
this.entity = new Entity('', id);
this.entity.scene = this.scene as any;
}
/**
* 设置实体名称
* @param name 实体名称
* @returns 实体构建器
*/
public named(name: string): EntityBuilder {
this.entity.name = name;
return this;
}
/**
* 设置实体标签
* @param tag 标签
* @returns 实体构建器
*/
public tagged(tag: number): EntityBuilder {
this.entity.tag = tag;
return this;
}
/**
* 添加组件
* @param component 组件实例
* @returns 实体构建器
*/
public with<T extends Component>(component: T): EntityBuilder {
this.entity.addComponent(component);
return this;
}
/**
* 添加多个组件
* @param components 组件数组
* @returns 实体构建器
*/
public withComponents(...components: Component[]): EntityBuilder {
for (const component of components) {
this.entity.addComponent(component);
}
return this;
}
/**
* 条件性添加组件
* @param condition 条件
* @param component 组件实例
* @returns 实体构建器
*/
public withIf<T extends Component>(condition: boolean, component: T): EntityBuilder {
if (condition) {
this.entity.addComponent(component);
}
return this;
}
/**
* 使用工厂函数创建并添加组件
* @param factory 组件工厂函数
* @returns 实体构建器
*/
public withFactory<T extends Component>(factory: () => T): EntityBuilder {
const component = factory();
this.entity.addComponent(component);
return this;
}
/**
* 配置组件属性
* @param componentType 组件类型
* @param configurator 配置函数
* @returns 实体构建器
*/
public configure<T extends Component>(
componentType: ComponentType<T>,
configurator: (component: T) => void
): EntityBuilder {
const component = this.entity.getComponent(componentType);
if (component) {
configurator(component);
}
return this;
}
/**
* 设置实体为启用状态
* @param enabled 是否启用
* @returns 实体构建器
*/
public enabled(enabled: boolean = true): EntityBuilder {
this.entity.enabled = enabled;
return this;
}
/**
* 设置实体为活跃状态
* @param active 是否活跃
* @returns 实体构建器
*/
public active(active: boolean = true): EntityBuilder {
this.entity.active = active;
return this;
}
/**
* 添加子实体
* @param childBuilder 子实体构建器
* @returns 实体构建器
*/
public withChild(childBuilder: EntityBuilder): EntityBuilder {
const child = childBuilder.build();
const hierarchySystem = this.scene.getSystem(HierarchySystem);
hierarchySystem?.setParent(child, this.entity);
return this;
}
/**
* 批量添加子实体
* @param childBuilders 子实体构建器数组
* @returns 实体构建器
*/
public withChildren(...childBuilders: EntityBuilder[]): EntityBuilder {
const hierarchySystem = this.scene.getSystem(HierarchySystem);
for (const childBuilder of childBuilders) {
const child = childBuilder.build();
hierarchySystem?.setParent(child, this.entity);
}
return this;
}
/**
* 使用工厂函数创建子实体
* @param childFactory 子实体工厂函数
* @returns 实体构建器
*/
public withChildFactory(childFactory: (parent: Entity) => EntityBuilder): EntityBuilder {
const childBuilder = childFactory(this.entity);
const child = childBuilder.build();
const hierarchySystem = this.scene.getSystem(HierarchySystem);
hierarchySystem?.setParent(child, this.entity);
return this;
}
/**
* 条件性添加子实体
* @param condition 条件
* @param childBuilder 子实体构建器
* @returns 实体构建器
*/
public withChildIf(condition: boolean, childBuilder: EntityBuilder): EntityBuilder {
if (condition) {
const child = childBuilder.build();
const hierarchySystem = this.scene.getSystem(HierarchySystem);
hierarchySystem?.setParent(child, this.entity);
}
return this;
}
/**
* 构建并返回实体
* @returns 构建的实体
*/
public build(): Entity {
return this.entity;
}
/**
* 构建实体并添加到场景
* @returns 构建的实体
*/
public spawn(): Entity {
this.scene.addEntity(this.entity);
return this.entity;
}
/**
* 克隆当前构建器
* @returns 新的实体构建器
*/
public clone(): EntityBuilder {
const newBuilder = new EntityBuilder(this.scene, this.storageManager);
// 这里需要深度克隆实体,简化实现
newBuilder.entity = this.entity; // 实际应该是深度克隆
return newBuilder;
}
}

View File

@@ -0,0 +1,90 @@
import { Entity } from '../../Entity';
import { Scene } from '../../Scene';
import { EntitySystem } from '../../Systems/EntitySystem';
import { EntityBuilder } from './EntityBuilder';
/**
* 场景构建器 - 提供流式API创建和配置场景
*/
export class SceneBuilder {
private scene: Scene;
constructor() {
this.scene = new Scene();
}
/**
* 设置场景名称
* @param name 场景名称
* @returns 场景构建器
*/
public named(name: string): SceneBuilder {
this.scene.name = name;
return this;
}
/**
* 添加实体
* @param entity 实体
* @returns 场景构建器
*/
public withEntity(entity: Entity): SceneBuilder {
this.scene.addEntity(entity);
return this;
}
/**
* 使用实体构建器添加实体
* @param builderFn 实体构建器函数
* @returns 场景构建器
*/
public withEntityBuilder(builderFn: (builder: EntityBuilder) => EntityBuilder): SceneBuilder {
const builder = new EntityBuilder(this.scene, this.scene.componentStorageManager);
const configuredBuilder = builderFn(builder);
const entity = configuredBuilder.build();
this.scene.addEntity(entity);
return this;
}
/**
* 批量添加实体
* @param entities 实体数组
* @returns 场景构建器
*/
public withEntities(...entities: Entity[]): SceneBuilder {
for (const entity of entities) {
this.scene.addEntity(entity);
}
return this;
}
/**
* 添加系统
* @param system 系统实例
* @returns 场景构建器
*/
public withSystem(system: EntitySystem): SceneBuilder {
this.scene.addSystem(system);
return this;
}
/**
* 批量添加系统
* @param systems 系统数组
* @returns 场景构建器
*/
public withSystems(...systems: EntitySystem[]): SceneBuilder {
for (const system of systems) {
this.scene.addSystem(system);
}
return this;
}
/**
* 构建并返回场景
* @returns 构建的场景
*/
public build(): Scene {
return this.scene;
}
}

View File

@@ -0,0 +1,5 @@
export { EntityBuilder } from './EntityBuilder';
export { SceneBuilder } from './SceneBuilder';
export { ComponentBuilder } from './ComponentBuilder';
export { EntityBatchOperator } from './EntityBatchOperator';
export { ECSFluentAPI, createECSAPI, initializeECS, ECS } from './ECSFluentAPI';

View File

@@ -0,0 +1,366 @@
/**
* 编译查询
*
* 预编译的查询执行计划,避免每次查询时重复构建匹配条件。
* 提供便捷的迭代方法和变更检测支持。
*
* Compiled Query.
* Pre-compiled query execution plan that avoids repeated condition building.
* Provides convenient iteration methods and change detection support.
*
* @example
* ```typescript
* class MovementSystem extends EntitySystem {
* private _query: CompiledQuery<[typeof Position, typeof Velocity]>;
*
* onInitialize(): void {
* this._query = this.scene.querySystem.compile(Position, Velocity);
* }
*
* update(): void {
* this._query.forEach((entity, pos, vel) => {
* pos.x += vel.x * this.deltaTime;
* pos.y += vel.y * this.deltaTime;
* });
* }
* }
* ```
*/
import { Entity } from '../../Entity';
import { Component } from '../../Component';
import { ComponentType } from '../ComponentStorage';
import { QuerySystem } from '../QuerySystem';
/**
* 组件实例类型提取
*
* Extract component instance types from component type tuple.
*/
export type InstanceTypes<T extends ComponentType[]> = {
[K in keyof T]: T[K] extends ComponentType<infer C> ? C : never;
};
/**
* 编译查询类
*
* Compiled query class.
*/
export class CompiledQuery<T extends ComponentType[] = ComponentType[]> {
/**
* 查询的组件类型列表
*
* Component types for this query.
*/
private readonly _componentTypes: T;
/**
* 查询系统引用
*
* Reference to query system.
*/
private readonly _querySystem: QuerySystem;
/**
* 上次查询的版本号(用于缓存失效检测)
*
* Last query version (for cache invalidation detection).
*/
private _lastVersion: number = -1;
/**
* 缓存的实体列表
*
* Cached entity list.
*/
private _cachedEntities: readonly Entity[] = [];
/**
* 创建编译查询
*
* Create compiled query.
*
* @param querySystem 查询系统引用 | Query system reference
* @param componentTypes 组件类型列表 | Component type list
*/
constructor(querySystem: QuerySystem, ...componentTypes: T) {
this._querySystem = querySystem;
this._componentTypes = componentTypes;
}
/**
* 获取组件类型列表
*
* Get component type list.
*/
public get componentTypes(): readonly ComponentType[] {
return this._componentTypes;
}
/**
* 获取匹配的实体列表
*
* Get matching entity list.
*/
public get entities(): readonly Entity[] {
this._refreshCache();
return this._cachedEntities;
}
/**
* 获取匹配的实体数量
*
* Get matching entity count.
*/
public get count(): number {
return this.entities.length;
}
/**
* 刷新缓存
*
* Refresh cache if needed.
*/
private _refreshCache(): void {
const currentVersion = this._querySystem.version;
if (this._lastVersion !== currentVersion) {
const result = this._querySystem.queryAll(...this._componentTypes);
this._cachedEntities = result.entities;
this._lastVersion = currentVersion;
}
}
/**
* 遍历所有匹配的实体及其组件
*
* Iterate all matching entities with their components.
*
* @param callback 回调函数,接收实体和组件实例 | Callback receiving entity and component instances
*
* @example
* ```typescript
* query.forEach((entity, position, velocity) => {
* position.x += velocity.x * deltaTime;
* });
* ```
*/
public forEach(
callback: (entity: Entity, ...components: InstanceTypes<T>) => void
): void {
const entities = this.entities;
const componentTypes = this._componentTypes;
const typeCount = componentTypes.length;
for (let i = 0, len = entities.length; i < len; i++) {
const entity = entities[i]!;
const components: Component[] = new Array(typeCount);
// 获取所有组件
for (let j = 0; j < typeCount; j++) {
const component = entity.getComponent(componentTypes[j]!);
if (!component) {
// 组件不存在,跳过这个实体
continue;
}
components[j] = component;
}
// 调用回调
callback(entity, ...(components as InstanceTypes<T>));
}
}
/**
* 遍历自指定 epoch 以来发生变更的实体
*
* Iterate entities with components changed since specified epoch.
*
* @param sinceEpoch 检查点 epoch | Checkpoint epoch
* @param callback 回调函数 | Callback function
*
* @example
* ```typescript
* class PhysicsSystem extends EntitySystem {
* private _lastEpoch = 0;
*
* update(): void {
* this._query.forEachChanged(this._lastEpoch, (entity, pos, vel) => {
* // 只处理变更的实体
* });
* this._lastEpoch = this.scene.epochManager.current;
* }
* }
* ```
*/
public forEachChanged(
sinceEpoch: number,
callback: (entity: Entity, ...components: InstanceTypes<T>) => void
): void {
const entities = this.entities;
const componentTypes = this._componentTypes;
const typeCount = componentTypes.length;
for (let i = 0, len = entities.length; i < len; i++) {
const entity = entities[i]!;
const components: Component[] = new Array(typeCount);
let hasChanged = false;
// 获取所有组件并检查变更
for (let j = 0; j < typeCount; j++) {
const component = entity.getComponent(componentTypes[j]!);
if (!component) {
continue;
}
components[j] = component;
if (component.lastWriteEpoch > sinceEpoch) {
hasChanged = true;
}
}
// 只在有变更时调用回调
if (hasChanged) {
callback(entity, ...(components as InstanceTypes<T>));
}
}
}
/**
* 获取第一个匹配的实体及其组件
*
* Get first matching entity with its components.
*
* @returns 实体和组件元组,或 null | Entity and components tuple, or null
*/
public first(): [Entity, ...InstanceTypes<T>] | null {
const entities = this.entities;
if (entities.length === 0) {
return null;
}
const entity = entities[0]!;
const components: Component[] = [];
for (const type of this._componentTypes) {
const component = entity.getComponent(type);
if (!component) {
return null;
}
components.push(component);
}
return [entity, ...(components as InstanceTypes<T>)];
}
/**
* 转换为数组
*
* Convert to array.
*
* @returns 实体和组件元组数组 | Array of entity and components tuples
*/
public toArray(): Array<[Entity, ...InstanceTypes<T>]> {
const result: Array<[Entity, ...InstanceTypes<T>]> = [];
this.forEach((entity, ...components) => {
result.push([entity, ...components]);
});
return result;
}
/**
* 映射转换
*
* Map transformation.
*
* @param callback 转换函数 | Transform function
* @returns 转换结果数组 | Array of transform results
*/
public map<R>(
callback: (entity: Entity, ...components: InstanceTypes<T>) => R
): R[] {
const result: R[] = [];
this.forEach((entity, ...components) => {
result.push(callback(entity, ...components));
});
return result;
}
/**
* 过滤实体
*
* Filter entities.
*
* @param predicate 过滤条件 | Filter predicate
* @returns 过滤后的实体数组 | Filtered entity array
*/
public filter(
predicate: (entity: Entity, ...components: InstanceTypes<T>) => boolean
): Entity[] {
const result: Entity[] = [];
this.forEach((entity, ...components) => {
if (predicate(entity, ...components)) {
result.push(entity);
}
});
return result;
}
/**
* 查找满足条件的实体
*
* Find entity matching predicate.
*
* @param predicate 查找条件 | Find predicate
* @returns 找到的实体或 undefined | Found entity or undefined
*/
public find(
predicate: (entity: Entity, ...components: InstanceTypes<T>) => boolean
): Entity | undefined {
const entities = this.entities;
const componentTypes = this._componentTypes;
const typeCount = componentTypes.length;
for (let i = 0, len = entities.length; i < len; i++) {
const entity = entities[i]!;
const components: Component[] = new Array(typeCount);
for (let j = 0; j < typeCount; j++) {
const component = entity.getComponent(componentTypes[j]!);
if (!component) {
continue;
}
components[j] = component;
}
if (predicate(entity, ...(components as InstanceTypes<T>))) {
return entity;
}
}
return undefined;
}
/**
* 检查是否有任何实体匹配
*
* Check if any entity matches.
*/
public any(): boolean {
return this.count > 0;
}
/**
* 检查是否没有实体匹配
*
* Check if no entity matches.
*/
public empty(): boolean {
return this.count === 0;
}
}

View File

@@ -0,0 +1,408 @@
/**
* 类型安全的Query查询系统
*
* 提供完整的TypeScript类型推断在编译时确保类型安全
*/
import type { Entity } from '../../Entity';
import type { ComponentConstructor } from '../../../Types/TypeHelpers';
import { Matcher, type QueryCondition } from '../../Utils/Matcher';
/**
* 类型安全的查询结果
*
* 根据查询条件自动推断实体必定拥有的组件类型
*/
export class TypedQueryResult<TAll extends readonly ComponentConstructor[]> {
private _entities: readonly Entity[];
private _componentTypes: TAll;
constructor(entities: readonly Entity[], componentTypes: TAll) {
this._entities = entities;
this._componentTypes = componentTypes;
}
/**
* 获取实体列表
*/
get entities(): readonly Entity[] {
return this._entities;
}
/**
* 实体数量
*/
get length(): number {
return this._entities.length;
}
/**
* 遍历所有实体
*
* @example
* ```typescript
* query.forEach((entity) => {
* // entity.getComponent返回类型自动推断
* const pos = entity.getComponent(Position); // Position类型
* const vel = entity.getComponent(Velocity); // Velocity类型
* });
* ```
*/
forEach(callback: (entity: Entity, index: number) => void): void {
this._entities.forEach(callback);
}
/**
* 映射转换实体
*/
map<R>(callback: (entity: Entity, index: number) => R): R[] {
return this._entities.map(callback);
}
/**
* 过滤实体
*/
filter(predicate: (entity: Entity, index: number) => boolean): TypedQueryResult<TAll> {
return new TypedQueryResult(this._entities.filter(predicate), this._componentTypes);
}
/**
* 查找第一个匹配的实体
*/
find(predicate: (entity: Entity, index: number) => boolean): Entity | undefined {
return this._entities.find(predicate);
}
/**
* 检查是否存在匹配的实体
*/
some(predicate: (entity: Entity, index: number) => boolean): boolean {
return this._entities.some(predicate);
}
/**
* 检查是否所有实体都匹配
*/
every(predicate: (entity: Entity, index: number) => boolean): boolean {
return this._entities.every(predicate);
}
/**
* 获取指定索引的实体
*/
get(index: number): Entity | undefined {
return this._entities[index];
}
/**
* 获取第一个实体
*/
get first(): Entity | undefined {
return this._entities[0];
}
/**
* 获取最后一个实体
*/
get last(): Entity | undefined {
return this._entities[this._entities.length - 1];
}
/**
* 检查查询结果是否为空
*/
get isEmpty(): boolean {
return this._entities.length === 0;
}
/**
* 转换为数组
*/
toArray(): Entity[] {
return [...this._entities];
}
/**
* 获取组件类型信息(用于调试)
*/
getComponentTypes(): readonly ComponentConstructor[] {
return this._componentTypes;
}
/**
* 迭代器支持
*/
[Symbol.iterator](): Iterator<Entity> {
return this._entities[Symbol.iterator]();
}
}
/**
* 类型安全的查询构建器
*
* 支持链式调用,自动推断查询结果的类型
*
* @example
* ```typescript
* // 基础查询
* const query = new TypedQueryBuilder()
* .withAll(Position, Velocity)
* .build();
*
* // 复杂查询
* const complexQuery = new TypedQueryBuilder()
* .withAll(Transform, Renderer)
* .withAny(BoxCollider, CircleCollider)
* .withNone(Disabled)
* .withTag(EntityTags.Enemy)
* .build();
* ```
*/
export class TypedQueryBuilder<
TAll extends readonly ComponentConstructor[] = [],
TAny extends readonly ComponentConstructor[] = [],
TNone extends readonly ComponentConstructor[] = []
> {
private _all: TAll;
private _any: TAny;
private _none: TNone;
private _tag?: number;
private _name?: string;
constructor(
all?: TAll,
any?: TAny,
none?: TNone,
tag?: number,
name?: string
) {
this._all = (all || []) as TAll;
this._any = (any || []) as TAny;
this._none = (none || []) as TNone;
if (tag !== undefined) {
this._tag = tag;
}
if (name !== undefined) {
this._name = name;
}
}
/**
* 要求实体拥有所有指定的组件
*
* @param types 组件类型
* @returns 新的查询构建器,类型参数更新
*/
withAll<TNewAll extends readonly ComponentConstructor[]>(
...types: TNewAll
): TypedQueryBuilder<
readonly [...TAll, ...TNewAll],
TAny,
TNone
> {
return new TypedQueryBuilder(
[...this._all, ...types] as readonly [...TAll, ...TNewAll],
this._any,
this._none,
this._tag,
this._name
);
}
/**
* 要求实体至少拥有一个指定的组件
*
* @param types 组件类型
* @returns 新的查询构建器
*/
withAny<TNewAny extends readonly ComponentConstructor[]>(
...types: TNewAny
): TypedQueryBuilder<
TAll,
readonly [...TAny, ...TNewAny],
TNone
> {
return new TypedQueryBuilder(
this._all,
[...this._any, ...types] as readonly [...TAny, ...TNewAny],
this._none,
this._tag,
this._name
);
}
/**
* 排除拥有指定组件的实体
*
* @param types 组件类型
* @returns 新的查询构建器
*/
withNone<TNewNone extends readonly ComponentConstructor[]>(
...types: TNewNone
): TypedQueryBuilder<
TAll,
TAny,
readonly [...TNone, ...TNewNone]
> {
return new TypedQueryBuilder(
this._all,
this._any,
[...this._none, ...types] as readonly [...TNone, ...TNewNone],
this._tag,
this._name
);
}
/**
* 按标签过滤实体
*
* @param tag 标签值
* @returns 新的查询构建器
*/
withTag(tag: number): TypedQueryBuilder<TAll, TAny, TNone> {
return new TypedQueryBuilder(
this._all,
this._any,
this._none,
tag,
this._name
);
}
/**
* 按名称过滤实体
*
* @param name 实体名称
* @returns 新的查询构建器
*/
withName(name: string): TypedQueryBuilder<TAll, TAny, TNone> {
return new TypedQueryBuilder(
this._all,
this._any,
this._none,
this._tag,
name
);
}
/**
* 构建Matcher对象
*
* @returns Matcher实例用于传统查询API
*/
buildMatcher(): Matcher {
let matcher = Matcher.complex();
if (this._all.length > 0) {
matcher = matcher.all(...(this._all as unknown as ComponentConstructor[]));
}
if (this._any.length > 0) {
matcher = matcher.any(...(this._any as unknown as ComponentConstructor[]));
}
if (this._none.length > 0) {
matcher = matcher.none(...(this._none as unknown as ComponentConstructor[]));
}
if (this._tag !== undefined) {
matcher = matcher.withTag(this._tag);
}
if (this._name !== undefined) {
matcher = matcher.withName(this._name);
}
return matcher;
}
/**
* 获取查询条件
*
* @returns 查询条件对象
*/
getCondition(): QueryCondition {
return {
all: [...this._all] as ComponentConstructor[],
any: [...this._any] as ComponentConstructor[],
none: [...this._none] as ComponentConstructor[],
...(this._tag !== undefined && { tag: this._tag }),
...(this._name !== undefined && { name: this._name })
};
}
/**
* 获取required组件类型用于类型推断
*/
getRequiredTypes(): TAll {
return this._all;
}
/**
* 克隆查询构建器
*/
clone(): TypedQueryBuilder<TAll, TAny, TNone> {
return new TypedQueryBuilder(
[...this._all] as unknown as TAll,
[...this._any] as unknown as TAny,
[...this._none] as unknown as TNone,
this._tag,
this._name
);
}
}
/**
* 创建类型安全的查询构建器
*
* @example
* ```typescript
* const query = createQuery()
* .withAll(Position, Velocity)
* .withNone(Disabled);
*
* // 在System或Scene中使用
* const entities = scene.query(query);
* entities.forEach(entity => {
* const pos = entity.getComponent(Position); // 自动推断为Position
* const vel = entity.getComponent(Velocity); // 自动推断为Velocity
* });
* ```
*/
export function createQuery(): TypedQueryBuilder<[], [], []> {
return new TypedQueryBuilder();
}
/**
* 创建单组件查询的便捷方法
*
* @param componentType 组件类型
* @returns 查询构建器
*
* @example
* ```typescript
* const healthEntities = queryFor(HealthComponent);
* ```
*/
export function queryFor<T extends ComponentConstructor>(
componentType: T
): TypedQueryBuilder<readonly [T], [], []> {
return new TypedQueryBuilder([componentType] as readonly [T]);
}
/**
* 创建多组件查询的便捷方法
*
* @param types 组件类型数组
* @returns 查询构建器
*
* @example
* ```typescript
* const movableEntities = queryForAll(Position, Velocity);
* ```
*/
export function queryForAll<T extends readonly ComponentConstructor[]>(
...types: T
): TypedQueryBuilder<T, [], []> {
return new TypedQueryBuilder(types);
}

View File

@@ -0,0 +1,2 @@
export { QuerySystem } from '../QuerySystem';
export { ECSFluentAPI, createECSAPI } from '../FluentAPI';

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,36 @@
import { ComponentType } from './ComponentStorage';
import { BitMask64Data } from '../Utils/BigIntCompatibility';
import { Entity } from '../Entity';
/**
* 查询条件类型
*/
export enum QueryConditionType {
/** 必须包含所有指定组件 */
ALL = 'all',
/** 必须包含任意一个指定组件 */
ANY = 'any',
/** 不能包含任何指定组件 */
NONE = 'none'
}
/**
* 查询条件接口
*/
export type QueryCondition = {
type: QueryConditionType;
componentTypes: ComponentType[];
mask: BitMask64Data;
}
/**
* 实体查询结果接口
*/
export type QueryResult = {
entities: readonly Entity[];
count: number;
/** 查询执行时间(毫秒) */
executionTime: number;
/** 是否来自缓存 */
fromCache: boolean;
}

View File

@@ -0,0 +1,501 @@
import { Entity } from '../Entity';
import type { QueryCondition } from './QueryTypes';
import { QueryConditionType } from './QueryTypes';
import { BitMask64Utils } from '../Utils/BigIntCompatibility';
import { createLogger } from '../../Utils/Logger';
const logger = createLogger('ReactiveQuery');
/**
* 响应式查询变化类型
*/
export enum ReactiveQueryChangeType {
/** 实体添加到查询结果 */
ADDED = 'added',
/** 实体从查询结果移除 */
REMOVED = 'removed',
/** 查询结果批量更新 */
BATCH_UPDATE = 'batch_update'
}
/**
* 响应式查询变化事件
*/
export type ReactiveQueryChange = {
/** 变化类型 */
type: ReactiveQueryChangeType;
/** 变化的实体 */
entity?: Entity;
/** 批量变化的实体 */
entities?: readonly Entity[];
/** 新增的实体列表(仅batch_update时有效) */
added?: readonly Entity[];
/** 移除的实体列表(仅batch_update时有效) */
removed?: readonly Entity[];
}
/**
* 响应式查询监听器
*/
export type ReactiveQueryListener = (change: ReactiveQueryChange) => void;
/**
* 响应式查询配置
*/
export type ReactiveQueryConfig = {
/** 是否启用批量模式(减少通知频率) */
enableBatchMode?: boolean;
/** 批量模式的延迟时间(毫秒) */
batchDelay?: number;
/** 调试模式 */
debug?: boolean;
}
/**
* 响应式查询类
*
* 提供基于事件驱动的实体查询机制,只在实体/组件真正变化时触发通知。
*
* 核心特性:
* - Event-driven: 基于事件的增量更新
* - 精确通知: 只通知真正匹配的变化
* - 性能优化: 避免每帧重复查询
*
* @example
* ```typescript
* // 创建响应式查询
* const query = new ReactiveQuery(querySystem, {
* type: QueryConditionType.ALL,
* componentTypes: [Position, Velocity],
* mask: createMask([Position, Velocity])
* });
*
* // 订阅变化
* query.subscribe((change) => {
* if (change.type === ReactiveQueryChangeType.ADDED) {
* console.log('新实体:', change.entity);
* }
* });
*
* // 获取当前结果
* const entities = query.getEntities();
* ```
*/
export class ReactiveQuery {
/** 当前查询结果 */
private _entities: Entity[] = [];
/** 实体ID集合,用于快速查找 */
private _entityIdSet: Set<number> = new Set();
/**
* 实体数组快照 - 用于安全迭代
* Entity array snapshot - for safe iteration
* 只在实体列表变化时才创建新快照,静态场景下所有系统共享同一快照
* Only create new snapshot when entity list changes, static scenes share the same snapshot
*/
private _snapshot: readonly Entity[] | null = null;
/** 查询条件 */
private readonly _condition: QueryCondition;
/** 监听器列表 */
private _listeners: ReactiveQueryListener[] = [];
/** 配置 */
private readonly _config: ReactiveQueryConfig;
/** 批量变化缓存 */
private _batchChanges: {
added: Entity[];
removed: Entity[];
timer: ReturnType<typeof setTimeout> | null;
};
/** 查询ID(用于调试) */
private readonly _id: string;
/** 是否已激活 */
private _active: boolean = true;
constructor(condition: QueryCondition, config: ReactiveQueryConfig = {}) {
this._condition = condition;
this._config = {
enableBatchMode: config.enableBatchMode ?? true,
batchDelay: config.batchDelay ?? 16, // 默认一帧
debug: config.debug ?? false
};
this._id = this.generateQueryId();
this._batchChanges = {
added: [],
removed: [],
timer: null
};
if (this._config.debug) {
logger.debug(`创建ReactiveQuery: ${this._id}`);
}
}
/**
* 生成查询ID
*/
private generateQueryId(): string {
const typeStr = this._condition.type;
const componentsStr = this._condition.componentTypes
.map((t) => t.name)
.sort()
.join(',');
return `${typeStr}:${componentsStr}`;
}
/**
* 订阅查询变化
*
* @param listener 监听器函数
* @returns 取消订阅的函数
*/
public subscribe(listener: ReactiveQueryListener): () => void {
if (!this._active) {
throw new Error(`Cannot subscribe to disposed ReactiveQuery ${this._id}`);
}
if (typeof listener !== 'function') {
throw new TypeError('Listener must be a function');
}
this._listeners.push(listener);
if (this._config.debug) {
logger.debug(`订阅ReactiveQuery: ${this._id}, 监听器数量: ${this._listeners.length}`);
}
return () => {
const index = this._listeners.indexOf(listener);
if (index !== -1) {
this._listeners.splice(index, 1);
}
};
}
/**
* 取消所有订阅
*/
public unsubscribeAll(): void {
this._listeners.length = 0;
}
/**
* 获取当前查询结果(返回安全快照)
* Get current query results (returns safe snapshot)
*
* 返回的快照在实体列表变化前保持不变,可安全用于迭代。
* 静态场景下所有系统共享同一快照,避免每帧创建数组副本。
*
* The returned snapshot remains unchanged until entity list changes, safe for iteration.
* Static scenes share the same snapshot, avoiding array copy every frame.
*/
public getEntities(): readonly Entity[] {
// 如果快照有效,直接返回 | Return snapshot if valid
if (this._snapshot !== null) {
return this._snapshot;
}
// 创建新快照 | Create new snapshot
this._snapshot = [...this._entities];
return this._snapshot;
}
/**
* 获取查询结果数量
*/
public get count(): number {
return this._entities.length;
}
/**
* 检查实体是否匹配查询条件
*
* @param entity 要检查的实体
* @returns 是否匹配
*/
public matches(entity: Entity): boolean {
const entityMask = entity.componentMask;
switch (this._condition.type) {
case QueryConditionType.ALL:
return BitMask64Utils.hasAll(entityMask, this._condition.mask);
case QueryConditionType.ANY:
return BitMask64Utils.hasAny(entityMask, this._condition.mask);
case QueryConditionType.NONE:
return BitMask64Utils.hasNone(entityMask, this._condition.mask);
default:
return false;
}
}
/**
* 通知实体添加
*
* 当Scene中添加实体时调用
*
* @param entity 添加的实体
*/
public notifyEntityAdded(entity: Entity): void {
if (!this._active) return;
// 检查实体是否匹配查询条件
if (!this.matches(entity)) {
return;
}
// 检查是否已存在
if (this._entityIdSet.has(entity.id)) {
return;
}
// 添加到结果集
this._entities.push(entity);
this._entityIdSet.add(entity.id);
this._snapshot = null; // 使快照失效 | Invalidate snapshot
// 通知监听器
if (this._config.enableBatchMode) {
this.addToBatch('added', entity);
} else {
this.notifyListeners({
type: ReactiveQueryChangeType.ADDED,
entity
});
}
if (this._config.debug) {
logger.debug(`ReactiveQuery ${this._id}: 实体添加 ${entity.name}(${entity.id})`);
}
}
/**
* 通知实体移除
*
* 当Scene中移除实体时调用
*
* @param entity 移除的实体
*/
public notifyEntityRemoved(entity: Entity): void {
if (!this._active) return;
// 检查是否在结果集中
if (!this._entityIdSet.has(entity.id)) {
return;
}
// 从结果集移除
const index = this._entities.indexOf(entity);
if (index !== -1) {
this._entities.splice(index, 1);
}
this._entityIdSet.delete(entity.id);
this._snapshot = null; // 使快照失效 | Invalidate snapshot
// 通知监听器
if (this._config.enableBatchMode) {
this.addToBatch('removed', entity);
} else {
this.notifyListeners({
type: ReactiveQueryChangeType.REMOVED,
entity
});
}
if (this._config.debug) {
logger.debug(`ReactiveQuery ${this._id}: 实体移除 ${entity.name}(${entity.id})`);
}
}
/**
* 通知实体组件变化
*
* 当实体的组件发生变化时调用
*
* @param entity 变化的实体
*/
public notifyEntityChanged(entity: Entity): void {
if (!this._active) return;
const wasMatching = this._entityIdSet.has(entity.id);
const isMatching = this.matches(entity);
if (wasMatching && !isMatching) {
// 实体不再匹配,从结果集移除
this.notifyEntityRemoved(entity);
} else if (!wasMatching && isMatching) {
// 实体现在匹配,添加到结果集
this.notifyEntityAdded(entity);
}
}
/**
* 批量初始化查询结果
*
* @param entities 初始实体列表
*/
public initializeWith(entities: readonly Entity[]): void {
// 清空现有结果
this._entities.length = 0;
this._entityIdSet.clear();
this._snapshot = null; // 使快照失效 | Invalidate snapshot
// 筛选匹配的实体
for (const entity of entities) {
if (this.matches(entity)) {
this._entities.push(entity);
this._entityIdSet.add(entity.id);
}
}
if (this._config.debug) {
logger.debug(`ReactiveQuery ${this._id}: 初始化 ${this._entities.length} 个实体`);
}
}
/**
* 添加到批量变化缓存
*/
private addToBatch(type: 'added' | 'removed', entity: Entity): void {
if (type === 'added') {
this._batchChanges.added.push(entity);
} else {
this._batchChanges.removed.push(entity);
}
// 启动批量通知定时器
if (this._batchChanges.timer === null) {
this._batchChanges.timer = setTimeout(() => {
this.flushBatchChanges();
}, this._config.batchDelay);
}
}
/**
* 刷新批量变化
*/
private flushBatchChanges(): void {
if (this._batchChanges.added.length === 0 && this._batchChanges.removed.length === 0) {
this._batchChanges.timer = null;
return;
}
const added = [...this._batchChanges.added];
const removed = [...this._batchChanges.removed];
// 清空缓存
this._batchChanges.added.length = 0;
this._batchChanges.removed.length = 0;
this._batchChanges.timer = null;
// 通知监听器
this.notifyListeners({
type: ReactiveQueryChangeType.BATCH_UPDATE,
added,
removed,
entities: this._entities
});
if (this._config.debug) {
logger.debug(`ReactiveQuery ${this._id}: 批量更新 +${added.length} -${removed.length}`);
}
}
/**
* 通知所有监听器
*/
private notifyListeners(change: ReactiveQueryChange): void {
const listeners = [...this._listeners];
for (const listener of listeners) {
try {
listener(change);
} catch (error) {
logger.error(`ReactiveQuery ${this._id}: 监听器执行出错`, error);
}
}
}
/**
* 暂停响应式查询
*
* 暂停后不再响应实体变化,但可以继续获取当前结果
*/
public pause(): void {
this._active = false;
// 清空批量变化缓存
if (this._batchChanges.timer !== null) {
clearTimeout(this._batchChanges.timer);
this._batchChanges.timer = null;
}
this._batchChanges.added.length = 0;
this._batchChanges.removed.length = 0;
}
/**
* 恢复响应式查询
*/
public resume(): void {
this._active = true;
}
/**
* 销毁响应式查询
*
* 释放所有资源,清空监听器和结果集
*/
public dispose(): void {
if (this._batchChanges.timer !== null) {
clearTimeout(this._batchChanges.timer);
this._batchChanges.timer = null;
}
this._batchChanges.added.length = 0;
this._batchChanges.removed.length = 0;
this._active = false;
this.unsubscribeAll();
this._entities.length = 0;
this._entityIdSet.clear();
this._snapshot = null;
if (this._config.debug) {
logger.debug(`ReactiveQuery ${this._id}: 已销毁`);
}
}
/**
* 获取查询条件
*/
public get condition(): QueryCondition {
return this._condition;
}
/**
* 获取查询ID
*/
public get id(): string {
return this._id;
}
/**
* 检查是否激活
*/
public get active(): boolean {
return this._active;
}
/**
* 获取监听器数量
*/
public get listenerCount(): number {
return this._listeners.length;
}
}

View File

@@ -0,0 +1,328 @@
import { Component } from '../Component';
import type { Entity } from '../Entity';
import type { IScene } from '../IScene';
/**
* WeakRef 接口定义
*
* 用于 ES2020 环境下的类型定义
*/
interface IWeakRef<T extends object> {
deref(): T | undefined;
}
/**
* WeakRef Polyfill for ES2020 compatibility
*
* 为了兼容 Cocos Creator、Laya、微信小游戏等目标平台仅支持 ES2020
* 提供 WeakRef 的 Polyfill 实现。
*
* - 现代浏览器:自动使用原生 WeakRef (自动 GC)
* - 旧环境:使用 Polyfill (无自动 GC但 Scene 销毁时会手动清理)
*/
class WeakRefPolyfill<T extends object> implements IWeakRef<T> {
private _target: T;
constructor(target: T) {
this._target = target;
}
deref(): T | undefined {
return this._target;
}
}
/**
* WeakRef 构造函数类型
*/
interface IWeakRefConstructor {
new <T extends object>(target: T): IWeakRef<T>;
}
/**
* WeakRef 实现
*
* 优先使用原生 WeakRef不支持时降级到 Polyfill
*/
const WeakRefImpl: IWeakRefConstructor = (
(typeof globalThis !== 'undefined' && (globalThis as any).WeakRef) ||
(typeof global !== 'undefined' && (global as any).WeakRef) ||
(typeof window !== 'undefined' && (window as any).WeakRef) ||
WeakRefPolyfill
) as IWeakRefConstructor;
/**
* Entity引用记录
*/
export type EntityRefRecord = {
component: IWeakRef<Component>;
propertyKey: string;
}
/**
* 全局EntityID到Scene的映射
*
* 使用全局Map记录每个Entity ID对应的Scene用于装饰器通过Component.entityId查找Scene。
*/
const globalEntitySceneMap = new Map<number, IWeakRef<IScene>>();
/**
* 通过Entity ID获取Scene
*
* @param entityId Entity ID
* @returns Scene实例如果不存在则返回null
*/
export function getSceneByEntityId(entityId: number): IScene | null {
const sceneRef = globalEntitySceneMap.get(entityId);
return sceneRef?.deref() || null;
}
/**
* Entity引用追踪器
*
* 追踪Component中对Entity的引用当Entity被销毁时自动清理所有引用。
*
* @example
* ```typescript
* const tracker = new ReferenceTracker();
* tracker.registerReference(targetEntity, component, 'parent');
* targetEntity.destroy(); // 自动将 component.parent 设为 null
* ```
*/
export class ReferenceTracker {
/**
* Entity ID -> 引用该Entity的所有组件记录
*/
private _references: Map<number, Set<EntityRefRecord>> = new Map();
/**
* 注册Entity引用
*
* @param entity 被引用的Entity
* @param component 持有引用的Component
* @param propertyKey Component中存储引用的属性名
*/
public registerReference(entity: Entity, component: Component, propertyKey: string): void {
const entityId = entity.id;
let records = this._references.get(entityId);
if (!records) {
records = new Set();
this._references.set(entityId, records);
}
const existingRecord = this._findRecord(records, component, propertyKey);
if (existingRecord) {
return;
}
records.add({
component: new WeakRefImpl(component),
propertyKey
});
}
/**
* 注销Entity引用
*
* @param entity 被引用的Entity
* @param component 持有引用的Component
* @param propertyKey Component中存储引用的属性名
*/
public unregisterReference(entity: Entity, component: Component, propertyKey: string): void {
const entityId = entity.id;
const records = this._references.get(entityId);
if (!records) {
return;
}
const record = this._findRecord(records, component, propertyKey);
if (record) {
records.delete(record);
if (records.size === 0) {
this._references.delete(entityId);
}
}
}
/**
* 清理所有指向指定Entity的引用
*
* 将所有引用该Entity的Component属性设为null。
*
* @param entityId 被销毁的Entity ID
*/
public clearReferencesTo(entityId: number): void {
const records = this._references.get(entityId);
if (!records) {
return;
}
const validRecords: EntityRefRecord[] = [];
for (const record of records) {
const component = record.component.deref();
if (component) {
validRecords.push(record);
}
}
for (const record of validRecords) {
const component = record.component.deref();
if (component) {
(component as any)[record.propertyKey] = null;
}
}
this._references.delete(entityId);
}
/**
* 清理Component的所有引用注册
*
* 当Component被移除时调用清理该Component注册的所有引用。
*
* @param component 被移除的Component
*/
public clearComponentReferences(component: Component): void {
for (const [entityId, records] of this._references.entries()) {
const toDelete: EntityRefRecord[] = [];
for (const record of records) {
const comp = record.component.deref();
if (!comp || comp === component) {
toDelete.push(record);
}
}
for (const record of toDelete) {
records.delete(record);
}
if (records.size === 0) {
this._references.delete(entityId);
}
}
}
/**
* 获取指向指定Entity的所有引用记录
*
* @param entityId Entity ID
* @returns 引用记录数组(仅包含有效引用)
*/
public getReferencesTo(entityId: number): EntityRefRecord[] {
const records = this._references.get(entityId);
if (!records) {
return [];
}
const validRecords: EntityRefRecord[] = [];
for (const record of records) {
const component = record.component.deref();
if (component) {
validRecords.push(record);
}
}
return validRecords;
}
/**
* 清理所有失效的WeakRef引用
*
* 遍历所有记录移除已被GC回收的Component引用。
*/
public cleanup(): void {
const entitiesToDelete: number[] = [];
for (const [entityId, records] of this._references.entries()) {
const toDelete: EntityRefRecord[] = [];
for (const record of records) {
if (!record.component.deref()) {
toDelete.push(record);
}
}
for (const record of toDelete) {
records.delete(record);
}
if (records.size === 0) {
entitiesToDelete.push(entityId);
}
}
for (const entityId of entitiesToDelete) {
this._references.delete(entityId);
}
}
/**
* 注册Entity到Scene的映射
*
* @param entityId Entity ID
* @param scene Scene实例
*/
public registerEntityScene(entityId: number, scene: IScene): void {
globalEntitySceneMap.set(entityId, new WeakRefImpl(scene));
}
/**
* 注销Entity到Scene的映射
*
* @param entityId Entity ID
*/
public unregisterEntityScene(entityId: number): void {
globalEntitySceneMap.delete(entityId);
}
/**
* 获取调试信息
*/
public getDebugInfo(): object {
const info: Record<string, any> = {};
for (const [entityId, records] of this._references.entries()) {
const validRecords = [];
for (const record of records) {
const component = record.component.deref();
if (component) {
validRecords.push({
componentId: component.id,
propertyKey: record.propertyKey
});
}
}
if (validRecords.length > 0) {
info[`entity_${entityId}`] = validRecords;
}
}
return info;
}
/**
* 查找指定的引用记录
*/
private _findRecord(
records: Set<EntityRefRecord>,
component: Component,
propertyKey: string
): EntityRefRecord | undefined {
for (const record of records) {
const comp = record.component.deref();
if (comp === component && record.propertyKey === propertyKey) {
return record;
}
}
return undefined;
}
}

View File

@@ -0,0 +1,108 @@
import { createLogger } from '../../Utils/Logger';
/**
* SoA 序列化器
* 负责复杂类型的序列化/反序列化和深拷贝
*/
export class SoASerializer {
private static readonly _logger = createLogger('SoASerializer');
/**
* 序列化值为 JSON 字符串
*/
public static serialize(
value: unknown,
fieldName: string,
options: {
isMap?: boolean;
isSet?: boolean;
isArray?: boolean;
} = {}
): string {
try {
if (options.isMap && value instanceof Map) {
return JSON.stringify(Array.from(value.entries()));
}
if (options.isSet && value instanceof Set) {
return JSON.stringify(Array.from(value));
}
if (options.isArray && Array.isArray(value)) {
return JSON.stringify(value);
}
return JSON.stringify(value);
} catch (error) {
this._logger.warn(`SoA序列化字段 ${fieldName} 失败:`, error);
return '{}';
}
}
/**
* 反序列化 JSON 字符串为值
*/
public static deserialize(
serialized: string,
fieldName: string,
options: {
isMap?: boolean;
isSet?: boolean;
isArray?: boolean;
} = {}
): unknown {
try {
const parsed = JSON.parse(serialized);
if (options.isMap) {
return new Map(parsed);
}
if (options.isSet) {
return new Set(parsed);
}
return parsed;
} catch (error) {
this._logger.warn(`SoA反序列化字段 ${fieldName} 失败:`, error);
return null;
}
}
/**
* 深拷贝对象
*/
public static deepClone<T>(obj: T): T {
if (obj === null || typeof obj !== 'object') {
return obj;
}
if (obj instanceof Date) {
return new Date(obj.getTime()) as T;
}
if (Array.isArray(obj)) {
return obj.map((item) => this.deepClone(item)) as T;
}
if (obj instanceof Map) {
const cloned = new Map();
for (const [key, value] of obj.entries()) {
cloned.set(key, this.deepClone(value));
}
return cloned as T;
}
if (obj instanceof Set) {
const cloned = new Set();
for (const value of obj.values()) {
cloned.add(this.deepClone(value));
}
return cloned as T;
}
// 普通对象
const cloned = {} as Record<string, unknown>;
for (const key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
cloned[key] = this.deepClone((obj as Record<string, unknown>)[key]);
}
}
return cloned as T;
}
}

View File

@@ -0,0 +1,785 @@
import { Component } from '../Component';
import { ComponentType } from './ComponentStorage';
import {
SoATypeRegistry,
SupportedTypedArray,
TypedArrayTypeName
} from './SoATypeRegistry';
import { SoASerializer } from './SoASerializer';
import type { IComponentTypeMetadata, ComponentTypeWithMetadata } from '../../Types';
// 重新导出类型,保持向后兼容
export type { SupportedTypedArray, TypedArrayTypeName } from './SoATypeRegistry';
export { SoATypeRegistry } from './SoATypeRegistry';
export { SoASerializer } from './SoASerializer';
/**
* @zh SoA 字段统计信息
* @en SoA field statistics
*/
export interface ISoAFieldStats {
size: number;
capacity: number;
type: string;
memory: number;
}
/**
* @zh SoA 存储统计信息
* @en SoA storage statistics
*/
export interface ISoAStorageStats {
size: number;
capacity: number;
totalSlots: number;
usedSlots: number;
freeSlots: number;
fragmentation: number;
memoryUsage: number;
fieldStats: Map<string, ISoAFieldStats>;
}
/**
* @zh 启用 SoA 优化装饰器 - 默认关闭,只在大规模批量操作场景下建议开启
* @en Enable SoA optimization decorator - disabled by default, recommended only for large-scale batch operations
*/
export function EnableSoA<T extends ComponentType>(target: T): T {
(target as ComponentType & IComponentTypeMetadata).__enableSoA = true;
return target;
}
/**
* @zh 组件字段元数据键(仅 Set<string> 类型的字段)
* @en Component field metadata keys (only Set<string> type fields)
*/
type ComponentFieldMetadataKey = Exclude<keyof IComponentTypeMetadata, '__enableSoA'>;
/**
* @zh 装饰器目标原型接口
* @en Decorator target prototype interface
*/
interface IDecoratorTarget {
constructor: IComponentTypeMetadata & Function;
}
/**
* @zh 辅助函数:获取或创建字段集合
* @en Helper function: get or create field set
*/
function getOrCreateFieldSet(
target: IDecoratorTarget,
fieldName: ComponentFieldMetadataKey
): Set<string> {
const ctor = target.constructor as IComponentTypeMetadata;
let fieldSet = ctor[fieldName];
if (!fieldSet) {
fieldSet = new Set<string>();
ctor[fieldName] = fieldSet;
}
return fieldSet;
}
/**
* @zh 64位浮点数装饰器 - 标记字段使用 Float64Array 存储(更高精度但更多内存)
* @en Float64 decorator - marks field to use Float64Array storage (higher precision but more memory)
*/
export function Float64(target: object, propertyKey: string | symbol): void {
getOrCreateFieldSet(target as IDecoratorTarget, '__float64Fields').add(String(propertyKey));
}
/**
* @zh 32位浮点数装饰器 - 标记字段使用 Float32Array 存储(默认类型,平衡性能和精度)
* @en Float32 decorator - marks field to use Float32Array storage (default, balanced performance and precision)
*/
export function Float32(target: object, propertyKey: string | symbol): void {
getOrCreateFieldSet(target as IDecoratorTarget, '__float32Fields').add(String(propertyKey));
}
/**
* @zh 32位整数装饰器 - 标记字段使用 Int32Array 存储(适用于整数值)
* @en Int32 decorator - marks field to use Int32Array storage (for integer values)
*/
export function Int32(target: object, propertyKey: string | symbol): void {
getOrCreateFieldSet(target as IDecoratorTarget, '__int32Fields').add(String(propertyKey));
}
/**
* @zh 32位无符号整数装饰器 - 标记字段使用 Uint32Array 存储
* @en Uint32 decorator - marks field to use Uint32Array storage
*/
export function Uint32(target: object, propertyKey: string | symbol): void {
getOrCreateFieldSet(target as IDecoratorTarget, '__uint32Fields').add(String(propertyKey));
}
/**
* @zh 16位整数装饰器 - 标记字段使用 Int16Array 存储
* @en Int16 decorator - marks field to use Int16Array storage
*/
export function Int16(target: object, propertyKey: string | symbol): void {
getOrCreateFieldSet(target as IDecoratorTarget, '__int16Fields').add(String(propertyKey));
}
/**
* @zh 16位无符号整数装饰器 - 标记字段使用 Uint16Array 存储
* @en Uint16 decorator - marks field to use Uint16Array storage
*/
export function Uint16(target: object, propertyKey: string | symbol): void {
getOrCreateFieldSet(target as IDecoratorTarget, '__uint16Fields').add(String(propertyKey));
}
/**
* @zh 8位整数装饰器 - 标记字段使用 Int8Array 存储
* @en Int8 decorator - marks field to use Int8Array storage
*/
export function Int8(target: object, propertyKey: string | symbol): void {
getOrCreateFieldSet(target as IDecoratorTarget, '__int8Fields').add(String(propertyKey));
}
/**
* @zh 8位无符号整数装饰器 - 标记字段使用 Uint8Array 存储
* @en Uint8 decorator - marks field to use Uint8Array storage
*/
export function Uint8(target: object, propertyKey: string | symbol): void {
getOrCreateFieldSet(target as IDecoratorTarget, '__uint8Fields').add(String(propertyKey));
}
/**
* @zh 8位夹紧整数装饰器 - 标记字段使用 Uint8ClampedArray 存储(适用于颜色值)
* @en Uint8Clamped decorator - marks field to use Uint8ClampedArray storage (for color values)
*/
export function Uint8Clamped(target: object, propertyKey: string | symbol): void {
getOrCreateFieldSet(target as IDecoratorTarget, '__uint8ClampedFields').add(String(propertyKey));
}
/**
* @zh 序列化 Map 装饰器 - 标记 Map 字段需要序列化/反序列化存储
* @en SerializeMap decorator - marks Map field for serialization/deserialization
*/
export function SerializeMap(target: object, propertyKey: string | symbol): void {
getOrCreateFieldSet(target as IDecoratorTarget, '__serializeMapFields').add(String(propertyKey));
}
/**
* @zh 序列化 Set 装饰器 - 标记 Set 字段需要序列化/反序列化存储
* @en SerializeSet decorator - marks Set field for serialization/deserialization
*/
export function SerializeSet(target: object, propertyKey: string | symbol): void {
getOrCreateFieldSet(target as IDecoratorTarget, '__serializeSetFields').add(String(propertyKey));
}
/**
* @zh 序列化 Array 装饰器 - 标记 Array 字段需要序列化/反序列化存储
* @en SerializeArray decorator - marks Array field for serialization/deserialization
*/
export function SerializeArray(target: object, propertyKey: string | symbol): void {
getOrCreateFieldSet(target as IDecoratorTarget, '__serializeArrayFields').add(String(propertyKey));
}
/**
* @zh 深拷贝装饰器 - 标记字段需要深拷贝处理(适用于嵌套对象)
* @en DeepCopy decorator - marks field for deep copy handling (for nested objects)
*/
export function DeepCopy(target: object, propertyKey: string | symbol): void {
getOrCreateFieldSet(target as IDecoratorTarget, '__deepCopyFields').add(String(propertyKey));
}
/**
* SoA存储器需要装饰器启用
* 使用Structure of Arrays存储模式在大规模批量操作时提供优异性能
*/
export class SoAStorage<T extends Component> {
private fields = new Map<string, SupportedTypedArray>();
private stringFields = new Map<string, Array<string | undefined>>();
private serializedFields = new Map<string, Array<string | undefined>>();
private complexFields = new Map<number, Map<string, unknown>>();
private entityToIndex = new Map<number, number>();
private indexToEntity: number[] = [];
private freeIndices: number[] = [];
private _size = 0;
private _capacity = 1000;
public readonly type: ComponentType<T>;
// 缓存字段类型信息,避免重复创建实例
private fieldTypes = new Map<string, string>();
// 缓存装饰器元数据
private serializeMapFields: Set<string> = new Set();
private serializeSetFields: Set<string> = new Set();
private serializeArrayFields: Set<string> = new Set();
constructor(componentType: ComponentType<T>) {
this.type = componentType;
this.initializeFields(componentType);
}
private initializeFields(componentType: ComponentType<T>): void {
const instance = new componentType();
const typeWithMeta = componentType as ComponentType<T> & {
__float64Fields?: Set<string>;
__float32Fields?: Set<string>;
__int32Fields?: Set<string>;
__uint32Fields?: Set<string>;
__int16Fields?: Set<string>;
__uint16Fields?: Set<string>;
__int8Fields?: Set<string>;
__uint8Fields?: Set<string>;
__uint8ClampedFields?: Set<string>;
__serializeMapFields?: Set<string>;
__serializeSetFields?: Set<string>;
__serializeArrayFields?: Set<string>;
};
const float64Fields = typeWithMeta.__float64Fields || new Set<string>();
const float32Fields = typeWithMeta.__float32Fields || new Set<string>();
const int32Fields = typeWithMeta.__int32Fields || new Set<string>();
const uint32Fields = typeWithMeta.__uint32Fields || new Set<string>();
const int16Fields = typeWithMeta.__int16Fields || new Set<string>();
const uint16Fields = typeWithMeta.__uint16Fields || new Set<string>();
const int8Fields = typeWithMeta.__int8Fields || new Set<string>();
const uint8Fields = typeWithMeta.__uint8Fields || new Set<string>();
const uint8ClampedFields = typeWithMeta.__uint8ClampedFields || new Set<string>();
// 缓存装饰器元数据
this.serializeMapFields = typeWithMeta.__serializeMapFields || new Set<string>();
this.serializeSetFields = typeWithMeta.__serializeSetFields || new Set<string>();
this.serializeArrayFields = typeWithMeta.__serializeArrayFields || new Set<string>();
// 先收集所有有装饰器的字段,避免重复遍历
const decoratedFields = new Map<string, string>(); // fieldName -> arrayType
// 处理各类型装饰器标记的字段
for (const key of float64Fields) decoratedFields.set(key, 'float64');
for (const key of float32Fields) decoratedFields.set(key, 'float32');
for (const key of int32Fields) decoratedFields.set(key, 'int32');
for (const key of uint32Fields) decoratedFields.set(key, 'uint32');
for (const key of int16Fields) decoratedFields.set(key, 'int16');
for (const key of uint16Fields) decoratedFields.set(key, 'uint16');
for (const key of int8Fields) decoratedFields.set(key, 'int8');
for (const key of uint8Fields) decoratedFields.set(key, 'uint8');
for (const key of uint8ClampedFields) decoratedFields.set(key, 'uint8clamped');
// 只遍历实例自身的属性(不包括原型链),跳过 id
const instanceKeys = Object.keys(instance).filter((key) => key !== 'id');
for (const key of instanceKeys) {
const value = (instance as Record<string, unknown>)[key];
const type = typeof value;
// 跳过函数(通常不会出现在 Object.keys 中,但以防万一)
if (type === 'function') continue;
// 检查装饰器类型
const decoratorType = decoratedFields.get(key);
const effectiveType = decoratorType ? 'number' : type;
this.fieldTypes.set(key, effectiveType);
if (decoratorType) {
// 有装饰器标记的数字字段
const ArrayConstructor = SoATypeRegistry.getConstructor(decoratorType as TypedArrayTypeName);
this.fields.set(key, new ArrayConstructor(this._capacity));
} else if (type === 'number') {
// 无装饰器的数字字段,默认使用 Float32Array
this.fields.set(key, new Float32Array(this._capacity));
} else if (type === 'boolean') {
// 布尔值使用 Uint8Array 存储为 0/1
this.fields.set(key, new Uint8Array(this._capacity));
} else if (type === 'string') {
// 字符串专门处理
this.stringFields.set(key, new Array(this._capacity));
} else if (type === 'object' && value !== null) {
// 处理集合类型
if (this.serializeMapFields.has(key) || this.serializeSetFields.has(key) || this.serializeArrayFields.has(key)) {
// 序列化存储
this.serializedFields.set(key, new Array(this._capacity));
}
// 其他对象类型会在updateComponentAtIndex中作为复杂对象处理
}
}
}
public addComponent(entityId: number, component: T): void {
if (this.entityToIndex.has(entityId)) {
const index = this.entityToIndex.get(entityId)!;
this.updateComponentAtIndex(index, component);
return;
}
let index: number;
if (this.freeIndices.length > 0) {
index = this.freeIndices.pop()!;
} else {
index = this._size;
if (index >= this._capacity) {
this.resize(this._capacity * 2);
}
}
this.entityToIndex.set(entityId, index);
this.indexToEntity[index] = entityId;
this.updateComponentAtIndex(index, component);
this._size++;
}
private updateComponentAtIndex(index: number, component: T): void {
const entityId = this.indexToEntity[index]!;
const complexFieldMap = new Map<string, unknown>();
const typeWithMeta = this.type as ComponentTypeWithMetadata<T>;
const highPrecisionFields = typeWithMeta.__highPrecisionFields || new Set<string>();
const serializeMapFields = typeWithMeta.__serializeMapFields || new Set<string>();
const serializeSetFields = typeWithMeta.__serializeSetFields || new Set<string>();
const serializeArrayFields = typeWithMeta.__serializeArrayFields || new Set<string>();
const deepCopyFields = typeWithMeta.__deepCopyFields || new Set<string>();
const componentRecord = component as Record<string, unknown>;
for (const key in component) {
if (Object.prototype.hasOwnProperty.call(component, key) && key !== 'id') {
const value = componentRecord[key];
const type = typeof value;
if (type === 'number') {
const numValue = value as number;
if (highPrecisionFields.has(key) || !this.fields.has(key)) {
complexFieldMap.set(key, numValue);
} else {
const array = this.fields.get(key)!;
array[index] = numValue;
}
} else if (type === 'boolean' && this.fields.has(key)) {
const array = this.fields.get(key)!;
array[index] = value ? 1 : 0;
} else if (this.stringFields.has(key)) {
// 字符串字段专门处理
const stringArray = this.stringFields.get(key)!;
stringArray[index] = String(value);
} else if (this.serializedFields.has(key)) {
// 序列化字段处理
const serializedArray = this.serializedFields.get(key)!;
serializedArray[index] = SoASerializer.serialize(value, key, {
isMap: serializeMapFields.has(key),
isSet: serializeSetFields.has(key),
isArray: serializeArrayFields.has(key)
});
} else {
// 复杂字段单独存储
if (deepCopyFields.has(key)) {
// 深拷贝处理
complexFieldMap.set(key, SoASerializer.deepClone(value));
} else {
complexFieldMap.set(key, value);
}
}
}
}
// 存储复杂字段
if (complexFieldMap.size > 0) {
this.complexFields.set(entityId, complexFieldMap);
}
}
public getComponent(entityId: number): T | null {
const index = this.entityToIndex.get(entityId);
if (index === undefined) {
return null;
}
// 返回 Proxy直接操作底层 TypedArray
return this.createProxyView(entityId, index);
}
/**
* 创建组件的 Proxy 视图
* 读写操作直接映射到底层 TypedArray无数据复制
*/
private createProxyView(entityId: number, index: number): T {
// eslint-disable-next-line @typescript-eslint/no-this-alias
const self = this;
// Proxy handler 类型定义
const handler: ProxyHandler<Record<string, unknown>> = {
get(_, prop: string | symbol) {
const propStr = String(prop);
// TypedArray 字段
const array = self.fields.get(propStr);
if (array) {
const fieldType = self.getFieldType(propStr);
if (fieldType === 'boolean') {
return array[index] === 1;
}
return array[index];
}
// 字符串字段
const stringArray = self.stringFields.get(propStr);
if (stringArray) {
return stringArray[index];
}
// 序列化字段
const serializedArray = self.serializedFields.get(propStr);
if (serializedArray) {
const serialized = serializedArray[index];
if (serialized) {
return SoASerializer.deserialize(serialized, propStr, {
isMap: self.serializeMapFields.has(propStr),
isSet: self.serializeSetFields.has(propStr),
isArray: self.serializeArrayFields.has(propStr)
});
}
return undefined;
}
// 复杂字段
const complexFieldMap = self.complexFields.get(entityId);
if (complexFieldMap?.has(propStr)) {
return complexFieldMap.get(propStr);
}
return undefined;
},
set(_, prop: string | symbol, value) {
const propStr = String(prop);
// entityId 是只读的
if (propStr === 'entityId') {
return false;
}
// TypedArray 字段
const array = self.fields.get(propStr);
if (array) {
const fieldType = self.getFieldType(propStr);
if (fieldType === 'boolean') {
array[index] = value ? 1 : 0;
} else {
array[index] = value;
}
return true;
}
// 字符串字段
const stringArray = self.stringFields.get(propStr);
if (stringArray) {
stringArray[index] = String(value);
return true;
}
// 序列化字段
if (self.serializedFields.has(propStr)) {
const serializedArray = self.serializedFields.get(propStr)!;
serializedArray[index] = SoASerializer.serialize(value, propStr, {
isMap: self.serializeMapFields.has(propStr),
isSet: self.serializeSetFields.has(propStr),
isArray: self.serializeArrayFields.has(propStr)
});
return true;
}
// 复杂字段
let complexFieldMap = self.complexFields.get(entityId);
if (!complexFieldMap) {
complexFieldMap = new Map();
self.complexFields.set(entityId, complexFieldMap);
}
complexFieldMap.set(propStr, value);
return true;
},
has(_, prop) {
const propStr = String(prop);
return self.fields.has(propStr) ||
self.stringFields.has(propStr) ||
self.serializedFields.has(propStr) ||
self.complexFields.get(entityId)?.has(propStr) || false;
},
ownKeys() {
const keys: string[] = [];
for (const key of self.fields.keys()) keys.push(key);
for (const key of self.stringFields.keys()) keys.push(key);
for (const key of self.serializedFields.keys()) keys.push(key);
const complexFieldMap = self.complexFields.get(entityId);
if (complexFieldMap) {
for (const key of complexFieldMap.keys()) keys.push(key);
}
return keys;
},
getOwnPropertyDescriptor(_, prop) {
const propStr = String(prop);
if (self.fields.has(propStr) ||
self.stringFields.has(propStr) ||
self.serializedFields.has(propStr) ||
self.complexFields.get(entityId)?.has(propStr)) {
return {
enumerable: true,
configurable: true,
// entityId 是只读的
writable: propStr !== 'entityId'
};
}
return undefined;
}
};
return new Proxy({} as Record<string, unknown>, handler) as unknown as T;
}
/**
* @zh 获取组件的快照副本(用于序列化等需要独立副本的场景)
* @en Get a snapshot copy of the component (for serialization scenarios)
*/
public getComponentSnapshot(entityId: number): T | null {
const index = this.entityToIndex.get(entityId);
if (index === undefined) {
return null;
}
const component = new this.type();
const componentRecord = component as unknown as Record<string, unknown>;
// 恢复数值字段
for (const [fieldName, array] of this.fields.entries()) {
const value = array[index];
const fieldType = this.getFieldType(fieldName);
componentRecord[fieldName] = fieldType === 'boolean' ? value === 1 : value;
}
// 恢复字符串字段
for (const [fieldName, stringArray] of this.stringFields.entries()) {
componentRecord[fieldName] = stringArray[index];
}
// 恢复序列化字段
for (const [fieldName, serializedArray] of this.serializedFields.entries()) {
const serialized = serializedArray[index];
if (serialized) {
componentRecord[fieldName] = SoASerializer.deserialize(serialized, fieldName, {
isMap: this.serializeMapFields.has(fieldName),
isSet: this.serializeSetFields.has(fieldName),
isArray: this.serializeArrayFields.has(fieldName)
});
}
}
// 恢复复杂字段
const complexFieldMap = this.complexFields.get(entityId);
if (complexFieldMap) {
for (const [fieldName, value] of complexFieldMap.entries()) {
componentRecord[fieldName] = value;
}
}
return component;
}
private getFieldType(fieldName: string): string {
// 使用缓存的字段类型
return this.fieldTypes.get(fieldName) || 'unknown';
}
public hasComponent(entityId: number): boolean {
return this.entityToIndex.has(entityId);
}
public removeComponent(entityId: number): T | null {
const index = this.entityToIndex.get(entityId);
if (index === undefined) {
return null;
}
// 获取组件副本以便返回
const component = this.getComponent(entityId);
// 清理复杂字段
this.complexFields.delete(entityId);
this.entityToIndex.delete(entityId);
this.freeIndices.push(index);
this._size--;
return component;
}
private resize(newCapacity: number): void {
// 调整数值字段的TypedArray
for (const [fieldName, oldArray] of this.fields.entries()) {
const newArray = SoATypeRegistry.createSameType(oldArray, newCapacity);
newArray.set(oldArray);
this.fields.set(fieldName, newArray);
}
// 调整字符串字段的数组
for (const [fieldName, oldArray] of this.stringFields.entries()) {
const newArray = new Array(newCapacity);
for (let i = 0; i < oldArray.length; i++) {
newArray[i] = oldArray[i];
}
this.stringFields.set(fieldName, newArray);
}
// 调整序列化字段的数组
for (const [fieldName, oldArray] of this.serializedFields.entries()) {
const newArray = new Array(newCapacity);
for (let i = 0; i < oldArray.length; i++) {
newArray[i] = oldArray[i];
}
this.serializedFields.set(fieldName, newArray);
}
this._capacity = newCapacity;
}
public getActiveIndices(): number[] {
return Array.from(this.entityToIndex.values());
}
public getFieldArray(fieldName: string): SupportedTypedArray | null {
return this.fields.get(fieldName) || null;
}
public getTypedFieldArray<K extends keyof T>(fieldName: K): SupportedTypedArray | null {
return this.fields.get(String(fieldName)) || null;
}
public getEntityIndex(entityId: number): number | undefined {
return this.entityToIndex.get(entityId);
}
public getEntityIdByIndex(index: number): number | undefined {
return this.indexToEntity[index];
}
public size(): number {
return this._size;
}
public clear(): void {
this.entityToIndex.clear();
this.indexToEntity = [];
this.freeIndices = [];
this.complexFields.clear();
this._size = 0;
// 重置数值字段数组
for (const array of this.fields.values()) {
array.fill(0);
}
// 重置字符串字段数组
for (const stringArray of this.stringFields.values()) {
for (let i = 0; i < stringArray.length; i++) {
stringArray[i] = undefined;
}
}
// 重置序列化字段数组
for (const serializedArray of this.serializedFields.values()) {
for (let i = 0; i < serializedArray.length; i++) {
serializedArray[i] = undefined;
}
}
}
public compact(): void {
if (this.freeIndices.length === 0) {
return;
}
const activeEntries = Array.from(this.entityToIndex.entries())
.sort((a, b) => a[1] - b[1]);
// 重新映射索引
const newEntityToIndex = new Map<number, number>();
const newIndexToEntity: number[] = [];
for (let newIndex = 0; newIndex < activeEntries.length; newIndex++) {
const entry = activeEntries[newIndex];
if (!entry) continue;
const [entityId, oldIndex] = entry;
newEntityToIndex.set(entityId, newIndex);
newIndexToEntity[newIndex] = entityId;
// 移动字段数据
if (newIndex !== oldIndex) {
// 移动数值字段
for (const [, array] of this.fields.entries()) {
const value = array[oldIndex];
if (value !== undefined) {
array[newIndex] = value;
}
}
// 移动字符串字段
for (const [, stringArray] of this.stringFields.entries()) {
const value = stringArray[oldIndex];
if (value !== undefined) {
stringArray[newIndex] = value;
}
}
// 移动序列化字段
for (const [, serializedArray] of this.serializedFields.entries()) {
const value = serializedArray[oldIndex];
if (value !== undefined) {
serializedArray[newIndex] = value;
}
}
}
}
this.entityToIndex = newEntityToIndex;
this.indexToEntity = newIndexToEntity;
this.freeIndices = [];
this._size = activeEntries.length;
}
/**
* @zh 获取 SoA 存储统计信息
* @en Get SoA storage statistics
*/
public getStats(): ISoAStorageStats {
let totalMemory = 0;
const fieldStats = new Map<string, ISoAFieldStats>();
for (const [fieldName, array] of this.fields.entries()) {
const typeName = SoATypeRegistry.getTypeName(array);
const bytesPerElement = SoATypeRegistry.getBytesPerElement(typeName);
const memory = array.length * bytesPerElement;
totalMemory += memory;
fieldStats.set(fieldName, {
size: this._size,
capacity: array.length,
type: typeName,
memory: memory
});
}
return {
size: this._size,
capacity: this._capacity,
totalSlots: this._capacity,
usedSlots: this._size,
freeSlots: this._capacity - this._size,
fragmentation: this.freeIndices.length / this._capacity,
memoryUsage: totalMemory,
fieldStats: fieldStats
};
}
/**
* 执行向量化批量操作
* @param operation 操作函数,接收字段数组和活跃索引
*/
public performVectorizedOperation(operation: (fieldArrays: Map<string, SupportedTypedArray>, activeIndices: number[]) => void): void {
const activeIndices = this.getActiveIndices();
operation(this.fields, activeIndices);
}
}

View File

@@ -0,0 +1,208 @@
/**
* SoA存储器支持的TypedArray类型
*/
export type SupportedTypedArray =
| Float32Array
| Float64Array
| Int32Array
| Uint32Array
| Int16Array
| Uint16Array
| Int8Array
| Uint8Array
| Uint8ClampedArray;
export type TypedArrayConstructor =
| typeof Float32Array
| typeof Float64Array
| typeof Int32Array
| typeof Uint32Array
| typeof Int16Array
| typeof Uint16Array
| typeof Int8Array
| typeof Uint8Array
| typeof Uint8ClampedArray;
/**
* TypedArray 类型名称
*/
export type TypedArrayTypeName =
| 'float32'
| 'float64'
| 'int32'
| 'uint32'
| 'int16'
| 'uint16'
| 'int8'
| 'uint8'
| 'uint8clamped';
/**
* 字段元数据
*/
export type FieldMetadata = {
name: string;
type: 'number' | 'boolean' | 'string' | 'object';
arrayType?: TypedArrayTypeName;
isSerializedMap?: boolean;
isSerializedSet?: boolean;
isSerializedArray?: boolean;
isDeepCopy?: boolean;
}
/**
* SoA 类型注册器
* 负责类型推断、TypedArray 创建和元数据管理
*/
export class SoATypeRegistry {
private static readonly TYPE_CONSTRUCTORS: Record<TypedArrayTypeName, TypedArrayConstructor> = {
float32: Float32Array,
float64: Float64Array,
int32: Int32Array,
uint32: Uint32Array,
int16: Int16Array,
uint16: Uint16Array,
int8: Int8Array,
uint8: Uint8Array,
uint8clamped: Uint8ClampedArray
};
private static readonly TYPE_BYTES: Record<TypedArrayTypeName, number> = {
float32: 4,
float64: 8,
int32: 4,
uint32: 4,
int16: 2,
uint16: 2,
int8: 1,
uint8: 1,
uint8clamped: 1
};
/**
* 获取 TypedArray 构造函数
*/
public static getConstructor(typeName: TypedArrayTypeName): TypedArrayConstructor {
return this.TYPE_CONSTRUCTORS[typeName] || Float32Array;
}
/**
* 获取每个元素的字节数
*/
public static getBytesPerElement(typeName: TypedArrayTypeName): number {
return this.TYPE_BYTES[typeName] || 4;
}
/**
* 从 TypedArray 实例获取类型名称
*/
public static getTypeName(array: SupportedTypedArray): TypedArrayTypeName {
if (array instanceof Float32Array) return 'float32';
if (array instanceof Float64Array) return 'float64';
if (array instanceof Int32Array) return 'int32';
if (array instanceof Uint32Array) return 'uint32';
if (array instanceof Int16Array) return 'int16';
if (array instanceof Uint16Array) return 'uint16';
if (array instanceof Int8Array) return 'int8';
if (array instanceof Uint8Array) return 'uint8';
if (array instanceof Uint8ClampedArray) return 'uint8clamped';
return 'float32';
}
/**
* 创建新的 TypedArray与原数组同类型
*/
public static createSameType(source: SupportedTypedArray, capacity: number): SupportedTypedArray {
const typeName = this.getTypeName(source);
const Constructor = this.getConstructor(typeName);
return new Constructor(capacity);
}
/**
* 从组件类型提取字段元数据
*/
public static extractFieldMetadata<T>(
componentType: new () => T
): Map<string, FieldMetadata> {
const instance = new componentType();
const metadata = new Map<string, FieldMetadata>();
const typeWithMeta = componentType as typeof componentType & {
__float64Fields?: Set<string>;
__float32Fields?: Set<string>;
__int32Fields?: Set<string>;
__uint32Fields?: Set<string>;
__int16Fields?: Set<string>;
__uint16Fields?: Set<string>;
__int8Fields?: Set<string>;
__uint8Fields?: Set<string>;
__uint8ClampedFields?: Set<string>;
__serializeMapFields?: Set<string>;
__serializeSetFields?: Set<string>;
__serializeArrayFields?: Set<string>;
__deepCopyFields?: Set<string>;
};
// 收集装饰器标记
const decoratorMap = new Map<string, TypedArrayTypeName>();
const addDecorators = (fields: Set<string> | undefined, type: TypedArrayTypeName) => {
if (fields) {
for (const key of fields) decoratorMap.set(key, type);
}
};
addDecorators(typeWithMeta.__float64Fields, 'float64');
addDecorators(typeWithMeta.__float32Fields, 'float32');
addDecorators(typeWithMeta.__int32Fields, 'int32');
addDecorators(typeWithMeta.__uint32Fields, 'uint32');
addDecorators(typeWithMeta.__int16Fields, 'int16');
addDecorators(typeWithMeta.__uint16Fields, 'uint16');
addDecorators(typeWithMeta.__int8Fields, 'int8');
addDecorators(typeWithMeta.__uint8Fields, 'uint8');
addDecorators(typeWithMeta.__uint8ClampedFields, 'uint8clamped');
// 遍历实例属性
const instanceKeys = Object.keys(instance as object).filter((key) => key !== 'id');
for (const key of instanceKeys) {
const value = (instance as Record<string, unknown>)[key];
const type = typeof value;
if (type === 'function') continue;
const fieldMeta: FieldMetadata = {
name: key,
type: type as 'number' | 'boolean' | 'string' | 'object'
};
const decoratorType = decoratorMap.get(key);
if (decoratorType) {
fieldMeta.arrayType = decoratorType;
} else if (type === 'number') {
fieldMeta.arrayType = 'float32';
} else if (type === 'boolean') {
fieldMeta.arrayType = 'uint8';
}
// 序列化标记
if (typeWithMeta.__serializeMapFields?.has(key)) {
fieldMeta.isSerializedMap = true;
}
if (typeWithMeta.__serializeSetFields?.has(key)) {
fieldMeta.isSerializedSet = true;
}
if (typeWithMeta.__serializeArrayFields?.has(key)) {
fieldMeta.isSerializedArray = true;
}
if (typeWithMeta.__deepCopyFields?.has(key)) {
fieldMeta.isDeepCopy = true;
}
metadata.set(key, fieldMeta);
}
return metadata;
}
}

View File

@@ -0,0 +1,5 @@
export { ComponentPoolManager } from '../ComponentPool';
export type { ComponentPool } from '../ComponentPool';
export { ComponentStorage, ComponentRegistry, GlobalComponentRegistry } from '../ComponentStorage';
export type { IComponentRegistry } from '../ComponentStorage';
export { EnableSoA, Float64, Float32, Int32, SerializeMap, SoAStorage } from '../SoAStorage';

View File

@@ -0,0 +1,37 @@
/**
* 统一的存储装饰器导出文件
*
* 用户可以从这里导入所有的SoA存储装饰器而不需要知道内部实现细节
*
* @example
* ```typescript
* import { EnableSoA, Float32, Int16, Uint8 } from './ECS/Core/StorageDecorators';
*
* @EnableSoA
* class TransformComponent extends Component {
* @Float32 x: number = 0;
* @Float32 y: number = 0;
* @Int16 layer: number = 0;
* @Uint8 visible: boolean = true;
* }
* ```
*/
export {
EnableSoA,
Float64,
Float32,
Int32,
Uint32,
Int16,
Uint16,
Int8,
Uint8,
Uint8Clamped,
SerializeMap,
SerializeSet,
SerializeArray,
DeepCopy
} from './SoAStorage';
export type { SupportedTypedArray } from './SoAStorage';

View File

@@ -0,0 +1,263 @@
/**
* 系统依赖图
*
* 用于构建系统间的依赖关系并进行拓扑排序。
* 支持 before/after 依赖声明和 set 分组。
*
* System dependency graph.
* Used to build dependency relationships between systems and perform topological sorting.
* Supports before/after dependencies and set grouping.
*/
/**
* 循环依赖错误
* Cycle dependency error
*/
export class CycleDependencyError extends Error {
/**
* 参与循环的节点 ID
* Node IDs involved in the cycle
*/
public readonly involvedNodes: string[];
constructor(involvedNodes: string[]) {
const message = `[SystemDependencyGraph] 检测到循环依赖 | Cycle dependency detected: ${involvedNodes.join(' -> ')}`;
super(message);
this.name = 'CycleDependencyError';
this.involvedNodes = involvedNodes;
Object.setPrototypeOf(this, new.target.prototype);
}
}
/**
* 依赖图节点
* Dependency graph node
*/
interface GraphNode {
/** 节点 ID | Node ID */
id: string;
/** 是否为虚拟集合节点 | Whether this is a virtual set node */
bIsVirtual: boolean;
/** 入边(依赖于此节点的节点) | Incoming edges (nodes that depend on this node) */
inEdges: Set<string>;
/** 出边(此节点依赖的节点) | Outgoing edges (nodes this node depends on) */
outEdges: Set<string>;
}
/**
* 系统依赖信息
* System dependency info
*/
export type SystemDependencyInfo = {
/** 系统名称 | System name */
name: string;
/** 在这些系统之前执行 | Execute before these systems */
before: string[];
/** 在这些系统之后执行 | Execute after these systems */
after: string[];
/** 所属集合 | Sets this system belongs to */
sets: string[];
}
/** 集合前缀 | Set prefix */
const SET_PREFIX = 'set:';
/**
* 系统依赖图
*
* 使用 Kahn 算法进行拓扑排序,检测循环依赖。
*
* System dependency graph.
* Uses Kahn's algorithm for topological sorting and cycle detection.
*/
export class SystemDependencyGraph {
/** 节点映射 | Node map */
private _nodes: Map<string, GraphNode> = new Map();
/**
* 添加系统节点
* Add system node
*
* @param name 系统名称 | System name
*/
public addSystemNode(name: string): void {
this.getOrCreateNode(name, false);
}
/**
* 添加集合节点(虚拟节点)
* Add set node (virtual node)
*
* @param setName 集合名称 | Set name
*/
public addSetNode(setName: string): void {
const nodeId = SET_PREFIX + setName;
this.getOrCreateNode(nodeId, true);
}
/**
* 添加依赖边
* Add dependency edge
*
* @param from 起始节点 | Source node
* @param to 目标节点 | Target node
*/
public addEdge(from: string, to: string): void {
if (from === to) return;
const fromNode = this.getOrCreateNode(from, from.startsWith(SET_PREFIX));
const toNode = this.getOrCreateNode(to, to.startsWith(SET_PREFIX));
fromNode.outEdges.add(to);
toNode.inEdges.add(from);
}
/**
* 从系统依赖信息构建图
* Build graph from system dependency info
*
* @param systems 系统依赖信息列表 | System dependency info list
*/
public buildFromSystems(systems: SystemDependencyInfo[]): void {
this.clear();
// 1. 添加所有系统节点
for (const sys of systems) {
this.addSystemNode(sys.name);
// 添加集合节点
for (const setName of sys.sets) {
this.addSetNode(setName);
}
}
// 2. 添加边
for (const sys of systems) {
// 集合 -> 系统(系统属于集合,集合执行后系统执行)
for (const setName of sys.sets) {
const setId = SET_PREFIX + setName;
this.addEdge(setId, sys.name);
}
// before: 此系统 -> 目标(此系统先执行)
for (const target of sys.before) {
const targetId = this.resolveTargetId(target);
this.addEdge(sys.name, targetId);
}
// after: 目标 -> 此系统(目标先执行)
for (const target of sys.after) {
const targetId = this.resolveTargetId(target);
this.addEdge(targetId, sys.name);
}
}
}
/**
* 执行拓扑排序Kahn 算法)
* Perform topological sort (Kahn's algorithm)
*
* @returns 排序后的系统名称列表(不包含虚拟节点) | Sorted system names (excluding virtual nodes)
* @throws {CycleDependencyError} 如果存在循环依赖 | If cycle dependency detected
*/
public topologicalSort(): string[] {
// 复制入边计数(避免修改原始数据)
const inDegree = new Map<string, number>();
for (const [id, node] of this._nodes) {
inDegree.set(id, node.inEdges.size);
}
// 初始化队列:入度为 0 的节点
const queue: string[] = [];
for (const [id, degree] of inDegree) {
if (degree === 0) {
queue.push(id);
}
}
const result: string[] = [];
let processedCount = 0;
while (queue.length > 0) {
const nodeId = queue.shift()!;
processedCount++;
const node = this._nodes.get(nodeId);
if (!node) continue;
// 只添加非虚拟节点到结果
if (!node.bIsVirtual) {
result.push(nodeId);
}
// 减少所有出边目标的入度
for (const outId of node.outEdges) {
const newDegree = (inDegree.get(outId) ?? 0) - 1;
inDegree.set(outId, newDegree);
if (newDegree === 0) {
queue.push(outId);
}
}
}
// 检测循环:如果处理的节点数少于总节点数,说明存在循环
if (processedCount < this._nodes.size) {
const cycleNodes: string[] = [];
for (const [id, degree] of inDegree) {
if (degree > 0) {
cycleNodes.push(id);
}
}
throw new CycleDependencyError(cycleNodes);
}
return result;
}
/**
* 清空图
* Clear graph
*/
public clear(): void {
this._nodes.clear();
}
/**
* 获取节点数量
* Get node count
*/
public get size(): number {
return this._nodes.size;
}
/**
* 获取或创建节点
* Get or create node
*/
private getOrCreateNode(id: string, bIsVirtual: boolean): GraphNode {
let node = this._nodes.get(id);
if (!node) {
node = {
id,
bIsVirtual,
inEdges: new Set(),
outEdges: new Set()
};
this._nodes.set(id, node);
}
return node;
}
/**
* 解析目标 ID支持 set: 前缀)
* Resolve target ID (supports set: prefix)
*/
private resolveTargetId(nameOrSet: string): string {
// 如果已经有 set: 前缀,直接返回
if (nameOrSet.startsWith(SET_PREFIX)) {
return nameOrSet;
}
return nameOrSet;
}
}

View File

@@ -0,0 +1,341 @@
/**
* 系统调度器
*
* 负责管理系统的执行阶段和依赖关系。
* 支持声明式的 before/after 依赖和拓扑排序。
*
* System scheduler.
* Manages system execution stages and dependencies.
* Supports declarative before/after dependencies and topological sorting.
*
* @example
* ```typescript
* // 使用装饰器声明依赖
* @Stage('update')
* @After('PhysicsSystem')
* @Before('RenderSystem')
* class MovementSystem extends EntitySystem { }
*
* // 或者使用方法链
* class MovementSystem extends EntitySystem {
* constructor() {
* super();
* this.stage('update').after('PhysicsSystem').before('RenderSystem');
* }
* }
* ```
*/
import { SystemDependencyGraph, CycleDependencyError, type SystemDependencyInfo } from './SystemDependencyGraph';
import { createLogger } from '../../Utils/Logger';
import type { EntitySystem } from '../Systems/EntitySystem';
const logger = createLogger('SystemScheduler');
export { CycleDependencyError };
/**
* 系统执行阶段
* System execution stage
*/
export type SystemStage = 'startup' | 'preUpdate' | 'update' | 'postUpdate' | 'cleanup';
/**
* 默认阶段执行顺序
* Default stage execution order
*/
export const DEFAULT_STAGE_ORDER: readonly SystemStage[] = [
'startup',
'preUpdate',
'update',
'postUpdate',
'cleanup'
];
/**
* 系统调度元数据
* System scheduling metadata
*/
export type SystemSchedulingMetadata = {
/** 执行阶段 | Execution stage */
stage: SystemStage;
/** 在这些系统之前执行 | Execute before these systems */
before: string[];
/** 在这些系统之后执行 | Execute after these systems */
after: string[];
/** 所属集合 | Sets this system belongs to */
sets: string[];
}
/**
* 默认调度元数据
* Default scheduling metadata
*/
export const DEFAULT_SCHEDULING_METADATA: Readonly<SystemSchedulingMetadata> = {
stage: 'update',
before: [],
after: [],
sets: []
};
/**
* 系统调度器
*
* 管理系统的执行顺序,支持:
* - 阶段分组startup, preUpdate, update, postUpdate, cleanup
* - before/after 依赖声明
* - 拓扑排序和循环检测
* - 与现有 updateOrder 的兼容
*
* System scheduler.
* Manages system execution order, supports:
* - Stage grouping (startup, preUpdate, update, postUpdate, cleanup)
* - before/after dependency declarations
* - Topological sorting and cycle detection
* - Compatibility with existing updateOrder
*/
export class SystemScheduler {
/** 按阶段分组的排序结果 | Sorted results grouped by stage */
private _sortedByStage: Map<SystemStage, EntitySystem[]> = new Map();
/** 是否需要重新构建 | Whether rebuild is needed */
private _dirty: boolean = true;
/** 依赖图 | Dependency graph */
private _graph: SystemDependencyGraph = new SystemDependencyGraph();
/** 是否启用依赖排序 | Whether dependency sorting is enabled */
private _useDependencySort: boolean = true;
/**
* 设置是否使用依赖排序
* Set whether to use dependency sorting
*
* @param enabled 是否启用 | Whether to enable
*/
public setUseDependencySort(enabled: boolean): void {
if (this._useDependencySort !== enabled) {
this._useDependencySort = enabled;
this._dirty = true;
}
}
/**
* 标记需要重新构建
* Mark as needing rebuild
*/
public markDirty(): void {
this._dirty = true;
}
/**
* 获取指定阶段的排序后系统列表
* Get sorted system list for specified stage
*
* @param systems 所有系统 | All systems
* @param stage 阶段 | Stage
* @returns 排序后的系统列表 | Sorted system list
*/
public getSortedSystems(systems: EntitySystem[], stage?: SystemStage): EntitySystem[] {
this.ensureBuilt(systems);
if (stage) {
return this._sortedByStage.get(stage) ?? [];
}
// 返回所有阶段按顺序合并的结果
const result: EntitySystem[] = [];
for (const s of DEFAULT_STAGE_ORDER) {
const stageSystems = this._sortedByStage.get(s);
if (stageSystems) {
result.push(...stageSystems);
}
}
return result;
}
/**
* 获取所有排序后的系统(按默认阶段顺序)
* Get all sorted systems (by default stage order)
*
* @param systems 所有系统 | All systems
* @returns 排序后的系统列表 | Sorted system list
*/
public getAllSortedSystems(systems: EntitySystem[]): EntitySystem[] {
return this.getSortedSystems(systems);
}
/**
* 确保已构建排序结果
* Ensure sorted results are built
*/
private ensureBuilt(systems: EntitySystem[]): void {
if (!this._dirty) return;
this._sortedByStage.clear();
if (!this._useDependencySort || !this.hasDependencies(systems)) {
// 使用简单的 updateOrder 排序
this.buildWithUpdateOrder(systems);
} else {
// 使用依赖图拓扑排序
this.buildWithDependencyGraph(systems);
}
this._dirty = false;
}
/**
* 检查系统是否有依赖声明
* Check if systems have dependency declarations
*/
private hasDependencies(systems: EntitySystem[]): boolean {
for (const sys of systems) {
const meta = this.getSchedulingMetadata(sys);
if (meta.before.length > 0 || meta.after.length > 0 || meta.sets.length > 0) {
return true;
}
// 检查 stage 是否非默认值
if (meta.stage !== 'update') {
return true;
}
}
return false;
}
/**
* 使用 updateOrder 构建排序
* Build sorting with updateOrder
*/
private buildWithUpdateOrder(systems: EntitySystem[]): void {
// 先按 updateOrder 和 addOrder 排序
const sorted = [...systems].sort((a, b) => {
const orderDiff = a.updateOrder - b.updateOrder;
if (orderDiff !== 0) return orderDiff;
return a.addOrder - b.addOrder;
});
// 所有系统放入 update 阶段
this._sortedByStage.set('update', sorted);
}
/**
* 使用依赖图构建排序
* Build sorting with dependency graph
*/
private buildWithDependencyGraph(systems: EntitySystem[]): void {
// 按阶段分组
const byStage = new Map<SystemStage, EntitySystem[]>();
for (const stage of DEFAULT_STAGE_ORDER) {
byStage.set(stage, []);
}
for (const sys of systems) {
const meta = this.getSchedulingMetadata(sys);
const stage = meta.stage;
const stageList = byStage.get(stage);
if (stageList) {
stageList.push(sys);
} else {
// 未知阶段放入 update
byStage.get('update')!.push(sys);
}
}
// 对每个阶段内的系统进行拓扑排序
for (const [stage, stageSystems] of byStage) {
if (stageSystems.length === 0) {
this._sortedByStage.set(stage, []);
continue;
}
const sorted = this.sortSystemsInStage(stageSystems);
this._sortedByStage.set(stage, sorted);
}
}
/**
* 在阶段内对系统进行拓扑排序
* Sort systems within a stage using topological sort
*/
private sortSystemsInStage(systems: EntitySystem[]): EntitySystem[] {
// 构建名称到系统的映射
const nameToSystem = new Map<string, EntitySystem>();
const depInfos: SystemDependencyInfo[] = [];
for (const sys of systems) {
const name = sys.systemName;
nameToSystem.set(name, sys);
const meta = this.getSchedulingMetadata(sys);
depInfos.push({
name,
before: meta.before,
after: meta.after,
sets: meta.sets
});
}
// 构建依赖图并排序
this._graph.buildFromSystems(depInfos);
try {
const sortedNames = this._graph.topologicalSort();
// 将排序结果映射回系统实例
const result: EntitySystem[] = [];
for (const name of sortedNames) {
const sys = nameToSystem.get(name);
if (sys) {
result.push(sys);
}
}
// 对于没有依赖的系统,保持 updateOrder 作为次要排序键
return this.stableSortByUpdateOrder(result);
} catch (error) {
if (error instanceof CycleDependencyError) {
// 重新抛出循环错误,让用户知道
throw error;
}
// 其他错误回退到 updateOrder 排序
logger.warn('Topological sort failed, falling back to updateOrder | 拓扑排序失败,回退到 updateOrder 排序', error);
return this.fallbackSort(systems);
}
}
/**
* 稳定排序:在拓扑顺序的基础上,用 updateOrder 作为次要排序键
* Stable sort: use updateOrder as secondary key after topological order
*/
private stableSortByUpdateOrder(systems: EntitySystem[]): EntitySystem[] {
// 拓扑排序已经保证了依赖顺序
// 对于没有直接依赖关系的系统,按 updateOrder 排序
// 这里简单处理:假设拓扑排序已经正确,只需保持结果
return systems;
}
/**
* 回退排序:使用 updateOrder
* Fallback sort: use updateOrder
*/
private fallbackSort(systems: EntitySystem[]): EntitySystem[] {
return [...systems].sort((a, b) => {
const orderDiff = a.updateOrder - b.updateOrder;
if (orderDiff !== 0) return orderDiff;
return a.addOrder - b.addOrder;
});
}
/**
* 获取系统的调度元数据
* Get system scheduling metadata
*/
private getSchedulingMetadata(system: EntitySystem): SystemSchedulingMetadata {
// 使用公共 getter 方法获取调度信息
// Use public getter methods to access scheduling info
return {
stage: system.getStage(),
before: [...system.getBefore()],
after: [...system.getAfter()],
sets: [...system.getSets()]
};
}
}

View File

@@ -0,0 +1,183 @@
/**
* ECS事件类型枚举
* 定义实体组件系统中的所有事件类型
*/
export enum ECSEventType {
// 实体相关事件
ENTITY_CREATED = 'entity:created',
ENTITY_DESTROYED = 'entity:destroyed',
ENTITY_ENABLED = 'entity:enabled',
ENTITY_DISABLED = 'entity:disabled',
ENTITY_TAG_ADDED = 'entity:tag:added',
ENTITY_TAG_REMOVED = 'entity:tag:removed',
ENTITY_NAME_CHANGED = 'entity:name:changed',
// 组件相关事件
COMPONENT_ADDED = 'component:added',
COMPONENT_REMOVED = 'component:removed',
COMPONENT_MODIFIED = 'component:modified',
COMPONENT_ENABLED = 'component:enabled',
COMPONENT_DISABLED = 'component:disabled',
// 系统相关事件
SYSTEM_ADDED = 'system:added',
SYSTEM_REMOVED = 'system:removed',
SYSTEM_ENABLED = 'system:enabled',
SYSTEM_DISABLED = 'system:disabled',
SYSTEM_PROCESSING_START = 'system:processing:start',
SYSTEM_PROCESSING_END = 'system:processing:end',
SYSTEM_ERROR = 'system:error',
// 场景相关事件
SCENE_CREATED = 'scene:created',
SCENE_DESTROYED = 'scene:destroyed',
SCENE_ACTIVATED = 'scene:activated',
SCENE_DEACTIVATED = 'scene:deactivated',
SCENE_PAUSED = 'scene:paused',
SCENE_RESUMED = 'scene:resumed',
// 查询相关事件
QUERY_EXECUTED = 'query:executed',
QUERY_CACHE_HIT = 'query:cache:hit',
QUERY_CACHE_MISS = 'query:cache:miss',
QUERY_OPTIMIZED = 'query:optimized',
// 性能相关事件
PERFORMANCE_WARNING = 'performance:warning',
PERFORMANCE_CRITICAL = 'performance:critical',
MEMORY_USAGE_HIGH = 'memory:usage:high',
FRAME_RATE_DROP = 'frame:rate:drop',
// 索引相关事件
INDEX_CREATED = 'index:created',
INDEX_UPDATED = 'index:updated',
INDEX_OPTIMIZED = 'index:optimized',
// Archetype相关事件
ARCHETYPE_CREATED = 'archetype:created',
ARCHETYPE_ENTITY_ADDED = 'archetype:entity:added',
ARCHETYPE_ENTITY_REMOVED = 'archetype:entity:removed',
// 脏标记相关事件
DIRTY_MARK_ADDED = 'dirty:mark:added',
DIRTY_BATCH_PROCESSED = 'dirty:batch:processed',
// 错误和警告事件
ERROR_OCCURRED = 'error:occurred',
WARNING_ISSUED = 'warning:issued',
// 生命周期事件
FRAMEWORK_INITIALIZED = 'framework:initialized',
FRAMEWORK_SHUTDOWN = 'framework:shutdown',
// 调试相关事件
DEBUG_INFO = 'debug:info',
DEBUG_STATS_UPDATED = 'debug:stats:updated'
}
/**
* 事件优先级枚举
* 定义事件处理的优先级级别
*/
export enum EventPriority {
LOWEST = 0,
LOW = 25,
NORMAL = 50,
HIGH = 75,
HIGHEST = 100,
CRITICAL = 200
}
/**
* 预定义的事件类型常量
* 提供类型安全的事件类型字符串
*/
export const EVENT_TYPES = {
// 实体事件
ENTITY: {
CREATED: ECSEventType.ENTITY_CREATED,
DESTROYED: ECSEventType.ENTITY_DESTROYED,
ENABLED: ECSEventType.ENTITY_ENABLED,
DISABLED: ECSEventType.ENTITY_DISABLED,
TAG_ADDED: ECSEventType.ENTITY_TAG_ADDED,
TAG_REMOVED: ECSEventType.ENTITY_TAG_REMOVED,
NAME_CHANGED: ECSEventType.ENTITY_NAME_CHANGED
},
// 组件事件
COMPONENT: {
ADDED: ECSEventType.COMPONENT_ADDED,
REMOVED: ECSEventType.COMPONENT_REMOVED,
MODIFIED: ECSEventType.COMPONENT_MODIFIED,
ENABLED: ECSEventType.COMPONENT_ENABLED,
DISABLED: ECSEventType.COMPONENT_DISABLED
},
// 系统事件
SYSTEM: {
ADDED: ECSEventType.SYSTEM_ADDED,
REMOVED: ECSEventType.SYSTEM_REMOVED,
ENABLED: ECSEventType.SYSTEM_ENABLED,
DISABLED: ECSEventType.SYSTEM_DISABLED,
PROCESSING_START: ECSEventType.SYSTEM_PROCESSING_START,
PROCESSING_END: ECSEventType.SYSTEM_PROCESSING_END,
ERROR: ECSEventType.SYSTEM_ERROR
},
// 性能事件
PERFORMANCE: {
WARNING: ECSEventType.PERFORMANCE_WARNING,
CRITICAL: ECSEventType.PERFORMANCE_CRITICAL,
MEMORY_HIGH: ECSEventType.MEMORY_USAGE_HIGH,
FRAME_DROP: ECSEventType.FRAME_RATE_DROP
}
} as const;
/**
* 事件类型验证器
* 验证事件类型是否有效
*/
export class EventTypeValidator {
private static validTypes = new Set<string>([
...Object.values(ECSEventType),
...Object.values(EVENT_TYPES.ENTITY),
...Object.values(EVENT_TYPES.COMPONENT),
...Object.values(EVENT_TYPES.SYSTEM),
...Object.values(EVENT_TYPES.PERFORMANCE)
]);
/**
* 验证事件类型是否有效
* @param eventType 事件类型
* @returns 是否有效
*/
public static isValid(eventType: string): boolean {
return this.validTypes.has(eventType);
}
/**
* 获取所有有效的事件类型
* @returns 事件类型数组
*/
public static getAllValidTypes(): string[] {
return Array.from(this.validTypes);
}
/**
* 添加自定义事件类型
* @param eventType 事件类型
*/
public static addCustomType(eventType: string): void {
this.validTypes.add(eventType);
}
/**
* 移除自定义事件类型
* @param eventType 事件类型
*/
public static removeCustomType(eventType: string): void {
this.validTypes.delete(eventType);
}
}

View File

@@ -0,0 +1,180 @@
import type { Entity } from '../Entity';
import type { Component } from '../Component';
import { getSceneByEntityId } from '../Core/ReferenceTracker';
import { createLogger } from '../../Utils/Logger';
const logger = createLogger('EntityRefDecorator');
/**
* EntityRef元数据的Symbol键
*/
export const ENTITY_REF_METADATA = Symbol('EntityRefMetadata');
/**
* EntityRef值存储的Symbol键
*/
const ENTITY_REF_VALUES = Symbol('EntityRefValues');
/**
* EntityRef元数据
*/
export type EntityRefMetadata = {
properties: Set<string>;
}
/**
* 获取或创建组件的EntityRef值存储Map
*/
function getValueMap(component: Component): Map<string, Entity | null> {
let map = (component as any)[ENTITY_REF_VALUES];
if (!map) {
map = new Map<string, Entity | null>();
(component as any)[ENTITY_REF_VALUES] = map;
}
return map;
}
/**
* Entity引用装饰器
*
* 标记Component属性为Entity引用自动追踪引用关系。
* 当被引用的Entity销毁时该属性会自动设为null。
*
* @example
* ```typescript
* class ParentComponent extends Component {
* @EntityRef() parent: Entity | null = null;
* }
*
* const parent = scene.createEntity('Parent');
* const child = scene.createEntity('Child');
* const comp = child.addComponent(new ParentComponent());
*
* comp.parent = parent;
* parent.destroy(); // comp.parent 自动变为 null
* ```
*/
export function EntityRef(): PropertyDecorator {
return function (target: any, propertyKey: string | symbol) {
const constructor = target.constructor;
let metadata: EntityRefMetadata = constructor[ENTITY_REF_METADATA];
if (!metadata) {
metadata = {
properties: new Set()
};
constructor[ENTITY_REF_METADATA] = metadata;
}
const propKeyString = typeof propertyKey === 'symbol' ? propertyKey.toString() : propertyKey;
metadata.properties.add(propKeyString);
Object.defineProperty(target, propertyKey, {
get: function (this: Component) {
const valueMap = getValueMap(this);
return valueMap.get(propKeyString) || null;
},
set: function (this: Component, newValue: Entity | null) {
const valueMap = getValueMap(this);
const oldValue = valueMap.get(propKeyString) || null;
if (oldValue === newValue) {
return;
}
const scene = this.entityId !== null ? getSceneByEntityId(this.entityId) : null;
if (!scene || !scene.referenceTracker) {
valueMap.set(propKeyString, newValue);
return;
}
const tracker = scene.referenceTracker;
if (oldValue) {
tracker.unregisterReference(oldValue, this, propKeyString);
}
if (newValue) {
if (newValue.scene !== scene) {
logger.error(`Cannot reference Entity from different Scene. Entity: ${newValue.name}, Scene: ${newValue.scene?.name || 'null'}`);
return;
}
if (newValue.isDestroyed) {
logger.warn(`Cannot reference destroyed Entity: ${newValue.name}`);
valueMap.set(propKeyString, null);
return;
}
tracker.registerReference(newValue, this, propKeyString);
}
valueMap.set(propKeyString, newValue);
},
enumerable: true,
configurable: true
});
};
}
/**
* 获取Component的EntityRef元数据
*
* @param component Component实例或Component类
* @returns EntityRef元数据如果不存在则返回null
*/
export function getEntityRefMetadata(component: any): EntityRefMetadata | null {
if (!component) {
return null;
}
const constructor = typeof component === 'function'
? component
: component.constructor;
return constructor[ENTITY_REF_METADATA] || null;
}
/**
* 检查Component是否有EntityRef属性
*
* @param component Component实例或Component类
* @returns 如果有EntityRef属性返回true
*/
export function hasEntityRef(component: any): boolean {
return getEntityRefMetadata(component) !== null;
}
/**
* 检查特定属性是否为EntityRef
*
* Check if a specific property is an EntityRef.
*
* @param component Component实例或Component类
* @param propertyKey 属性名
* @returns 如果是EntityRef属性返回true
*/
export function isEntityRefProperty(component: any, propertyKey: string): boolean {
const metadata = getEntityRefMetadata(component);
if (!metadata) {
return false;
}
return metadata.properties.has(propertyKey);
}
/**
* 获取组件的所有EntityRef属性名
*
* Get all EntityRef property names of a component.
*
* @param component Component实例或Component类
* @returns EntityRef属性名数组
*/
export function getEntityRefProperties(component: any): string[] {
const metadata = getEntityRefMetadata(component);
if (!metadata) {
return [];
}
return Array.from(metadata.properties);
}

View File

@@ -0,0 +1,283 @@
/**
* 属性元数据存储
* Property metadata storage
*/
const metadataStorage = new WeakMap<Function, Record<string, unknown>>();
export type PropertyType = 'number' | 'integer' | 'string' | 'boolean' | 'color' | 'vector2' | 'vector3' | 'vector4' | 'enum' | 'asset' | 'array' | 'animationClips' | 'collisionLayer' | 'collisionMask' | 'entityRef';
/**
* 属性资源类型
* Asset type for property decorators
*/
export type PropertyAssetType = 'texture' | 'audio' | 'scene' | 'prefab' | 'animation' | 'any';
/** @deprecated Use PropertyAssetType instead */
export type AssetType = PropertyAssetType;
/**
* 枚举选项 - 支持简单字符串或带标签的对象
* Enum option - supports simple string or labeled object
*/
export type EnumOption = string | { label: string; value: any };
/**
* Action button configuration for property fields
* 属性字段的操作按钮配置
*/
export type PropertyAction = {
/** Action identifier | 操作标识符 */
id: string;
/** Button label | 按钮标签 */
label: string;
/** Button tooltip | 按钮提示 */
tooltip?: string;
/** Icon name from Lucide | Lucide图标名称 */
icon?: string;
}
/**
* 控制关系声明
* Control relationship declaration
*/
export type PropertyControl = {
/** 被控制的组件名称 | Target component name */
component: string;
/** 被控制的属性名称 | Target property name */
property: string;
}
/**
* 属性基础选项
* Base property options shared by all types
*/
interface PropertyOptionsBase {
/** 显示标签 | Display label */
label?: string;
/** 是否只读 | Read-only flag */
readOnly?: boolean;
/**
* 是否在 Inspector 中隐藏
* Whether to hide this property in Inspector
*
* Hidden properties are still serialized but not shown in the default PropertyInspector.
* Useful when a custom Inspector handles the property.
* 隐藏的属性仍然会被序列化,但不会在默认的 PropertyInspector 中显示。
* 适用于自定义 Inspector 处理该属性的情况。
*/
hidden?: boolean;
/** Action buttons | 操作按钮 */
actions?: PropertyAction[];
/** 此属性控制的其他组件属性 | Properties this field controls */
controls?: PropertyControl[];
}
/**
* 数值类型属性选项
* Number property options
*/
interface NumberPropertyOptions extends PropertyOptionsBase {
type: 'number' | 'integer';
min?: number;
max?: number;
step?: number;
}
/**
* 字符串类型属性选项
* String property options
*/
interface StringPropertyOptions extends PropertyOptionsBase {
type: 'string';
/** 多行文本 | Multiline text */
multiline?: boolean;
}
/**
* 布尔类型属性选项
* Boolean property options
*/
interface BooleanPropertyOptions extends PropertyOptionsBase {
type: 'boolean';
}
/**
* 颜色类型属性选项
* Color property options
*/
interface ColorPropertyOptions extends PropertyOptionsBase {
type: 'color';
/** 是否包含透明度 | Include alpha channel */
alpha?: boolean;
}
/**
* 向量类型属性选项
* Vector property options
*/
interface VectorPropertyOptions extends PropertyOptionsBase {
type: 'vector2' | 'vector3' | 'vector4';
}
/**
* 枚举类型属性选项
* Enum property options
*/
interface EnumPropertyOptions extends PropertyOptionsBase {
type: 'enum';
/** 枚举选项列表 | Enum options list */
options: EnumOption[];
}
/**
* 资源类型属性选项
* Asset property options
*/
interface AssetPropertyOptions extends PropertyOptionsBase {
type: 'asset';
/** 资源类型 | Asset type */
assetType?: PropertyAssetType;
/** 文件扩展名过滤 | File extension filter */
extensions?: string[];
}
/**
* 数组元素类型选项
* Array item type options
*/
export type ArrayItemType =
| { type: 'string' }
| { type: 'number'; min?: number; max?: number }
| { type: 'integer'; min?: number; max?: number }
| { type: 'boolean' }
| { type: 'asset'; assetType?: PropertyAssetType; extensions?: string[] }
| { type: 'vector2' }
| { type: 'vector3' }
| { type: 'vector4' }
| { type: 'color'; alpha?: boolean }
| { type: 'enum'; options: EnumOption[] };
/**
* 数组类型属性选项
* Array property options
*
* @example
* ```typescript
* @Property({
* type: 'array',
* label: 'Particle Assets',
* itemType: { type: 'asset', extensions: ['.particle'] }
* })
* public particleAssets: string[] = [];
* ```
*/
interface ArrayPropertyOptions extends PropertyOptionsBase {
type: 'array';
/** 数组元素类型 | Array item type */
itemType: ArrayItemType;
/** 最小数组长度 | Minimum array length */
minLength?: number;
/** 最大数组长度 | Maximum array length */
maxLength?: number;
/** 是否允许重排序 | Allow reordering */
reorderable?: boolean;
}
/**
* 动画剪辑类型属性选项
* Animation clips property options
*/
interface AnimationClipsPropertyOptions extends PropertyOptionsBase {
type: 'animationClips';
}
/**
* 碰撞层属性选项
* Collision layer property options
*/
interface CollisionLayerPropertyOptions extends PropertyOptionsBase {
type: 'collisionLayer';
}
/**
* 碰撞掩码属性选项
* Collision mask property options
*/
interface CollisionMaskPropertyOptions extends PropertyOptionsBase {
type: 'collisionMask';
}
/**
* 实体引用属性选项
* Entity reference property options
*
* Used for properties that store entity IDs and support drag-and-drop from SceneHierarchy.
* 用于存储实体 ID 的属性,支持从场景层级面板拖放。
*/
interface EntityRefPropertyOptions extends PropertyOptionsBase {
type: 'entityRef';
}
/**
* 属性选项联合类型
* Property options union type
*/
export type PropertyOptions =
| NumberPropertyOptions
| StringPropertyOptions
| BooleanPropertyOptions
| ColorPropertyOptions
| VectorPropertyOptions
| EnumPropertyOptions
| AssetPropertyOptions
| ArrayPropertyOptions
| AnimationClipsPropertyOptions
| CollisionLayerPropertyOptions
| CollisionMaskPropertyOptions
| EntityRefPropertyOptions;
// 使用 Symbol.for 创建全局 Symbol确保跨包共享元数据
// Use Symbol.for to create a global Symbol to ensure metadata sharing across packages
export const PROPERTY_METADATA = Symbol.for('@esengine/property:metadata');
/**
* 属性装饰器 - 声明组件属性的编辑器元数据
*
* @example
* ```typescript
* @ECSComponent('Transform')
* export class TransformComponent extends Component {
* @Property({ type: 'vector3', label: 'Position' })
* public position: Vector3 = { x: 0, y: 0, z: 0 };
*
* @Property({ type: 'number', label: 'Speed', min: 0, max: 100 })
* public speed: number = 10;
* }
* ```
*/
export function Property(options: PropertyOptions): PropertyDecorator {
return (target: object, propertyKey: string | symbol) => {
const constructor = target.constructor as Function;
const existingMetadata = metadataStorage.get(constructor) || {};
existingMetadata[propertyKey as string] = options;
metadataStorage.set(constructor, existingMetadata);
};
}
/**
* 获取组件类的所有属性元数据
* Get all property metadata for a component class
*/
export function getPropertyMetadata(target: Function): Record<string, PropertyOptions> | undefined {
return metadataStorage.get(target) as Record<string, PropertyOptions> | undefined;
}
/**
* 检查组件类是否有属性元数据
* Check if a component class has property metadata
*/
export function hasPropertyMetadata(target: Function): boolean {
return metadataStorage.has(target);
}

View File

@@ -0,0 +1,177 @@
/**
* 系统调度装饰器
*
* 提供声明式的系统调度配置,包括执行阶段和依赖关系。
*
* System scheduling decorators.
* Provides declarative system scheduling configuration including execution stage and dependencies.
*
* @example
* ```typescript
* @Stage('update')
* @After('PhysicsSystem')
* @Before('RenderSystem')
* @InSet('GameplaySystems')
* class MovementSystem extends EntitySystem {
* // ...
* }
* ```
*/
import type { SystemStage, SystemSchedulingMetadata } from '../Core/SystemScheduler';
/**
* 调度元数据 Symbol
* Scheduling metadata symbol
*/
export const SCHEDULING_METADATA = Symbol('schedulingMetadata');
/**
* 获取或创建调度元数据
* Get or create scheduling metadata
*/
function getOrCreateMetadata(target: object): SystemSchedulingMetadata {
const proto = target as Record<symbol, SystemSchedulingMetadata | undefined>;
if (!proto[SCHEDULING_METADATA]) {
proto[SCHEDULING_METADATA] = {
stage: 'update',
before: [],
after: [],
sets: []
};
}
return proto[SCHEDULING_METADATA]!;
}
/**
* 设置系统执行阶段
* Set system execution stage
*
* @param stage 执行阶段 | Execution stage
* - 'startup': 只在首帧执行一次 | Execute once on first frame
* - 'preUpdate': 在 update 之前执行 | Execute before update
* - 'update': 主更新阶段(默认)| Main update stage (default)
* - 'postUpdate': 在 update 之后执行 | Execute after update
* - 'cleanup': 帧末清理阶段 | End of frame cleanup stage
*
* @example
* ```typescript
* @Stage('preUpdate')
* class InputSystem extends EntitySystem { }
*
* @Stage('postUpdate')
* class RenderSystem extends EntitySystem { }
* ```
*/
export function Stage(stage: SystemStage): ClassDecorator {
return function (target) {
const metadata = getOrCreateMetadata(target.prototype);
metadata.stage = stage;
return target;
};
}
/**
* 声明此系统在指定系统之前执行
* Declare this system executes before specified systems
*
* @param systems 系统名称列表 | System names
*
* @example
* ```typescript
* @Before('RenderSystem', 'UISystem')
* class TransformSystem extends EntitySystem { }
* ```
*/
export function Before(...systems: string[]): ClassDecorator {
return function (target) {
const metadata = getOrCreateMetadata(target.prototype);
metadata.before.push(...systems);
return target;
};
}
/**
* 声明此系统在指定系统之后执行
* Declare this system executes after specified systems
*
* @param systems 系统名称列表(可使用 'set:' 前缀指定集合)| System names (use 'set:' prefix for sets)
*
* @example
* ```typescript
* @After('PhysicsSystem')
* class CollisionResponseSystem extends EntitySystem { }
*
* // 使用集合
* @After('set:PhysicsSystems')
* class GameLogicSystem extends EntitySystem { }
* ```
*/
export function After(...systems: string[]): ClassDecorator {
return function (target) {
const metadata = getOrCreateMetadata(target.prototype);
metadata.after.push(...systems);
return target;
};
}
/**
* 将系统加入指定集合
* Add system to specified sets
*
* 集合是虚拟分组,可用于批量依赖声明。
* Sets are virtual groups that can be used for batch dependency declarations.
*
* @param sets 集合名称列表 | Set names
*
* @example
* ```typescript
* @InSet('PhysicsSystems')
* class RigidBodySystem extends EntitySystem { }
*
* @InSet('PhysicsSystems')
* class CollisionSystem extends EntitySystem { }
*
* // 其他系统可以依赖整个集合
* @After('set:PhysicsSystems')
* class GameLogicSystem extends EntitySystem { }
* ```
*/
export function InSet(...sets: string[]): ClassDecorator {
return function (target) {
const metadata = getOrCreateMetadata(target.prototype);
metadata.sets.push(...sets);
return target;
};
}
/**
* 获取系统的调度元数据
* Get system scheduling metadata
*
* @param target 系统类或实例 | System class or instance
* @returns 调度元数据或 undefined | Scheduling metadata or undefined
*/
export function getSchedulingMetadata(target: object): SystemSchedulingMetadata | undefined {
// 从原型链查找
let proto = Object.getPrototypeOf(target);
while (proto) {
const metadata = (proto as Record<symbol, SystemSchedulingMetadata | undefined>)[SCHEDULING_METADATA];
if (metadata) {
return metadata;
}
proto = Object.getPrototypeOf(proto);
}
return undefined;
}
/**
* 检查系统是否有调度元数据
* Check if system has scheduling metadata
*
* @param target 系统类或实例 | System class or instance
* @returns 是否有元数据 | Whether has metadata
*/
export function hasSchedulingMetadata(target: object): boolean {
return getSchedulingMetadata(target) !== undefined;
}

View File

@@ -0,0 +1,277 @@
/**
* Type Decorators for ECS Components and Systems
* ECS 组件和系统的类型装饰器
*
* Provides decorators to mark component/system types with stable names
* that survive code minification.
*
* 提供装饰器为组件/系统类型标记稳定的名称,使其在代码混淆后仍然有效。
*/
import type { Component } from '../Component';
import type { EntitySystem } from '../Systems';
import { GlobalComponentRegistry } from '../Core/ComponentStorage/ComponentRegistry';
import {
COMPONENT_TYPE_NAME,
COMPONENT_DEPENDENCIES,
COMPONENT_EDITOR_OPTIONS,
getWritableComponentTypeMetadata,
type ComponentEditorOptions,
type ComponentType
} from '../Core/ComponentStorage/ComponentTypeUtils';
/**
* 存储系统类型名称的Symbol键
* Symbol key for storing system type name
*/
export const SYSTEM_TYPE_NAME = Symbol('SystemTypeName');
/**
* 系统类型元数据接口
* System type metadata interface
*/
export type SystemTypeMetadata = {
readonly [SYSTEM_TYPE_NAME]?: string;
readonly __systemMetadata__?: SystemMetadata;
}
/**
* 可写的系统类型元数据
* Writable system type metadata
*/
interface WritableSystemTypeMetadata {
[SYSTEM_TYPE_NAME]?: string;
__systemMetadata__?: SystemMetadata;
}
/**
* 获取系统类型元数据
* Get system type metadata
*/
function getSystemTypeMetadata(systemType: SystemConstructor): SystemTypeMetadata {
return systemType as unknown as SystemTypeMetadata;
}
/**
* 获取可写的系统类型元数据
* Get writable system type metadata
*/
function getWritableSystemTypeMetadata(systemType: SystemConstructor): WritableSystemTypeMetadata {
return systemType as unknown as WritableSystemTypeMetadata;
}
/**
* 系统构造函数类型
* System constructor type
*
* 注意:构造函数参数使用 any[] 是必要的,因为系统可以有各种不同签名的构造函数
* Note: Constructor args use any[] because systems can have various constructor signatures
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
type SystemConstructor<T extends EntitySystem = EntitySystem> = new (...args: any[]) => T;
/**
* 组件装饰器配置选项
* Component decorator options
*/
export type ComponentOptions = {
/** 依赖的其他组件名称列表 | List of required component names */
requires?: string[];
/**
* 编辑器相关选项
* Editor-related options
*/
editor?: ComponentEditorOptions;
}
/**
* 组件类型装饰器
* Component type decorator
*
* 用于为组件类指定固定的类型名称,避免在代码混淆后失效。
* 装饰器执行时会自动注册到 ComponentRegistry使组件可以通过名称反序列化。
*
* Assigns a stable type name to component classes that survives minification.
* The decorator automatically registers to ComponentRegistry, enabling deserialization by name.
*
* @param typeName 组件类型名称 | Component type name
* @param options 组件配置选项 | Component options
* @example
* ```typescript
* @ECSComponent('Position')
* class PositionComponent extends Component {
* x: number = 0;
* y: number = 0;
* }
*
* // 带依赖声明 | With dependency declaration
* @ECSComponent('SpriteAnimator', { requires: ['Sprite'] })
* class SpriteAnimatorComponent extends Component {
* // ...
* }
* ```
*/
export function ECSComponent(typeName: string, options?: ComponentOptions) {
return function <T extends ComponentType<Component>>(target: T): T {
if (!typeName || typeof typeName !== 'string') {
throw new Error('ECSComponent装饰器必须提供有效的类型名称');
}
// 获取可写的元数据对象
// Get writable metadata object
const metadata = getWritableComponentTypeMetadata(target);
// 在构造函数上存储类型名称
// Store type name on constructor
metadata[COMPONENT_TYPE_NAME] = typeName;
// 存储依赖关系
// Store dependencies
if (options?.requires) {
metadata[COMPONENT_DEPENDENCIES] = options.requires;
}
// 存储编辑器选项
// Store editor options
if (options?.editor) {
metadata[COMPONENT_EDITOR_OPTIONS] = options.editor;
}
// 自动注册到全局 ComponentRegistry使组件可以通过名称查找
// Auto-register to GlobalComponentRegistry, enabling lookup by name
GlobalComponentRegistry.register(target);
return target;
};
}
/**
* System 元数据配置
* System metadata configuration
*/
export type SystemMetadata = {
/**
* 更新顺序数值越小越先执行默认0
* Update order (lower values execute first, default 0)
*/
updateOrder?: number;
/**
* 是否默认启用默认true
* Whether enabled by default (default true)
*/
enabled?: boolean;
/**
* 是否在编辑模式下运行(默认 true
* Whether to run in edit mode (default true)
*
* 默认情况下,所有系统在编辑模式下都会运行。
* 当设置为 false 时,此系统在编辑模式(非 Play 状态)下不会执行。
* 适用于物理系统、AI 系统等只应在游戏运行时执行的系统。
*
* By default, all systems run in edit mode.
* When set to false, this system will NOT execute during edit mode
* (when not playing). Useful for physics, AI, and other systems
* that should only run during gameplay.
*/
runInEditMode?: boolean;
}
/**
* 系统类型装饰器
* System type decorator
*
* 用于为系统类指定固定的类型名称,避免在代码混淆后失效。
* Assigns a stable type name to system classes that survives minification.
*
* @param typeName 系统类型名称 | System type name
* @param metadata 系统元数据配置 | System metadata configuration
* @example
* ```typescript
* @ECSSystem('Movement')
* class MovementSystem extends EntitySystem {
* protected process(entities: Entity[]): void {
* // 系统逻辑
* }
* }
*
* @ECSSystem('Physics', { updateOrder: 10 })
* class PhysicsSystem extends EntitySystem {
* // ...
* }
* ```
*/
export function ECSSystem(typeName: string, metadata?: SystemMetadata) {
return function <T extends SystemConstructor>(target: T): T {
if (!typeName || typeof typeName !== 'string') {
throw new Error('ECSSystem装饰器必须提供有效的类型名称');
}
// 获取可写的元数据对象
// Get writable metadata object
const meta = getWritableSystemTypeMetadata(target);
// 在构造函数上存储类型名称
// Store type name on constructor
meta[SYSTEM_TYPE_NAME] = typeName;
// 存储元数据
// Store metadata
if (metadata) {
meta.__systemMetadata__ = metadata;
}
return target;
};
}
/**
* 获取 System 的元数据
* Get System metadata
*/
export function getSystemMetadata(systemType: SystemConstructor): SystemMetadata | undefined {
const meta = getSystemTypeMetadata(systemType);
return meta.__systemMetadata__;
}
/**
* 从系统实例获取元数据
* Get metadata from system instance
*
* @param system 系统实例 | System instance
* @returns 系统元数据 | System metadata
*/
export function getSystemInstanceMetadata(system: EntitySystem): SystemMetadata | undefined {
return getSystemMetadata(system.constructor as SystemConstructor);
}
/**
* 获取系统类型的名称,优先使用装饰器指定的名称
* Get system type name, preferring decorator-specified name
*
* @param systemType 系统构造函数 | System constructor
* @returns 系统类型名称 | System type name
*/
export function getSystemTypeName<T extends EntitySystem>(
systemType: SystemConstructor<T>
): string {
const meta = getSystemTypeMetadata(systemType);
const decoratorName = meta[SYSTEM_TYPE_NAME];
if (decoratorName) {
return decoratorName;
}
return systemType.name || 'UnknownSystem';
}
/**
* 从系统实例获取类型名称
* Get type name from system instance
*
* @param system 系统实例 | System instance
* @returns 系统类型名称 | System type name
*/
export function getSystemInstanceTypeName(system: EntitySystem): string {
return getSystemTypeName(system.constructor as SystemConstructor);
}

View File

@@ -0,0 +1,84 @@
// ============================================================================
// Component Type Utilities (from ComponentTypeUtils - no circular deps)
// 组件类型工具(来自 ComponentTypeUtils - 无循环依赖)
// ============================================================================
export {
COMPONENT_TYPE_NAME,
COMPONENT_DEPENDENCIES,
COMPONENT_EDITOR_OPTIONS,
getComponentTypeName,
getComponentInstanceTypeName,
getComponentDependencies,
getComponentEditorOptions,
getComponentInstanceEditorOptions,
isComponentHiddenInInspector,
isComponentInstanceHiddenInInspector,
hasECSComponentDecorator
} from '../Core/ComponentStorage/ComponentTypeUtils';
export type { ComponentType, ComponentEditorOptions } from '../Core/ComponentStorage/ComponentTypeUtils';
// ============================================================================
// Type Decorators (ECSComponent, ECSSystem)
// 类型装饰器
// ============================================================================
export {
ECSComponent,
ECSSystem,
getSystemTypeName,
getSystemInstanceTypeName,
getSystemMetadata,
getSystemInstanceMetadata,
SYSTEM_TYPE_NAME
} from './TypeDecorators';
export type { SystemMetadata, ComponentOptions } from './TypeDecorators';
// ============================================================================
// Entity Reference Decorator
// 实体引用装饰器
// ============================================================================
export {
EntityRef,
getEntityRefMetadata,
hasEntityRef,
isEntityRefProperty,
getEntityRefProperties,
ENTITY_REF_METADATA
} from './EntityRefDecorator';
export type { EntityRefMetadata } from './EntityRefDecorator';
// ============================================================================
// Property Decorator
// 属性装饰器
// ============================================================================
export {
Property,
getPropertyMetadata,
hasPropertyMetadata,
PROPERTY_METADATA
} from './PropertyDecorator';
export type {
PropertyOptions,
PropertyType,
PropertyControl,
PropertyAction,
PropertyAssetType,
EnumOption
} from './PropertyDecorator';
// ============================================================================
// System Scheduling Decorators
// 系统调度装饰器
// ============================================================================
export {
Stage,
Before,
After,
InSet,
getSchedulingMetadata,
hasSchedulingMetadata,
SCHEDULING_METADATA
} from './SystemScheduling';

View File

@@ -0,0 +1,872 @@
import { Component } from './Component';
import { ComponentType, GlobalComponentRegistry } from './Core/ComponentStorage';
import { EEntityLifecyclePolicy } from './Core/EntityLifecyclePolicy';
import { BitMask64Utils, BitMask64Data } from './Utils/BigIntCompatibility';
import { createLogger } from '../Utils/Logger';
import { getComponentInstanceTypeName, getComponentTypeName } from './Decorators';
import { generateGUID } from '../Utils/GUID';
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
*
* @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 function compareEntities(a: Entity, b: Entity): number {
return a.updateOrder - b.updateOrder || a.id - b.id;
}
/**
* @zh 游戏实体类
* @en Game entity class
*
* @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);
*
* // @zh 层级关系使用 HierarchySystem | @en Use HierarchySystem for hierarchy
* const hierarchySystem = scene.getSystem(HierarchySystem);
* hierarchySystem.setParent(childEntity, parentEntity);
* ```
*/
export class Entity {
/**
* @zh Entity专用日志器
* @en Entity logger
*/
private static _logger = createLogger('Entity');
/**
* @zh 实体名称
* @en Entity name
*/
public name: string;
/**
* @zh 实体唯一标识符运行时ID用于快速查找
* @en Unique entity identifier (runtime ID) for fast lookups
*/
public readonly id: number;
/**
* @zh 持久化唯一标识符GUID
* @en Persistent unique identifier (GUID)
*
* @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 存储等优化。
* @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;
/**
* @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;
/**
* @zh 生命周期策略,控制实体在场景切换时的行为
* @en Lifecycle policy controlling entity behavior during scene transitions
*/
private _lifecyclePolicy: EEntityLifecyclePolicy = EEntityLifecyclePolicy.SceneLocal;
/**
* @zh 构造函数
* @en Constructor
*
* @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;
this.id = id;
this.persistentId = persistentId ?? generateGUID();
}
/**
* @zh 获取生命周期策略
* @en Get lifecycle policy
*/
public get lifecyclePolicy(): EEntityLifecyclePolicy {
return this._lifecyclePolicy;
}
/**
* @zh 检查实体是否为持久化实体(跨场景保留)
* @en Check if entity is persistent (survives scene transitions)
*/
public get isPersistent(): boolean {
return this._lifecyclePolicy === EEntityLifecyclePolicy.Persistent;
}
/**
* @zh 获取实体句柄
* @en Get entity handle
*
* @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)
*
* @zh 此方法供 Scene 在创建实体时调用
* @en Called by Scene when creating entities
*
* @internal
*/
public setHandle(handle: EntityHandle): void {
this._handle = handle;
}
/**
* @zh 设置实体为持久化(跨场景保留)
* @en Mark entity as persistent (survives scene transitions)
*
* @zh 标记后的实体在场景切换时不会被销毁,会自动迁移到新场景
* @en Persistent entities are automatically migrated to the new scene
*
* @returns @zh this支持链式调用 @en Returns this for chaining
*
* @example
* ```typescript
* const player = scene.createEntity('Player')
* .setPersistent()
* .addComponent(new PlayerComponent());
* ```
*/
public setPersistent(): this {
this._lifecyclePolicy = EEntityLifecyclePolicy.Persistent;
return this;
}
/**
* @zh 设置实体为场景本地(随场景销毁),恢复默认行为
* @en Mark entity as scene-local (destroyed with scene), restores default behavior
*
* @returns @zh this支持链式调用 @en Returns this for chaining
*/
public setSceneLocal(): this {
this._lifecyclePolicy = EEntityLifecyclePolicy.SceneLocal;
return this;
}
/**
* @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)
*
* @zh 此方法供Scene和批量操作使用以提高性能。不应在普通业务逻辑中调用应使用destroy()方法
* @en Used by Scene and batch operations for performance. Should not be called in normal business logic, use destroy() instead
*
* @internal
*/
public setDestroyedState(destroyed: boolean): void {
this._isDestroyed = destroyed;
}
/**
* @zh 获取组件数组(懒加载)
* @en Get component array (lazy-loaded)
*
* @returns @zh 只读的组件数组 @en Readonly component array
*/
public get components(): readonly Component[] {
if (this._componentCache === null) {
this._rebuildComponentCache();
}
return this._componentCache!;
}
/**
* @zh 从存储重建组件缓存
* @en Rebuild component cache from storage
*/
private _rebuildComponentCache(): void {
const components: Component[] = [];
if (!this.scene?.componentStorageManager) {
this._componentCache = components;
return;
}
const mask = this._componentMask;
const registry = this.scene.componentRegistry;
const maxBitIndex = registry.getRegisteredCount();
for (let bitIndex = 0; bitIndex < maxBitIndex; bitIndex++) {
if (BitMask64Utils.getBit(mask, bitIndex)) {
const componentType = registry.getTypeByBitIndex(bitIndex);
if (componentType) {
const component = this.scene.componentStorageManager.getComponent(this.id, componentType);
if (component) {
components.push(component);
}
}
}
}
this._componentCache = components;
}
/**
* 获取活跃状态
*
* @returns 如果实体处于活跃状态则返回true
*/
public get active(): boolean {
return this._active;
}
/**
* 设置活跃状态
*
* @param value - 新的活跃状态
*/
public set active(value: boolean) {
if (this._active !== value) {
this._active = value;
this.onActiveChanged();
}
}
/**
* @zh 获取实体标签
* @en Get entity tag
*
* @returns @zh 实体的数字标签 @en Entity's numeric tag
*/
public get tag(): number {
return this._tag;
}
/**
* @zh 设置实体标签
* @en Set entity tag
*
* @param value - @zh 新的标签值 @en New tag value
*/
public set tag(value: number) {
this._tag = value;
}
/**
* @zh 获取启用状态
* @en Get enabled state
*
* @returns @zh 如果实体已启用则返回true @en Returns true if entity is enabled
*/
public get enabled(): boolean {
return this._enabled;
}
/**
* @zh 设置启用状态
* @en Set enabled state
*
* @param value - @zh 新的启用状态 @en New enabled state
*/
public set enabled(value: boolean) {
this._enabled = value;
}
/**
* @zh 获取更新顺序
* @en Get update order
*
* @returns @zh 实体的更新顺序值 @en Entity's update order value
*/
public get updateOrder(): number {
return this._updateOrder;
}
/**
* @zh 设置更新顺序
* @en Set update order
*
* @param value - @zh 新的更新顺序值 @en New update order value
*/
public set updateOrder(value: number) {
this._updateOrder = value;
}
/**
* @zh 获取组件位掩码
* @en Get component bitmask
*
* @returns @zh 实体的组件位掩码 @en Entity's component bitmask
*/
public get componentMask(): BitMask64Data {
return this._componentMask;
}
/**
* @zh 创建并添加组件
* @en Create and add component
*
* @param componentType - @zh 组件类型构造函数 @en Component type constructor
* @param args - @zh 组件构造函数参数 @en Component constructor arguments
* @returns @zh 创建的组件实例 @en Created component instance
*
* @example
* ```typescript
* const position = entity.createComponent(Position, 100, 200);
* const health = entity.createComponent(Health, 100);
* ```
*/
public createComponent<T extends Component>(
componentType: ComponentType<T>,
...args: ConstructorParameters<ComponentType<T>>
): T {
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
const component = new componentType(...args);
return this.addComponent(component);
}
private addComponentInternal<T extends Component>(component: T): T {
const componentType = component.constructor as ComponentType<T>;
const registry = this.scene?.componentRegistry ?? GlobalComponentRegistry;
const componentMask = registry.getBitMask(componentType);
BitMask64Utils.orInPlace(this._componentMask, componentMask);
this._componentCache = null;
return component;
}
private notifyQuerySystems(changedComponentType?: ComponentType): void {
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 - @zh 要添加的组件实例 @en Component instance to add
* @returns @zh 添加的组件实例 @en Added component instance
* @throws @zh 如果实体已存在该类型的组件 @en If entity already has this component type
*
* @example
* ```typescript
* const position = new Position(100, 200);
* entity.addComponent(position);
* ```
*/
public addComponent<T extends Component>(component: T): T {
const componentType = component.constructor as ComponentType<T>;
if (!this.scene) {
throw new Error(
'Entity must be added to Scene before adding components. Use scene.createEntity() instead of new Entity()'
);
}
if (!this.scene.componentStorageManager) {
throw new Error('Scene does not have componentStorageManager');
}
if (this.hasComponent(componentType)) {
throw new Error(`Entity ${this.name} already has component ${getComponentTypeName(componentType)}`);
}
this.addComponentInternal(component);
this.scene.componentStorageManager.addComponent(this.id, component);
component.entityId = this.id;
this.scene.referenceTracker?.registerEntityScene(this.id, this.scene);
if (this.scene.isEditorMode) {
this.scene.queueDeferredComponentCallback(() => component.onAddedToEntity());
} else {
component.onAddedToEntity();
}
if (this.scene.eventSystem) {
this.scene.eventSystem.emitSync('component:added', {
timestamp: Date.now(),
source: 'Entity',
entityId: this.id,
entityName: this.name,
entityTag: this.tag?.toString(),
componentType: getComponentTypeName(componentType),
component: component
});
}
this.notifyQuerySystems(componentType);
return component;
}
/**
* @zh 获取指定类型的组件
* @en Get component of specified type
*
* @param type - @zh 组件类型构造函数 @en Component type constructor
* @returns @zh 组件实例如果不存在则返回null @en Component instance, or null if not found
*
* @example
* ```typescript
* const position = entity.getComponent(Position);
* if (position) {
* position.x += 10;
* position.y += 20;
* }
* ```
*/
public getComponent<T extends Component>(type: ComponentType<T>): T | null {
// 快速检查:位掩码
if (!this.hasComponent(type)) {
return null;
}
// 从Scene存储获取
if (!this.scene?.componentStorageManager) {
return null;
}
const component = this.scene.componentStorageManager.getComponent(this.id, type);
return component as T | null;
}
/**
* @zh 检查实体是否拥有指定类型的组件
* @en Check if entity has component of specified type
*
* @param type - @zh 组件类型构造函数 @en Component type constructor
* @returns @zh 如果实体拥有该组件返回true @en Returns true if entity has the component
*
* @example
* ```typescript
* if (entity.hasComponent(Position)) {
* const position = entity.getComponent(Position)!;
* position.x += 10;
* }
* ```
*/
public hasComponent<T extends Component>(type: ComponentType<T>): boolean {
const registry = this.scene?.componentRegistry ?? GlobalComponentRegistry;
if (!registry.isRegistered(type)) {
return false;
}
const mask = registry.getBitMask(type);
return BitMask64Utils.hasAny(this._componentMask, mask);
}
/**
* @zh 获取或创建指定类型的组件
* @en Get or create component of specified type
*
* @zh 如果组件已存在则返回现有组件,否则创建新组件并添加到实体
* @en Returns existing component if present, otherwise creates and adds new component
*
* @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
* // @zh 确保实体拥有Position组件 | @en Ensure entity has Position component
* const position = entity.getOrCreateComponent(Position, 0, 0);
* position.x = 100;
* ```
*/
public getOrCreateComponent<T extends Component>(
type: ComponentType<T>,
...args: ConstructorParameters<ComponentType<T>>
): T {
let component = this.getComponent(type);
if (!component) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
component = this.createComponent(type, ...args);
}
return component;
}
/**
* @zh 标记组件为已修改
* @en Mark component(s) as modified
*
* @zh 便捷方法,自动从场景获取当前 epoch 并标记组件。用于帧级变更检测系统。
* @en Convenience method that auto-gets epoch from scene and marks components. Used for frame-level change detection system.
*
* @param components - @zh 要标记的组件 @en Components to mark
*
* @example
* ```typescript
* const pos = entity.getComponent(Position)!;
* pos.x = 100;
* entity.markDirty(pos);
*
* // @zh 或者标记多个组件 | @en Or mark multiple components
* entity.markDirty(pos, vel);
* ```
*/
public markDirty(...components: Component[]): void {
if (!this.scene) {
return;
}
const epoch = this.scene.epochManager.current;
for (const component of components) {
component.markDirty(epoch);
}
}
/**
* @zh 移除指定的组件
* @en Remove specified component
*
* @param component - @zh 要移除的组件实例 @en Component instance to remove
*/
public removeComponent(component: Component): void {
const componentType = component.constructor as ComponentType;
const registry = this.scene?.componentRegistry ?? GlobalComponentRegistry;
if (!registry.isRegistered(componentType)) {
return;
}
const bitIndex = registry.getBitIndex(componentType);
BitMask64Utils.clearBit(this._componentMask, bitIndex);
this._componentCache = null;
this.scene?.componentStorageManager?.removeComponent(this.id, componentType);
this.scene?.referenceTracker?.clearComponentReferences(component);
component.onRemovedFromEntity?.();
component.entityId = null;
if (this.scene?.eventSystem) {
this.scene.eventSystem.emitSync('component:removed', {
timestamp: Date.now(),
source: 'Entity',
entityId: this.id,
entityName: this.name,
entityTag: this.tag?.toString(),
componentType: getComponentTypeName(componentType),
component: component
});
}
this.notifyQuerySystems(componentType);
}
/**
* @zh 移除指定类型的组件
* @en Remove component by type
*
* @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);
if (component) {
this.removeComponent(component);
return component;
}
return null;
}
/**
* @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;
this.scene?.componentStorageManager?.removeComponent(this.id, componentType);
component.onRemovedFromEntity();
}
this.notifyQuerySystems();
}
/**
* @zh 批量添加组件
* @en Add multiple components
*
* @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[] = [];
for (const component of components) {
try {
addedComponents.push(this.addComponent(component));
} catch (error) {
Entity._logger.warn(`添加组件失败 ${getComponentInstanceTypeName(component)}:`, error);
}
}
return addedComponents;
}
/**
* 批量移除组件类型
*
* @param componentTypes - 要移除的组件类型数组
* @returns 被移除的组件数组
*/
public removeComponentsByTypes<T extends Component>(componentTypes: ComponentType<T>[]): (T | null)[] {
const removedComponents: (T | null)[] = [];
for (const componentType of componentTypes) {
removedComponents.push(this.removeComponentByType(componentType));
}
return removedComponents;
}
/**
* 获取所有指定类型的组件
*
* @param type - 组件类型
* @returns 组件实例数组
*/
public getComponents<T extends Component>(type: ComponentType<T>): T[] {
const result: T[] = [];
for (const component of this.components) {
if (component instanceof type) {
result.push(component as T);
}
}
return result;
}
/**
* 获取指定基类的组件(支持继承查找)
*
* 与 getComponent() 不同,此方法使用 instanceof 检查,支持子类查找。
* 性能比位掩码查询稍慢,但支持继承层次结构。
*
* @param baseType - 组件基类类型
* @returns 第一个匹配的组件实例,如果不存在则返回 null
*
* @example
* ```typescript
* // 查找 CompositeNodeComponent 或其子类
* const composite = entity.getComponentByType(CompositeNodeComponent);
* if (composite) {
* // composite 可能是 SequenceNode, SelectorNode 等
* }
* ```
*/
public getComponentByType<T extends Component>(baseType: ComponentType<T>): T | null {
for (const component of this.components) {
if (component instanceof baseType) {
return component as T;
}
}
return null;
}
/**
* 活跃状态改变时的回调
*/
private onActiveChanged(): void {
for (const component of this.components) {
if ('onActiveChanged' in component && typeof component.onActiveChanged === 'function') {
(component as IActiveChangeable).onActiveChanged();
}
}
if (this.scene && this.scene.eventSystem) {
this.scene.eventSystem.emitSync('entity:activeChanged', {
entity: this,
active: this._active
});
}
}
/**
* @zh 销毁实体
* @en Destroy entity
*
* @zh 移除所有组件并标记为已销毁。层级关系的清理由 HierarchySystem 处理。
* @en Removes all components and marks as destroyed. Hierarchy cleanup is handled by HierarchySystem.
*/
public destroy(): void {
if (this._isDestroyed) {
return;
}
this._isDestroyed = true;
if (this.scene && this.scene.referenceTracker) {
this.scene.referenceTracker.clearReferencesTo(this.id);
this.scene.referenceTracker.unregisterEntityScene(this.id);
}
this.removeAllComponents();
if (this.scene) {
if (this.scene.querySystem) {
this.scene.querySystem.removeEntity(this);
}
if (this.scene.entities) {
this.scene.entities.remove(this);
}
}
}
/**
* @zh 比较实体优先级
* @en Compare entity priority
*
* @param other - @zh 另一个实体 @en Another entity
* @returns @zh 比较结果 @en Comparison result
*/
public compareTo(other: Entity): number {
return compareEntities(this, other);
}
/**
* 获取实体的字符串表示
*
* @returns 实体的字符串描述
*/
public toString(): string {
return `Entity[${this.name}:${this.id}:${this.persistentId.slice(0, 8)}]`;
}
/**
* 获取实体的调试信息(包含组件缓存信息)
*
* @returns 包含实体详细信息的对象
*/
public getDebugInfo(): {
name: string;
id: number;
persistentId: string;
enabled: boolean;
active: boolean;
destroyed: boolean;
componentCount: number;
componentTypes: string[];
componentMask: string;
cacheBuilt: boolean;
} {
return {
name: this.name,
id: this.id,
persistentId: this.persistentId,
enabled: this._enabled,
active: this._active,
destroyed: this._isDestroyed,
componentCount: this.components.length,
componentTypes: this.components.map((c) => getComponentInstanceTypeName(c)),
componentMask: BitMask64Utils.toString(this._componentMask, 2),
cacheBuilt: this._componentCache !== null
};
}
}

View File

@@ -0,0 +1,95 @@
/**
* 实体标签常量
*
* 用于标识特殊类型的实体,如文件夹、摄像机等。
* 使用位掩码实现,支持多标签组合。
*
* @example
* ```typescript
* // 创建文件夹实体
* entity.tag = EntityTags.FOLDER;
*
* // 检查是否是文件夹
* if ((entity.tag & EntityTags.FOLDER) !== 0) {
* // 是文件夹
* }
*
* // 组合多个标签
* entity.tag = EntityTags.FOLDER | EntityTags.HIDDEN;
* ```
*/
export const EntityTags = {
/** 无标签 */
NONE: 0x0000,
/** 文件夹实体 - 用于场景层级中的组织分类 */
FOLDER: 0x1000,
/** 隐藏实体 - 在编辑器层级中不显示 */
HIDDEN: 0x2000,
/** 锁定实体 - 在编辑器中不可选择/编辑 */
LOCKED: 0x4000,
/** 编辑器专用实体 - 仅在编辑器中存在,不导出到运行时 */
EDITOR_ONLY: 0x8000,
/** 预制件实例 */
PREFAB_INSTANCE: 0x0100,
/** 预制件根节点 */
PREFAB_ROOT: 0x0200
} as const;
export type EntityTagValue = (typeof EntityTags)[keyof typeof EntityTags];
/**
* 检查实体是否具有指定标签
*
* @param entityTag - 实体的 tag 值
* @param tag - 要检查的标签
*/
export function hasEntityTag(entityTag: number, tag: EntityTagValue): boolean {
return (entityTag & tag) !== 0;
}
/**
* 添加标签到实体
*
* @param entityTag - 当前 tag 值
* @param tag - 要添加的标签
*/
export function addEntityTag(entityTag: number, tag: EntityTagValue): number {
return entityTag | tag;
}
/**
* 从实体移除标签
*
* @param entityTag - 当前 tag 值
* @param tag - 要移除的标签
*/
export function removeEntityTag(entityTag: number, tag: EntityTagValue): number {
return entityTag & ~tag;
}
/**
* 检查实体是否是文件夹
*/
export function isFolder(entityTag: number): boolean {
return hasEntityTag(entityTag, EntityTags.FOLDER);
}
/**
* 检查实体是否隐藏
*/
export function isHidden(entityTag: number): boolean {
return hasEntityTag(entityTag, EntityTags.HIDDEN);
}
/**
* 检查实体是否锁定
*/
export function isLocked(entityTag: number): boolean {
return hasEntityTag(entityTag, EntityTags.LOCKED);
}

View File

@@ -0,0 +1,398 @@
import { Entity } from './Entity';
import { EntityList } from './Utils/EntityList';
import { IdentifierPool } from './Utils/IdentifierPool';
import { EntitySystem } from './Systems/EntitySystem';
import { ComponentStorageManager, ComponentType } from './Core/ComponentStorage';
import type { IComponentRegistry } from './Core/ComponentStorage';
import { QuerySystem } from './Core/QuerySystem';
import { TypeSafeEventSystem } from './Core/EventSystem';
import { EpochManager } from './Core/EpochManager';
import type { ReferenceTracker } from './Core/ReferenceTracker';
import type { ServiceContainer, ServiceType } from '../Core/ServiceContainer';
import type { TypedQueryBuilder } from './Core/Query/TypedQuery';
import type { SceneSerializationOptions, SceneDeserializationOptions } from './Serialization/SceneSerializer';
import type { IncrementalSnapshot, IncrementalSerializationOptions } from './Serialization/IncrementalSerializer';
/**
* 场景接口定义
*
* 定义场景应该实现的核心功能和属性,使用接口而非继承提供更灵活的实现方式。
*/
export type IScene = {
/**
* 场景名称
*/
name: string;
/**
* 场景自定义数据
*
* 用于存储场景级别的配置和状态数据,例如:
* - 天气状态
* - 时间设置
* - 游戏难度
* - 音频配置
* - 关卡检查点
*
* @example
* ```typescript
* scene.sceneData.set('weather', 'rainy');
* scene.sceneData.set('timeOfDay', 14.5);
* scene.sceneData.set('checkpoint', { x: 100, y: 200 });
* ```
*/
readonly sceneData: Map<string, any>;
/**
* 场景中的实体集合
*/
readonly entities: EntityList;
/**
* 标识符池
*/
readonly identifierPool: IdentifierPool;
/**
* 组件存储管理器
*/
readonly componentStorageManager: ComponentStorageManager;
/**
* 组件注册表
* Component Registry
*
* Each scene has its own registry for component type isolation.
* Clones from GlobalComponentRegistry on creation.
* 每个场景有自己的组件类型注册表以实现隔离。
* 创建时从 GlobalComponentRegistry 克隆。
*/
readonly componentRegistry: IComponentRegistry;
/**
* 查询系统
*/
readonly querySystem: QuerySystem;
/**
* 事件系统
*/
readonly eventSystem: TypeSafeEventSystem;
/**
* 引用追踪器
*/
readonly referenceTracker: ReferenceTracker;
/**
* Epoch 管理器
*
* 用于帧级变更检测,追踪组件修改。
*
* Epoch manager.
* Used for frame-level change detection, tracking component modifications.
*/
readonly epochManager: EpochManager;
/**
* 服务容器
*
* 场景级别的依赖注入容器,用于管理服务的生命周期。
*/
readonly services: ServiceContainer;
/**
* 编辑器模式标志
*
* 当为 true 时,组件的生命周期回调(如 onAddedToEntity会被延迟
* 直到调用 begin() 开始运行场景时才会触发。
*
* Editor mode flag.
* When true, component lifecycle callbacks (like onAddedToEntity) are deferred
* until begin() is called to start running the scene.
*/
isEditorMode: boolean;
/**
* 获取系统列表
*/
readonly systems: EntitySystem[];
/**
* 初始化场景
*/
initialize(): void;
/**
* 场景开始运行时的回调
*/
onStart(): void;
/**
* 场景卸载时的回调
*/
unload(): void;
/**
* 添加延迟的组件生命周期回调
*
* Queue a deferred component lifecycle callback.
*
* @param callback 要延迟执行的回调 | The callback to defer
*/
queueDeferredComponentCallback(callback: () => void): void;
/**
* 开始场景
*/
begin(): void;
/**
* 结束场景
*/
end(): void;
/**
* 更新场景
*/
update(): void;
/**
* 创建实体
*/
createEntity(name: string): Entity;
/**
* 清除所有EntitySystem的实体缓存
* Clear all EntitySystem entity caches
*/
clearSystemEntityCaches(): void;
/**
* 通知相关系统实体的组件发生了变化
*
* 当组件被添加或移除时调用,立即通知相关系统检查该实体是否匹配,
* 并触发 onAdded/onRemoved 回调。通过组件ID索引优化只通知关心该组件的系统。
*
* Notify relevant systems that an entity's components have changed.
* Called when a component is added or removed, immediately notifying
* relevant systems to check if the entity matches and trigger onAdded/onRemoved callbacks.
* Optimized via component ID indexing to only notify systems that care about the changed component.
*
* @param entity 组件发生变化的实体 | The entity whose components changed
* @param changedComponentType 变化的组件类型(可选) | The changed component type (optional)
*/
notifyEntityComponentChanged(entity: Entity, changedComponentType?: ComponentType): void;
/**
* 添加实体
*/
addEntity(entity: Entity, deferCacheClear?: boolean): Entity;
/**
* 批量创建实体
*/
createEntities(count: number, namePrefix?: string): Entity[];
/**
* 销毁所有实体
*/
destroyAllEntities(): void;
/**
* 查找实体
*/
findEntity(name: string): Entity | null;
/**
* 根据标签查找实体
*/
findEntitiesByTag(tag: number): Entity[];
/**
* 添加实体处理器
*/
addEntityProcessor(processor: EntitySystem): EntitySystem;
/**
* 移除实体处理器
*/
removeEntityProcessor(processor: EntitySystem): void;
/**
* 获取实体处理器
*/
getEntityProcessor<T extends EntitySystem>(type: new (...args: any[]) => T): T | null;
/**
* 根据ID查找实体
*/
findEntityById(id: number): Entity | null;
/**
* 根据名称查找实体
*/
getEntityByName(name: string): Entity | null;
/**
* 根据标签查找实体
*/
getEntitiesByTag(tag: number): Entity[];
/**
* 批量销毁实体
*/
destroyEntities(entities: Entity[]): void;
/**
* 查询拥有所有指定组件的实体
*/
queryAll(...componentTypes: any[]): { entities: readonly Entity[] };
/**
* 查询拥有任意一个指定组件的实体
*/
queryAny(...componentTypes: any[]): { entities: readonly Entity[] };
/**
* 查询不包含指定组件的实体
*/
queryNone(...componentTypes: any[]): { entities: readonly Entity[] };
/**
* 创建类型安全的查询构建器
*/
query(): TypedQueryBuilder;
/**
* 通过类型获取System实例
*/
getSystem<T extends EntitySystem>(systemType: ServiceType<T>): T | null;
/**
* 批量注册EntitySystem到场景
*/
registerSystems(systemTypes: Array<ServiceType<EntitySystem>>): EntitySystem[];
/**
* 添加系统到场景
*/
addSystem(system: EntitySystem): EntitySystem;
/**
* 从场景中删除系统
*/
removeSystem(system: EntitySystem): void;
/**
* 获取场景统计信息
*/
getStats(): {
entityCount: number;
processorCount: number;
componentStorageStats: Map<string, any>;
};
/**
* 获取场景的调试信息
*/
getDebugInfo(): {
name: string;
entityCount: number;
processorCount: number;
isRunning: boolean;
entities: Array<{
name: string;
id: number;
componentCount: number;
componentTypes: string[];
}>;
processors: Array<{
name: string;
updateOrder: number;
entityCount: number;
}>;
componentStats: Map<string, any>;
};
/**
* 序列化场景
*/
serialize(options?: SceneSerializationOptions): string | Uint8Array;
/**
* 反序列化场景
*/
deserialize(saveData: string | Uint8Array, options?: SceneDeserializationOptions): void;
/**
* 创建增量序列化的基础快照
*/
createIncrementalSnapshot(options?: IncrementalSerializationOptions): void;
/**
* 增量序列化场景
*/
serializeIncremental(options?: IncrementalSerializationOptions): IncrementalSnapshot;
/**
* 应用增量变更到场景
*/
applyIncremental(
incremental: IncrementalSnapshot | string | Uint8Array,
componentRegistry?: Map<string, any>
): void;
/**
* 更新增量快照基准
*/
updateIncrementalSnapshot(options?: IncrementalSerializationOptions): void;
/**
* 清除增量快照
*/
clearIncrementalSnapshot(): void;
/**
* 检查是否有增量快照
*/
hasIncrementalSnapshot(): boolean;
}
/**
* 场景工厂接口
*/
export type ISceneFactory<T extends IScene> = {
/**
* 创建场景实例
*/
createScene(): T;
}
/**
* @zh 场景配置接口
* @en Scene configuration interface
*/
export type ISceneConfig = {
/**
* @zh 场景名称
* @en Scene name
*/
name?: string;
/**
* @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;
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,308 @@
import { IScene } from './IScene';
import { Scene } from './Scene';
import { Entity } from './Entity';
import { ECSFluentAPI, createECSAPI } from './Core/FluentAPI';
import { Time } from '../Utils/Time';
import { createLogger } from '../Utils/Logger';
import type { IService } from '../Core/ServiceContainer';
import { World } from './World';
import { PerformanceMonitor } from '../Utils/PerformanceMonitor';
/**
* 单场景管理器
*
* 适用场景:
* - 单人游戏
* - 简单场景切换
* - 不需要多World隔离的项目
*
* 特点:
* - 轻量级,零额外开销
* - 简单直观的API
* - 支持延迟场景切换
* - 自动管理ECS API
*
* @example
* ```typescript
* // 初始化Core
* Core.create({ debug: true });
*
* // 创建场景管理器
* const sceneManager = new SceneManager();
*
* // 设置场景
* class GameScene extends Scene {
* initialize() {
* const player = this.createEntity('Player');
* player.addComponent(new Transform(100, 100));
* }
* }
*
* sceneManager.setScene(new GameScene());
*
* // 游戏循环
* function gameLoop(deltaTime: number) {
* Core.update(deltaTime); // 更新全局服务
* sceneManager.update(); // 更新场景
* }
*
* // 延迟切换场景(下一帧生效)
* sceneManager.loadScene(new MenuScene());
* ```
*/
export class SceneManager implements IService {
/**
* 内部默认World
*/
private _defaultWorld: World;
/**
* 待切换的下一个场景(延迟切换用)
*/
private _nextScene: IScene | null = null;
/**
* ECS流式API
*/
private _ecsAPI: ECSFluentAPI | null = null;
/**
* 日志器
*/
private _logger = createLogger('SceneManager');
/**
* 场景切换回调函数
*/
private _onSceneChangedCallback?: () => void;
/**
* 性能监控器(从 Core 注入)
*/
private _performanceMonitor: PerformanceMonitor | null = null;
/**
* 待迁移的持久化实体
*
* Pending persistent entities for migration.
*/
private _pendingPersistentEntities: Entity[] = [];
/**
* 默认场景ID
*/
private static readonly DEFAULT_SCENE_ID = '__main__';
constructor(performanceMonitor?: PerformanceMonitor) {
this._defaultWorld = new World({ name: '__default__' });
this._defaultWorld.start();
this._performanceMonitor = performanceMonitor || null;
}
/**
* 设置场景切换回调
*
* @param callback 场景切换时的回调函数
* @internal
*/
public setSceneChangedCallback(callback: () => void): void {
this._onSceneChangedCallback = callback;
}
/**
* 设置当前场景(立即切换)
*
* 会自动处理旧场景的结束和新场景的初始化。
* 持久化实体会自动迁移到新场景。
*
* Set current scene (immediate transition).
* Automatically handles old scene cleanup and new scene initialization.
* Persistent entities are automatically migrated to the new scene.
*
* @param scene - 要设置的场景实例 | Scene instance to set
* @returns 返回设置的场景实例,便于链式调用 | Returns the scene for chaining
*
* @example
* ```typescript
* const gameScene = sceneManager.setScene(new GameScene());
* console.log(gameScene.name);
* ```
*/
public setScene<T extends IScene>(scene: T): T {
// 从当前场景提取持久化实体
const currentScene = this.currentScene;
if (currentScene && currentScene instanceof Scene) {
this._pendingPersistentEntities = currentScene.extractPersistentEntities();
if (this._pendingPersistentEntities.length > 0) {
this._logger.debug(
`Extracted ${this._pendingPersistentEntities.length} persistent entities for migration`
);
}
}
// 移除旧场景
this._defaultWorld.removeAllScenes();
// 注册全局 PerformanceMonitor 到 Scene 的 ServiceContainer
if (this._performanceMonitor) {
scene.services.registerInstance(PerformanceMonitor, this._performanceMonitor);
}
// 通过 World 创建新场景
this._defaultWorld.createScene(SceneManager.DEFAULT_SCENE_ID, scene);
this._defaultWorld.setSceneActive(SceneManager.DEFAULT_SCENE_ID, true);
// 迁移持久化实体到新场景
if (this._pendingPersistentEntities.length > 0 && scene instanceof Scene) {
scene.receiveMigratedEntities(this._pendingPersistentEntities);
this._logger.debug(
`Migrated ${this._pendingPersistentEntities.length} persistent entities to new scene`
);
this._pendingPersistentEntities = [];
}
// 重建ECS API
if (scene.querySystem && scene.eventSystem) {
this._ecsAPI = createECSAPI(scene, scene.querySystem, scene.eventSystem);
} else {
this._ecsAPI = null;
}
// 触发场景切换回调
Time.sceneChanged();
// 通知调试管理器(通过回调)
if (this._onSceneChangedCallback) {
this._onSceneChangedCallback();
}
this._logger.info(`Scene changed to: ${scene.name}`);
return scene;
}
/**
* 延迟加载场景(下一帧切换)
*
* 场景不会立即切换,而是在下一次调用 update() 时切换。
* 这对于避免在当前帧的中途切换场景很有用。
*
* @param scene - 要加载的场景实例
*
* @example
* ```typescript
* // 在某个System中触发场景切换
* class GameOverSystem extends EntitySystem {
* process(entities: readonly Entity[]) {
* if (playerHealth <= 0) {
* sceneManager.loadScene(new GameOverScene());
* // 当前帧继续执行,场景将在下一帧切换
* }
* }
* }
* ```
*/
public loadScene<T extends IScene>(scene: T): void {
this._nextScene = scene;
this._logger.info(`Scheduled scene load: ${scene.name}`);
}
/**
* 获取当前活跃的场景
*
* @returns 当前场景实例如果没有场景则返回null
*/
public get currentScene(): IScene | null {
return this._defaultWorld.getScene(SceneManager.DEFAULT_SCENE_ID);
}
/**
* 获取ECS流式API
*
* 提供便捷的实体查询、事件发射等功能。
*
* @returns ECS API实例如果当前没有场景则返回null
*
* @example
* ```typescript
* const api = sceneManager.api;
* if (api) {
* // 查询所有敌人
* const enemies = api.find(Enemy, Transform);
*
* // 发射事件
* api.emit('game:start', { level: 1 });
* }
* ```
*/
public get api(): ECSFluentAPI | null {
return this._ecsAPI;
}
/**
* 更新场景
*
* 应该在每帧的游戏循环中调用。
* 会自动处理延迟场景切换。
*
* @example
* ```typescript
* function gameLoop(deltaTime: number) {
* Core.update(deltaTime);
* sceneManager.update(); // 每帧调用
* }
* ```
*/
public update(): void {
// 处理延迟场景切换
if (this._nextScene) {
this.setScene(this._nextScene);
this._nextScene = null;
}
// 通过 World 统一更新
this._defaultWorld.updateGlobalSystems();
this._defaultWorld.updateScenes();
}
/**
* 销毁场景管理器
*
* 会自动结束当前场景并清理所有资源。
* 通常在应用程序关闭时调用。
*/
public destroy(): void {
this._logger.info('SceneManager destroying');
this._defaultWorld.destroy();
this._nextScene = null;
this._ecsAPI = null;
this._logger.info('SceneManager destroyed');
}
/**
* 检查是否有活跃场景
*
* @returns 如果有活跃场景返回true否则返回false
*/
public get hasScene(): boolean {
return this._defaultWorld.getScene(SceneManager.DEFAULT_SCENE_ID) !== null;
}
/**
* 检查是否有待切换的场景
*
* @returns 如果有待切换场景返回true否则返回false
*/
public get hasPendingScene(): boolean {
return this._nextScene !== null;
}
/**
* 释放资源IService接口
*/
public dispose(): void {
this.destroy();
}
}

View File

@@ -0,0 +1,154 @@
/**
* 组件序列化器
*
* Component serializer for ECS components.
*/
import { Component } from '../Component';
import { ComponentType } from '../Core/ComponentStorage';
import { getComponentTypeName, isEntityRefProperty } from '../Decorators';
import { getSerializationMetadata } from './SerializationDecorators';
import { ValueSerializer, SerializableValue } from './ValueSerializer';
import { createLogger } from '../../Utils/Logger';
import type { Entity } from '../Entity';
import type { SerializationContext, SerializedEntityRef } from './SerializationContext';
const logger = createLogger('ComponentSerializer');
export type { SerializableValue } from './ValueSerializer';
export type SerializedComponent = {
type: string;
version: number;
data: Record<string, SerializableValue>;
}
export class ComponentSerializer {
static serialize(component: Component): SerializedComponent | null {
const metadata = getSerializationMetadata(component);
if (!metadata) return null;
const componentType = component.constructor as ComponentType;
const typeName = metadata.options.typeId || getComponentTypeName(componentType);
const data: Record<string, SerializableValue> = {};
for (const [fieldName, options] of metadata.fields) {
if (metadata.ignoredFields.has(fieldName)) continue;
const fieldKey = typeof fieldName === 'symbol' ? fieldName.toString() : fieldName;
const value = (component as unknown as Record<string | symbol, unknown>)[fieldName];
let serializedValue: SerializableValue;
if (isEntityRefProperty(component, fieldKey)) {
serializedValue = this.serializeEntityRef(value as Entity | null);
} else if (options.serializer) {
serializedValue = options.serializer(value);
} else {
serializedValue = ValueSerializer.serialize(value);
}
data[options.alias || fieldKey] = serializedValue;
}
return { type: typeName, version: metadata.options.version, data };
}
static deserialize(
serializedData: SerializedComponent,
componentRegistry: Map<string, ComponentType>,
context?: SerializationContext
): Component | null {
const componentClass = componentRegistry.get(serializedData.type);
if (!componentClass) {
logger.warn(`Component type not found: ${serializedData.type} | 未找到组件类型: ${serializedData.type}`);
return null;
}
const metadata = getSerializationMetadata(componentClass);
if (!metadata) {
logger.warn(`Component ${serializedData.type} is not serializable | 组件 ${serializedData.type} 不可序列化`);
return null;
}
const component = new componentClass();
for (const [fieldName, options] of metadata.fields) {
const fieldKey = typeof fieldName === 'symbol' ? fieldName.toString() : fieldName;
const key = options.alias || fieldKey;
const serializedValue = serializedData.data[key];
if (serializedValue === undefined) continue;
if (this.isSerializedEntityRef(serializedValue)) {
if (context) {
const ref = serializedValue.__entityRef;
context.registerPendingRef(component, fieldKey, ref.id, ref.guid);
}
(component as unknown as Record<string | symbol, unknown>)[fieldName] = null;
continue;
}
const value = options.deserializer
? options.deserializer(serializedValue)
: ValueSerializer.deserialize(serializedValue);
(component as unknown as Record<string | symbol, unknown>)[fieldName] = value;
}
return component;
}
static serializeComponents(components: Component[]): SerializedComponent[] {
return components
.map(c => this.serialize(c))
.filter((s): s is SerializedComponent => s !== null);
}
static deserializeComponents(
serializedComponents: SerializedComponent[],
componentRegistry: Map<string, ComponentType>,
context?: SerializationContext
): Component[] {
return serializedComponents
.map(s => this.deserialize(s, componentRegistry, context))
.filter((c): c is Component => c !== null);
}
static validateVersion(serializedData: SerializedComponent, expectedVersion: number): boolean {
return serializedData.version === expectedVersion;
}
static getSerializationInfo(component: Component | ComponentType): {
type: string;
version: number;
fields: string[];
ignoredFields: string[];
isSerializable: boolean;
} {
const metadata = getSerializationMetadata(component);
if (!metadata) {
return { type: 'unknown', version: 0, fields: [], ignoredFields: [], isSerializable: false };
}
const componentType = typeof component === 'function'
? component
: (component.constructor as ComponentType);
return {
type: metadata.options.typeId || getComponentTypeName(componentType),
version: metadata.options.version,
fields: Array.from(metadata.fields.keys()).map(k => typeof k === 'symbol' ? k.toString() : k),
ignoredFields: Array.from(metadata.ignoredFields).map(k => typeof k === 'symbol' ? k.toString() : k),
isSerializable: true
};
}
static serializeEntityRef(entity: Entity | null): SerializableValue {
if (!entity) return null;
return { __entityRef: { id: entity.id, guid: entity.persistentId } };
}
static isSerializedEntityRef(value: unknown): value is { __entityRef: SerializedEntityRef } {
return typeof value === 'object' && value !== null && '__entityRef' in value;
}
}

View File

@@ -0,0 +1,305 @@
/**
* 实体序列化器
*
* 负责实体的序列化和反序列化操作
*/
import { Entity } from '../Entity';
import { ComponentType } from '../Core/ComponentStorage';
import { ComponentSerializer, SerializedComponent } from './ComponentSerializer';
import { IScene } from '../IScene';
import { HierarchyComponent } from '../Components/HierarchyComponent';
import { HierarchySystem } from '../Systems/HierarchySystem';
import { SerializationContext } from './SerializationContext';
/**
* 序列化后的实体数据
*/
export type SerializedEntity = {
/**
* 实体ID运行时ID
*
* Runtime ID.
*/
id: number;
/**
* 持久化 GUID
*
* Persistent GUID for cross-session reference resolution.
*/
guid?: string;
/**
* 实体名称
*/
name: string;
/**
* 实体标签
*/
tag: number;
/**
* 激活状态
*/
active: boolean;
/**
* 启用状态
*/
enabled: boolean;
/**
* 更新顺序
*/
updateOrder: number;
/**
* 组件列表
*/
components: SerializedComponent[];
/**
* 子实体列表
*/
children: SerializedEntity[];
/**
* 父实体ID如果有
*/
parentId?: number;
}
/**
* 实体序列化器类
*/
export class EntitySerializer {
/**
* 序列化单个实体
*
* @param entity 要序列化的实体
* @param includeChildren 是否包含子实体默认true
* @param hierarchySystem 层级系统(可选,用于获取层级信息)
* @returns 序列化后的实体数据
*/
public static serialize(
entity: Entity,
includeChildren: boolean = true,
hierarchySystem?: HierarchySystem
): SerializedEntity {
const serializedComponents = ComponentSerializer.serializeComponents(
Array.from(entity.components)
);
const serializedEntity: SerializedEntity = {
id: entity.id,
guid: entity.persistentId,
name: entity.name,
tag: entity.tag,
active: entity.active,
enabled: entity.enabled,
updateOrder: entity.updateOrder,
components: serializedComponents,
children: []
};
// 通过 HierarchyComponent 获取层级信息
const hierarchy = entity.getComponent(HierarchyComponent);
if (hierarchy?.parentId !== null && hierarchy?.parentId !== undefined) {
serializedEntity.parentId = hierarchy.parentId;
}
// 序列化子实体
// 直接使用 HierarchyComponent.childIds 获取子实体
if (includeChildren && hierarchy && hierarchy.childIds.length > 0) {
// 获取场景引用:优先从 hierarchySystem否则从 entity.scene
const scene = hierarchySystem?.scene ?? entity.scene;
if (scene) {
for (const childId of hierarchy.childIds) {
const child = scene.findEntityById(childId);
if (child) {
serializedEntity.children.push(this.serialize(child, true, hierarchySystem));
}
}
}
}
return serializedEntity;
}
/**
* 反序列化实体
*
* Deserialize an entity from serialized data.
*
* @param serializedEntity 序列化的实体数据
* @param componentRegistry 组件类型注册表
* @param idGenerator 实体ID生成器用于生成新ID或保持原ID
* @param preserveIds 是否保持原始ID默认false
* @param scene 目标场景可选用于设置entity.scene以支持添加组件
* @param hierarchySystem 层级系统(可选,用于建立层级关系)
* @param allEntities 所有实体的映射(可选,用于收集所有实体)
* @param context 序列化上下文(可选,用于解析 EntityRef
* @returns 反序列化后的实体
*/
public static deserialize(
serializedEntity: SerializedEntity,
componentRegistry: Map<string, ComponentType>,
idGenerator: () => number,
preserveIds: boolean = false,
scene?: IScene,
hierarchySystem?: HierarchySystem | null,
allEntities?: Map<number, Entity>,
context?: SerializationContext
): Entity {
// 创建实体使用原始ID或新生成的ID保留原始 GUID
const entityId = preserveIds ? serializedEntity.id : idGenerator();
const entity = new Entity(serializedEntity.name, entityId, serializedEntity.guid);
// 将实体添加到收集 Map 中(用于后续添加到场景)
allEntities?.set(entity.id, entity);
// 注册实体到序列化上下文(用于后续解析 EntityRef
if (context) {
context.registerEntity(entity, serializedEntity.id, serializedEntity.guid);
}
// 如果提供了scene先设置entity.scene以支持添加组件
if (scene) {
entity.scene = scene;
}
// 恢复实体属性
entity.tag = serializedEntity.tag;
entity.active = serializedEntity.active;
entity.enabled = serializedEntity.enabled;
entity.updateOrder = serializedEntity.updateOrder;
// 反序列化组件(传入 context 以支持 EntityRef 解析)
const components = ComponentSerializer.deserializeComponents(
serializedEntity.components,
componentRegistry,
context
);
for (const component of components) {
entity.addComponent(component);
}
// 重要:清除 HierarchyComponent 中的旧 ID
// 当 preserveIds=false 时,序列化的 parentId 和 childIds 是旧 ID需要重新建立
// 通过 hierarchySystem.setParent 会正确设置新的 ID
const hierarchy = entity.getComponent(HierarchyComponent);
if (hierarchy) {
hierarchy.parentId = null;
hierarchy.childIds = [];
}
// 反序列化子实体并建立层级关系
for (const childData of serializedEntity.children) {
const childEntity = this.deserialize(
childData,
componentRegistry,
idGenerator,
preserveIds,
scene,
hierarchySystem,
allEntities,
context
);
// 使用 HierarchySystem 建立层级关系
hierarchySystem?.setParent(childEntity, entity);
}
return entity;
}
/**
* 批量序列化实体
*
* @param entities 实体数组
* @param includeChildren 是否包含子实体
* @param hierarchySystem 层级系统(可选,用于获取层级信息)
* @returns 序列化后的实体数据数组
*/
public static serializeEntities(
entities: Entity[],
includeChildren: boolean = true,
hierarchySystem?: HierarchySystem
): SerializedEntity[] {
const result: SerializedEntity[] = [];
for (const entity of entities) {
// 只序列化顶层实体(没有父实体的实体)
// 子实体会在父实体序列化时一并处理
const hierarchy = entity.getComponent(HierarchyComponent);
const bHasParent = hierarchy?.parentId !== null && hierarchy?.parentId !== undefined;
if (!bHasParent || !includeChildren) {
result.push(this.serialize(entity, includeChildren, hierarchySystem));
}
}
return result;
}
/**
* 批量反序列化实体
*
* Deserialize multiple entities from serialized data.
*
* @param serializedEntities 序列化的实体数据数组
* @param componentRegistry 组件类型注册表
* @param idGenerator 实体ID生成器
* @param preserveIds 是否保持原始ID
* @param scene 目标场景可选用于设置entity.scene以支持添加组件
* @param hierarchySystem 层级系统(可选,用于建立层级关系)
* @param context 序列化上下文(可选,用于解析 EntityRef
* @returns 反序列化后的实体数组
*/
public static deserializeEntities(
serializedEntities: SerializedEntity[],
componentRegistry: Map<string, ComponentType>,
idGenerator: () => number,
preserveIds: boolean = false,
scene?: IScene,
hierarchySystem?: HierarchySystem | null,
context?: SerializationContext
): { rootEntities: Entity[]; allEntities: Map<number, Entity> } {
const rootEntities: Entity[] = [];
const allEntities = new Map<number, Entity>();
for (const serialized of serializedEntities) {
const entity = this.deserialize(
serialized,
componentRegistry,
idGenerator,
preserveIds,
scene,
hierarchySystem,
allEntities,
context
);
rootEntities.push(entity);
}
return { rootEntities, allEntities };
}
/**
* 创建实体的深拷贝
*
* @param entity 要拷贝的实体
* @param componentRegistry 组件类型注册表
* @param idGenerator ID生成器
* @returns 拷贝后的新实体
*/
public static clone(
entity: Entity,
componentRegistry: Map<string, ComponentType>,
idGenerator: () => number
): Entity {
const serialized = this.serialize(entity, true);
return this.deserialize(serialized, componentRegistry, idGenerator, false);
}
}

View File

@@ -0,0 +1,832 @@
/**
* @zh 增量序列化器
* @en Incremental Serializer
*
* @zh 提供高性能的增量序列化支持,只序列化变更的数据。
* 适用于网络同步、大场景存档、时间回溯等场景。
* @en Provides high-performance incremental serialization, serializing only changed data.
* Suitable for network sync, large scene archiving, and time rewind scenarios.
*/
import type { IScene } from '../IScene';
import { Entity } from '../Entity';
import { ComponentSerializer, SerializedComponent } from './ComponentSerializer';
import { SerializedEntity } from './EntitySerializer';
import { ComponentType } from '../Core/ComponentStorage';
import { BinarySerializer } from '../../Utils/BinarySerializer';
import { HierarchyComponent } from '../Components/HierarchyComponent';
import { HierarchySystem } from '../Systems/HierarchySystem';
// =============================================================================
// 枚举 | Enums
// =============================================================================
/**
* @zh 变更操作类型
* @en Change operation type
*/
export enum ChangeOperation {
/** @zh 添加新实体 @en Entity added */
EntityAdded = 'entity_added',
/** @zh 删除实体 @en Entity removed */
EntityRemoved = 'entity_removed',
/** @zh 实体属性更新 @en Entity updated */
EntityUpdated = 'entity_updated',
/** @zh 添加组件 @en Component added */
ComponentAdded = 'component_added',
/** @zh 删除组件 @en Component removed */
ComponentRemoved = 'component_removed',
/** @zh 组件数据更新 @en Component updated */
ComponentUpdated = 'component_updated',
/** @zh 场景数据更新 @en Scene data updated */
SceneDataUpdated = 'scene_data_updated'
}
// =============================================================================
// 类型定义 | Type Definitions
// =============================================================================
/**
* @zh 实体变更记录
* @en Entity change record
*/
export interface EntityChange {
/** @zh 操作类型 @en Operation type */
readonly operation: ChangeOperation;
/** @zh 实体ID @en Entity ID */
readonly entityId: number;
/** @zh 实体名称用于Added操作@en Entity name (for Added operation) */
readonly entityName?: string;
/** @zh 实体数据用于Added/Updated操作@en Entity data (for Added/Updated operation) */
readonly entityData?: Partial<SerializedEntity>;
}
/**
* @zh 组件变更记录
* @en Component change record
*/
export interface ComponentChange {
/** @zh 操作类型 @en Operation type */
readonly operation: ChangeOperation;
/** @zh 实体ID @en Entity ID */
readonly entityId: number;
/** @zh 组件类型名称 @en Component type name */
readonly componentType: string;
/** @zh 组件数据用于Added/Updated操作@en Component data (for Added/Updated operation) */
readonly componentData?: SerializedComponent;
}
/**
* @zh 场景数据变更记录
* @en Scene data change record
*/
export interface SceneDataChange {
/** @zh 操作类型 @en Operation type */
readonly operation: ChangeOperation;
/** @zh 变更的键 @en Changed key */
readonly key: string;
/** @zh 新值 @en New value */
readonly value: unknown;
/** @zh 是否删除 @en Whether deleted */
readonly deleted?: boolean;
}
/**
* @zh 增量序列化数据
* @en Incremental snapshot data
*/
export interface IncrementalSnapshot {
/** @zh 快照版本号 @en Snapshot version */
readonly version: number;
/** @zh 时间戳 @en Timestamp */
readonly timestamp: number;
/** @zh 场景名称 @en Scene name */
readonly sceneName: string;
/** @zh 基础版本号(相对于哪个快照的增量)@en Base version (incremental from which snapshot) */
readonly baseVersion: number;
/** @zh 实体变更列表 @en Entity changes list */
readonly entityChanges: EntityChange[];
/** @zh 组件变更列表 @en Component changes list */
readonly componentChanges: ComponentChange[];
/** @zh 场景数据变更列表 @en Scene data changes list */
readonly sceneDataChanges: SceneDataChange[];
}
/**
* @zh 实体快照数据
* @en Entity snapshot data
*/
interface EntitySnapshotData {
readonly name: string;
readonly tag: number;
readonly active: boolean;
readonly enabled: boolean;
readonly updateOrder: number;
readonly parentId?: number;
}
/**
* @zh 场景快照(用于对比)
* @en Scene snapshot (for comparison)
*/
export interface SceneSnapshot {
/** @zh 快照版本号 @en Snapshot version */
readonly version: number;
/** @zh 实体ID集合 @en Entity ID set */
readonly entityIds: Set<number>;
/** @zh 实体数据映射 @en Entity data map */
readonly entities: Map<number, EntitySnapshotData>;
/** @zh 组件数据映射 (entityId -> componentType -> serializedData JSON) @en Component data map */
readonly components: Map<number, Map<string, string>>;
/** @zh 场景自定义数据 @en Scene custom data */
readonly sceneData: Map<string, string>;
}
/**
* @zh 增量序列化格式
* @en Incremental serialization format
*/
export type IncrementalSerializationFormat = 'json' | 'binary';
/**
* @zh 增量快照统计信息
* @en Incremental snapshot statistics
*/
export interface IIncrementalStats {
/** @zh 总变更数 @en Total changes */
readonly totalChanges: number;
/** @zh 实体变更数 @en Entity changes count */
readonly entityChanges: number;
/** @zh 组件变更数 @en Component changes count */
readonly componentChanges: number;
/** @zh 场景数据变更数 @en Scene data changes count */
readonly sceneDataChanges: number;
/** @zh 新增实体数 @en Added entities count */
readonly addedEntities: number;
/** @zh 删除实体数 @en Removed entities count */
readonly removedEntities: number;
/** @zh 更新实体数 @en Updated entities count */
readonly updatedEntities: number;
/** @zh 新增组件数 @en Added components count */
readonly addedComponents: number;
/** @zh 删除组件数 @en Removed components count */
readonly removedComponents: number;
/** @zh 更新组件数 @en Updated components count */
readonly updatedComponents: number;
}
/**
* @zh 增量序列化选项
* @en Incremental serialization options
*/
export interface IncrementalSerializationOptions {
/**
* @zh 实体过滤器 - 只快照符合条件的实体
* @en Entity filter - only snapshot entities that match the condition
*
* @example
* ```typescript
* // 只快照玩家实体
* const snapshot = IncrementalSerializer.createSnapshot(scene, {
* entityFilter: (entity) => entity.tag === PLAYER_TAG
* });
*
* // 只快照有特定组件的实体
* const snapshot = IncrementalSerializer.createSnapshot(scene, {
* entityFilter: (entity) => entity.hasComponent(PlayerMarker)
* });
* ```
*/
entityFilter?: (entity: Entity) => boolean;
/**
* @zh 是否包含组件数据的深度对比默认true
* @en Whether to deep compare component data, default true
*/
deepComponentComparison?: boolean;
/**
* @zh 是否跟踪场景数据变更默认true
* @en Whether to track scene data changes, default true
*/
trackSceneData?: boolean;
/**
* @zh 是否压缩快照默认false
* @en Whether to compress snapshot, default false
*/
compressSnapshot?: boolean;
/**
* @zh 序列化格式,默认 'json'
* @en Serialization format, default 'json'
*/
format?: IncrementalSerializationFormat;
/**
* @zh 是否美化JSON输出仅在format='json'时有效默认false
* @en Whether to prettify JSON output (only for format='json'), default false
*/
pretty?: boolean;
}
// =============================================================================
// 常量 | Constants
// =============================================================================
const DEFAULT_OPTIONS: Required<Omit<IncrementalSerializationOptions, 'entityFilter'>> = {
deepComponentComparison: true,
trackSceneData: true,
compressSnapshot: false,
format: 'json',
pretty: false
};
// =============================================================================
// IncrementalSerializer
// =============================================================================
/**
* @zh 增量序列化器类
* @en Incremental serializer class
*
* @zh 提供场景快照创建、增量计算、应用和序列化功能
* @en Provides scene snapshot creation, incremental computation, application and serialization
*/
export class IncrementalSerializer {
/** @zh 当前快照版本号 @en Current snapshot version */
private static snapshotVersion = 0;
// =========================================================================
// 快照创建 | Snapshot Creation
// =========================================================================
/**
* @zh 创建场景快照
* @en Create scene snapshot
*
* @param scene - @zh 要快照的场景 @en Scene to snapshot
* @param options - @zh 序列化选项 @en Serialization options
* @returns @zh 场景快照对象 @en Scene snapshot object
*/
public static createSnapshot(
scene: IScene,
options?: IncrementalSerializationOptions
): SceneSnapshot {
const opts = { ...DEFAULT_OPTIONS, ...options };
const snapshot: SceneSnapshot = {
version: ++this.snapshotVersion,
entityIds: new Set(),
entities: new Map(),
components: new Map(),
sceneData: new Map()
};
// 快照实体(支持过滤)
for (const entity of scene.entities.buffer) {
// 应用实体过滤器
if (opts.entityFilter && !opts.entityFilter(entity)) {
continue;
}
snapshot.entityIds.add(entity.id);
// 获取层级信息
const hierarchy = entity.getComponent(HierarchyComponent);
const parentId = hierarchy?.parentId;
// 存储实体基本信息
snapshot.entities.set(entity.id, {
name: entity.name,
tag: entity.tag,
active: entity.active,
enabled: entity.enabled,
updateOrder: entity.updateOrder,
...(parentId !== null && parentId !== undefined && { parentId })
});
// 快照组件
if (opts.deepComponentComparison) {
const componentMap = new Map<string, string>();
for (const component of entity.components) {
const serialized = ComponentSerializer.serialize(component);
if (serialized) {
// 使用JSON字符串存储便于后续对比
componentMap.set(
serialized.type,
JSON.stringify(serialized.data)
);
}
}
if (componentMap.size > 0) {
snapshot.components.set(entity.id, componentMap);
}
}
}
// 快照场景数据
if (opts.trackSceneData) {
for (const [key, value] of scene.sceneData) {
snapshot.sceneData.set(key, JSON.stringify(value));
}
}
return snapshot;
}
// =========================================================================
// 增量计算 | Incremental Computation
// =========================================================================
/**
* @zh 计算增量变更
* @en Compute incremental changes
*
* @param scene - @zh 当前场景 @en Current scene
* @param baseSnapshot - @zh 基础快照 @en Base snapshot
* @param options - @zh 序列化选项 @en Serialization options
* @returns @zh 增量快照 @en Incremental snapshot
*/
public static computeIncremental(
scene: IScene,
baseSnapshot: SceneSnapshot,
options?: IncrementalSerializationOptions
): IncrementalSnapshot {
const opts = { ...DEFAULT_OPTIONS, ...options };
const incremental: IncrementalSnapshot = {
version: ++this.snapshotVersion,
timestamp: Date.now(),
sceneName: scene.name,
baseVersion: baseSnapshot.version,
entityChanges: [],
componentChanges: [],
sceneDataChanges: []
};
const currentEntityIds = new Set<number>();
// 检测实体变更(支持过滤)
for (const entity of scene.entities.buffer) {
// 应用实体过滤器
if (opts.entityFilter && !opts.entityFilter(entity)) {
continue;
}
currentEntityIds.add(entity.id);
// 获取层级信息
const hierarchy = entity.getComponent(HierarchyComponent);
const parentId = hierarchy?.parentId;
if (!baseSnapshot.entityIds.has(entity.id)) {
// 新增实体
incremental.entityChanges.push({
operation: ChangeOperation.EntityAdded,
entityId: entity.id,
entityName: entity.name,
entityData: {
id: entity.id,
name: entity.name,
tag: entity.tag,
active: entity.active,
enabled: entity.enabled,
updateOrder: entity.updateOrder,
...(parentId !== null && parentId !== undefined && { parentId }),
components: [],
children: []
}
});
// 新增实体的所有组件都是新增
for (const component of entity.components) {
const serialized = ComponentSerializer.serialize(component);
if (serialized) {
incremental.componentChanges.push({
operation: ChangeOperation.ComponentAdded,
entityId: entity.id,
componentType: serialized.type,
componentData: serialized
});
}
}
} else {
// 检查实体属性变更
const oldData = baseSnapshot.entities.get(entity.id)!;
const entityChanged =
oldData.name !== entity.name ||
oldData.tag !== entity.tag ||
oldData.active !== entity.active ||
oldData.enabled !== entity.enabled ||
oldData.updateOrder !== entity.updateOrder ||
oldData.parentId !== parentId;
if (entityChanged) {
incremental.entityChanges.push({
operation: ChangeOperation.EntityUpdated,
entityId: entity.id,
entityData: {
name: entity.name,
tag: entity.tag,
active: entity.active,
enabled: entity.enabled,
updateOrder: entity.updateOrder,
...(parentId !== null && parentId !== undefined && { parentId })
}
});
}
// 检查组件变更
if (opts.deepComponentComparison) {
this.detectComponentChanges(
entity,
baseSnapshot,
incremental.componentChanges
);
}
}
}
// 检测删除的实体
for (const oldEntityId of baseSnapshot.entityIds) {
if (!currentEntityIds.has(oldEntityId)) {
incremental.entityChanges.push({
operation: ChangeOperation.EntityRemoved,
entityId: oldEntityId
});
}
}
// 检测场景数据变更
if (opts.trackSceneData) {
this.detectSceneDataChanges(
scene,
baseSnapshot,
incremental.sceneDataChanges
);
}
return incremental;
}
// =========================================================================
// 私有方法 - 变更检测 | Private Methods - Change Detection
// =========================================================================
/**
* @zh 检测组件变更
* @en Detect component changes
*/
private static detectComponentChanges(
entity: Entity,
baseSnapshot: SceneSnapshot,
componentChanges: ComponentChange[]
): void {
const oldComponents = baseSnapshot.components.get(entity.id);
const currentComponents = new Map<string, SerializedComponent>();
// 收集当前组件
for (const component of entity.components) {
const serialized = ComponentSerializer.serialize(component);
if (serialized) {
currentComponents.set(serialized.type, serialized);
}
}
// 检测新增和更新的组件
for (const [type, serialized] of currentComponents) {
const currentData = JSON.stringify(serialized.data);
if (!oldComponents || !oldComponents.has(type)) {
// 新增组件
componentChanges.push({
operation: ChangeOperation.ComponentAdded,
entityId: entity.id,
componentType: type,
componentData: serialized
});
} else if (oldComponents.get(type) !== currentData) {
// 组件数据变更
componentChanges.push({
operation: ChangeOperation.ComponentUpdated,
entityId: entity.id,
componentType: type,
componentData: serialized
});
}
}
// 检测删除的组件
if (oldComponents) {
for (const oldType of oldComponents.keys()) {
if (!currentComponents.has(oldType)) {
componentChanges.push({
operation: ChangeOperation.ComponentRemoved,
entityId: entity.id,
componentType: oldType
});
}
}
}
}
/**
* @zh 检测场景数据变更
* @en Detect scene data changes
*/
private static detectSceneDataChanges(
scene: IScene,
baseSnapshot: SceneSnapshot,
sceneDataChanges: SceneDataChange[]
): void {
const currentKeys = new Set<string>();
// 检测新增和更新的场景数据
for (const [key, value] of scene.sceneData) {
currentKeys.add(key);
const currentValue = JSON.stringify(value);
const oldValue = baseSnapshot.sceneData.get(key);
if (!oldValue || oldValue !== currentValue) {
sceneDataChanges.push({
operation: ChangeOperation.SceneDataUpdated,
key,
value
});
}
}
// 检测删除的场景数据
for (const oldKey of baseSnapshot.sceneData.keys()) {
if (!currentKeys.has(oldKey)) {
sceneDataChanges.push({
operation: ChangeOperation.SceneDataUpdated,
key: oldKey,
value: undefined,
deleted: true
});
}
}
}
// =========================================================================
// 增量应用 | Incremental Application
// =========================================================================
/**
* @zh 应用增量变更到场景
* @en Apply incremental changes to scene
*
* @param scene - @zh 目标场景 @en Target scene
* @param incremental - @zh 增量快照 @en Incremental snapshot
* @param componentRegistry - @zh 组件类型注册表 @en Component type registry
*/
public static applyIncremental(
scene: IScene,
incremental: IncrementalSnapshot,
componentRegistry: Map<string, ComponentType>
): void {
// 应用实体变更
for (const change of incremental.entityChanges) {
switch (change.operation) {
case ChangeOperation.EntityAdded:
this.applyEntityAdded(scene, change);
break;
case ChangeOperation.EntityRemoved:
this.applyEntityRemoved(scene, change);
break;
case ChangeOperation.EntityUpdated:
this.applyEntityUpdated(scene, change);
break;
}
}
// 应用组件变更
for (const change of incremental.componentChanges) {
switch (change.operation) {
case ChangeOperation.ComponentAdded:
this.applyComponentAdded(scene, change, componentRegistry);
break;
case ChangeOperation.ComponentRemoved:
this.applyComponentRemoved(scene, change, componentRegistry);
break;
case ChangeOperation.ComponentUpdated:
this.applyComponentUpdated(scene, change, componentRegistry);
break;
}
}
// 应用场景数据变更
for (const change of incremental.sceneDataChanges) {
if (change.deleted) {
scene.sceneData.delete(change.key);
} else {
scene.sceneData.set(change.key, change.value);
}
}
}
private static applyEntityAdded(scene: IScene, change: EntityChange): void {
if (!change.entityData) return;
const entity = new Entity(change.entityName || 'Entity', change.entityId);
entity.tag = change.entityData.tag || 0;
entity.active = change.entityData.active ?? true;
entity.enabled = change.entityData.enabled ?? true;
entity.updateOrder = change.entityData.updateOrder || 0;
scene.addEntity(entity);
}
private static applyEntityRemoved(scene: IScene, change: EntityChange): void {
const entity = scene.entities.findEntityById(change.entityId);
if (entity) {
entity.destroy();
}
}
private static applyEntityUpdated(scene: IScene, change: EntityChange): void {
if (!change.entityData) return;
const entity = scene.entities.findEntityById(change.entityId);
if (!entity) return;
if (change.entityData.name !== undefined) entity.name = change.entityData.name;
if (change.entityData.tag !== undefined) entity.tag = change.entityData.tag;
if (change.entityData.active !== undefined) entity.active = change.entityData.active;
if (change.entityData.enabled !== undefined) entity.enabled = change.entityData.enabled;
if (change.entityData.updateOrder !== undefined) entity.updateOrder = change.entityData.updateOrder;
// 使用 HierarchySystem 更新层级关系
const hierarchySystem = scene.getSystem(HierarchySystem);
if (hierarchySystem) {
const hierarchy = entity.getComponent(HierarchyComponent);
const currentParentId = hierarchy?.parentId;
if (change.entityData.parentId !== undefined) {
const newParent = scene.entities.findEntityById(change.entityData.parentId);
if (newParent && currentParentId !== change.entityData.parentId) {
hierarchySystem.setParent(entity, newParent);
}
} else if (currentParentId !== null && currentParentId !== undefined) {
hierarchySystem.setParent(entity, null);
}
}
}
private static applyComponentAdded(
scene: IScene,
change: ComponentChange,
componentRegistry: Map<string, ComponentType>
): void {
if (!change.componentData) return;
const entity = scene.entities.findEntityById(change.entityId);
if (!entity) return;
const component = ComponentSerializer.deserialize(change.componentData, componentRegistry);
if (component) {
entity.addComponent(component);
}
}
private static applyComponentRemoved(
scene: IScene,
change: ComponentChange,
componentRegistry: Map<string, ComponentType>
): void {
const entity = scene.entities.findEntityById(change.entityId);
if (!entity) return;
const componentClass = componentRegistry.get(change.componentType);
if (!componentClass) return;
entity.removeComponentByType(componentClass);
}
private static applyComponentUpdated(
scene: IScene,
change: ComponentChange,
componentRegistry: Map<string, ComponentType>
): void {
if (!change.componentData) return;
const entity = scene.entities.findEntityById(change.entityId);
if (!entity) return;
const componentClass = componentRegistry.get(change.componentType);
if (!componentClass) return;
entity.removeComponentByType(componentClass);
const component = ComponentSerializer.deserialize(change.componentData, componentRegistry);
if (component) {
entity.addComponent(component);
}
}
// =========================================================================
// 序列化与反序列化 | Serialization & Deserialization
// =========================================================================
/**
* @zh 序列化增量快照
* @en Serialize incremental snapshot
*
* @param incremental - @zh 增量快照 @en Incremental snapshot
* @param options - @zh 序列化选项 @en Serialization options
* @returns @zh 序列化后的数据 @en Serialized data
*
* @example
* ```typescript
* // JSON格式默认
* const jsonData = IncrementalSerializer.serializeIncremental(snapshot);
*
* // 二进制格式
* const binaryData = IncrementalSerializer.serializeIncremental(snapshot, {
* format: 'binary'
* });
* ```
*/
public static serializeIncremental(
incremental: IncrementalSnapshot,
options?: { format?: IncrementalSerializationFormat; pretty?: boolean }
): string | Uint8Array {
const format = options?.format ?? 'json';
const pretty = options?.pretty ?? false;
if (format === 'binary') {
return BinarySerializer.encode(incremental);
}
return pretty ? JSON.stringify(incremental, null, 2) : JSON.stringify(incremental);
}
/**
* @zh 反序列化增量快照
* @en Deserialize incremental snapshot
*
* @param data - @zh 序列化的数据 @en Serialized data
* @returns @zh 增量快照 @en Incremental snapshot
*/
public static deserializeIncremental(data: string | Uint8Array): IncrementalSnapshot {
if (typeof data === 'string') {
return JSON.parse(data);
}
return BinarySerializer.decode(data) as IncrementalSnapshot;
}
// =========================================================================
// 统计与工具 | Statistics & Utilities
// =========================================================================
/**
* @zh 获取增量快照的统计信息
* @en Get incremental snapshot statistics
*
* @param incremental - @zh 增量快照 @en Incremental snapshot
* @returns @zh 统计信息 @en Statistics
*/
public static getIncrementalStats(incremental: IncrementalSnapshot): IIncrementalStats {
const entityStats = { added: 0, removed: 0, updated: 0 };
const componentStats = { added: 0, removed: 0, updated: 0 };
for (const change of incremental.entityChanges) {
if (change.operation === ChangeOperation.EntityAdded) entityStats.added++;
else if (change.operation === ChangeOperation.EntityRemoved) entityStats.removed++;
else if (change.operation === ChangeOperation.EntityUpdated) entityStats.updated++;
}
for (const change of incremental.componentChanges) {
if (change.operation === ChangeOperation.ComponentAdded) componentStats.added++;
else if (change.operation === ChangeOperation.ComponentRemoved) componentStats.removed++;
else if (change.operation === ChangeOperation.ComponentUpdated) componentStats.updated++;
}
return {
totalChanges:
incremental.entityChanges.length +
incremental.componentChanges.length +
incremental.sceneDataChanges.length,
entityChanges: incremental.entityChanges.length,
componentChanges: incremental.componentChanges.length,
sceneDataChanges: incremental.sceneDataChanges.length,
addedEntities: entityStats.added,
removedEntities: entityStats.removed,
updatedEntities: entityStats.updated,
addedComponents: componentStats.added,
removedComponents: componentStats.removed,
updatedComponents: componentStats.updated
};
}
/**
* @zh 重置快照版本号(用于测试)
* @en Reset snapshot version (for testing)
*/
public static resetVersion(): void {
this.snapshotVersion = 0;
}
}

View File

@@ -0,0 +1,470 @@
/**
* 预制体序列化器
* Prefab serializer
*
* 提供预制体的创建和实例化功能。
* Provides prefab creation and instantiation functionality.
*/
import { Entity } from '../Entity';
import { IScene } from '../IScene';
import { ComponentType } from '../Core/ComponentStorage';
import { EntitySerializer, SerializedEntity } from './EntitySerializer';
import { HierarchySystem } from '../Systems/HierarchySystem';
import { PrefabInstanceComponent } from '../Components/PrefabInstanceComponent';
/**
* 序列化的预制体实体(扩展自 SerializedEntity
* Serialized prefab entity (extends SerializedEntity)
*/
export interface SerializedPrefabEntity extends SerializedEntity {
/**
* 是否为预制体根节点
* Whether this is the prefab root entity
*/
isPrefabRoot?: boolean;
/**
* 嵌套预制体的 GUID
* GUID of nested prefab
*/
nestedPrefabGuid?: string;
}
/**
* 预制体元数据
* Prefab metadata
*/
export type PrefabMetadata = {
/** 预制体名称 | Prefab name */
name: string;
/** 资产 GUID | Asset GUID */
guid?: string;
/** 创建时间戳 | Creation timestamp */
createdAt: number;
/** 最后修改时间戳 | Last modification timestamp */
modifiedAt: number;
/** 使用的组件类型列表 | List of component types used */
componentTypes: string[];
/** 引用的资产 GUID 列表 | List of referenced asset GUIDs */
referencedAssets: string[];
/** 预制体描述 | Prefab description */
description?: string;
/** 预制体标签 | Prefab tags */
tags?: string[];
}
/**
* 组件类型注册条目
* Component type registry entry
*/
export type PrefabComponentTypeEntry = {
/** 组件类型名称 | Component type name */
typeName: string;
/** 组件版本号 | Component version number */
version: number;
}
/**
* 预制体数据格式
* Prefab data format
*/
export type PrefabData = {
/** 预制体格式版本号 | Prefab format version number */
version: number;
/** 预制体元数据 | Prefab metadata */
metadata: PrefabMetadata;
/** 根实体数据 | Root entity data */
root: SerializedPrefabEntity;
/** 组件类型注册表 | Component type registry */
componentTypeRegistry: PrefabComponentTypeEntry[];
}
/**
* 预制体创建选项
* Prefab creation options
*/
export type PrefabCreateOptions = {
/** 预制体名称 | Prefab name */
name: string;
/** 预制体描述 | Prefab description */
description?: string;
/** 预制体标签 | Prefab tags */
tags?: string[];
/** 是否包含子实体 | Whether to include child entities */
includeChildren?: boolean;
}
/**
* 预制体实例化选项
* Prefab instantiation options
*/
export type PrefabInstantiateOptions = {
/** 父实体 ID | Parent entity ID */
parentId?: number;
/** 位置覆盖 | Position override */
position?: { x: number; y: number };
/** 旋转覆盖(角度) | Rotation override (in degrees) */
rotation?: number;
/** 缩放覆盖 | Scale override */
scale?: { x: number; y: number };
/** 实体名称覆盖 | Entity name override */
name?: string;
/** 是否保留原始实体 ID | Whether to preserve original entity IDs */
preserveIds?: boolean;
/** 是否标记为预制体实例 | Whether to mark as prefab instance */
trackInstance?: boolean;
}
/**
* 预制体格式版本
* Prefab format version
*/
export const PREFAB_FORMAT_VERSION = 1;
/**
* 预制体序列化器类
* Prefab serializer class
*
* 提供预制体的创建、序列化和实例化功能。
* Provides prefab creation, serialization, and instantiation functionality.
*/
export class PrefabSerializer {
/**
* 从实体创建预制体数据
* Create prefab data from entity
*
* @param entity - 源实体 | Source entity
* @param options - 创建选项 | Creation options
* @param hierarchySystem - 层级系统 | Hierarchy system
* @returns 预制体数据 | Prefab data
*/
public static createPrefab(
entity: Entity,
options: PrefabCreateOptions,
hierarchySystem?: HierarchySystem
): PrefabData {
const includeChildren = options.includeChildren ?? true;
// 序列化实体 | Serialize entity
const serializedEntity = EntitySerializer.serialize(
entity,
includeChildren,
hierarchySystem
);
// 转换为预制体实体格式 | Convert to prefab entity format
const prefabEntity = this.toPrefabEntity(serializedEntity, true);
// 收集组件类型信息 | Collect component type information
const { componentTypes, componentTypeRegistry } = this.collectComponentTypes(prefabEntity);
// 收集引用的资产TODO: 实现资产引用扫描)
// Collect referenced assets (TODO: implement asset reference scanning)
const referencedAssets: string[] = [];
const now = Date.now();
const metadata: PrefabMetadata = {
name: options.name,
createdAt: now,
modifiedAt: now,
componentTypes,
referencedAssets
};
// 只在有值时添加可选属性 | Only add optional properties when they have values
if (options.description) {
metadata.description = options.description;
}
if (options.tags) {
metadata.tags = options.tags;
}
return {
version: PREFAB_FORMAT_VERSION,
metadata,
root: prefabEntity,
componentTypeRegistry
};
}
/**
* 从预制体数据实例化实体
* Instantiate entity from prefab data
*
* @param prefabData - 预制体数据 | Prefab data
* @param scene - 目标场景 | Target scene
* @param componentRegistry - 组件类型注册表 | Component type registry
* @param options - 实例化选项 | Instantiation options
* @returns 创建的根实体 | Created root entity
*/
public static instantiate(
prefabData: PrefabData,
scene: IScene,
componentRegistry: Map<string, ComponentType>,
options: PrefabInstantiateOptions = {}
): Entity {
const {
parentId,
name,
preserveIds = false,
trackInstance = true
} = options;
// 获取层级系统 | Get hierarchy system
const hierarchySystem = scene.getSystem(HierarchySystem) ?? null;
// ID 生成器 | ID generator
let nextId = 1;
const idGenerator = (): number => {
while (scene.findEntityById(nextId)) {
nextId++;
}
return nextId++;
};
// 反序列化实体 | Deserialize entity
const { rootEntities, allEntities } = EntitySerializer.deserializeEntities(
[prefabData.root],
componentRegistry,
idGenerator,
preserveIds,
scene,
hierarchySystem
);
const rootEntity = rootEntities[0];
if (!rootEntity) {
throw new Error('Failed to instantiate prefab: no root entity created');
}
// 覆盖名称 | Override name
if (name) {
rootEntity.name = name;
}
// 将所有实体添加到场景 | Add all entities to scene
for (const entity of allEntities.values()) {
scene.entities.add(entity);
}
// 设置父级 | Set parent
if (parentId !== undefined && hierarchySystem) {
const parent = scene.findEntityById(parentId);
if (parent) {
hierarchySystem.setParent(rootEntity, parent);
}
}
// 添加预制体实例组件 | Add prefab instance component
if (trackInstance) {
const prefabGuid = prefabData.metadata.guid || '';
this.addPrefabInstanceComponents(
rootEntity,
allEntities,
prefabGuid,
'',
hierarchySystem
);
}
// TODO: 应用位置、旋转、缩放覆盖(需要 TransformComponent
// TODO: Apply position, rotation, scale overrides (requires TransformComponent)
return rootEntity;
}
/**
* 将序列化实体转换为预制体实体格式
* Convert serialized entity to prefab entity format
*/
private static toPrefabEntity(
entity: SerializedEntity,
isRoot: boolean
): SerializedPrefabEntity {
const prefabEntity: SerializedPrefabEntity = {
...entity,
isPrefabRoot: isRoot,
children: entity.children.map(child => this.toPrefabEntity(child, false))
};
return prefabEntity;
}
/**
* 收集预制体中使用的组件类型
* Collect component types used in prefab
*/
private static collectComponentTypes(
entity: SerializedPrefabEntity
): {
componentTypes: string[];
componentTypeRegistry: PrefabComponentTypeEntry[];
} {
const typeMap = new Map<string, number>();
const collectFromEntity = (e: SerializedPrefabEntity): void => {
for (const comp of e.components) {
if (!typeMap.has(comp.type)) {
typeMap.set(comp.type, comp.version);
}
}
for (const child of e.children as SerializedPrefabEntity[]) {
collectFromEntity(child);
}
};
collectFromEntity(entity);
const componentTypes = Array.from(typeMap.keys());
const componentTypeRegistry: PrefabComponentTypeEntry[] = Array.from(
typeMap.entries()
).map(([typeName, version]) => ({ typeName, version }));
return { componentTypes, componentTypeRegistry };
}
/**
* 为实例化的实体添加预制体实例组件
* Add prefab instance components to instantiated entities
*/
private static addPrefabInstanceComponents(
rootEntity: Entity,
allEntities: Map<number, Entity>,
prefabGuid: string,
prefabPath: string,
_hierarchySystem: HierarchySystem | null
): void {
const rootId = rootEntity.id;
// 为根实体添加组件 | Add component to root entity
const rootComp = new PrefabInstanceComponent(prefabGuid, prefabPath, true);
rootComp.rootInstanceEntityId = rootId;
rootEntity.addComponent(rootComp);
// 为所有子实体添加组件 | Add component to all child entities
for (const entity of allEntities.values()) {
if (entity.id === rootId) continue;
const childComp = new PrefabInstanceComponent(prefabGuid, prefabPath, false);
childComp.rootInstanceEntityId = rootId;
entity.addComponent(childComp);
}
}
/**
* 检查实体是否为预制体实例
* Check if entity is a prefab instance
*/
public static isPrefabInstance(entity: Entity): boolean {
return entity.hasComponent(PrefabInstanceComponent);
}
/**
* 获取预制体实例的源预制体 GUID
* Get source prefab GUID of a prefab instance
*/
public static getSourcePrefabGuid(entity: Entity): string | null {
const comp = entity.getComponent(PrefabInstanceComponent);
return comp?.sourcePrefabGuid || null;
}
/**
* 获取预制体实例的根实体
* Get root entity of a prefab instance
*/
public static getPrefabInstanceRoot(entity: Entity): Entity | null {
const comp = entity.getComponent(PrefabInstanceComponent);
if (!comp || !comp.rootInstanceEntityId) return null;
const scene = entity.scene;
if (!scene) return null;
return scene.findEntityById(comp.rootInstanceEntityId) || null;
}
/**
* 将预制体数据序列化为 JSON 字符串
* Serialize prefab data to JSON string
*/
public static serialize(prefabData: PrefabData, pretty: boolean = true): string {
return JSON.stringify(prefabData, null, pretty ? 2 : undefined);
}
/**
* 从 JSON 字符串解析预制体数据
* Parse prefab data from JSON string
*/
public static deserialize(json: string): PrefabData {
const data = JSON.parse(json) as PrefabData;
// 基本验证 | Basic validation
if (!data.version || !data.metadata || !data.root) {
throw new Error('Invalid prefab data format');
}
return data;
}
/**
* 验证预制体数据格式
* Validate prefab data format
*/
public static validate(prefabData: PrefabData): { valid: boolean; errors?: string[] } {
const errors: string[] = [];
if (typeof prefabData.version !== 'number') {
errors.push('Invalid or missing version');
}
if (!prefabData.metadata) {
errors.push('Missing metadata');
} else {
if (!prefabData.metadata.name) {
errors.push('Missing metadata.name');
}
if (!Array.isArray(prefabData.metadata.componentTypes)) {
errors.push('Invalid metadata.componentTypes');
}
}
if (!prefabData.root) {
errors.push('Missing root entity');
} else {
this.validateEntity(prefabData.root, errors, 'root');
}
if (!Array.isArray(prefabData.componentTypeRegistry)) {
errors.push('Invalid componentTypeRegistry');
}
if (errors.length > 0) {
return { valid: false, errors };
}
return { valid: true };
}
/**
* 验证实体数据
* Validate entity data
*/
private static validateEntity(
entity: SerializedPrefabEntity,
errors: string[],
path: string
): void {
if (typeof entity.id !== 'number') {
errors.push(`${path}: Invalid or missing id`);
}
if (typeof entity.name !== 'string') {
errors.push(`${path}: Invalid or missing name`);
}
if (!Array.isArray(entity.components)) {
errors.push(`${path}: Invalid or missing components`);
}
if (!Array.isArray(entity.children)) {
errors.push(`${path}: Invalid or missing children`);
} else {
entity.children.forEach((child, index) => {
this.validateEntity(child as SerializedPrefabEntity, errors, `${path}.children[${index}]`);
});
}
}
}

View File

@@ -0,0 +1,535 @@
/**
* 场景序列化器
*
* 负责整个场景的序列化和反序列化,包括实体、组件等
*/
import type { IScene } from '../IScene';
import { Entity } from '../Entity';
import { ComponentType, GlobalComponentRegistry } from '../Core/ComponentStorage';
import { EntitySerializer, SerializedEntity } from './EntitySerializer';
import { getComponentTypeName } from '../Decorators';
import { getSerializationMetadata } from './SerializationDecorators';
import { BinarySerializer } from '../../Utils/BinarySerializer';
import { HierarchySystem } from '../Systems/HierarchySystem';
import { HierarchyComponent } from '../Components/HierarchyComponent';
import { SerializationContext } from './SerializationContext';
import { ValueSerializer, SerializableValue } from './ValueSerializer';
import { createLogger } from '../../Utils/Logger';
const logger = createLogger('SceneSerializer');
/**
* 场景序列化格式
*/
export type SerializationFormat = 'json' | 'binary';
/**
* 场景序列化策略
*/
export type DeserializationStrategy = 'merge' | 'replace';
/**
* 版本迁移函数
*/
export type MigrationFunction = (
oldVersion: number,
newVersion: number,
data: any
) => any;
/**
* 场景序列化选项
*/
export type SceneSerializationOptions = {
/**
* 要序列化的组件类型列表
* 如果未指定,则序列化所有可序列化的组件
*/
components?: ComponentType[];
/**
* 是否序列化系统状态(当前不支持)
*/
systems?: boolean;
/**
* 序列化格式
*/
format?: SerializationFormat;
/**
* 是否美化JSON输出仅在format='json'时有效)
*/
pretty?: boolean;
/**
* 是否包含元数据(如序列化时间、版本等)
*/
includeMetadata?: boolean;
}
/**
* 场景反序列化选项
*/
export type SceneDeserializationOptions = {
/**
* 反序列化策略
* - 'merge': 合并到现有场景
* - 'replace': 替换现有场景内容
*/
strategy?: DeserializationStrategy;
/**
* 版本迁移函数
*/
migration?: MigrationFunction;
/**
* 是否保持原始实体ID
*/
preserveIds?: boolean;
/**
* 组件类型注册表
* 如果未提供,将尝试从全局注册表获取
*/
componentRegistry?: Map<string, ComponentType>;
}
/**
* 序列化后的场景数据
*/
export type SerializedScene = {
/**
* 场景名称
*/
name: string;
/**
* 序列化版本
*/
version: number;
/**
* 序列化时间戳
*/
timestamp?: number;
/**
* 场景自定义数据
*
* 存储场景级别的配置和状态
*/
sceneData?: Record<string, any>;
/**
* 实体列表
*/
entities: SerializedEntity[];
/**
* 元数据
*/
metadata?: {
entityCount: number;
componentTypeCount: number;
serializationOptions?: SceneSerializationOptions;
};
/**
* 组件类型注册信息
*/
componentTypeRegistry: Array<{
typeName: string;
version: number;
}>;
}
/**
* 场景序列化器类
*/
export class SceneSerializer {
/**
* 当前序列化版本
*/
private static readonly SERIALIZATION_VERSION = 1;
/**
* 序列化场景
*
* @param scene 要序列化的场景
* @param options 序列化选项
* @returns 序列化后的数据JSON字符串或二进制Uint8Array
*/
public static serialize(scene: IScene, options?: SceneSerializationOptions): string | Uint8Array {
const opts: SceneSerializationOptions = {
systems: false,
format: 'json',
pretty: true,
includeMetadata: true,
...options
};
// 过滤实体和组件
const entities = this.filterEntities(scene, opts);
// 获取层级系统用于序列化子实体
const hierarchySystem = scene.getSystem(HierarchySystem);
// 序列化实体(传入 hierarchySystem 以正确序列化子实体)
const serializedEntities = EntitySerializer.serializeEntities(entities, true, hierarchySystem ?? undefined);
// 收集组件类型信息
const componentTypeRegistry = this.buildComponentTypeRegistry(entities);
// 序列化场景自定义数据
const sceneData = this.serializeSceneData(scene.sceneData);
// 构建序列化数据
const serializedScene: SerializedScene = {
name: scene.name,
version: this.SERIALIZATION_VERSION,
entities: serializedEntities,
componentTypeRegistry
};
// 添加场景数据(如果有)
if (sceneData && Object.keys(sceneData).length > 0) {
serializedScene.sceneData = sceneData;
}
// 添加元数据
if (opts.includeMetadata) {
serializedScene.timestamp = Date.now();
serializedScene.metadata = {
entityCount: serializedEntities.length,
componentTypeCount: componentTypeRegistry.length,
serializationOptions: opts
};
}
if (opts.format === 'json') {
return opts.pretty
? JSON.stringify(serializedScene, null, 2)
: JSON.stringify(serializedScene);
} else {
return BinarySerializer.encode(serializedScene);
}
}
/**
* 反序列化场景
*
* 使用两阶段反序列化:
* 1. 创建所有实体和组件,收集待解析的 EntityRef
* 2. 解析所有 EntityRef建立正确的对象引用
*
* Deserialize scene using two-phase approach:
* 1. Create all entities and components, collect pending EntityRefs
* 2. Resolve all EntityRefs, establish correct object references
*
* @param scene 目标场景
* @param saveData 序列化的数据JSON字符串或二进制Uint8Array
* @param options 反序列化选项
*/
public static deserialize(
scene: IScene,
saveData: string | Uint8Array,
options?: SceneDeserializationOptions
): void {
const opts: SceneDeserializationOptions = {
strategy: 'replace',
preserveIds: false,
...options
};
let serializedScene: SerializedScene;
try {
if (typeof saveData === 'string') {
serializedScene = JSON.parse(saveData);
} else {
serializedScene = BinarySerializer.decode(saveData) as SerializedScene;
}
} catch (error) {
throw new Error(`Failed to parse save data: ${error}`);
}
// 版本迁移
if (opts.migration && serializedScene.version !== this.SERIALIZATION_VERSION) {
serializedScene = opts.migration(
serializedScene.version,
this.SERIALIZATION_VERSION,
serializedScene
);
}
// 构建组件注册表
const componentRegistry = opts.componentRegistry || this.getGlobalComponentRegistry();
// 根据策略处理场景
if (opts.strategy === 'replace') {
// 清空场景
scene.destroyAllEntities();
}
// ID生成器
const idGenerator = () => scene.identifierPool.checkOut();
// 获取层级系统
const hierarchySystem = scene.getSystem(HierarchySystem);
// ========== 阶段 1创建实体和组件收集 EntityRef ==========
// Phase 1: Create entities and components, collect EntityRefs
const context = new SerializationContext();
context.setPreserveIds(opts.preserveIds || false);
// 反序列化实体(传入 context 收集 EntityRef
const { rootEntities, allEntities } = EntitySerializer.deserializeEntities(
serializedScene.entities,
componentRegistry,
idGenerator,
opts.preserveIds || false,
scene,
hierarchySystem,
context
);
// 将所有实体添加到场景(包括子实体)
// 先添加根实体,再递归添加子实体
for (const entity of rootEntities) {
scene.addEntity(entity, true);
this.addChildrenRecursively(entity, scene, hierarchySystem, allEntities);
}
// 统一清理缓存(批量操作完成后)
scene.querySystem.clearCache();
scene.clearSystemEntityCaches();
// ========== 阶段 2解析所有 EntityRef ==========
// Phase 2: Resolve all EntityRefs
const resolvedCount = context.resolveAllReferences();
const unresolvedCount = context.getUnresolvedCount();
if (unresolvedCount > 0) {
logger.warn(
`${unresolvedCount} EntityRef(s) could not be resolved. ` +
`Resolved: ${resolvedCount}, Total pending: ${context.getPendingCount()} | ` +
`${unresolvedCount} 个实体引用无法解析`
);
}
// 反序列化场景自定义数据
if (serializedScene.sceneData) {
this.deserializeSceneData(serializedScene.sceneData, scene.sceneData);
}
// 调用所有组件的 onDeserialized 生命周期方法
// Call onDeserialized lifecycle method on all components
const deserializedPromises: Promise<void>[] = [];
for (const entity of allEntities.values()) {
this.callOnDeserializedForEntity(entity, deserializedPromises);
}
// 如果有异步的 onDeserialized在后台执行
if (deserializedPromises.length > 0) {
Promise.all(deserializedPromises).catch(error => {
logger.error('Error in onDeserialized | onDeserialized 执行错误:', error);
});
}
}
/**
* 调用实体所有组件的 onDeserialized 方法(不递归)
*/
private static callOnDeserializedForEntity(
entity: Entity,
promises: Promise<void>[]
): void {
for (const component of entity.components) {
try {
const result = component.onDeserialized();
if (result instanceof Promise) {
promises.push(result);
}
} catch (error) {
const typeName = getComponentTypeName(component.constructor as ComponentType);
logger.error(`Error calling onDeserialized on component ${typeName} | 调用组件 ${typeName} 的 onDeserialized 时出错:`, error);
}
}
}
/**
* 递归添加实体的所有子实体到场景
*
* 修复反序列化时子实体丢失的问题:
* EntitySerializer.deserialize会提前设置子实体的scene引用
* 导致Entity.addChild的条件判断(!child.scene)跳过scene.addEntity调用。
* 因此需要在SceneSerializer中统一递归添加所有子实体。
*
* @param entity 父实体
* @param scene 目标场景
* @param hierarchySystem 层级系统
*/
private static addChildrenRecursively(
entity: Entity,
scene: IScene,
hierarchySystem?: HierarchySystem | null,
childEntitiesMap?: Map<number, Entity>
): void {
const hierarchy = entity.getComponent(HierarchyComponent);
if (!hierarchy || hierarchy.childIds.length === 0) return;
// 获取子实体
// 注意:此时子实体还没有被添加到场景,所以不能用 scene.findEntityById
// 需要从 childEntitiesMap 中查找(如果提供了的话)
for (const childId of hierarchy.childIds) {
// 尝试从 map 中获取,否则从场景获取(用于已添加的情况)
const child = childEntitiesMap?.get(childId) ?? scene.findEntityById(childId);
if (child) {
scene.addEntity(child, true); // 延迟缓存清理
this.addChildrenRecursively(child, scene, hierarchySystem, childEntitiesMap);
}
}
}
private static serializeSceneData(sceneData: Map<string, unknown>): Record<string, unknown> {
const result: Record<string, unknown> = {};
for (const [key, value] of sceneData) {
result[key] = ValueSerializer.serialize(value);
}
return result;
}
private static deserializeSceneData(data: Record<string, unknown>, targetMap: Map<string, unknown>): void {
targetMap.clear();
for (const [key, value] of Object.entries(data)) {
targetMap.set(key, ValueSerializer.deserialize(value as SerializableValue));
}
}
/**
* 过滤要序列化的实体和组件
*/
private static filterEntities(scene: IScene, options: SceneSerializationOptions): Entity[] {
const entities = Array.from(scene.entities.buffer);
// 如果指定了组件类型过滤
if (options.components && options.components.length > 0) {
const componentTypeSet = new Set(options.components);
// 只返回拥有指定组件的实体
return entities.filter((entity) => {
return Array.from(entity.components).some((component) =>
componentTypeSet.has(component.constructor as ComponentType)
);
});
}
return entities;
}
/**
* 构建组件类型注册表
*/
private static buildComponentTypeRegistry(
entities: Entity[]
): Array<{ typeName: string; version: number }> {
const registry = new Map<string, number>();
for (const entity of entities) {
for (const component of entity.components) {
const componentType = component.constructor as ComponentType;
const typeName = getComponentTypeName(componentType);
const metadata = getSerializationMetadata(component);
if (metadata && !registry.has(typeName)) {
registry.set(typeName, metadata.options.version);
}
}
}
return Array.from(registry.entries()).map(([typeName, version]) => ({
typeName,
version
}));
}
/**
* 获取全局组件注册表
*
* 从所有已注册的组件类型构建注册表
*/
private static getGlobalComponentRegistry(): Map<string, ComponentType> {
return GlobalComponentRegistry.getAllComponentNames() as Map<string, ComponentType>;
}
/**
* 验证保存数据的有效性
*
* @param saveData 序列化的数据
* @returns 验证结果
*/
public static validate(saveData: string): {
valid: boolean;
version?: number;
errors?: string[];
} {
const errors: string[] = [];
try {
const data = JSON.parse(saveData);
if (!data.version) {
errors.push('Missing version field');
}
if (!data.entities || !Array.isArray(data.entities)) {
errors.push('Missing or invalid entities field');
}
if (!data.componentTypeRegistry || !Array.isArray(data.componentTypeRegistry)) {
errors.push('Missing or invalid componentTypeRegistry field');
}
return {
valid: errors.length === 0,
version: data.version,
...(errors.length > 0 && { errors })
};
} catch (error) {
return {
valid: false,
errors: [`JSON parse error: ${error}`]
};
}
}
/**
* 获取保存数据的信息(不完全反序列化)
*
* @param saveData 序列化的数据
* @returns 保存数据的元信息
*/
public static getInfo(saveData: string): {
name: string;
version: number;
timestamp?: number;
entityCount: number;
componentTypeCount: number;
} | null {
try {
const data: SerializedScene = JSON.parse(saveData);
return {
name: data.name,
version: data.version,
...(data.timestamp !== undefined && { timestamp: data.timestamp }),
entityCount: data.metadata?.entityCount || data.entities.length,
componentTypeCount: data.componentTypeRegistry.length
};
} catch (error) {
return null;
}
}
}

View File

@@ -0,0 +1,321 @@
import type { Entity } from '../Entity';
import type { Component } from '../Component';
/**
* 序列化的实体引用格式
*
* Serialized entity reference format.
*/
export type SerializedEntityRef = {
/**
* 运行时 ID向后兼容
*
* Runtime ID (backward compatible).
*/
id?: number | undefined;
/**
* 持久化 GUID新格式
*
* Persistent GUID (new format).
*/
guid?: string | undefined;
}
/**
* 待解析的实体引用记录
*
* Pending entity reference record.
*/
interface PendingEntityRef {
/**
* 持有引用的组件
*/
component: Component;
/**
* 属性名
*/
propertyKey: string;
/**
* 原始运行时 ID可选
*/
originalId: number | undefined;
/**
* 原始 GUID可选
*/
originalGuid: string | undefined;
}
/**
* 序列化上下文
*
* 用于管理两阶段序列化/反序列化过程中的状态。
* 第一阶段:创建所有实体和组件,收集待解析的引用。
* 第二阶段:解析所有实体引用,建立正确的对象关系。
*
* Serialization context for managing two-phase serialization/deserialization.
* Phase 1: Create all entities and components, collect pending references.
* Phase 2: Resolve all entity references, establish correct object relationships.
*
* @example
* ```typescript
* const context = new SerializationContext();
*
* // 第一阶段:反序列化实体
* for (const entityData of entities) {
* const entity = scene.createEntity(entityData.name);
* context.registerEntity(entity, entityData.id, entityData.guid);
*
* // 反序列化组件时,遇到 EntityRef 注册为待解析
* context.registerPendingRef(component, 'target', entityData.targetId, entityData.targetGuid);
* }
*
* // 第二阶段:解析所有引用
* context.resolveAllReferences();
* ```
*/
export class SerializationContext {
/**
* 运行时 ID 映射:原始 ID -> Entity
*
* Runtime ID mapping: original ID -> Entity.
*/
private _idRemapping: Map<number, Entity> = new Map();
/**
* GUID 映射persistentId -> Entity
*
* GUID mapping: persistentId -> Entity.
*/
private _guidLookup: Map<string, Entity> = new Map();
/**
* 待解析的实体引用列表
*
* Pending entity references to resolve.
*/
private _pendingRefs: PendingEntityRef[] = [];
/**
* 是否保留原始 ID
*
* Whether to preserve original IDs.
*/
private _preserveIds: boolean = false;
/**
* 设置是否保留原始 ID
*
* Set whether to preserve original IDs.
*/
public setPreserveIds(value: boolean): void {
this._preserveIds = value;
}
/**
* 获取是否保留原始 ID
*
* Get whether to preserve original IDs.
*/
public get preserveIds(): boolean {
return this._preserveIds;
}
/**
* 注册实体到上下文
*
* Register entity to context for later reference resolution.
*
* @param entity - 实体实例
* @param originalId - 原始运行时 ID可选用于 ID 映射)
* @param originalGuid - 原始 GUID可选用于 GUID 映射,默认使用 entity.persistentId
*/
public registerEntity(entity: Entity, originalId?: number, originalGuid?: string): void {
// 使用实体自身的 persistentId 或提供的 originalGuid
const guid = originalGuid ?? entity.persistentId;
this._guidLookup.set(guid, entity);
// 如果提供了原始 ID建立 ID 映射
if (originalId !== undefined) {
this._idRemapping.set(originalId, entity);
}
}
/**
* 根据原始 ID 获取实体
*
* Get entity by original runtime ID.
*
* @param originalId - 原始运行时 ID
* @returns 实体实例或 null
*/
public getEntityById(originalId: number): Entity | null {
return this._idRemapping.get(originalId) ?? null;
}
/**
* 根据 GUID 获取实体
*
* Get entity by GUID.
*
* @param guid - 持久化 GUID
* @returns 实体实例或 null
*/
public getEntityByGuid(guid: string): Entity | null {
return this._guidLookup.get(guid) ?? null;
}
/**
* 解析实体引用
*
* Resolve entity reference, preferring GUID over ID.
*
* @param ref - 序列化的实体引用
* @returns 实体实例或 null
*/
public resolveEntityRef(ref: SerializedEntityRef | null | undefined): Entity | null {
if (!ref) {
return null;
}
// 优先使用 GUID
if (ref.guid) {
const entity = this._guidLookup.get(ref.guid);
if (entity) {
return entity;
}
}
// 降级使用 ID
if (ref.id !== undefined) {
const entity = this._idRemapping.get(ref.id);
if (entity) {
return entity;
}
}
return null;
}
/**
* 注册待解析的实体引用
*
* Register a pending entity reference to be resolved later.
*
* @param component - 持有引用的组件
* @param propertyKey - 属性名
* @param originalId - 原始运行时 ID
* @param originalGuid - 原始 GUID
*/
public registerPendingRef(
component: Component,
propertyKey: string,
originalId?: number,
originalGuid?: string
): void {
this._pendingRefs.push({
component,
propertyKey,
originalId,
originalGuid
});
}
/**
* 解析所有待处理的实体引用
*
* Resolve all pending entity references.
* Should be called after all entities have been created.
*
* @returns 成功解析的引用数量
*/
public resolveAllReferences(): number {
let resolvedCount = 0;
for (const pending of this._pendingRefs) {
const entity = this.resolveEntityRef({
id: pending.originalId,
guid: pending.originalGuid
});
if (entity) {
// 使用类型断言设置属性值
(pending.component as unknown as Record<string, unknown>)[pending.propertyKey] = entity;
resolvedCount++;
}
// 如果无法解析,保持为 null已在反序列化时设置
}
return resolvedCount;
}
/**
* 获取未解析的引用数量
*
* Get count of unresolved references.
*/
public getUnresolvedCount(): number {
let count = 0;
for (const pending of this._pendingRefs) {
const entity = this.resolveEntityRef({
id: pending.originalId,
guid: pending.originalGuid
});
if (!entity) {
count++;
}
}
return count;
}
/**
* 获取待解析引用数量
*
* Get count of pending references.
*/
public getPendingCount(): number {
return this._pendingRefs.length;
}
/**
* 获取已注册实体数量
*
* Get count of registered entities.
*/
public getRegisteredEntityCount(): number {
return this._guidLookup.size;
}
/**
* 清除上下文状态
*
* Clear context state.
*/
public clear(): void {
this._idRemapping.clear();
this._guidLookup.clear();
this._pendingRefs = [];
}
/**
* 获取调试信息
*
* Get debug information.
*/
public getDebugInfo(): {
registeredEntities: number;
pendingRefs: number;
unresolvedRefs: number;
preserveIds: boolean;
} {
return {
registeredEntities: this._guidLookup.size,
pendingRefs: this._pendingRefs.length,
unresolvedRefs: this.getUnresolvedCount(),
preserveIds: this._preserveIds
};
}
}

View File

@@ -0,0 +1,281 @@
/**
* 序列化装饰器
*
* 提供组件级别的序列化支持,包括字段级装饰器和类级装饰器
*/
import { Component } from '../Component';
/**
* 序列化元数据的Symbol键
*/
export const SERIALIZABLE_METADATA = Symbol('SerializableMetadata');
export const SERIALIZE_FIELD = Symbol('SerializeField');
export const SERIALIZE_OPTIONS = Symbol('SerializeOptions');
/**
* 可序列化配置选项
*/
export type SerializableOptions = {
/**
* 序列化版本号,用于数据迁移
*/
version: number;
/**
* 组件类型标识符(可选,默认使用类名)
*/
typeId?: string;
}
/**
* 字段序列化配置
*/
export type FieldSerializeOptions = {
/**
* 自定义序列化器
*/
serializer?: (value: any) => any;
/**
* 自定义反序列化器
*/
deserializer?: (value: any) => any;
/**
* 字段别名(用于序列化后的键名)
*/
alias?: string;
}
/**
* 序列化元数据
*/
export type SerializationMetadata = {
options: SerializableOptions;
fields: Map<string | symbol, FieldSerializeOptions>;
ignoredFields: Set<string | symbol>;
}
/**
* 组件可序列化装饰器
*
* 标记组件类为可序列化,必须与字段装饰器配合使用
*
* @param options 序列化配置选项
*
* @example
* ```typescript
* @ECSComponent('Player')
* @Serializable({ version: 1 })
* class PlayerComponent extends Component {
* @Serialize() name: string = 'Player';
* @Serialize() level: number = 1;
* }
* ```
*/
export function Serializable(options: SerializableOptions) {
return function <T extends new (...args: any[]) => Component>(target: T): T {
if (!options || typeof options.version !== 'number') {
throw new Error('Serializable装饰器必须提供有效的版本号');
}
// 检查是否有自己的元数据(不是从父类继承的)
const hasOwnMetadata = Object.prototype.hasOwnProperty.call(target, SERIALIZABLE_METADATA);
let metadata: SerializationMetadata;
if (hasOwnMetadata) {
// 已有自己的元数据,更新 options
metadata = (target as any)[SERIALIZABLE_METADATA];
metadata.options = options;
} else {
// 没有自己的元数据,检查是否有继承的元数据
const inheritedMetadata: SerializationMetadata | undefined = (target as any)[SERIALIZABLE_METADATA];
// 创建新的元数据对象(从继承的元数据复制字段,但使用新的 options
metadata = {
options,
fields: inheritedMetadata ? new Map(inheritedMetadata.fields) : new Map(),
ignoredFields: inheritedMetadata ? new Set(inheritedMetadata.ignoredFields) : new Set()
};
(target as any)[SERIALIZABLE_METADATA] = metadata;
}
return target;
};
}
/**
* 字段序列化装饰器
*
* 标记字段为可序列化
*
* @param options 字段序列化选项(可选)
*
* @example
* ```typescript
* @Serialize()
* name: string = 'Player';
*
* @Serialize({ alias: 'hp' })
* health: number = 100;
* ```
*/
export function Serialize(options?: FieldSerializeOptions) {
return function (target: any, propertyKey: string | symbol) {
const constructor = target.constructor;
// 检查是否有自己的元数据(不是从父类继承的)
const hasOwnMetadata = Object.prototype.hasOwnProperty.call(constructor, SERIALIZABLE_METADATA);
let metadata: SerializationMetadata;
if (hasOwnMetadata) {
// 已有自己的元数据
metadata = constructor[SERIALIZABLE_METADATA];
} else {
// 没有自己的元数据,检查是否有继承的元数据
const inheritedMetadata: SerializationMetadata | undefined = constructor[SERIALIZABLE_METADATA];
// 创建新的元数据对象(从继承的元数据复制)
metadata = {
options: inheritedMetadata ? { ...inheritedMetadata.options } : { version: 1 },
fields: inheritedMetadata ? new Map(inheritedMetadata.fields) : new Map(),
ignoredFields: inheritedMetadata ? new Set(inheritedMetadata.ignoredFields) : new Set()
};
constructor[SERIALIZABLE_METADATA] = metadata;
}
// 记录字段
metadata.fields.set(propertyKey, options || {});
};
}
/**
* Map序列化装饰器
*
* 专门用于序列化Map类型字段
*
* @example
* ```typescript
* @SerializeAsMap()
* inventory: Map<string, number> = new Map();
* ```
*/
export function SerializeAsMap() {
return function (target: any, propertyKey: string | symbol) {
Serialize({
serializer: (value: Map<any, any>) => {
if (!(value instanceof Map)) {
return null;
}
return Array.from(value.entries());
},
deserializer: (value: any) => {
if (!Array.isArray(value)) {
return new Map();
}
return new Map(value);
}
})(target, propertyKey);
};
}
/**
* Set序列化装饰器
*
* 专门用于序列化Set类型字段
*
* @example
* ```typescript
* @SerializeAsSet()
* tags: Set<string> = new Set();
* ```
*/
export function SerializeAsSet() {
return function (target: any, propertyKey: string | symbol) {
Serialize({
serializer: (value: Set<any>) => {
if (!(value instanceof Set)) {
return null;
}
return Array.from(value);
},
deserializer: (value: any) => {
if (!Array.isArray(value)) {
return new Set();
}
return new Set(value);
}
})(target, propertyKey);
};
}
/**
* 忽略序列化装饰器
*
* 标记字段不参与序列化
*
* @example
* ```typescript
* @IgnoreSerialization()
* tempCache: any = null;
* ```
*/
export function IgnoreSerialization() {
return function (target: any, propertyKey: string | symbol) {
const constructor = target.constructor;
// 检查是否有自己的元数据(不是从父类继承的)
const hasOwnMetadata = Object.prototype.hasOwnProperty.call(constructor, SERIALIZABLE_METADATA);
let metadata: SerializationMetadata;
if (hasOwnMetadata) {
// 已有自己的元数据
metadata = constructor[SERIALIZABLE_METADATA];
} else {
// 没有自己的元数据,检查是否有继承的元数据
const inheritedMetadata: SerializationMetadata | undefined = constructor[SERIALIZABLE_METADATA];
// 创建新的元数据对象(从继承的元数据复制)
metadata = {
options: inheritedMetadata ? { ...inheritedMetadata.options } : { version: 1 },
fields: inheritedMetadata ? new Map(inheritedMetadata.fields) : new Map(),
ignoredFields: inheritedMetadata ? new Set(inheritedMetadata.ignoredFields) : new Set()
};
constructor[SERIALIZABLE_METADATA] = metadata;
}
// 记录忽略字段
metadata.ignoredFields.add(propertyKey);
};
}
/**
* 获取组件的序列化元数据
*
* @param componentClass 组件类或组件实例
* @returns 序列化元数据如果组件不可序列化则返回null
*/
export function getSerializationMetadata(componentClass: any): SerializationMetadata | null {
if (!componentClass) {
return null;
}
// 如果是实例,获取其构造函数
const constructor = typeof componentClass === 'function'
? componentClass
: componentClass.constructor;
return constructor[SERIALIZABLE_METADATA] || null;
}
/**
* 检查组件是否可序列化
*
* @param component 组件类或组件实例
* @returns 如果组件可序列化返回true
*/
export function isSerializable(component: any): boolean {
return getSerializationMetadata(component) !== null;
}

View File

@@ -0,0 +1,113 @@
/**
* 值序列化器
*
* Value serializer with circular reference detection and extensible type handlers.
*/
export type PrimitiveValue = string | number | boolean | null | undefined;
export type SerializableValue =
| PrimitiveValue
| SerializableValue[]
| { readonly [key: string]: SerializableValue }
| { readonly __type: string; readonly value: unknown };
type Serializer<T> = (value: T, serialize: (v: unknown) => SerializableValue) => SerializableValue;
type Deserializer<T> = (data: { __type: string; value: unknown }) => T;
interface TypeDef<T = unknown> {
check: (value: unknown) => value is T;
serialize: Serializer<T>;
deserialize: Deserializer<T>;
}
const types = new Map<string, TypeDef>();
function registerType<T>(name: string, def: TypeDef<T>): void {
types.set(name, def as TypeDef);
}
// 内置类型
registerType<Date>('Date', {
check: (v): v is Date => v instanceof Date,
serialize: (v) => ({ __type: 'Date', value: v.toISOString() }),
deserialize: (d) => new Date(d.value as string)
});
registerType<Map<unknown, unknown>>('Map', {
check: (v): v is Map<unknown, unknown> => v instanceof Map,
serialize: (v, ser) => ({ __type: 'Map', value: [...v].map(([k, val]) => [ser(k), ser(val)]) }),
deserialize: (d) => new Map(d.value as Array<[unknown, unknown]>)
});
registerType<Set<unknown>>('Set', {
check: (v): v is Set<unknown> => v instanceof Set,
serialize: (v, ser) => ({ __type: 'Set', value: [...v].map(ser) }),
deserialize: (d) => new Set(d.value as unknown[])
});
function serialize(value: unknown, seen = new WeakSet<object>()): SerializableValue {
if (value == null) return value as null | undefined;
const t = typeof value;
if (t === 'string' || t === 'number' || t === 'boolean') return value as PrimitiveValue;
if (t === 'function') return undefined;
const obj = value as object;
if (seen.has(obj)) return undefined;
seen.add(obj);
for (const [, def] of types) {
if (def.check(value)) {
return def.serialize(value, (v) => serialize(v, seen));
}
}
if (Array.isArray(value)) {
return value.map((v) => serialize(v, seen));
}
const result: Record<string, SerializableValue> = {};
for (const k of Object.keys(value as object)) {
result[k] = serialize((value as Record<string, unknown>)[k], seen);
}
return result;
}
function deserialize(value: SerializableValue): unknown {
if (value == null) return value;
const t = typeof value;
if (t === 'string' || t === 'number' || t === 'boolean') return value;
if (isTypedValue(value)) {
const def = types.get(value.__type);
return def ? def.deserialize(value) : value;
}
if (Array.isArray(value)) {
return value.map(deserialize);
}
const result: Record<string, unknown> = {};
for (const k of Object.keys(value)) {
result[k] = deserialize((value as Record<string, SerializableValue>)[k]);
}
return result;
}
function isTypedValue(v: unknown): v is { __type: string; value: unknown } {
if (v === null || typeof v !== 'object') {
return false;
}
return '__type' in v;
}
export const ValueSerializer = {
serialize,
deserialize,
register: registerType
} as const;
export type { TypeDef as TypeHandler };
export type TypedValue = { readonly __type: string; readonly value: unknown };

View File

@@ -0,0 +1,375 @@
/**
* 版本迁移系统
*
* 提供组件和场景数据的版本迁移支持
*/
import { SerializedComponent } from './ComponentSerializer';
import { SerializedScene } from './SceneSerializer';
import { createLogger } from '../../Utils/Logger';
const logger = createLogger('VersionMigration');
/**
* 组件迁移函数
*/
export type ComponentMigrationFunction = (data: any, fromVersion: number, toVersion: number) => any;
/**
* 场景迁移函数
*/
export type SceneMigrationFunction = (
scene: SerializedScene,
fromVersion: number,
toVersion: number
) => SerializedScene;
/**
* 版本迁移管理器
*/
export class VersionMigrationManager {
/**
* 组件迁移函数注册表
* Map<组件类型名, Map<版本号, 迁移函数>>
*/
private static componentMigrations = new Map<string, Map<number, ComponentMigrationFunction>>();
/**
* 场景迁移函数注册表
* Map<版本号, 迁移函数>
*/
private static sceneMigrations = new Map<number, SceneMigrationFunction>();
/**
* 注册组件迁移函数
*
* @param componentType 组件类型名称
* @param fromVersion 源版本号
* @param toVersion 目标版本号
* @param migration 迁移函数
*
* @example
* ```typescript
* // 从版本1迁移到版本2
* VersionMigrationManager.registerComponentMigration(
* 'PlayerComponent',
* 1,
* 2,
* (data) => {
* // 添加新字段
* data.experience = 0;
* return data;
* }
* );
* ```
*/
public static registerComponentMigration(
componentType: string,
fromVersion: number,
_toVersion: number,
migration: ComponentMigrationFunction
): void {
if (!this.componentMigrations.has(componentType)) {
this.componentMigrations.set(componentType, new Map());
}
const versionMap = this.componentMigrations.get(componentType)!;
// 使用fromVersion作为key表示"从这个版本迁移"
versionMap.set(fromVersion, migration);
}
/**
* 注册场景迁移函数
*
* @param fromVersion 源版本号
* @param toVersion 目标版本号
* @param migration 迁移函数
*
* @example
* ```typescript
* VersionMigrationManager.registerSceneMigration(
* 1,
* 2,
* (scene) => {
* // 迁移场景结构
* scene.metadata = { ...scene.metadata, migratedFrom: 1 };
* return scene;
* }
* );
* ```
*/
public static registerSceneMigration(
fromVersion: number,
_toVersion: number,
migration: SceneMigrationFunction
): void {
this.sceneMigrations.set(fromVersion, migration);
}
/**
* 迁移组件数据
*
* @param component 序列化的组件数据
* @param targetVersion 目标版本号
* @returns 迁移后的组件数据
*/
public static migrateComponent(
component: SerializedComponent,
targetVersion: number
): SerializedComponent {
const currentVersion = component.version;
if (currentVersion === targetVersion) {
return component; // 版本相同,无需迁移
}
const migrations = this.componentMigrations.get(component.type);
if (!migrations) {
logger.warn(`No migration path found for component ${component.type} | 未找到组件 ${component.type} 的迁移路径`);
return component;
}
const migratedData = { ...component };
let version = currentVersion;
// 执行迁移链
while (version < targetVersion) {
const migration = migrations.get(version);
if (!migration) {
logger.warn(
`Missing migration from version ${version} to ${version + 1} for ${component.type} | ` +
`缺少组件 ${component.type} 从版本 ${version}${version + 1} 的迁移`
);
break;
}
migratedData.data = migration(migratedData.data, version, version + 1);
version++;
}
migratedData.version = version;
return migratedData;
}
/**
* 迁移场景数据
*
* @param scene 序列化的场景数据
* @param targetVersion 目标版本号
* @returns 迁移后的场景数据
*/
public static migrateScene(scene: SerializedScene, targetVersion: number): SerializedScene {
const currentVersion = scene.version;
if (currentVersion === targetVersion) {
return scene; // 版本相同,无需迁移
}
let migratedScene = { ...scene };
let version = currentVersion;
// 执行场景级迁移
while (version < targetVersion) {
const migration = this.sceneMigrations.get(version);
if (!migration) {
logger.warn(`Missing scene migration from version ${version} to ${version + 1} | 缺少场景从版本 ${version}${version + 1} 的迁移`);
break;
}
migratedScene = migration(migratedScene, version, version + 1);
version++;
}
migratedScene.version = version;
// 迁移所有组件
migratedScene = this.migrateSceneComponents(migratedScene);
return migratedScene;
}
/**
* 迁移场景中所有组件的版本
*/
private static migrateSceneComponents(scene: SerializedScene): SerializedScene {
const migratedScene = { ...scene };
migratedScene.entities = scene.entities.map((entity) => ({
...entity,
components: entity.components.map((component) => {
// 查找组件的目标版本
const typeInfo = scene.componentTypeRegistry.find(
(t) => t.typeName === component.type
);
if (typeInfo && typeInfo.version !== component.version) {
return this.migrateComponent(component, typeInfo.version);
}
return component;
}),
children: this.migrateEntitiesComponents(entity.children, scene.componentTypeRegistry)
}));
return migratedScene;
}
/**
* 递归迁移实体的组件
*/
private static migrateEntitiesComponents(
entities: any[],
typeRegistry: Array<{ typeName: string; version: number }>
): any[] {
return entities.map((entity) => ({
...entity,
components: entity.components.map((component: SerializedComponent) => {
const typeInfo = typeRegistry.find((t) => t.typeName === component.type);
if (typeInfo && typeInfo.version !== component.version) {
return this.migrateComponent(component, typeInfo.version);
}
return component;
}),
children: this.migrateEntitiesComponents(entity.children, typeRegistry)
}));
}
/**
* 清除所有迁移函数
*/
public static clearMigrations(): void {
this.componentMigrations.clear();
this.sceneMigrations.clear();
}
/**
* 获取组件的迁移路径
*
* @param componentType 组件类型名称
* @returns 可用的迁移版本列表
*/
public static getComponentMigrationPath(componentType: string): number[] {
const migrations = this.componentMigrations.get(componentType);
if (!migrations) {
return [];
}
return Array.from(migrations.keys()).sort((a, b) => a - b);
}
/**
* 获取场景的迁移路径
*
* @returns 可用的场景迁移版本列表
*/
public static getSceneMigrationPath(): number[] {
return Array.from(this.sceneMigrations.keys()).sort((a, b) => a - b);
}
/**
* 检查是否可以迁移组件
*
* @param componentType 组件类型名称
* @param fromVersion 源版本
* @param toVersion 目标版本
* @returns 是否存在完整的迁移路径
*/
public static canMigrateComponent(
componentType: string,
fromVersion: number,
toVersion: number
): boolean {
if (fromVersion === toVersion) {
return true;
}
const migrations = this.componentMigrations.get(componentType);
if (!migrations) {
return false;
}
// 检查是否存在完整的迁移路径
for (let v = fromVersion; v < toVersion; v++) {
if (!migrations.has(v)) {
return false;
}
}
return true;
}
/**
* 检查是否可以迁移场景
*
* @param fromVersion 源版本
* @param toVersion 目标版本
* @returns 是否存在完整的迁移路径
*/
public static canMigrateScene(fromVersion: number, toVersion: number): boolean {
if (fromVersion === toVersion) {
return true;
}
// 检查是否存在完整的场景迁移路径
for (let v = fromVersion; v < toVersion; v++) {
if (!this.sceneMigrations.has(v)) {
return false;
}
}
return true;
}
}
/**
* 便捷的迁移构建器
*
* 提供链式API来定义迁移
*/
export class MigrationBuilder {
private componentType?: string;
private fromVersion: number = 1;
private toVersion: number = 2;
/**
* 设置组件类型
*/
public forComponent(componentType: string): this {
this.componentType = componentType;
return this;
}
/**
* 设置版本范围
*/
public fromVersionToVersion(from: number, to: number): this {
this.fromVersion = from;
this.toVersion = to;
return this;
}
/**
* 注册迁移函数
*/
public migrate(migration: ComponentMigrationFunction | SceneMigrationFunction): void {
if (this.componentType) {
VersionMigrationManager.registerComponentMigration(
this.componentType,
this.fromVersion,
this.toVersion,
migration as ComponentMigrationFunction
);
} else {
VersionMigrationManager.registerSceneMigration(
this.fromVersion,
this.toVersion,
migration as SceneMigrationFunction
);
}
}
}

View File

@@ -0,0 +1,81 @@
/**
* ECS序列化系统
*
* 提供完整的场景、实体和组件序列化支持
*/
// 装饰器
export {
Serializable,
Serialize,
SerializeAsMap,
SerializeAsSet,
IgnoreSerialization,
getSerializationMetadata,
isSerializable,
SERIALIZABLE_METADATA,
SERIALIZE_FIELD,
SERIALIZE_OPTIONS
} from './SerializationDecorators';
export type {
SerializableOptions,
FieldSerializeOptions,
SerializationMetadata
} from './SerializationDecorators';
// 值序列化器
export { ValueSerializer } from './ValueSerializer';
export type { SerializableValue, TypeHandler, TypedValue } from './ValueSerializer';
// 组件序列化器
export { ComponentSerializer } from './ComponentSerializer';
export type { SerializedComponent } from './ComponentSerializer';
// 实体序列化器
export { EntitySerializer } from './EntitySerializer';
export type { SerializedEntity } from './EntitySerializer';
// 场景序列化器
export { SceneSerializer } from './SceneSerializer';
export type {
SerializedScene,
SerializationFormat,
DeserializationStrategy,
MigrationFunction,
SceneSerializationOptions,
SceneDeserializationOptions
} from './SceneSerializer';
// 版本迁移
export { VersionMigrationManager, MigrationBuilder } from './VersionMigration';
export type {
ComponentMigrationFunction,
SceneMigrationFunction
} from './VersionMigration';
// 增量序列化
export { IncrementalSerializer, ChangeOperation } from './IncrementalSerializer';
export type {
IncrementalSnapshot,
IncrementalSerializationOptions,
IncrementalSerializationFormat,
EntityChange,
ComponentChange,
SceneDataChange
} from './IncrementalSerializer';
// 预制体序列化
export { PrefabSerializer, PREFAB_FORMAT_VERSION } from './PrefabSerializer';
export type {
SerializedPrefabEntity,
PrefabMetadata,
PrefabComponentTypeEntry,
PrefabData,
PrefabCreateOptions,
PrefabInstantiateOptions
} from './PrefabSerializer';
// 序列化上下文
export { SerializationContext } from './SerializationContext';
export type { SerializedEntityRef } from './SerializationContext';

View File

@@ -0,0 +1,166 @@
import { Entity } from '../Entity';
/**
* 实体缓存管理器
*
* 负责管理 EntitySystem 中的实体缓存,提供帧缓存和持久缓存两级缓存机制。
* 使用面向对象设计,将数据和行为封装在类中。
*
* @example
* ```typescript
* const cache = new EntityCache();
* cache.setPersistent(entities);
* const cached = cache.getPersistent();
* cache.invalidate();
* ```
*/
export class EntityCache {
/**
* 帧缓存
*
* 在update周期内使用每帧结束后清理
*/
private _frameCache: readonly Entity[] | null = null;
/**
* 持久缓存
*
* 跨帧使用,直到被显式失效
*/
private _persistentCache: readonly Entity[] | null = null;
/**
* 被跟踪的实体集合
*
* 用于跟踪哪些实体正在被此系统处理
*/
private _trackedEntities: Set<Entity> = new Set();
/**
* 获取帧缓存
*/
public getFrame(): readonly Entity[] | null {
return this._frameCache;
}
/**
* 设置帧缓存
*
* @param entities 要缓存的实体列表
*/
public setFrame(entities: readonly Entity[]): void {
this._frameCache = entities;
}
/**
* 获取持久缓存
*/
public getPersistent(): readonly Entity[] | null {
return this._persistentCache;
}
/**
* 设置持久缓存
*
* @param entities 要缓存的实体列表
*/
public setPersistent(entities: readonly Entity[]): void {
this._persistentCache = entities;
}
/**
* 获取被跟踪的实体集合
*/
public getTracked(): ReadonlySet<Entity> {
return this._trackedEntities;
}
/**
* 添加被跟踪的实体
*
* @param entity 要跟踪的实体
*/
public addTracked(entity: Entity): void {
this._trackedEntities.add(entity);
}
/**
* 移除被跟踪的实体
*
* @param entity 要移除的实体
*/
public removeTracked(entity: Entity): void {
this._trackedEntities.delete(entity);
}
/**
* 检查实体是否被跟踪
*
* @param entity 要检查的实体
*/
public isTracked(entity: Entity): boolean {
return this._trackedEntities.has(entity);
}
/**
* 使持久缓存失效
*
* 当实体变化时调用,强制下次查询时重新计算
*/
public invalidate(): void {
this._persistentCache = null;
}
/**
* 清除帧缓存
*
* 在每帧结束时调用
*/
public clearFrame(): void {
this._frameCache = null;
}
/**
* 清除所有缓存
*
* 在系统重置或销毁时调用
*/
public clearAll(): void {
this._frameCache = null;
this._persistentCache = null;
this._trackedEntities.clear();
}
/**
* 检查是否有有效的持久缓存
*/
public hasPersistent(): boolean {
return this._persistentCache !== null;
}
/**
* 检查是否有有效的帧缓存
*/
public hasFrame(): boolean {
return this._frameCache !== null;
}
/**
* 获取缓存统计信息
*/
public getStats(): {
hasFrame: boolean;
hasPersistent: boolean;
trackedCount: number;
frameEntityCount: number;
persistentEntityCount: number;
} {
return {
hasFrame: this._frameCache !== null,
hasPersistent: this._persistentCache !== null,
trackedCount: this._trackedEntities.size,
frameEntityCount: this._frameCache?.length ?? 0,
persistentEntityCount: this._persistentCache?.length ?? 0
};
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,582 @@
import { Entity } from '../Entity';
import { EntitySystem } from './EntitySystem';
import { Matcher } from '../Utils/Matcher';
import { HierarchyComponent } from '../Components/HierarchyComponent';
/**
* 层级关系系统 - 管理实体间的父子关系
*
* 提供层级操作的统一 API维护层级缓存depth、activeInHierarchy
* 所有层级操作应通过此系统进行,而非直接修改 HierarchyComponent。
*
* @example
* ```typescript
* const hierarchySystem = scene.getSystem(HierarchySystem);
*
* // 设置父子关系
* hierarchySystem.setParent(child, parent);
*
* // 查询层级
* const parent = hierarchySystem.getParent(entity);
* const children = hierarchySystem.getChildren(entity);
* const depth = hierarchySystem.getDepth(entity);
* ```
*/
export class HierarchySystem extends EntitySystem {
private static readonly MAX_DEPTH = 32;
/**
* 脏实体集合 - 只有这些实体需要在 process() 中更新缓存
* Dirty entity set - only these entities need cache update in process()
*/
private dirtyEntities: Set<Entity> = new Set();
constructor() {
super(Matcher.empty().all(HierarchyComponent));
}
/**
* 系统优先级,确保在其他系统之前更新层级缓存
*/
public override get updateOrder(): number {
return -1000;
}
protected override process(_entities: readonly Entity[]): void {
// 只更新脏实体,不遍历所有实体 | Only update dirty entities, no full iteration
if (this.dirtyEntities.size === 0) {
return;
}
for (const entity of this.dirtyEntities) {
// 确保实体仍然有效 | Ensure entity is still valid
if (entity.scene) {
this.updateHierarchyCache(entity);
}
}
this.dirtyEntities.clear();
}
/**
* 设置实体的父级
*
* @param child - 子实体
* @param parent - 父实体null 表示移动到根级
*/
public setParent(child: Entity, parent: Entity | null): void {
let childHierarchy = child.getComponent(HierarchyComponent);
// 如果子实体没有 HierarchyComponent自动添加
if (!childHierarchy) {
childHierarchy = new HierarchyComponent();
child.addComponent(childHierarchy);
}
// 检查是否需要变更
const currentParentId = childHierarchy.parentId;
const newParentId = parent?.id ?? null;
if (currentParentId === newParentId) {
return;
}
// 防止循环引用
if (parent && this.isAncestorOf(child, parent)) {
throw new Error('Cannot set parent: would create circular reference');
}
// 从旧父级移除
if (currentParentId !== null) {
const oldParent = this.scene?.findEntityById(currentParentId);
if (oldParent) {
const oldParentHierarchy = oldParent.getComponent(HierarchyComponent);
if (oldParentHierarchy) {
const idx = oldParentHierarchy.childIds.indexOf(child.id);
if (idx !== -1) {
oldParentHierarchy.childIds.splice(idx, 1);
}
}
}
}
// 添加到新父级
if (parent) {
let parentHierarchy = parent.getComponent(HierarchyComponent);
if (!parentHierarchy) {
parentHierarchy = new HierarchyComponent();
parent.addComponent(parentHierarchy);
}
childHierarchy.parentId = parent.id;
parentHierarchy.childIds.push(child.id);
} else {
childHierarchy.parentId = null;
}
// 标记缓存脏
this.markCacheDirty(child);
}
/**
* 在指定位置插入子实体
*
* @param parent - 父实体
* @param child - 子实体
* @param index - 插入位置索引,-1 表示追加到末尾
*/
public insertChildAt(parent: Entity, child: Entity, index: number): void {
let childHierarchy = child.getComponent(HierarchyComponent);
let parentHierarchy = parent.getComponent(HierarchyComponent);
// 自动添加 HierarchyComponent
if (!childHierarchy) {
childHierarchy = new HierarchyComponent();
child.addComponent(childHierarchy);
}
if (!parentHierarchy) {
parentHierarchy = new HierarchyComponent();
parent.addComponent(parentHierarchy);
}
// 防止循环引用
if (this.isAncestorOf(child, parent)) {
throw new Error('Cannot set parent: would create circular reference');
}
// 从旧父级移除
if (childHierarchy.parentId !== null && childHierarchy.parentId !== parent.id) {
const oldParent = this.scene?.findEntityById(childHierarchy.parentId);
if (oldParent) {
const oldParentHierarchy = oldParent.getComponent(HierarchyComponent);
if (oldParentHierarchy) {
const idx = oldParentHierarchy.childIds.indexOf(child.id);
if (idx !== -1) {
oldParentHierarchy.childIds.splice(idx, 1);
}
}
}
}
// 设置新父级
childHierarchy.parentId = parent.id;
// 从当前父级的子列表中移除(如果已存在)
const existingIdx = parentHierarchy.childIds.indexOf(child.id);
if (existingIdx !== -1) {
parentHierarchy.childIds.splice(existingIdx, 1);
}
// 插入到指定位置
if (index < 0 || index >= parentHierarchy.childIds.length) {
parentHierarchy.childIds.push(child.id);
} else {
parentHierarchy.childIds.splice(index, 0, child.id);
}
// 标记缓存脏
this.markCacheDirty(child);
}
/**
* 移除子实体(将其移动到根级)
*/
public removeChild(parent: Entity, child: Entity): boolean {
const parentHierarchy = parent.getComponent(HierarchyComponent);
const childHierarchy = child.getComponent(HierarchyComponent);
if (!parentHierarchy || !childHierarchy) {
return false;
}
if (childHierarchy.parentId !== parent.id) {
return false;
}
const idx = parentHierarchy.childIds.indexOf(child.id);
if (idx !== -1) {
parentHierarchy.childIds.splice(idx, 1);
}
childHierarchy.parentId = null;
this.markCacheDirty(child);
return true;
}
/**
* 移除所有子实体
*/
public removeAllChildren(parent: Entity): void {
const parentHierarchy = parent.getComponent(HierarchyComponent);
if (!parentHierarchy) return;
const childIds = [...parentHierarchy.childIds];
for (const childId of childIds) {
const child = this.scene?.findEntityById(childId);
if (child) {
this.removeChild(parent, child);
}
}
}
/**
* 获取实体的父级
*/
public getParent(entity: Entity): Entity | null {
const hierarchy = entity.getComponent(HierarchyComponent);
if (!hierarchy || hierarchy.parentId === null) {
return null;
}
return this.scene?.findEntityById(hierarchy.parentId) ?? null;
}
/**
* 获取实体的子级列表
*/
public getChildren(entity: Entity): Entity[] {
const hierarchy = entity.getComponent(HierarchyComponent);
if (!hierarchy) return [];
const children: Entity[] = [];
for (const childId of hierarchy.childIds) {
const child = this.scene?.findEntityById(childId);
if (child) {
children.push(child);
}
}
return children;
}
/**
* 获取子级数量
*/
public getChildCount(entity: Entity): number {
const hierarchy = entity.getComponent(HierarchyComponent);
return hierarchy?.childIds.length ?? 0;
}
/**
* 检查实体是否有子级
*/
public hasChildren(entity: Entity): boolean {
return this.getChildCount(entity) > 0;
}
/**
* 检查 ancestor 是否是 entity 的祖先
*/
public isAncestorOf(ancestor: Entity, entity: Entity): boolean {
let current = this.getParent(entity);
let depth = 0;
while (current && depth < HierarchySystem.MAX_DEPTH) {
if (current.id === ancestor.id) {
return true;
}
current = this.getParent(current);
depth++;
}
return false;
}
/**
* 检查 descendant 是否是 entity 的后代
*/
public isDescendantOf(descendant: Entity, entity: Entity): boolean {
return this.isAncestorOf(entity, descendant);
}
/**
* 获取根实体
*/
public getRoot(entity: Entity): Entity {
let current = entity;
let parent = this.getParent(current);
let depth = 0;
while (parent && depth < HierarchySystem.MAX_DEPTH) {
current = parent;
parent = this.getParent(current);
depth++;
}
return current;
}
/**
* 获取实体在层级中的深度
*/
public getDepth(entity: Entity): number {
const hierarchy = entity.getComponent(HierarchyComponent);
if (!hierarchy) return 0;
// 如果缓存有效,直接返回
if (!hierarchy.bCacheDirty) {
return hierarchy.depth;
}
// 重新计算
let depth = 0;
let current = this.getParent(entity);
while (current && depth < HierarchySystem.MAX_DEPTH) {
depth++;
current = this.getParent(current);
}
hierarchy.depth = depth;
return depth;
}
/**
* 检查实体在层级中是否激活
*/
public isActiveInHierarchy(entity: Entity): boolean {
if (!entity.active) return false;
const hierarchy = entity.getComponent(HierarchyComponent);
if (!hierarchy) return entity.active;
// 如果缓存有效,直接返回
if (!hierarchy.bCacheDirty) {
return hierarchy.bActiveInHierarchy;
}
// 重新计算
const parent = this.getParent(entity);
if (!parent) {
hierarchy.bActiveInHierarchy = entity.active;
} else {
hierarchy.bActiveInHierarchy = entity.active && this.isActiveInHierarchy(parent);
}
return hierarchy.bActiveInHierarchy;
}
/**
* 获取所有根实体(没有父级的实体)
*/
public getRootEntities(): Entity[] {
const roots: Entity[] = [];
for (const entity of this.entities) {
const hierarchy = entity.getComponent(HierarchyComponent);
if (hierarchy && hierarchy.parentId === null) {
roots.push(entity);
}
}
return roots;
}
/**
* 根据名称查找子实体
*
* @param entity - 父实体
* @param name - 子实体名称
* @param bRecursive - 是否递归查找
*/
public findChild(entity: Entity, name: string, bRecursive: boolean = false): Entity | null {
const children = this.getChildren(entity);
for (const child of children) {
if (child.name === name) {
return child;
}
}
if (bRecursive) {
for (const child of children) {
const found = this.findChild(child, name, true);
if (found) {
return found;
}
}
}
return null;
}
/**
* 根据标签查找子实体
*
* @param entity - 父实体
* @param tag - 标签值
* @param bRecursive - 是否递归查找
*/
public findChildrenByTag(entity: Entity, tag: number, bRecursive: boolean = false): Entity[] {
const result: Entity[] = [];
const children = this.getChildren(entity);
for (const child of children) {
if ((child.tag & tag) !== 0) {
result.push(child);
}
if (bRecursive) {
result.push(...this.findChildrenByTag(child, tag, true));
}
}
return result;
}
/**
* 遍历所有子级
*
* @param entity - 父实体
* @param callback - 回调函数
* @param bRecursive - 是否递归遍历
*/
public forEachChild(
entity: Entity,
callback: (child: Entity) => void,
bRecursive: boolean = false
): void {
const children = this.getChildren(entity);
for (const child of children) {
callback(child);
if (bRecursive) {
this.forEachChild(child, callback, true);
}
}
}
/**
* 扁平化层级树(用于虚拟化渲染)
*
* @param expandedIds - 展开的实体 ID 集合
* @returns 扁平化的节点列表
*/
public flattenHierarchy(expandedIds: Set<number>): Array<{
entity: Entity;
depth: number;
bHasChildren: boolean;
bIsExpanded: boolean;
}> {
const result: Array<{
entity: Entity;
depth: number;
bHasChildren: boolean;
bIsExpanded: boolean;
}> = [];
const traverse = (entity: Entity, depth: number): void => {
const bHasChildren = this.hasChildren(entity);
const bIsExpanded = expandedIds.has(entity.id);
result.push({
entity,
depth,
bHasChildren,
bIsExpanded
});
if (bHasChildren && bIsExpanded) {
for (const child of this.getChildren(entity)) {
traverse(child, depth + 1);
}
}
};
for (const root of this.getRootEntities()) {
traverse(root, 0);
}
return result;
}
/**
* 标记缓存为脏,并添加到脏实体集合
* Mark cache as dirty and add to dirty entity set
*/
private markCacheDirty(entity: Entity): void {
const hierarchy = entity.getComponent(HierarchyComponent);
if (!hierarchy) return;
// 如果已经是脏的,跳过(避免重复递归)
// Skip if already dirty (avoid redundant recursion)
if (hierarchy.bCacheDirty) return;
hierarchy.bCacheDirty = true;
this.dirtyEntities.add(entity);
// 递归标记所有子级 | Recursively mark all children
for (const childId of hierarchy.childIds) {
const child = this.scene?.findEntityById(childId);
if (child) {
this.markCacheDirty(child);
}
}
}
/**
* 更新层级缓存
*/
private updateHierarchyCache(entity: Entity): void {
const hierarchy = entity.getComponent(HierarchyComponent);
if (!hierarchy) return;
// 计算深度
hierarchy.depth = this.getDepth(entity);
// 计算激活状态
hierarchy.bActiveInHierarchy = this.isActiveInHierarchy(entity);
// 标记缓存有效
hierarchy.bCacheDirty = false;
}
/**
* 当实体被添加到系统时,将其加入脏集合
* When entity is added to system, add it to dirty set
*/
protected override onAdded(entity: Entity): void {
const hierarchy = entity.getComponent(HierarchyComponent);
if (hierarchy && hierarchy.bCacheDirty) {
this.dirtyEntities.add(entity);
}
}
/**
* 当实体被移除时清理层级关系
* When entity is removed, clean up hierarchy relationships
*/
protected override onRemoved(entity: Entity): void {
// 从脏集合中移除 | Remove from dirty set
this.dirtyEntities.delete(entity);
const hierarchy = entity.getComponent(HierarchyComponent);
if (!hierarchy) return;
// 从父级移除 | Remove from parent
if (hierarchy.parentId !== null) {
const parent = this.scene?.findEntityById(hierarchy.parentId);
if (parent) {
const parentHierarchy = parent.getComponent(HierarchyComponent);
if (parentHierarchy) {
const idx = parentHierarchy.childIds.indexOf(entity.id);
if (idx !== -1) {
parentHierarchy.childIds.splice(idx, 1);
}
}
}
}
// 处理子级:将子级移动到根级
// Handle children: move children to root level
for (const childId of hierarchy.childIds) {
const child = this.scene?.findEntityById(childId);
if (child) {
const childHierarchy = child.getComponent(HierarchyComponent);
if (childHierarchy) {
childHierarchy.parentId = null;
this.markCacheDirty(child);
}
}
}
}
public override dispose(): void {
// 清理脏实体集合 | Clear dirty entity set
this.dirtyEntities.clear();
}
}

View File

@@ -0,0 +1,58 @@
import { EntitySystem } from './EntitySystem';
import { Matcher } from '../Utils/Matcher';
import { Time } from '../../Utils/Time';
/**
* 间隔系统抽象类
* 定义一个按时间间隔处理的抽象类继承自EntitySystem类
* 子类需要实现process方法用于实现具体的处理逻辑
*/
export abstract class IntervalSystem extends EntitySystem {
/** 累积增量以跟踪间隔 */
private acc: number = 0;
/** 更新之间需要等待多长时间 */
private readonly interval: number;
/** 时间间隔的余数,用于计算下一次需要等待的时间 */
private intervalRemainder: number = 0;
/**
* 构造函数,初始化时间间隔
* @param interval 时间间隔
* @param matcher 实体匹配器
*/
constructor(interval: number, matcher?: Matcher) {
super(matcher);
this.interval = interval;
}
/**
* 判断是否需要进行处理
* 如果需要进行处理则更新累积增量和时间间隔余数返回true
* 否则返回false
*/
protected override onCheckProcessing(): boolean {
// 更新累积增量
this.acc += Time.deltaTime;
// 如果累积增量超过时间间隔,则进行处理
if (this.acc >= this.interval) {
// 更新时间间隔余数
this.intervalRemainder = this.acc - this.interval;
// 重置累积增量
this.acc = 0;
// 返回true表示需要进行处理
return true;
}
// 返回false表示不需要进行处理
return false;
}
/**
* 获取本系统上次处理后的实际delta值
* 实际delta值等于时间间隔加上时间间隔余数
*/
protected getIntervalDelta(): number {
return this.interval + this.intervalRemainder;
}
}

View File

@@ -0,0 +1,23 @@
import { EntitySystem } from './EntitySystem';
import { Entity } from '../Entity';
import { Matcher } from '../Utils/Matcher';
/**
* 被动实体系统
* 定义一个被动的实体系统继承自EntitySystem类
* 被动的实体系统不会对实体进行任何修改,只会被动地接收实体的变化事件
*/
export abstract class PassiveSystem extends EntitySystem {
constructor(matcher?: Matcher) {
super(matcher);
}
/**
* 不进行任何处理
* @param entities 实体数组,未被使用
*/
protected override process(_entities: Entity[]): void {
// 被动系统不进行任何处理
}
}

View File

@@ -0,0 +1,384 @@
/**
* @zh 平台适配的 Worker 池管理器
* @en Platform-adapted Worker Pool Manager
*/
import type { PlatformWorker } from '../../Platform/IPlatformAdapter';
// =============================================================================
// 常量 | Constants
// =============================================================================
const ERROR_POOL_DESTROYED = 'Worker pool has been destroyed';
// =============================================================================
// 类型定义 | Type Definitions
// =============================================================================
/**
* @zh Worker 任务接口
* @en Worker task interface
*/
interface IWorkerTask {
/** @zh 任务唯一标识 @en Unique task identifier */
readonly id: string;
/** @zh 任务数据 @en Task data */
readonly data: Record<string, unknown>;
/** @zh 成功回调 @en Success callback */
readonly resolve: (result: unknown) => void;
/** @zh 失败回调 @en Error callback */
readonly reject: (error: Error) => void;
}
/**
* @zh Worker 消息数据接口
* @en Worker message data interface
*/
interface IWorkerMessageData {
type?: string;
id?: string;
error?: string;
result?: unknown;
success?: boolean;
}
/**
* @zh Worker 池状态接口
* @en Worker pool status interface
*/
export interface IWorkerPoolStatus {
/** @zh Worker 总数 @en Total number of workers */
readonly total: number;
/** @zh 空闲 Worker 数量 @en Number of idle workers */
readonly idle: number;
/** @zh 忙碌 Worker 数量 @en Number of busy workers */
readonly busy: number;
/** @zh 初始化中的 Worker 数量 @en Number of initializing workers */
readonly initializing: number;
/** @zh 队列中等待的任务数 @en Number of queued tasks */
readonly queuedTasks: number;
}
/**
* @zh Worker 状态枚举
* @en Worker state enum
*/
const enum WorkerState {
/** @zh 初始化中 @en Initializing */
Initializing = 0,
/** @zh 空闲 @en Idle */
Idle = 1,
/** @zh 忙碌 @en Busy */
Busy = 2
}
// =============================================================================
// PlatformWorkerPool
// =============================================================================
/**
* @zh 平台适配的 Worker 池管理器
* @en Platform-adapted Worker Pool Manager
*
* @zh 管理 Worker 生命周期、任务分发和状态跟踪
* @en Manages Worker lifecycle, task distribution and state tracking
*/
export class PlatformWorkerPool {
private readonly workers: PlatformWorker[];
private readonly workerStates: Map<number, WorkerState> = new Map();
private readonly pendingTasks: Map<number, IWorkerTask> = new Map();
private readonly taskQueue: IWorkerTask[] = [];
private taskCounter = 0;
private _isDestroyed = false;
// =========================================================================
// 构造函数 | Constructor
// =========================================================================
/**
* @zh 创建 Worker 池
* @en Create Worker pool
*
* @param workers - @zh Worker 实例数组 @en Array of Worker instances
* @param sharedBuffer - @zh 共享内存缓冲区 @en Shared memory buffer
*/
constructor(
workers: PlatformWorker[],
sharedBuffer?: SharedArrayBuffer | null
) {
this.workers = workers;
this.initializeWorkers(sharedBuffer);
}
// =========================================================================
// 公共属性 | Public Properties
// =========================================================================
/**
* @zh 池是否已销毁
* @en Whether the pool has been destroyed
*/
get isDestroyed(): boolean {
return this._isDestroyed;
}
/**
* @zh Worker 数量
* @en Number of workers in the pool
*/
get workerCount(): number {
return this.workers.length;
}
/**
* @zh 所有 Worker 是否已就绪(无初始化中的 Worker
* @en Whether all workers are ready (no initializing workers)
*/
get isReady(): boolean {
if (this._isDestroyed) return false;
for (const state of this.workerStates.values()) {
if (state === WorkerState.Initializing) return false;
}
return this.workers.length > 0;
}
/**
* @zh 是否有待处理的任务(队列中或执行中)
* @en Whether there are pending tasks (queued or executing)
*/
get hasPendingTasks(): boolean {
return this.taskQueue.length > 0 || this.pendingTasks.size > 0;
}
// =========================================================================
// 公共方法 | Public Methods
// =========================================================================
/**
* @zh 执行 SharedArrayBuffer 任务
* @en Execute SharedArrayBuffer task
*
* @param data - @zh 任务数据 @en Task data
* @returns @zh 任务完成的 Promise @en Promise that resolves when task completes
*/
executeSharedBuffer(data: Record<string, unknown>): Promise<void> {
return this.createTask<void>(
`shared-${++this.taskCounter}`,
{ ...data, type: 'shared' },
() => undefined
);
}
/**
* @zh 执行普通任务
* @en Execute normal task
*
* @param data - @zh 任务数据 @en Task data
* @returns @zh 包含任务结果的 Promise @en Promise with task result
*/
execute<TResult = unknown>(data: Record<string, unknown>): Promise<TResult> {
return this.createTask<TResult>(
`task-${++this.taskCounter}`,
data,
(result) => result as TResult
);
}
/**
* @zh 获取 Worker 池状态
* @en Get Worker pool status
*
* @returns @zh 池状态对象 @en Pool status object
*/
getStatus(): IWorkerPoolStatus {
const status = { idle: 0, busy: 0, initializing: 0 };
for (const state of this.workerStates.values()) {
if (state === WorkerState.Idle) status.idle++;
else if (state === WorkerState.Busy) status.busy++;
else if (state === WorkerState.Initializing) status.initializing++;
}
return {
total: this.workers.length,
...status,
queuedTasks: this.taskQueue.length
};
}
/**
* @zh 销毁 Worker 池,释放所有资源
* @en Destroy Worker pool and release all resources
*/
destroy(): void {
if (this._isDestroyed) return;
this._isDestroyed = true;
const destroyError = new Error(ERROR_POOL_DESTROYED);
// Reject all pending and queued tasks
for (const task of this.pendingTasks.values()) {
task.reject(destroyError);
}
for (const task of this.taskQueue) {
task.reject(destroyError);
}
// Terminate all workers
for (const worker of this.workers) {
worker.terminate();
}
// Clear state
this.workers.length = 0;
this.taskQueue.length = 0;
this.pendingTasks.clear();
this.workerStates.clear();
}
// =========================================================================
// 私有方法 | Private Methods
// =========================================================================
/**
* @zh 初始化所有 Worker
* @en Initialize all Workers
*/
private initializeWorkers(sharedBuffer?: SharedArrayBuffer | null): void {
for (let i = 0; i < this.workers.length; i++) {
const worker = this.workers[i];
if (!worker) continue;
this.workerStates.set(i, sharedBuffer ? WorkerState.Initializing : WorkerState.Idle);
worker.onMessage((event) => this.handleMessage(i, event.data));
worker.onError((error) => this.handleError(i, error));
if (sharedBuffer) {
worker.postMessage({ type: 'init', sharedBuffer });
}
}
}
/**
* @zh 创建并入队任务
* @en Create and enqueue task
*/
private createTask<T>(
id: string,
data: Record<string, unknown>,
transform: (result: unknown) => T
): Promise<T> {
return new Promise((resolve, reject) => {
if (this._isDestroyed) {
reject(new Error(ERROR_POOL_DESTROYED));
return;
}
this.enqueueTask({
id,
data,
resolve: (result) => resolve(transform(result)),
reject
});
});
}
/**
* @zh 将任务加入队列
* @en Enqueue task
*/
private enqueueTask(task: IWorkerTask): void {
this.taskQueue.push(task);
this.dispatchTasks();
}
/**
* @zh 分发任务到空闲 Worker
* @en Dispatch tasks to idle Workers
*/
private dispatchTasks(): void {
while (this.taskQueue.length > 0) {
const workerIndex = this.findIdleWorker();
if (workerIndex === -1) break;
const task = this.taskQueue.shift()!;
this.assignTask(workerIndex, task);
}
}
/**
* @zh 查找空闲 Worker
* @en Find idle Worker
*/
private findIdleWorker(): number {
for (let i = 0; i < this.workers.length; i++) {
if (this.workerStates.get(i) === WorkerState.Idle) {
return i;
}
}
return -1;
}
/**
* @zh 分配任务给指定 Worker
* @en Assign task to specified Worker
*/
private assignTask(workerIndex: number, task: IWorkerTask): void {
const worker = this.workers[workerIndex];
if (!worker) return;
this.workerStates.set(workerIndex, WorkerState.Busy);
this.pendingTasks.set(workerIndex, task);
worker.postMessage({
id: task.id,
...task.data
});
}
/**
* @zh 处理 Worker 消息
* @en Handle Worker message
*/
private handleMessage(workerIndex: number, data: IWorkerMessageData): void {
if (data.type === 'init') {
this.workerStates.set(workerIndex, WorkerState.Idle);
this.dispatchTasks();
return;
}
if (data.error) {
this.completeTask(workerIndex, new Error(data.error));
} else {
this.completeTask(workerIndex, undefined, data.result);
}
}
/**
* @zh 处理 Worker 错误
* @en Handle Worker error
*/
private handleError(workerIndex: number, error: ErrorEvent): void {
this.completeTask(workerIndex, new Error(error.message));
}
/**
* @zh 完成任务并释放 Worker
* @en Complete task and release Worker
*/
private completeTask(workerIndex: number, error?: Error, result?: unknown): void {
const task = this.pendingTasks.get(workerIndex);
if (!task) return;
this.pendingTasks.delete(workerIndex);
this.workerStates.set(workerIndex, WorkerState.Idle);
if (error) {
task.reject(error);
} else {
task.resolve(result);
}
this.dispatchTasks();
}
}

View File

@@ -0,0 +1,29 @@
import { EntitySystem } from './EntitySystem';
import { Entity } from '../Entity';
import { Matcher } from '../Utils/Matcher';
/**
* 处理系统抽象类
* 定义一个处理实体的抽象类继承自EntitySystem类
* 子类需要实现processSystem方法用于实现具体的处理逻辑
*/
export abstract class ProcessingSystem extends EntitySystem {
constructor(matcher?: Matcher) {
super(matcher);
}
/**
* 处理实体每帧调用processSystem方法进行处理
* @param entities 实体数组,未被使用
*/
protected override process(_entities: Entity[]): void {
// 调用子类实现的processSystem方法进行实体处理
this.processSystem();
}
/**
* 处理实体的具体方法,由子类实现
*/
public abstract processSystem(): void;
}

View File

@@ -0,0 +1,902 @@
/**
* @zh 支持 Worker 并行处理的实体系统基类
* @en Base class for entity systems with Worker parallel processing support
*/
import { Entity } from '../Entity';
import { EntitySystem } from './EntitySystem';
import { Matcher } from '../Utils/Matcher';
import { Time } from '../../Utils/Time';
import { PlatformManager } from '../../Platform/PlatformManager';
import type { IPlatformAdapter, PlatformWorker } from '../../Platform/IPlatformAdapter';
import { getSystemInstanceTypeName } from '../Decorators';
import { PlatformWorkerPool } from './PlatformWorkerPool';
// =============================================================================
// 类型定义 | Type Definitions
// =============================================================================
/**
* @zh Worker 处理函数类型
* @en Worker process function type
*
* @zh 用户编写的处理逻辑,会被序列化到 Worker 中执行
* @en User-defined processing logic, serialized and executed in Worker
*/
export type WorkerProcessFunction<T extends Record<string, unknown> = Record<string, unknown>> = (
entities: T[],
deltaTime: number,
config?: unknown
) => T[] | Promise<T[]>;
/**
* @zh SharedArrayBuffer 处理函数类型
* @en SharedArrayBuffer process function type
*/
export type SharedArrayBufferProcessFunction = (
sharedFloatArray: Float32Array,
startIndex: number,
endIndex: number,
deltaTime: number,
systemConfig?: unknown
) => void;
/**
* @zh Worker 系统配置接口
* @en Worker system configuration interface
*/
export interface IWorkerSystemConfig {
/**
* @zh 是否启用 Worker 并行处理
* @en Enable Worker parallel processing
* @default true
*/
enableWorker?: boolean;
/**
* @zh Worker 数量,默认为 CPU 核心数,自动限制在系统最大值内
* @en Worker count, defaults to CPU cores, auto-limited to system max
*/
workerCount?: number;
/**
* @zh 每个 Worker 处理的实体数量,用于控制负载分布
* @en Entities per Worker, for load distribution control
*/
entitiesPerWorker?: number;
/**
* @zh 系统配置数据,会传递给 Worker
* @en System config data, passed to Worker
*/
systemConfig?: unknown;
/**
* @zh 是否使用 SharedArrayBuffer 优化
* @en Use SharedArrayBuffer optimization
*/
useSharedArrayBuffer?: boolean;
/**
* @zh 每个实体在 SharedArrayBuffer 中占用的 Float32 数量
* @en Float32 count per entity in SharedArrayBuffer
*/
entityDataSize?: number;
/**
* @zh 最大实体数量(用于预分配 SharedArrayBuffer
* @en Max entities (for SharedArrayBuffer pre-allocation)
* @default 10000
*/
maxEntities?: number;
/**
* @zh 预编译 Worker 脚本路径(微信小游戏等不支持动态脚本的平台必需)
* @en Pre-compiled Worker script path (required for platforms like WeChat Mini Game)
*
* @example
* ```typescript
* // 微信小游戏使用方式:
* workerScriptPath: 'workers/physics-worker.js'
* ```
*/
workerScriptPath?: string;
}
/**
* @zh 内部使用的完整配置类型
* @en Internal complete config type
*/
type ResolvedConfig = Required<Omit<IWorkerSystemConfig, 'systemConfig' | 'entitiesPerWorker' | 'workerScriptPath'>> & {
systemConfig?: unknown;
entitiesPerWorker?: number;
workerScriptPath?: string;
};
/**
* @zh 处理模式
* @en Processing mode
*/
export type ProcessingMode = 'shared-buffer' | 'worker' | 'sync';
// =============================================================================
// WorkerEntitySystem
// =============================================================================
/**
* @zh 支持 Worker 并行处理的 EntitySystem 基类
* @en Base EntitySystem class with Worker parallel processing support
*
* @zh 支持传统 Worker 和 SharedArrayBuffer 两种优化模式:
* - 传统模式:数据序列化传输,适用于复杂计算
* - SharedArrayBuffer 模式:零拷贝数据共享,适用于大量简单计算
*
* @en Supports two optimization modes:
* - Traditional mode: Data serialization, for complex calculations
* - SharedArrayBuffer mode: Zero-copy sharing, for many simple calculations
*
* @example
* ```typescript
* class PhysicsSystem extends WorkerEntitySystem<PhysicsData> {
* constructor() {
* super(Matcher.all(Transform, Velocity), {
* enableWorker: true,
* workerCount: 8,
* systemConfig: { gravity: 100 }
* });
* }
*
* protected getDefaultEntityDataSize(): number {
* return 6; // x, y, vx, vy, radius, mass
* }
*
* protected extractEntityData(entity: Entity): PhysicsData {
* // Extract component data
* }
*
* protected workerProcess(entities: PhysicsData[], deltaTime: number, config: unknown): PhysicsData[] {
* // Pure function executed in Worker
* }
*
* protected applyResult(entity: Entity, result: PhysicsData): void {
* // Apply result back to components
* }
* }
* ```
*/
export abstract class WorkerEntitySystem<TEntityData = unknown> extends EntitySystem {
// =========================================================================
// 成员变量 | Member Variables
// =========================================================================
protected config: ResolvedConfig;
protected sharedBuffer: SharedArrayBuffer | null = null;
protected sharedFloatArray: Float32Array | null = null;
private workerPool: PlatformWorkerPool | null = null;
private isProcessing = false;
private hasLoggedSyncMode = false;
private readonly platformAdapter: IPlatformAdapter;
// =========================================================================
// 构造函数 | Constructor
// =========================================================================
constructor(matcher?: Matcher, config: IWorkerSystemConfig = {}) {
super(matcher);
this.platformAdapter = PlatformManager.getInstance().getAdapter();
this.config = this.resolveConfig(config);
if (this.config.enableWorker && this.isWorkerSupported()) {
this.initializeWorkerSystem();
}
}
// =========================================================================
// 配置解析 | Config Resolution
// =========================================================================
/**
* @zh 解析并验证配置
* @en Resolve and validate config
*/
private resolveConfig(config: IWorkerSystemConfig): ResolvedConfig {
const maxWorkerCount = this.getMaxSystemWorkerCount();
const requestedWorkerCount = config.workerCount ?? maxWorkerCount;
const validatedWorkerCount = Math.min(requestedWorkerCount, maxWorkerCount);
if (requestedWorkerCount > maxWorkerCount) {
this.logger.warn(
`请求 ${requestedWorkerCount} 个 Worker但系统最多支持 ${maxWorkerCount} 个。` +
`实际使用 ${validatedWorkerCount} 个 Worker。`
);
}
return {
enableWorker: config.enableWorker ?? true,
workerCount: validatedWorkerCount,
useSharedArrayBuffer: config.useSharedArrayBuffer ?? this.isSharedArrayBufferSupported(),
entityDataSize: config.entityDataSize ?? this.getDefaultEntityDataSize(),
maxEntities: config.maxEntities ?? 10000,
systemConfig: config.systemConfig,
...(config.entitiesPerWorker !== undefined && { entitiesPerWorker: config.entitiesPerWorker }),
...(config.workerScriptPath !== undefined && { workerScriptPath: config.workerScriptPath })
};
}
// =========================================================================
// 平台能力检测 | Platform Capability Detection
// =========================================================================
/**
* @zh 检查是否支持 Worker
* @en Check Worker support
*/
private isWorkerSupported(): boolean {
return this.platformAdapter.isWorkerSupported();
}
/**
* @zh 检查是否支持 SharedArrayBuffer
* @en Check SharedArrayBuffer support
*/
private isSharedArrayBufferSupported(): boolean {
return this.platformAdapter.isSharedArrayBufferSupported();
}
/**
* @zh 获取系统支持的最大 Worker 数量
* @en Get max Worker count supported by system
*/
private getMaxSystemWorkerCount(): number {
return this.platformAdapter.getPlatformConfig().maxWorkerCount;
}
// =========================================================================
// 初始化 | Initialization
// =========================================================================
/**
* @zh 初始化 Worker 系统
* @en Initialize Worker system
*/
private initializeWorkerSystem(): void {
if (this.config.useSharedArrayBuffer) {
this.initializeSharedArrayBuffer();
}
this.initializeWorkerPool();
}
/**
* @zh 初始化 SharedArrayBuffer
* @en Initialize SharedArrayBuffer
*/
private initializeSharedArrayBuffer(): void {
try {
if (!this.isSharedArrayBufferSupported()) {
this.fallbackToSingleWorker('平台不支持 SharedArrayBuffer');
return;
}
const bufferSize = this.config.maxEntities * this.config.entityDataSize * 4;
this.sharedBuffer = this.platformAdapter.createSharedArrayBuffer(bufferSize);
if (this.sharedBuffer) {
this.sharedFloatArray = new Float32Array(this.sharedBuffer);
this.logger.info(`${this.systemName}: SharedArrayBuffer 初始化成功 (${bufferSize} bytes)`);
}
} catch (error) {
this.fallbackToSingleWorker('SharedArrayBuffer 初始化失败');
this.logger.warn(`${this.systemName}:`, error);
}
}
/**
* @zh 降级到单 Worker 模式
* @en Fallback to single Worker mode
*/
private fallbackToSingleWorker(reason: string): void {
this.logger.warn(`${this.systemName}: ${reason},降级到单 Worker 模式`);
this.config.useSharedArrayBuffer = false;
this.config.workerCount = 1;
this.sharedBuffer = null;
this.sharedFloatArray = null;
}
/**
* @zh 初始化 Worker 池
* @en Initialize Worker pool
*/
private initializeWorkerPool(): void {
try {
const scriptOrPath = this.resolveWorkerScript();
if (!scriptOrPath) {
this.config.enableWorker = false;
return;
}
const workers = this.createWorkers(scriptOrPath);
this.workerPool = new PlatformWorkerPool(workers, this.sharedBuffer);
} catch (error) {
this.logger.error(`${this.systemName}: Worker 池初始化失败`, error);
this.config.enableWorker = false;
}
}
/**
* @zh 解析 Worker 脚本
* @en Resolve Worker script
*/
private resolveWorkerScript(): string | null {
const platformConfig = this.platformAdapter.getPlatformConfig();
// External script path (WeChat Mini Game, etc.)
if (this.config.workerScriptPath) {
this.logger.info(`${this.systemName}: 使用外部 Worker 文件: ${this.config.workerScriptPath}`);
return this.config.workerScriptPath;
}
// Platform doesn't support dynamic scripts
if (platformConfig.limitations?.noEval) {
this.logger.error(
`${this.systemName}: 当前平台不支持动态 Worker 脚本,` +
`请配置 workerScriptPath 指定预编译的 Worker 文件`
);
return null;
}
// Dynamic script (browsers, etc.)
const script = this.createWorkerScript();
return (platformConfig.workerScriptPrefix || '') + script;
}
/**
* @zh 创建 Worker 实例数组
* @en Create Worker instance array
*/
private createWorkers(scriptOrPath: string): PlatformWorker[] {
const workers: PlatformWorker[] = [];
for (let i = 0; i < this.config.workerCount; i++) {
const worker = this.platformAdapter.createWorker(scriptOrPath, {
name: `${this.systemName}-Worker-${i}`
});
workers.push(worker);
}
return workers;
}
/**
* @zh 创建 Worker 脚本
* @en Create Worker script
*/
private createWorkerScript(): string {
const functionBody = this.extractFunctionBody(this.workerProcess.toString());
const sharedProcessBody = this.getSharedProcessFunctionBody();
const entityDataSize = this.config.entityDataSize;
return `
let sharedFloatArray = null;
const ENTITY_DATA_SIZE = ${entityDataSize};
self.onmessage = function(e) {
const { type, id, entities, deltaTime, systemConfig, startIndex, endIndex, sharedBuffer } = e.data;
try {
if (type === 'init' && sharedBuffer) {
sharedFloatArray = new Float32Array(sharedBuffer);
self.postMessage({ type: 'init', success: true });
return;
}
if (type === 'shared') {
if (!sharedFloatArray) {
self.postMessage({ id, error: 'SharedArrayBuffer not initialized' });
return;
}
processSharedArrayBuffer(startIndex, endIndex, deltaTime, systemConfig);
self.postMessage({ id, result: null });
return;
}
if (entities) {
function workerProcess(entities, deltaTime, systemConfig) {
${functionBody}
}
const result = workerProcess(entities, deltaTime, systemConfig);
if (result && typeof result.then === 'function') {
result.then(finalResult => {
self.postMessage({ id, result: finalResult });
}).catch(error => {
self.postMessage({ id, error: error.message });
});
} else {
self.postMessage({ id, result });
}
}
} catch (error) {
self.postMessage({ id, error: error.message });
}
};
function processSharedArrayBuffer(startIndex, endIndex, deltaTime, systemConfig) {
if (!sharedFloatArray) return;
${sharedProcessBody}
}
`;
}
/**
* @zh 提取函数体
* @en Extract function body
*/
private extractFunctionBody(methodStr: string): string {
const match = methodStr.match(/\{([\s\S]*)\}/);
if (!match || match[1] === undefined) {
throw new Error('无法解析 workerProcess 方法');
}
return match[1];
}
/**
* @zh 获取 SharedArrayBuffer 处理函数体
* @en Get SharedArrayBuffer process function body
*/
private getSharedProcessFunctionBody(): string {
const processFunc = this.getSharedArrayBufferProcessFunction?.();
if (!processFunc) return '';
const body = this.extractFunctionBody(processFunc.toString());
return `
const userProcessFunction = function(sharedFloatArray, startIndex, endIndex, deltaTime, systemConfig) {
${body}
};
userProcessFunction(sharedFloatArray, startIndex, endIndex, deltaTime, systemConfig);
`;
}
// =========================================================================
// 处理逻辑 | Processing Logic
// =========================================================================
/**
* @zh 重写 process 方法,支持 Worker 并行处理
* @en Override process method with Worker parallel processing
*/
protected override process(entities: readonly Entity[]): void {
if (this.isProcessing) return;
this.isProcessing = true;
const mode = this.getCurrentProcessingMode();
try {
switch (mode) {
case 'shared-buffer':
this.processWithSharedArrayBuffer(entities).finally(() => {
this.isProcessing = false;
});
break;
case 'worker':
this.processWithWorker(entities).finally(() => {
this.isProcessing = false;
});
break;
case 'sync':
default:
this.processSynchronously(entities);
this.isProcessing = false;
break;
}
} catch (error) {
this.isProcessing = false;
this.logger.error(`${this.systemName}: 处理失败`, error);
throw error;
}
}
/**
* @zh 获取当前处理模式
* @en Get current processing mode
*/
private getCurrentProcessingMode(): ProcessingMode {
if (!this.config.enableWorker || !this.workerPool) {
if (!this.hasLoggedSyncMode) {
this.logger.info(`${this.systemName}: Worker 不可用,使用同步处理`);
this.hasLoggedSyncMode = true;
}
return 'sync';
}
if (this.config.useSharedArrayBuffer && this.sharedFloatArray) {
return 'shared-buffer';
}
return 'worker';
}
/**
* @zh 使用 SharedArrayBuffer 优化的 Worker 处理
* @en Worker processing with SharedArrayBuffer optimization
*/
private async processWithSharedArrayBuffer(entities: readonly Entity[]): Promise<void> {
if (!this.sharedFloatArray || !this.workerPool) return;
this.writeEntitiesToBuffer(entities);
const tasks = this.createBatchTasks(entities.length, true);
await Promise.all(tasks);
this.readResultsFromBuffer(entities);
}
/**
* @zh 使用 Worker 并行处理
* @en Worker parallel processing
*/
private async processWithWorker(entities: readonly Entity[]): Promise<void> {
if (!this.workerPool) return;
const entityData = entities.map(entity => this.extractEntityData(entity));
const batches = this.createDataBatches(entityData);
const deltaTime = Time.deltaTime;
const results = await Promise.all(
batches.map(batch =>
this.workerPool!.execute<TEntityData[]>({
entities: batch,
deltaTime,
systemConfig: this.config.systemConfig
})
)
);
this.applyBatchResults(entities, results);
}
/**
* @zh 同步处理fallback
* @en Synchronous processing (fallback)
*/
private processSynchronously(entities: readonly Entity[]): void {
const entityData = entities.map(entity => this.extractEntityData(entity));
const deltaTime = Time.deltaTime;
const results = this.workerProcess(entityData, deltaTime, this.config.systemConfig);
if (results && typeof (results as Promise<TEntityData[]>).then === 'function') {
(results as Promise<TEntityData[]>).then(finalResults => {
this.applyResults(entities, finalResults);
});
} else {
this.applyResults(entities, results as TEntityData[]);
}
}
// =========================================================================
// 批次处理 | Batch Processing
// =========================================================================
/**
* @zh 创建数据批次
* @en Create data batches
*/
private createDataBatches<T>(data: T[]): T[][] {
return this.splitIntoBatches(
data.length,
(start, end) => data.slice(start, end)
);
}
/**
* @zh 创建批次任务
* @en Create batch tasks
*/
private createBatchTasks(entityCount: number, useSharedBuffer: boolean): Promise<void>[] {
return this.splitIntoBatches(
entityCount,
(startIndex, endIndex) => {
if (useSharedBuffer) {
return this.workerPool!.executeSharedBuffer({
startIndex,
endIndex,
deltaTime: Time.deltaTime,
systemConfig: this.config.systemConfig
});
}
return Promise.resolve();
}
);
}
/**
* @zh 通用批次分割逻辑
* @en Generic batch splitting logic
*/
private splitIntoBatches<T>(
totalCount: number,
createBatch: (start: number, end: number) => T
): T[] {
const batches: T[] = [];
const { workerCount, entitiesPerWorker } = this.config;
if (entitiesPerWorker) {
for (let i = 0; i < totalCount; i += entitiesPerWorker) {
const end = Math.min(i + entitiesPerWorker, totalCount);
batches.push(createBatch(i, end));
}
} else {
const batchSize = Math.ceil(totalCount / workerCount);
for (let i = 0; i < workerCount; i++) {
const start = i * batchSize;
const end = Math.min(start + batchSize, totalCount);
if (start < totalCount) {
batches.push(createBatch(start, end));
}
}
}
return batches;
}
// =========================================================================
// 结果应用 | Result Application
// =========================================================================
/**
* @zh 应用批次结果
* @en Apply batch results
*/
private applyBatchResults(entities: readonly Entity[], batchResults: TEntityData[][]): void {
let entityIndex = 0;
for (const batchResult of batchResults) {
for (const result of batchResult) {
if (entityIndex < entities.length && result) {
this.applyResult(entities[entityIndex]!, result);
}
entityIndex++;
}
}
}
/**
* @zh 应用结果数组
* @en Apply results array
*/
private applyResults(entities: readonly Entity[], results: TEntityData[]): void {
for (let i = 0; i < entities.length && i < results.length; i++) {
this.applyResult(entities[i]!, results[i]!);
}
}
// =========================================================================
// SharedArrayBuffer 操作 | SharedArrayBuffer Operations
// =========================================================================
/**
* @zh 将实体数据写入 SharedArrayBuffer
* @en Write entity data to SharedArrayBuffer
*/
private writeEntitiesToBuffer(entities: readonly Entity[]): void {
if (!this.sharedFloatArray) return;
const count = Math.min(entities.length, this.config.maxEntities);
for (let i = 0; i < count; i++) {
const data = this.extractEntityData(entities[i]!);
const offset = i * this.config.entityDataSize;
this.writeEntityToBuffer(data, offset);
}
}
/**
* @zh 从 SharedArrayBuffer 读取结果并应用
* @en Read results from SharedArrayBuffer and apply
*/
private readResultsFromBuffer(entities: readonly Entity[]): void {
if (!this.sharedFloatArray) return;
const count = Math.min(entities.length, this.config.maxEntities);
for (let i = 0; i < count; i++) {
const offset = i * this.config.entityDataSize;
const result = this.readEntityFromBuffer(offset);
if (result) {
this.applyResult(entities[i]!, result);
}
}
}
// =========================================================================
// 配置更新 | Config Update
// =========================================================================
/**
* @zh 更新 Worker 配置
* @en Update Worker config
*/
public updateConfig(newConfig: Partial<IWorkerSystemConfig>): void {
const oldConfig = { ...this.config };
if (newConfig.workerCount !== undefined) {
const maxCount = this.getMaxSystemWorkerCount();
const validated = Math.min(newConfig.workerCount, maxCount);
if (newConfig.workerCount > maxCount) {
this.logger.warn(
`请求 ${newConfig.workerCount} 个 Worker但系统最多支持 ${maxCount} 个。` +
`实际使用 ${validated} 个 Worker。`
);
}
newConfig.workerCount = validated;
}
Object.assign(this.config, newConfig);
if (oldConfig.useSharedArrayBuffer !== this.config.useSharedArrayBuffer) {
this.reinitializeSystem();
} else if (oldConfig.workerCount !== this.config.workerCount) {
this.reinitializeWorkerPool();
} else if (!this.config.enableWorker && this.workerPool) {
this.destroyWorkerPool();
} else if (this.config.enableWorker && !this.workerPool && this.isWorkerSupported()) {
this.initializeWorkerPool();
}
}
/**
* @zh 重新初始化整个系统
* @en Reinitialize entire system
*/
private reinitializeSystem(): void {
this.destroyWorkerPool();
this.sharedBuffer = null;
this.sharedFloatArray = null;
if (!this.config.useSharedArrayBuffer) {
this.config.workerCount = 1;
}
if (this.config.enableWorker && this.isWorkerSupported()) {
this.initializeWorkerSystem();
}
}
/**
* @zh 重新初始化 Worker 池
* @en Reinitialize Worker pool
*/
private reinitializeWorkerPool(): void {
this.destroyWorkerPool();
if (this.config.enableWorker && this.isWorkerSupported()) {
this.initializeWorkerPool();
}
}
/**
* @zh 销毁 Worker 池
* @en Destroy Worker pool
*/
private destroyWorkerPool(): void {
if (this.workerPool) {
this.workerPool.destroy();
this.workerPool = null;
}
}
// =========================================================================
// 公共 API | Public API
// =========================================================================
/**
* @zh 获取 Worker 系统信息
* @en Get Worker system info
*/
public getWorkerInfo(): {
enabled: boolean;
workerCount: number;
entitiesPerWorker?: number;
maxSystemWorkerCount: number;
isProcessing: boolean;
sharedArrayBufferSupported: boolean;
sharedArrayBufferEnabled: boolean;
currentMode: ProcessingMode;
} {
return {
enabled: this.config.enableWorker,
workerCount: this.config.workerCount,
...(this.config.entitiesPerWorker !== undefined && { entitiesPerWorker: this.config.entitiesPerWorker }),
maxSystemWorkerCount: this.getMaxSystemWorkerCount(),
isProcessing: this.isProcessing,
sharedArrayBufferSupported: this.isSharedArrayBufferSupported(),
sharedArrayBufferEnabled: this.config.useSharedArrayBuffer,
currentMode: this.getCurrentProcessingMode()
};
}
// =========================================================================
// 生命周期 | Lifecycle
// =========================================================================
/**
* @zh 销毁系统时清理资源
* @en Clean up resources on destroy
*/
protected override onDestroy(): void {
super.onDestroy();
this.destroyWorkerPool();
}
protected override getLoggerName(): string {
return getSystemInstanceTypeName(this);
}
// =========================================================================
// 抽象方法 | Abstract Methods
// =========================================================================
/**
* @zh 获取实体数据大小 - 子类必须实现
* @en Get entity data size - subclass must implement
*
* @zh 返回每个实体在 SharedArrayBuffer 中占用的 Float32 数量
* @en Returns Float32 count per entity in SharedArrayBuffer
*/
protected abstract getDefaultEntityDataSize(): number;
/**
* @zh 将单个实体数据写入 SharedArrayBuffer - 子类必须实现
* @en Write single entity data to SharedArrayBuffer - subclass must implement
*/
protected abstract writeEntityToBuffer(entityData: TEntityData, offset: number): void;
/**
* @zh 从 SharedArrayBuffer 读取单个实体数据 - 子类必须实现
* @en Read single entity data from SharedArrayBuffer - subclass must implement
*/
protected abstract readEntityFromBuffer(offset: number): TEntityData | null;
/**
* @zh 提取实体数据 - 子类必须实现
* @en Extract entity data - subclass must implement
*/
protected abstract extractEntityData(entity: Entity): TEntityData;
/**
* @zh Worker 处理函数 - 子类必须实现(必须是纯函数)
* @en Worker process function - subclass must implement (must be pure function)
*/
protected abstract workerProcess(
entities: TEntityData[],
deltaTime: number,
systemConfig?: unknown
): TEntityData[] | Promise<TEntityData[]>;
/**
* @zh 应用处理结果 - 子类必须实现
* @en Apply result - subclass must implement
*/
protected abstract applyResult(entity: Entity, result: TEntityData): void;
/**
* @zh 获取 SharedArrayBuffer 处理函数 - 子类可选实现
* @en Get SharedArrayBuffer process function - optional for subclass
*/
protected getSharedArrayBufferProcessFunction?(): SharedArrayBufferProcessFunction;
}
// =============================================================================
// 类型导出(向后兼容)| Type Exports (Backward Compatibility)
// =============================================================================
/**
* @deprecated Use IWorkerSystemConfig instead
*/
export type WorkerSystemConfig = IWorkerSystemConfig;

View File

@@ -0,0 +1,30 @@
/**
* @zh ECS 系统模块导出
* @en ECS Systems Module Exports
*/
// =============================================================================
// 系统类 | System Classes
// =============================================================================
export { EntitySystem } from './EntitySystem';
export { ProcessingSystem } from './ProcessingSystem';
export { PassiveSystem } from './PassiveSystem';
export { IntervalSystem } from './IntervalSystem';
export { WorkerEntitySystem } from './WorkerEntitySystem';
export { HierarchySystem } from './HierarchySystem';
export { PlatformWorkerPool } from './PlatformWorkerPool';
export type { IWorkerPoolStatus } from './PlatformWorkerPool';
// =============================================================================
// Worker 系统类型导出 | Worker System Type Exports
// =============================================================================
export type {
WorkerProcessFunction,
SharedArrayBufferProcessFunction,
IWorkerSystemConfig,
ProcessingMode,
// 向后兼容 | Backward compatibility
WorkerSystemConfig
} from './WorkerEntitySystem';

View File

@@ -0,0 +1,309 @@
/**
* Entity类型安全工具函数
*
* 提供类型安全的组件操作工具函数无需修改Entity类
*/
import { Entity } from './Entity';
import type { Component } from './Component';
import type { ComponentType } from './Core/ComponentStorage';
import type { ComponentConstructor, ComponentInstance } from '../Types/TypeHelpers';
import { HierarchySystem } from './Systems/HierarchySystem';
/**
* 获取组件,如果不存在则抛出错误
*
* @param entity - 实体实例
* @param componentType - 组件类型构造函数
* @returns 组件实例(保证非空)
* @throws {Error} 如果组件不存在
*
* @example
* ```typescript
* const position = requireComponent(entity, Position);
* position.x += 10;
* ```
*/
export function requireComponent<T extends ComponentConstructor>(
entity: Entity,
componentType: T
): ComponentInstance<T> {
const component = entity.getComponent(componentType as unknown as ComponentType<ComponentInstance<T>>);
if (!component) {
throw new Error(
`Component ${componentType.name} not found on entity ${entity.name} (id: ${entity.id})`
);
}
return component;
}
/**
* 尝试获取组件
*
* @param entity - 实体实例
* @param componentType - 组件类型构造函数
* @returns 组件实例或undefined
*
* @example
* ```typescript
* const health = tryGetComponent(entity, Health);
* if (health) {
* health.value -= 10;
* }
* ```
*/
export function tryGetComponent<T extends ComponentConstructor>(
entity: Entity,
componentType: T
): ComponentInstance<T> | undefined {
const component = entity.getComponent(componentType as unknown as ComponentType<ComponentInstance<T>>);
return component !== null ? component : undefined;
}
/**
* 批量获取组件
*
* @param entity - 实体实例
* @param types - 组件类型构造函数数组
* @returns 组件实例元组每个元素可能为null
*
* @example
* ```typescript
* const [pos, vel, health] = getComponents(entity, Position, Velocity, Health);
* if (pos && vel && health) {
* pos.x += vel.dx;
* }
* ```
*/
export function getComponents<T extends readonly ComponentConstructor[]>(
entity: Entity,
...types: T
): { [K in keyof T]: ComponentInstance<T[K]> | null } {
return types.map((type) =>
entity.getComponent(type as unknown as ComponentType<ComponentInstance<typeof type>>)
) as { [K in keyof T]: ComponentInstance<T[K]> | null };
}
/**
* 检查实体是否拥有所有指定的组件
*
* @param entity - 实体实例
* @param types - 组件类型构造函数数组
* @returns 如果拥有所有组件返回true否则返回false
*
* @example
* ```typescript
* if (hasComponents(entity, Position, Velocity)) {
* const pos = entity.getComponent(Position)!;
* const vel = entity.getComponent(Velocity)!;
* }
* ```
*/
export function hasComponents(entity: Entity, ...types: ComponentConstructor[]): boolean {
return types.every((type) => entity.hasComponent(type as unknown as ComponentType));
}
/**
* 检查实体是否拥有至少一个指定的组件
*
* @param entity - 实体实例
* @param types - 组件类型构造函数数组
* @returns 如果拥有任意一个组件返回true否则返回false
*/
export function hasAnyComponent(entity: Entity, ...types: ComponentConstructor[]): boolean {
return types.some((type) => entity.hasComponent(type as unknown as ComponentType));
}
/**
* 添加组件并立即配置
*
* @param entity - 实体实例
* @param component - 组件实例
* @param configure - 配置回调函数
* @returns 实体实例(支持链式调用)
*
* @example
* ```typescript
* addAndConfigure(entity, new Health(), health => {
* health.maxValue = 100;
* health.value = 50;
* });
* ```
*/
export function addAndConfigure<T extends Component>(
entity: Entity,
component: T,
configure: (component: T) => void
): Entity {
entity.addComponent(component);
configure(component);
return entity;
}
/**
* 获取或添加组件
*
* 如果组件已存在则返回现有组件,否则通过工厂函数创建并添加
*
* @param entity - 实体实例
* @param componentType - 组件类型构造函数
* @param factory - 组件工厂函数(仅在组件不存在时调用)
* @returns 组件实例
*
* @example
* ```typescript
* const health = getOrAddComponent(entity, Health, () => new Health(100));
* ```
*/
export function getOrAddComponent<T extends ComponentConstructor>(
entity: Entity,
componentType: T,
factory: () => ComponentInstance<T>
): ComponentInstance<T> {
let component = entity.getComponent(componentType as unknown as ComponentType<ComponentInstance<T>>);
if (!component) {
component = factory();
entity.addComponent(component);
}
return component;
}
/**
* 更新组件的部分字段
*
* @param entity - 实体实例
* @param componentType - 组件类型构造函数
* @param data - 要更新的部分数据
* @returns 如果更新成功返回true组件不存在返回false
*
* @example
* ```typescript
* updateComponent(entity, Position, { x: 100 });
* ```
*/
export function updateComponent<T extends ComponentConstructor>(
entity: Entity,
componentType: T,
data: Partial<ComponentInstance<T>>
): boolean {
const component = entity.getComponent(componentType as unknown as ComponentType<ComponentInstance<T>>);
if (!component) {
return false;
}
Object.assign(component, data);
return true;
}
/**
* 类型安全的实体构建器
*
* @example
* ```typescript
* const player = buildEntity(scene.createEntity("Player"))
* .with(new Position(100, 100))
* .with(new Velocity(0, 0))
* .withTag(1)
* .build();
* ```
*/
export class TypedEntityBuilder {
private _entity: Entity;
constructor(entity: Entity) {
this._entity = entity;
}
/**
* 添加组件
*/
with<T extends Component>(component: T): this {
this._entity.addComponent(component);
return this;
}
/**
* 添加并配置组件
*/
withConfigured<T extends Component>(
component: T,
configure: (component: T) => void
): this {
this._entity.addComponent(component);
configure(component);
return this;
}
/**
* 设置标签
*/
withTag(tag: number): this {
this._entity.tag = tag;
return this;
}
/**
* 设置名称
*/
withName(name: string): this {
this._entity.name = name;
return this;
}
/**
* 设置激活状态
*/
withActive(active: boolean): this {
this._entity.active = active;
return this;
}
/**
* 设置启用状态
*/
withEnabled(enabled: boolean): this {
this._entity.enabled = enabled;
return this;
}
/**
* 设置更新顺序
*/
withUpdateOrder(order: number): this {
this._entity.updateOrder = order;
return this;
}
/**
* 添加子实体
*/
withChild(child: Entity): this {
const hierarchySystem = this._entity.scene?.getSystem(HierarchySystem);
hierarchySystem?.setParent(child, this._entity);
return this;
}
/**
* 完成构建
*/
build(): Entity {
return this._entity;
}
/**
* 获取正在构建的实体
*/
get entity(): Entity {
return this._entity;
}
}
/**
* 创建类型安全的实体构建器
*
* @param entity - 要包装的实体
* @returns 实体构建器
*/
export function buildEntity(entity: Entity): TypedEntityBuilder {
return new TypedEntityBuilder(entity);
}

View File

@@ -0,0 +1,496 @@
/**
* 位枚举
*/
export enum SegmentPart {
LOW = 0,
HIGH = 1,
}
/**
* 基础 64 位段结构
* [低32位高32位]
*/
export type BitMask64Segment = [number,number]
/**
* 位掩码数据结构
* 基础模式64位使用 base[lo , hi] 存储 64 位segments 为空
* 扩展模式128+位base[lo , hi] 作为第一段segments 存储额外的 64 位段
* segments[0] 对应 bit 64-127segments[1] 对应 bit 128-191以此类推
*/
export type BitMask64Data = {
base: BitMask64Segment;
/** 扩展段数组,每个元素是一个 64 位段,用于超过 64 位的场景 */
segments?: BitMask64Segment[];
}
export class BitMask64Utils {
/** 零掩码常量所有位都为0 */
public static readonly ZERO: Readonly<BitMask64Data> = { base: [0, 0] };
/**
* 根据位索引创建64位掩码
* @param bitIndex 位索引,范围 [0, 63]
* @returns 包含指定位设置为1的掩码
* @throws 当位索引超出范围时抛出错误
*/
public static create(bitIndex: number): BitMask64Data {
if (bitIndex < 0) {
throw new Error(`Bit index ${bitIndex} out of range [0, ∞)`);
}
const mask: BitMask64Data = { base: [0, 0] };
BitMask64Utils.setBit(mask, bitIndex);
return mask;
}
/**
* 从32位数字创建64位掩码
* @param value 32位数字值
* @returns 低32位为输入值、高32位为0的掩码
*/
public static fromNumber(value: number): BitMask64Data {
return { base: [value >>> 0, 0] };
}
/**
* 检查掩码是否包含任意指定的位
* @param mask 要检查的掩码
* @param bits 指定的位模式
* @returns 如果掩码包含bits中的任意位则返回true
*/
public static hasAny(mask: BitMask64Data, bits: BitMask64Data): boolean {
const bitsBase = bits.base;
const maskBase = mask.base;
const bitsSegments = bits.segments;
const maskSegments = mask.segments;
const baseHasAny = (maskBase[SegmentPart.LOW] & bitsBase[SegmentPart.LOW]) !== 0 || (maskBase[SegmentPart.HIGH] & bitsBase[SegmentPart.HIGH]) !== 0;
// 基础区段就包含指定的位,或任意一个参数不含扩展区段,直接短路
if(baseHasAny || !bitsSegments || !maskSegments) return baseHasAny;
// 额外检查扩展区域是否包含指定的位 - 如果bitsSegments[index]不存在会被转为NaNNaN的位运算始终返回0
return maskSegments.some((seg, index) => {
const bitsSeg = bitsSegments[index];
return bitsSeg && ((seg[SegmentPart.LOW] & bitsSeg[SegmentPart.LOW]) !== 0 || (seg[SegmentPart.HIGH] & bitsSeg[SegmentPart.HIGH]) !== 0);
});
}
/**
* 检查掩码是否包含所有指定的位
* @param mask 要检查的掩码
* @param bits 指定的位模式
* @returns 如果掩码包含bits中的所有位则返回true
*/
public static hasAll(mask: BitMask64Data, bits: BitMask64Data): boolean {
const maskBase = mask.base;
const bitsBase = bits.base;
const maskSegments = mask.segments;
const bitsSegments = bits.segments;
const baseHasAll = (maskBase[SegmentPart.LOW] & bitsBase[SegmentPart.LOW]) === bitsBase[SegmentPart.LOW] && (maskBase[SegmentPart.HIGH] & bitsBase[SegmentPart.HIGH]) === bitsBase[SegmentPart.HIGH];
// 基础区域就不包含指定的位或bits没有扩展区段直接短路。
if(!baseHasAll || !bitsSegments) return baseHasAll;
// 扩展区段的hasAll匹配逻辑
const maskSegmentsLength = maskSegments?.length ?? 0;
// 对mask/bits中都存在的区段进行hasAll判断
if(maskSegments){
for (let i = 0; i < Math.min(maskSegmentsLength,bitsSegments.length); i++) {
const maskSeg = maskSegments[i]!;
const bitsSeg = bitsSegments[i]!;
if((maskSeg[SegmentPart.LOW] & bitsSeg[SegmentPart.LOW]) !== bitsSeg[SegmentPart.LOW] || (maskSeg[SegmentPart.HIGH] & bitsSeg[SegmentPart.HIGH]) !== bitsSeg[SegmentPart.HIGH]){
// 存在不匹配的位,直接短路
return false;
}
}
}
// 对mask中不存在但bits中存在的区段进行isZero判断
for (let i = maskSegmentsLength; i < bitsSegments.length; i++) {
const bitsSeg = bitsSegments[i]!;
if(bitsSeg[SegmentPart.LOW] !== 0 || bitsSeg[SegmentPart.HIGH] !== 0){
// 存在不为0的区段直接短路
return false;
}
}
return true;
}
/**
* 检查掩码是否不包含任何指定的位
* @param mask 要检查的掩码
* @param bits 指定的位模式
* @returns 如果掩码不包含bits中的任何位则返回true
*/
public static hasNone(mask: BitMask64Data, bits: BitMask64Data): boolean {
const maskBase = mask.base;
const bitsBase = bits.base;
const maskSegments = mask.segments;
const bitsSegments = bits.segments;
const baseHasNone = (maskBase[SegmentPart.LOW] & bitsBase[SegmentPart.LOW]) === 0 && (maskBase[SegmentPart.HIGH] & bitsBase[SegmentPart.HIGH]) === 0;
//不含扩展区域或基础区域就包含指定的位或bits不含拓展区段直接短路。
if(!maskSegments || !baseHasNone || !bitsSegments) return baseHasNone;
// 额外检查扩展区域是否都包含指定的位 - 此时bitsSegments存在,如果bitsSegments[index]不存在会被转为NaNNaN的位运算始终返回0
return maskSegments.every((seg, index) => {
const bitsSeg = bitsSegments[index];
if (!bitsSeg) return true;
return (seg[SegmentPart.LOW] & bitsSeg[SegmentPart.LOW]) === 0 && (seg[SegmentPart.HIGH] & bitsSeg[SegmentPart.HIGH]) === 0;
});
}
/**
* 检查掩码是否为零
* @param mask 要检查的掩码
* @returns 如果掩码所有位都为0则返回true
*/
public static isZero(mask: BitMask64Data): boolean {
const baseIsZero = mask.base[SegmentPart.LOW] === 0 && mask.base[SegmentPart.HIGH] === 0;
if(!mask.segments || !baseIsZero){
// 不含扩展区域或基础区域值就不为0直接短路
return baseIsZero;
}
// 额外检查扩展区域是否都为0
return mask.segments.every((seg) => seg[SegmentPart.LOW] === 0 && seg[SegmentPart.HIGH] === 0);
}
/**
* 检查两个掩码是否相等
* @param a 第一个掩码
* @param b 第二个掩码
* @returns 如果两个掩码完全相等则返回true
*/
public static equals(a: BitMask64Data, b: BitMask64Data): boolean {
const baseEquals = a.base[SegmentPart.LOW] === b.base[SegmentPart.LOW] && a.base[SegmentPart.HIGH] === b.base[SegmentPart.HIGH];
// base不相等或ab都没有扩展区域位直接返回base比较结果
if(!baseEquals || (!a.segments && !b.segments)) return baseEquals;
// 不能假设ab的segments都存在或长度相同.
const aSegments = a.segments ?? [];
const bSegments = b.segments ?? [];
for (let i = 0; i < Math.max(aSegments.length, bSegments.length); i++) {
const aSeg: BitMask64Segment | undefined = aSegments[i]; // 可能为undefined
const bSeg: BitMask64Segment | undefined = bSegments[i]; // 可能为undefined
if(aSeg && !bSeg){
//bSeg不存在则必须要求aSeg全为0
if(aSeg[SegmentPart.LOW] !== 0 || aSeg[SegmentPart.HIGH] !== 0) return false;
}else if(!aSeg && bSeg){
//aSeg不存在则必须要求bSeg全为0
if(bSeg[SegmentPart.LOW] !== 0 || bSeg[SegmentPart.HIGH] !== 0) return false;
}else if(aSeg && bSeg){
//理想状态aSeg/bSeg都存在
if(aSeg[SegmentPart.LOW] !== bSeg[SegmentPart.LOW] || aSeg[SegmentPart.HIGH] !== bSeg[SegmentPart.HIGH]) return false;
}
}
return true;
}
/**
* 设置掩码中指定位为1必要时自动扩展
* @param mask 要修改的掩码(原地修改)
* @param bitIndex 位索引,不小于零
* @throws 当位索引超出范围时抛出错误
*/
public static setBit(mask: BitMask64Data, bitIndex: number): void {
if (bitIndex < 0) {
throw new Error(`Bit index ${bitIndex} out of range [0, 63]`);
}
const targetSeg = BitMask64Utils.getSegmentByBitIndex(mask, bitIndex)!;
const mod = bitIndex & 63; // bitIndex % 64 优化方案
if(mod < 32){
targetSeg[SegmentPart.LOW] |= (1 << mod);
} else {
targetSeg[SegmentPart.HIGH] |= (1 << (mod - 32));
}
}
/**
* 获取掩码中指定位如果位超出当前掩码的区段长度则直接返回0
* @param mask 掩码
* @param bitIndex 位索引,不小于零
*/
public static getBit(mask: BitMask64Data, bitIndex: number): boolean {
if (bitIndex < 0) {
return false;
}
const targetSeg = BitMask64Utils.getSegmentByBitIndex(mask, bitIndex, false);
if(!targetSeg) return false;
const mod = bitIndex & 63; // bitIndex % 64 优化方案
if(mod < 32){
return (targetSeg[SegmentPart.LOW] & (1 << mod)) !== 0;
} else {
return (targetSeg[SegmentPart.HIGH] & (1 << (mod - 32))) !== 0;
}
}
/**
* 清除掩码中指定位为0如果位超出当前掩码的区段长度则什么也不做
* @param mask 要修改的掩码(原地修改)
* @param bitIndex 位索引不小于0
*/
public static clearBit(mask: BitMask64Data, bitIndex: number): void {
if (bitIndex < 0) {
throw new Error(`Bit index ${bitIndex} out of range [0, 63]`);
}
const targetSeg = BitMask64Utils.getSegmentByBitIndex(mask, bitIndex, false);
if(!targetSeg) return;
const mod = bitIndex & 63; // bitIndex % 64 优化方案
if(mod < 32){
targetSeg[SegmentPart.LOW] &= ~(1 << mod);
} else {
targetSeg[SegmentPart.HIGH] &= ~(1 << (mod - 32));
}
}
/**
* 对目标掩码执行按位或操作
* @param target 目标掩码(原地修改)
* @param other 用于按位或的掩码
*/
public static orInPlace(target: BitMask64Data, other: BitMask64Data): void {
target.base[SegmentPart.LOW] |= other.base[SegmentPart.LOW];
target.base[SegmentPart.HIGH] |= other.base[SegmentPart.HIGH];
// 处理扩展段
const otherSegments = other.segments;
if (otherSegments && otherSegments.length > 0) {
if (!target.segments) {
target.segments = [];
}
const targetSegments = target.segments;
// 确保 target 有足够的段
while (targetSegments.length < otherSegments.length) {
targetSegments.push([0, 0]);
}
// 对每个段执行或操作
for (let i = 0; i < otherSegments.length; i++) {
const targetSeg = targetSegments[i]!;
const otherSeg = otherSegments[i]!;
targetSeg[SegmentPart.LOW] |= otherSeg[SegmentPart.LOW];
targetSeg[SegmentPart.HIGH] |= otherSeg[SegmentPart.HIGH];
}
}
}
/**
* 对目标掩码执行按位与操作
* @param target 目标掩码(原地修改)
* @param other 用于按位与的掩码
*/
public static andInPlace(target: BitMask64Data, other: BitMask64Data): void {
target.base[SegmentPart.LOW] &= other.base[SegmentPart.LOW];
target.base[SegmentPart.HIGH] &= other.base[SegmentPart.HIGH];
// 处理扩展段
const otherSegments = other.segments;
if (otherSegments && otherSegments.length > 0) {
if (!target.segments) {
target.segments = [];
}
const targetSegments = target.segments;
// 确保 target 有足够的段
while (targetSegments.length < otherSegments.length) {
targetSegments.push([0, 0]);
}
// 对每个段执行与操作
for (let i = 0; i < otherSegments.length; i++) {
const targetSeg = targetSegments[i]!;
const otherSeg = otherSegments[i]!;
targetSeg[SegmentPart.LOW] &= otherSeg[SegmentPart.LOW];
targetSeg[SegmentPart.HIGH] &= otherSeg[SegmentPart.HIGH];
}
}
}
/**
* 对目标掩码执行按位异或操作
* @param target 目标掩码(原地修改)
* @param other 用于按位异或的掩码
*/
public static xorInPlace(target: BitMask64Data, other: BitMask64Data): void {
target.base[SegmentPart.LOW] ^= other.base[SegmentPart.LOW];
target.base[SegmentPart.HIGH] ^= other.base[SegmentPart.HIGH];
// 处理扩展段
const otherSegments = other.segments;
if (!otherSegments || otherSegments.length == 0) return;
if (!target.segments) target.segments = [];
const targetSegments = target.segments;
// 确保 target 有足够的段
while (targetSegments.length < otherSegments.length) {
targetSegments.push([0, 0]);
}
// 对每个段执行异或操作
for (let i = 0; i < otherSegments.length; i++) {
const targetSeg = targetSegments[i]!;
const otherSeg = otherSegments[i]!;
targetSeg[SegmentPart.LOW] ^= otherSeg[SegmentPart.LOW];
targetSeg[SegmentPart.HIGH] ^= otherSeg[SegmentPart.HIGH];
}
}
/**
* 清除掩码的所有位为0
* @param mask 要清除的掩码(原地修改)
*/
public static clear(mask: BitMask64Data): void {
mask.base[SegmentPart.LOW] = 0;
mask.base[SegmentPart.HIGH] = 0;
if (mask.segments) {
for (let i = 0; i < mask.segments.length; i++) {
const seg = mask.segments[i]!;
seg[SegmentPart.LOW] = 0;
seg[SegmentPart.HIGH] = 0;
}
}
}
/**
* 将源掩码的值复制到目标掩码如果source包含扩展段则target也会至少扩展到source扩展段的长度
* @param source 源掩码
* @param target 目标掩码(原地修改)
*/
public static copy(source: BitMask64Data, target: BitMask64Data): void {
BitMask64Utils.clear(target);
target.base[SegmentPart.LOW] = source.base[SegmentPart.LOW];
target.base[SegmentPart.HIGH] = source.base[SegmentPart.HIGH];
// source没有扩展段直接退出
if(!source.segments || source.segments.length == 0) return;
// 没有拓展段,则直接复制数组
if(!target.segments){
target.segments = source.segments.map((seg) => [...seg]);
return;
}
// source有扩展段target扩展段不足则补充长度
const copyLength = source.segments.length - target.segments.length;
for (let i = 0; i < copyLength; i++) {
target.segments.push([0,0]);
}
// 逐个重写
const targetSegments = target.segments;
const sourceSegments = source.segments;
for (let i = 0; i < sourceSegments.length; i++) {
const targetSeg = targetSegments[i]!;
const sourSeg = sourceSegments[i]!;
targetSeg[SegmentPart.LOW] = sourSeg[SegmentPart.LOW];
targetSeg[SegmentPart.HIGH] = sourSeg[SegmentPart.HIGH];
}
}
/**
* 创建掩码的深拷贝
* @param mask 要拷贝的掩码
* @returns 新的掩码对象,内容与源掩码相同
*/
public static clone(mask: BitMask64Data): BitMask64Data {
return {
base: mask.base.slice() as BitMask64Segment,
...(mask.segments && { segments: mask.segments.map((seg) => [...seg] as BitMask64Segment) })
};
}
/**
* 将掩码转换为字符串表示,每个区段之间将使用空格分割。
* @param mask 要转换的掩码
* @param radix 进制支持2二进制或16十六进制默认为2其他的值被视为2
* @param printHead 打印头
* @returns 掩码的字符串表示二进制不带前缀十六进制带0x前缀
*/
public static toString(mask: BitMask64Data, radix: 2 | 16 = 2, printHead: boolean = false): string {
if(radix != 2 && radix != 16) radix = 2;
const totalLength = mask.segments?.length ?? 0;
let result: string = '';
if(printHead){
let paddingLength = 0;
if(radix === 2){
paddingLength = 64 + 1 + 1;
}else{
paddingLength = 16 + 2 + 1;
}
for (let i = 0; i <= totalLength; i++) {
const title = i === 0 ? '0 (Base):' : `${i} (${64 * i}):`;
result += title.toString().padEnd(paddingLength);
}
result += '\n';
}
for (let i = -1; i < totalLength; i++) {
let segResult = '';
const bitMaskData = i == -1 ? mask.base : mask.segments![i]!;
const hi = bitMaskData[SegmentPart.HIGH];
const lo = bitMaskData[SegmentPart.LOW];
if(radix == 2){
const hiBits = hi.toString(2).padStart(32, '0');
const loBits = lo.toString(2).padStart(32, '0');
segResult = hiBits + '_' + loBits; //高低位之间使用_隔离
}else{
let hiBits = hi ? hi.toString(16).toUpperCase() : '';
if(printHead){
// 存在标头,则输出高位之前需要补齐位数
hiBits = hiBits.padStart(8, '0');
}
let loBits = lo.toString(16).toUpperCase();
if(hiBits){
// 存在高位 则输出低位之前需要补齐位数
loBits = loBits.padStart(8, '0');
}
segResult = '0x' + hiBits + loBits;
}
if(i === -1)
result += segResult;
else
result += ' ' + segResult; // 不同段之间使用空格隔离
}
return result;
}
/**
* 计算掩码中设置为1的位数
* @param mask 要计算的掩码
* @returns 掩码中1的位数
*/
public static popCount(mask: BitMask64Data): number {
let count = 0;
for (let i = -1; i < (mask.segments?.length ?? 0); i++) {
const bitMaskData = i == -1 ? mask.base : mask.segments![i]!;
let lo = bitMaskData[SegmentPart.LOW];
let hi = bitMaskData[SegmentPart.HIGH];
while (lo) {
lo &= lo - 1;
count++;
}
while (hi) {
hi &= hi - 1;
count++;
}
}
return count;
}
/**
* 获取包含目标位的BitMask64Segment
* @param mask 要操作的掩码
* @param bitIndex 目标位
* @param createNewSegment 如果bitIndex超过了当前范围是否自动补充扩展区域默认为真
* @private
*/
private static getSegmentByBitIndex(mask: BitMask64Data,bitIndex: number, createNewSegment: boolean = true): BitMask64Segment | null{
if(bitIndex <= 63){
// 基础位
return mask.base;
}else{
// 扩展位
let segments = mask.segments;
if(!segments) {
if(!createNewSegment) return null;
segments = mask.segments = [];
}
const targetSegIndex = (bitIndex >> 6) - 1; // Math.floor(bitIndex / 64) - 1的位运算优化
if(segments.length <= targetSegIndex){
if(!createNewSegment) return null;
const diff = targetSegIndex - segments.length + 1;
for (let i = 0; i < diff; i++) {
segments.push([0, 0]);
}
}
return segments[targetSegIndex] ?? null;
}
}
}

View File

@@ -0,0 +1,143 @@
import { BitMask64Data } from './BigIntCompatibility';
// FlatHashMapFast.ts
/**
* 高性能 HashMap使用BitMask64Data作为Key。内部计算两层哈希
* - primaryHash: MurmurHash3(seed1) => 定位 bucket
* - secondaryHash: MurmurHash3(seed2) => 处理 bucket 内碰撞判定
*
* 理论上在1e5数量数据规模下碰撞概率在数学意义上的可忽略。
* 在本地测试中,一千万次连续/随机BitMask64Data生成未发生一级哈希冲突考虑到使用场景原型系统、组件系统等远达不到此数量级因此可安全用于生产环境。
*/
export class BitMaskHashMap<T> {
private buckets: Map<number, [number, T][]> = new Map();
private _size = 0;
constructor() {}
get size(): number {
return this._size;
}
get innerBuckets(): Map<number, [number, T][]> {
return this.buckets;
}
/** MurmurHash3 (32bit) 简化实现 */
private murmur32(key: BitMask64Data, seed: number): number {
let h = seed >>> 0;
const mix = (k: number) => {
k = Math.imul(k, 0xcc9e2d51) >>> 0; // 第一个 32 位魔术常数
k = (k << 15) | (k >>> 17);
k = Math.imul(k, 0x1b873593) >>> 0; // 第二个 32 位魔术常数
h ^= k;
h = (h << 13) | (h >>> 19);
h = (Math.imul(h, 5) + 0xe6546b64) >>> 0;
};
// base
mix(key.base[0] >>> 0);
mix(key.base[1] >>> 0);
// segments
if (key.segments) {
for (const seg of key.segments) {
mix(seg[0] >>> 0);
mix(seg[1] >>> 0);
}
}
h ^= (key.segments ? key.segments.length * 8 : 8);
h ^= h >>> 16;
h = Math.imul(h, 0x85ebca6b) >>> 0;
h ^= h >>> 13;
h = Math.imul(h, 0xc2b2ae35) >>> 0;
h ^= h >>> 16;
return h >>> 0;
}
/** primaryHash + secondaryHash 计算 */
private getHashes(key: BitMask64Data): [number, number] {
const primary = this.murmur32(key, 0x9747b28c); // seed1
const secondary = this.murmur32(key, 0x12345678); // seed2
return [primary, secondary];
}
set(key: BitMask64Data, value: T): this {
const [primary, secondary] = this.getHashes(key);
let bucket = this.buckets.get(primary);
if (!bucket) {
bucket = [];
this.buckets.set(primary, bucket);
}
// 查找是否存在 secondaryHash
for (let i = 0; i < bucket.length; i++) {
if (bucket[i]![0] === secondary) {
bucket[i]![1] = value;
return this;
}
}
// 新增
bucket.push([secondary, value]);
this._size++;
return this;
}
get(key: BitMask64Data): T | undefined {
const [primary, secondary] = this.getHashes(key);
const bucket = this.buckets.get(primary);
if (!bucket) return undefined;
for (let i = 0; i < bucket.length; i++) {
if (bucket[i]![0] === secondary) {
return bucket[i]![1];
}
}
return undefined;
}
has(key: BitMask64Data): boolean {
return this.get(key) !== undefined;
}
delete(key: BitMask64Data): boolean {
const [primary, secondary] = this.getHashes(key);
const bucket = this.buckets.get(primary);
if (!bucket) return false;
for (let i = 0; i < bucket.length; i++) {
if (bucket[i]![0] === secondary) {
bucket.splice(i, 1);
this._size--;
if (bucket.length === 0) {
this.buckets.delete(primary);
}
return true;
}
}
return false;
}
clear(): void {
this.buckets.clear();
this._size = 0;
}
*entries(): IterableIterator<[BitMask64Data, T]> {
for (const [_, bucket] of this.buckets) {
for (const [_secondary, value] of bucket) {
// 无法还原原始 key只存二级 hash所以 entries 返回不了 key
yield [undefined as any, value];
}
}
}
*values(): IterableIterator<T> {
for (const bucket of this.buckets.values()) {
for (const [_, value] of bucket) {
yield value;
}
}
}
}

View File

@@ -0,0 +1,361 @@
import { SegmentPart, BitMask64Data, BitMask64Utils } from './BigIntCompatibility';
/**
* 位集合类,用于高效的位操作
* 支持任意位的位运算操作.
*/
export class Bits {
/** 存储位数据的掩码,支持扩展 */
private _value: BitMask64Data;
/**
* 构造函数,创建位集合
* @param initialValue 初始值可以是BitMask64Data对象、数字或字符串
*/
constructor(initialValue?: BitMask64Data | number | string) {
if (initialValue && typeof initialValue === 'object') {
this._value = BitMask64Utils.clone(initialValue);
} else if (typeof initialValue === 'number') {
this._value = BitMask64Utils.fromNumber(initialValue);
} else if (typeof initialValue === 'string') {
const num = parseInt(initialValue, 10);
this._value = BitMask64Utils.fromNumber(num);
} else {
this._value = BitMask64Utils.clone(BitMask64Utils.ZERO);
}
}
/**
* 设置指定位为1
* 自动扩展:当索引超过 64 时,自动扩展到 128/256 位
* @param index 位索引0-based
* @throws 当位索引为负数时抛出错误
*/
public set(index: number): void {
if (index < 0) {
throw new Error('Bit index cannot be negative');
}
BitMask64Utils.setBit(this._value, index);
}
/**
* 清除指定位为0
* @param index 位索引
* @throws 当位索引为负数时抛出错误
*/
public clear(index: number): void {
if (index < 0) {
throw new Error('Bit index cannot be negative');
}
BitMask64Utils.clearBit(this._value, index);
}
/**
* 获取指定位的值
* @param index 位索引
* @returns 如果位被设置为1则返回true否则返回false
*/
public get(index: number): boolean {
return BitMask64Utils.getBit(this._value, index);
}
/**
* 检查是否包含另一个位集合的所有位
* @param other 另一个位集合
* @returns 如果包含other的所有设置位则返回true
*/
public containsAll(other: Bits): boolean {
return BitMask64Utils.hasAll(this._value, other._value);
}
/**
* 检查是否与另一个位集合有交集
* @param other 另一个位集合
* @returns 如果有共同的设置位则返回true
*/
public intersects(other: Bits): boolean {
return BitMask64Utils.hasAny(this._value, other._value);
}
/**
* 检查是否与另一个位集合没有交集
* @param other 另一个位集合
* @returns 如果没有共同的设置位则返回true
*/
public excludes(other: Bits): boolean {
return BitMask64Utils.hasNone(this._value, other._value);
}
/**
* 清除所有位为0
*/
public clearAll(): void {
BitMask64Utils.clear(this._value);
}
/**
* 检查位集合是否为空
* @returns 如果所有位都为0则返回true
*/
public isEmpty(): boolean {
return BitMask64Utils.isZero(this._value);
}
/**
* 计算设置为1的位数
* @returns 设置位的总数
*/
public cardinality(): number {
return BitMask64Utils.popCount(this._value);
}
/**
* 与另一个位集合执行按位与操作
* @param other 另一个位集合
* @returns 新的位集合,包含按位与的结果
*/
public and(other: Bits): Bits {
const result = new Bits();
BitMask64Utils.copy(this._value, result._value);
BitMask64Utils.andInPlace(result._value, other._value);
return result;
}
/**
* 与另一个位集合执行按位或操作
* @param other 另一个位集合
* @returns 新的位集合,包含按位或的结果
*/
public or(other: Bits): Bits {
const result = new Bits();
BitMask64Utils.copy(this._value, result._value);
BitMask64Utils.orInPlace(result._value, other._value);
return result;
}
/**
* 与另一个位集合执行按位异或操作
* @param other 另一个位集合
* @returns 新的位集合,包含按位异或的结果
*/
public xor(other: Bits): Bits {
const result = new Bits();
BitMask64Utils.copy(this._value, result._value);
BitMask64Utils.xorInPlace(result._value, other._value);
return result;
}
/**
* 执行按位取反操作
* @param maxBits 最大位数默认为64
* @returns 新的位集合,包含按位取反的结果
*/
public not(maxBits: number = 64): Bits {
if (maxBits > 64) {
maxBits = 64;
}
const result = new Bits();
BitMask64Utils.copy(this._value, result._value);
if (maxBits <= 32) {
const mask = (1 << maxBits) - 1;
result._value.base[SegmentPart.LOW] = (~result._value.base[SegmentPart.LOW]) & mask;
result._value.base[SegmentPart.HIGH] = 0;
} else {
result._value.base[SegmentPart.LOW] = ~result._value.base[SegmentPart.LOW];
if (maxBits < 64) {
const remainingBits = maxBits - 32;
const mask = (1 << remainingBits) - 1;
result._value.base[SegmentPart.HIGH] = (~result._value.base[SegmentPart.HIGH]) & mask;
} else {
result._value.base[SegmentPart.HIGH] = ~result._value.base[SegmentPart.HIGH];
}
}
return result;
}
/**
* 从另一个位集合复制值
* @param other 源位集合
*/
public copyFrom(other: Bits): void {
BitMask64Utils.copy(other._value, this._value);
}
/**
* 创建当前位集合的深拷贝
* @returns 新的位集合,内容与当前位集合相同
*/
public clone(): Bits {
return new Bits(this._value);
}
/**
* 获取内部的64位掩码数据
* @returns 内部存储的BitMask64Data对象
*/
public getValue(): BitMask64Data {
return this._value;
}
/**
* 设置位集合的值
* @param value 新值可以是BitMask64Data对象、数字或字符串
*/
public setValue(value: BitMask64Data | number | string): void {
if (typeof value === 'object') {
BitMask64Utils.copy(value, this._value);
} else if (typeof value === 'number') {
this._value = BitMask64Utils.fromNumber(value);
} else {
const num = parseInt(value, 10);
this._value = BitMask64Utils.fromNumber(num);
}
}
/**
* 将位集合转换为可读字符串
* @returns 格式为"Bits[index1, index2, ...]"的字符串
*/
public toString(): string {
const bits: string[] = [];
for (let i = 0; i < 64; i++) {
if (this.get(i)) {
bits.push(i.toString());
}
}
return `Bits[${bits.join(', ')}]`;
}
/**
* 将位集合转换为二进制字符串
* @param maxBits 最大位数默认为64
* @returns 二进制字符串表示每8位用空格分隔
*/
public toBinaryString(maxBits: number = 0): string {
if(maxBits == 0){
maxBits = 64 + (this._value.segments ? this._value.segments.length * 64 : 0);
}
let result = '';
for (let i = maxBits - 1; i >= 0; i--) {
result += this.get(i) ? '1' : '0';
if (i % 8 === 0 && i > 0) {
result += ' ';
}
}
return result;
}
/**
* 将位集合转换为十六进制字符串
* @returns 十六进制字符串表示带0x前缀
*/
public toHexString(): string {
return BitMask64Utils.toString(this._value, 16);
}
/**
* 从二进制字符串创建位集合
* @param binaryString 二进制字符串,可以包含空格
* @returns 新的位集合对象
*/
public static fromBinaryString(binaryString: string): Bits {
const cleanString = binaryString.replace(/\s/g, '');
let data: BitMask64Data;
if (cleanString.length <= 32) {
const num = parseInt(cleanString, 2);
data = { base: [num >>> 0, 0] };
} else {
const loBits = cleanString.substring(cleanString.length - 32);
const hiBits = cleanString.substring(0, cleanString.length - 32);
const lo = parseInt(loBits, 2);
const hi = parseInt(hiBits, 2);
data = { base: [lo >>> 0, hi >>> 0] };
}
return new Bits(data);
}
/**
* 从十六进制字符串创建位集合
* @param hexString 十六进制字符串可以带或不带0x前缀
* @returns 新的位集合对象
*/
public static fromHexString(hexString: string): Bits {
const cleanString = hexString.replace(/^0x/i, '');
let data: BitMask64Data;
if (cleanString.length <= 8) {
const num = parseInt(cleanString, 16);
data = { base: [num >>> 0, 0] };
} else {
const loBits = cleanString.substring(cleanString.length - 8);
const hiBits = cleanString.substring(0, cleanString.length - 8);
const lo = parseInt(loBits, 16);
const hi = parseInt(hiBits, 16);
data = { base: [lo >>> 0, hi >>> 0] };
}
return new Bits(data);
}
/**
* 检查是否与另一个位集合相等
* @param other 另一个位集合
* @returns 如果两个位集合完全相同则返回true
*/
public equals(other: Bits): boolean {
return BitMask64Utils.equals(this._value, other._value);
}
/**
* 获取最高位设置位的索引
* @returns 最高位设置位的索引,如果位集合为空则返回-1
*/
public getHighestBitIndex(): number {
if (BitMask64Utils.isZero(this._value)) {
return -1;
}
if (this._value.base[SegmentPart.HIGH] !== 0) {
for (let i = 31; i >= 0; i--) {
if ((this._value.base[SegmentPart.HIGH] & (1 << i)) !== 0) {
return i + 32;
}
}
}
for (let i = 31; i >= 0; i--) {
if ((this._value.base[SegmentPart.LOW] & (1 << i)) !== 0) {
return i;
}
}
return -1;
}
/**
* 获取最低位设置位的索引
* @returns 最低位设置位的索引,如果位集合为空则返回-1
*/
public getLowestBitIndex(): number {
if (BitMask64Utils.isZero(this._value)) {
return -1;
}
for (let i = 0; i < 32; i++) {
if ((this._value.base[SegmentPart.LOW] & (1 << i)) !== 0) {
return i;
}
}
for (let i = 0; i < 32; i++) {
if ((this._value.base[SegmentPart.HIGH] & (1 << i)) !== 0) {
return i + 32;
}
}
return -1;
}
}

View File

@@ -0,0 +1,414 @@
import { Entity } from '../Entity';
import { ComponentType, GlobalComponentRegistry } from '../Core/ComponentStorage';
import { BitMask64Utils, BitMask64Data } from './BigIntCompatibility';
import { SparseSet } from './SparseSet';
import { Pool } from '../../Utils/Pool/Pool';
import { IPoolable } from '../../Utils/Pool/IPoolable';
/**
* 可池化的实体集合
*
* 实现IPoolable接口支持对象池复用以减少内存分配开销。
*/
class PoolableEntitySet extends Set<Entity> implements IPoolable {
constructor(..._args: unknown[]) {
super();
}
reset(): void {
this.clear();
}
}
/**
* 组件稀疏集合实现
*
* 结合通用稀疏集合和组件位掩码
*
* 存储结构:
* - 稀疏集合存储实体
* - 位掩码数组存储组件信息
* - 组件类型映射表
*/
export class ComponentSparseSet {
/**
* 实体稀疏集合
*
* 存储所有拥有组件的实体提供O(1)的实体操作。
*/
private _entities: SparseSet<Entity>;
/**
* 组件位掩码数组
*
* 与实体稀疏集合的密集数组对应,存储每个实体的组件位掩码。
* 数组索引与稀疏集合的密集数组索引一一对应。
*/
private _componentMasks: BitMask64Data[] = [];
/**
* 组件类型到实体集合的映射
*
* 维护每个组件类型对应的实体集合,用于快速的单组件查询。
*/
private _componentToEntities = new Map<ComponentType, PoolableEntitySet>();
/**
* 实体集合对象池
*
* 使用core库的Pool系统来管理PoolableEntitySet对象的复用。
*/
private static _entitySetPool = Pool.getPool(PoolableEntitySet, 50, 512);
constructor() {
this._entities = new SparseSet<Entity>();
}
/**
* 添加实体到组件索引
*
* 分析实体的组件组成,生成位掩码,并更新所有相关索引。
*
* @param entity 要添加的实体
*/
public addEntity(entity: Entity): void {
// 如果实体已存在,先移除旧数据
if (this._entities.has(entity)) {
this.removeEntity(entity);
}
const componentMask = BitMask64Utils.clone(BitMask64Utils.ZERO);
const entityComponents = new Set<ComponentType>();
// 分析实体组件并构建位掩码
for (const component of entity.components) {
const componentType = component.constructor as ComponentType;
entityComponents.add(componentType);
// 获取组件位掩码并合并
const bitMask = GlobalComponentRegistry.getBitMask(componentType);
BitMask64Utils.orInPlace(componentMask, bitMask);
}
// 添加实体到稀疏集合
this._entities.add(entity);
const entityIndex = this._entities.getIndex(entity)!;
// 确保位掩码数组有足够空间
while (this._componentMasks.length <= entityIndex) {
this._componentMasks.push(BitMask64Utils.clone(BitMask64Utils.ZERO));
}
this._componentMasks[entityIndex] = componentMask;
// 更新组件类型到实体的映射
this.updateComponentMappings(entity, entityComponents, true);
}
/**
* 从组件索引中移除实体
*
* 清理实体相关的所有索引数据,保持数据结构的紧凑性。
*
* @param entity 要移除的实体
*/
public removeEntity(entity: Entity): void {
const entityIndex = this._entities.getIndex(entity);
if (entityIndex === undefined) {
return; // 实体不存在
}
// 获取实体的组件类型集合
const entityComponents = this.getEntityComponentTypes(entity);
// 更新组件类型到实体的映射
this.updateComponentMappings(entity, entityComponents, false);
// 从稀疏集合中移除实体
this._entities.remove(entity);
// 维护位掩码数组的紧凑性
const lastIndex = this._componentMasks.length - 1;
if (entityIndex !== lastIndex) {
// 将最后一个位掩码移动到当前位置
this._componentMasks[entityIndex] = this._componentMasks[lastIndex]!;
}
this._componentMasks.pop();
}
/**
* 查询包含指定组件的所有实体
*
* @param componentType 组件类型
* @returns 包含该组件的实体集合
*/
public queryByComponent(componentType: ComponentType): Set<Entity> {
const entities = this._componentToEntities.get(componentType);
return entities ? new Set(entities) : new Set<Entity>();
}
/**
* 多组件查询AND操作
*
* 查找同时包含所有指定组件的实体。
*
* @param componentTypes 组件类型数组
* @returns 满足条件的实体集合
*/
public queryMultipleAnd(componentTypes: ComponentType[]): Set<Entity> {
if (componentTypes.length === 0) {
return new Set<Entity>();
}
if (componentTypes.length === 1) {
return this.queryByComponent(componentTypes[0]!);
}
// 构建目标位掩码
const targetMask = BitMask64Utils.clone(BitMask64Utils.ZERO);
for (const componentType of componentTypes) {
if (!GlobalComponentRegistry.isRegistered(componentType)) {
return new Set<Entity>(); // 未注册的组件类型,结果为空
}
const bitMask = GlobalComponentRegistry.getBitMask(componentType);
BitMask64Utils.orInPlace(targetMask, bitMask);
}
const result = ComponentSparseSet._entitySetPool.obtain();
// 遍历所有实体,检查位掩码匹配
this._entities.forEach((entity, index) => {
const entityMask = this._componentMasks[index]!;
if (BitMask64Utils.hasAll(entityMask, targetMask)) {
result.add(entity);
}
});
return result;
}
/**
* 多组件查询OR操作
*
* 查找包含任意一个指定组件的实体。
*
* @param componentTypes 组件类型数组
* @returns 满足条件的实体集合
*/
public queryMultipleOr(componentTypes: ComponentType[]): Set<Entity> {
if (componentTypes.length === 0) {
return new Set<Entity>();
}
if (componentTypes.length === 1) {
return this.queryByComponent(componentTypes[0]!);
}
// 构建目标位掩码
const targetMask = BitMask64Utils.clone(BitMask64Utils.ZERO);
for (const componentType of componentTypes) {
if (GlobalComponentRegistry.isRegistered(componentType)) {
const bitMask = GlobalComponentRegistry.getBitMask(componentType);
BitMask64Utils.orInPlace(targetMask, bitMask);
}
}
if (BitMask64Utils.equals(targetMask, BitMask64Utils.ZERO)) {
return new Set<Entity>(); // 没有有效的组件类型
}
const result = ComponentSparseSet._entitySetPool.obtain();
// 遍历所有实体,检查位掩码匹配
this._entities.forEach((entity, index) => {
const entityMask = this._componentMasks[index]!;
if (BitMask64Utils.hasAny(entityMask, targetMask)) {
result.add(entity);
}
});
return result;
}
/**
* 检查实体是否包含指定组件
*
* @param entity 实体
* @param componentType 组件类型
* @returns 是否包含该组件
*/
public hasComponent(entity: Entity, componentType: ComponentType): boolean {
const entityIndex = this._entities.getIndex(entity);
if (entityIndex === undefined) {
return false;
}
if (!GlobalComponentRegistry.isRegistered(componentType)) {
return false;
}
const entityMask = this._componentMasks[entityIndex]!;
const componentMask = GlobalComponentRegistry.getBitMask(componentType);
return BitMask64Utils.hasAny(entityMask, componentMask);
}
/**
* 获取实体的组件位掩码
*
* @param entity 实体
* @returns 组件位掩码如果实体不存在则返回undefined
*/
public getEntityMask(entity: Entity): BitMask64Data | undefined {
const entityIndex = this._entities.getIndex(entity);
if (entityIndex === undefined) {
return undefined;
}
return this._componentMasks[entityIndex];
}
/**
* 获取所有实体
*
* @returns 所有实体的数组
*/
public getAllEntities(): Entity[] {
return this._entities.toArray();
}
/**
* 获取实体数量
*/
public get size(): number {
return this._entities.size;
}
/**
* 检查是否为空
*/
public get isEmpty(): boolean {
return this._entities.isEmpty;
}
/**
* 遍历所有实体
*
* @param callback 遍历回调函数
*/
public forEach(callback: (entity: Entity, mask: BitMask64Data, index: number) => void): void {
this._entities.forEach((entity, index) => {
callback(entity, this._componentMasks[index]!, index);
});
}
/**
* 清空所有数据
*/
public clear(): void {
this._entities.clear();
this._componentMasks.length = 0;
// 清理时将所有持有的实体集合返回到池中
for (const entitySet of this._componentToEntities.values()) {
ComponentSparseSet._entitySetPool.release(entitySet);
}
this._componentToEntities.clear();
}
/**
* 获取内存使用统计
*/
public getMemoryStats(): {
entitiesMemory: number;
masksMemory: number;
mappingsMemory: number;
totalMemory: number;
} {
const entitiesStats = this._entities.getMemoryStats();
const masksMemory = this._componentMasks.length * 16; // 估计每个BigInt 16字节
let mappingsMemory = this._componentToEntities.size * 16; // Map条目开销
for (const entitySet of this._componentToEntities.values()) {
mappingsMemory += entitySet.size * 8; // 每个实体引用8字节
}
return {
entitiesMemory: entitiesStats.totalMemory,
masksMemory,
mappingsMemory,
totalMemory: entitiesStats.totalMemory + masksMemory + mappingsMemory
};
}
/**
* 验证数据结构完整性
*/
public validate(): boolean {
// 检查稀疏集合的有效性
if (!this._entities.validate()) {
return false;
}
// 检查位掩码数组长度一致性
if (this._componentMasks.length !== this._entities.size) {
return false;
}
// 检查组件映射的一致性
const allMappedEntities = new Set<Entity>();
for (const entitySet of this._componentToEntities.values()) {
for (const entity of entitySet) {
allMappedEntities.add(entity);
}
}
// 验证映射中的实体都在稀疏集合中
for (const entity of allMappedEntities) {
if (!this._entities.has(entity)) {
return false;
}
}
return true;
}
/**
* 获取实体的组件类型集合
*/
private getEntityComponentTypes(entity: Entity): Set<ComponentType> {
const componentTypes = new Set<ComponentType>();
for (const component of entity.components) {
componentTypes.add(component.constructor as ComponentType);
}
return componentTypes;
}
/**
* 更新组件类型到实体的映射
*/
private updateComponentMappings(
entity: Entity,
componentTypes: Set<ComponentType>,
add: boolean
): void {
for (const componentType of componentTypes) {
let entities = this._componentToEntities.get(componentType);
if (add) {
if (!entities) {
entities = ComponentSparseSet._entitySetPool.obtain();
this._componentToEntities.set(componentType, entities);
}
entities.add(entity);
} else {
if (entities) {
entities.delete(entity);
if (entities.size === 0) {
this._componentToEntities.delete(componentType);
ComponentSparseSet._entitySetPool.release(entities);
}
}
}
}
}
}

View File

@@ -0,0 +1,316 @@
import { Entity } from '../Entity';
import { Component } from '../Component';
/**
* 高性能实体列表管理器
* 管理场景中的所有实体,支持快速查找和批量操作
*/
export class EntityList {
public buffer: Entity[] = [];
private _scene: any; // 临时使用any避免循环依赖
// 索引映射,提升查找性能
private _idToEntity = new Map<number, Entity>();
private _nameToEntities = new Map<string, Entity[]>();
// 延迟操作队列
private _entitiesToAdd: Entity[] = [];
private _entitiesToRemove: Entity[] = [];
public get count(): number {
return this.buffer.length;
}
constructor(scene: any) {
this._scene = scene;
}
/**
* 添加实体
* @param entity 要添加的实体
*/
public add(entity: Entity): void {
this.addImmediate(entity);
}
/**
* 立即添加实体
* @param entity 要添加的实体
*/
private addImmediate(entity: Entity): void {
// 检查是否已存在
if (this._idToEntity.has(entity.id)) {
return;
}
this.buffer.push(entity);
this._idToEntity.set(entity.id, entity);
// 更新名称索引
this.updateNameIndex(entity, true);
}
/**
* 移除实体
* @param entity 要移除的实体
*/
public remove(entity: Entity): void {
this.removeImmediate(entity);
}
/**
* 立即移除实体
* @param entity 要移除的实体
*/
private removeImmediate(entity: Entity): void {
const index = this.buffer.indexOf(entity);
if (index !== -1) {
this.buffer.splice(index, 1);
this._idToEntity.delete(entity.id);
// 更新名称索引
this.updateNameIndex(entity, false);
// 回收实体ID到ID池
if (this._scene && this._scene.identifierPool) {
this._scene.identifierPool.checkIn(entity.id);
}
}
}
/**
* 移除所有实体
* Remove all entities
*
* 包括 buffer 中的实体和待添加队列中的实体。
* Includes entities in buffer and entities in pending add queue.
*/
public removeAllEntities(): void {
const idsToRecycle: number[] = [];
// 销毁 buffer 中的实体
// Destroy entities in buffer
for (let i = this.buffer.length - 1; i >= 0; i--) {
idsToRecycle.push(this.buffer[i]!.id);
this.buffer[i]!.destroy();
}
// 销毁待添加队列中的实体(这些实体已创建但尚未加入 buffer
// Destroy entities in pending add queue (created but not yet in buffer)
for (const entity of this._entitiesToAdd) {
idsToRecycle.push(entity.id);
entity.destroy();
}
// 批量回收 ID
// Recycle IDs in batch
if (this._scene && this._scene.identifierPool) {
for (const id of idsToRecycle) {
this._scene.identifierPool.checkIn(id);
}
}
this.buffer.length = 0;
this._idToEntity.clear();
this._nameToEntities.clear();
this._entitiesToAdd.length = 0;
this._entitiesToRemove.length = 0;
}
/**
* 更新实体列表,处理延迟操作
*/
public updateLists(): void {
// 处理延迟添加的实体
if (this._entitiesToAdd.length > 0) {
for (const entity of this._entitiesToAdd) {
this.addImmediate(entity);
}
this._entitiesToAdd.length = 0;
}
// 处理延迟移除的实体
if (this._entitiesToRemove.length > 0) {
for (const entity of this._entitiesToRemove) {
this.removeImmediate(entity);
}
this._entitiesToRemove.length = 0;
}
}
/**
* 更新实体列表
*
* 处理延迟操作(添加/删除实体)
*/
public update(): void {
// 处理延迟操作
this.updateLists();
}
/**
* 根据名称查找实体使用索引O(1)复杂度)
* @param name 实体名称
* @returns 找到的第一个实体或null
*/
public findEntity(name: string): Entity | null {
const entities = this._nameToEntities.get(name);
return entities && entities.length > 0 ? entities[0]! : null;
}
/**
* 根据名称查找所有实体
* @param name 实体名称
* @returns 找到的所有实体数组
*/
public findEntitiesByName(name: string): Entity[] {
return this._nameToEntities.get(name) || [];
}
/**
* 根据ID查找实体使用索引O(1)复杂度)
* @param id 实体ID
* @returns 找到的实体或null
*/
public findEntityById(id: number): Entity | null {
return this._idToEntity.get(id) || null;
}
/**
* 根据标签查找实体
* @param tag 标签
* @returns 找到的所有实体数组
*/
public findEntitiesByTag(tag: number): Entity[] {
const result: Entity[] = [];
for (const entity of this.buffer) {
if (entity.tag === tag) {
result.push(entity);
}
}
return result;
}
/**
* 根据组件类型查找实体
* @param componentType 组件类型
* @returns 找到的所有实体数组
*/
public findEntitiesWithComponent<T extends Component>(componentType: new (...args: any[]) => T): Entity[] {
const result: Entity[] = [];
for (const entity of this.buffer) {
if (entity.hasComponent(componentType)) {
result.push(entity);
}
}
return result;
}
/**
* 批量操作:对所有实体执行指定操作
* @param action 要执行的操作
*/
public forEach(action: (entity: Entity) => void): void {
for (const entity of this.buffer) {
action(entity);
}
}
/**
* 批量操作:对符合条件的实体执行指定操作
* @param predicate 筛选条件
* @param action 要执行的操作
*/
public forEachWhere(predicate: (entity: Entity) => boolean, action: (entity: Entity) => void): void {
for (const entity of this.buffer) {
if (predicate(entity)) {
action(entity);
}
}
}
/**
* 更新名称索引
* @param entity 实体
* @param isAdd 是否为添加操作
*/
private updateNameIndex(entity: Entity, isAdd: boolean): void {
if (!entity.name) {
return;
}
if (isAdd) {
let entities = this._nameToEntities.get(entity.name);
if (!entities) {
entities = [];
this._nameToEntities.set(entity.name, entities);
}
entities.push(entity);
} else {
const entities = this._nameToEntities.get(entity.name);
if (entities) {
const index = entities.indexOf(entity);
if (index !== -1) {
entities.splice(index, 1);
// 如果数组为空,删除映射
if (entities.length === 0) {
this._nameToEntities.delete(entity.name);
}
}
}
}
}
/**
* 重新排序实体
* @param entityId 要移动的实体ID
* @param newIndex 新的索引位置
*/
public reorderEntity(entityId: number, newIndex: number): void {
const entity = this._idToEntity.get(entityId);
if (!entity) return;
const currentIndex = this.buffer.indexOf(entity);
if (currentIndex === -1 || currentIndex === newIndex) return;
// 限制索引范围
const clampedIndex = Math.max(0, Math.min(newIndex, this.buffer.length - 1));
// 从当前位置移除
this.buffer.splice(currentIndex, 1);
// 插入到新位置
this.buffer.splice(clampedIndex, 0, entity);
}
/**
* 获取实体列表的统计信息
* @returns 统计信息
*/
public getStats(): {
totalEntities: number;
activeEntities: number;
pendingAdd: number;
pendingRemove: number;
nameIndexSize: number;
} {
let activeCount = 0;
for (const entity of this.buffer) {
if (entity.enabled && !entity.isDestroyed) {
activeCount++;
}
}
return {
totalEntities: this.buffer.length,
activeEntities: activeCount,
pendingAdd: this._entitiesToAdd.length,
pendingRemove: this._entitiesToRemove.length,
nameIndexSize: this._nameToEntities.size
};
}
}

View File

@@ -0,0 +1,123 @@
import { EntitySystem } from '../Systems/EntitySystem';
import { createLogger } from '../../Utils/Logger';
import { getSystemInstanceTypeName } from '../Decorators';
/**
* 实体处理器列表管理器
* 管理场景中的所有实体系统
*/
export class EntityProcessorList {
private static readonly _logger = createLogger('EntityProcessorList');
private _processors: EntitySystem[] = [];
private _isDirty = false;
/**
* 设置为脏状态,需要重新排序
*/
public setDirty(): void {
this._isDirty = true;
}
/**
* 添加实体处理器
* @param processor 要添加的处理器
*/
public add(processor: EntitySystem): void {
this._processors.push(processor);
this.setDirty();
}
/**
* 移除实体处理器
* @param processor 要移除的处理器
*/
public remove(processor: EntitySystem): void {
const index = this._processors.indexOf(processor);
if (index !== -1) {
this._processors.splice(index, 1);
}
}
/**
* 获取指定类型的处理器
* @param type 处理器类型
*/
public getProcessor<T extends EntitySystem>(type: new (...args: unknown[]) => T): T | null {
for (const processor of this._processors) {
if (processor instanceof type) {
return processor as T;
}
}
return null;
}
/**
* 开始处理
*
* 对所有处理器进行排序以确保正确的执行顺序。
*/
public begin(): void {
this.sortProcessors();
}
/**
* 结束处理
*/
public end(): void {
// 清理处理器
for (const processor of this._processors) {
try {
processor.reset();
} catch (error) {
EntityProcessorList._logger.error(`Error in processor ${getSystemInstanceTypeName(processor)}:`, error);
}
}
this._isDirty = false;
this._processors.length = 0;
}
/**
* 更新所有处理器
*/
public update(): void {
this.sortProcessors();
for (const processor of this._processors) {
try {
processor.update();
} catch (error) {
EntityProcessorList._logger.error(`Error in processor ${getSystemInstanceTypeName(processor)}:`, error);
}
}
}
/**
* 后期更新所有处理器
*/
public lateUpdate(): void {
for (const processor of this._processors) {
processor.lateUpdate();
}
}
/**
* 排序处理器
*/
private sortProcessors(): void {
if (this._isDirty) {
this._processors.sort((a, b) => a.updateOrder - b.updateOrder);
this._isDirty = false;
}
}
/** 获取处理器列表 */
public get processors() {
return this._processors;
}
/** 获取处理器数量 */
public get count() {
return this._processors.length;
}
}

View File

@@ -0,0 +1,405 @@
/**
* 世代式ID池管理器
*
* 用于管理实体ID的分配和回收支持世代版本控制以防止悬空引用问题。
* 世代式ID由索引和版本组成当ID被回收时版本会递增确保旧引用失效。
*
* 支持动态扩展理论上可以支持到65535个索引16位每个索引65535个版本16位
* 总计可以处理超过42亿个独特的ID组合完全满足ECS大规模实体需求。
*
* @example
* ```typescript
* const pool = new IdentifierPool();
*
* // 分配ID
* const id = pool.checkOut(); // 例如: 65536 (版本1索引0)
*
* // 回收ID
* pool.checkIn(id);
*
* // 验证ID是否有效
* const isValid = pool.isValid(id); // false因为版本已递增
* ```
*/
export class IdentifierPool {
/**
* 下一个可用的索引
*/
private _nextAvailableIndex = 0;
/**
* 空闲的索引列表
*/
private _freeIndices: number[] = [];
/**
* 每个索引对应的世代版本
* 动态扩展的Map按需分配内存
*/
private _generations = new Map<number, number>();
/**
* 延迟回收队列
* 防止在同一帧内立即重用ID避免时序问题
*/
private _pendingRecycle: Array<{
index: number;
generation: number;
timestamp: number;
}> = [];
/**
* 延迟回收时间(毫秒)
*/
private _recycleDelay: number = 100;
/**
* 最大索引限制16位
* 这是框架设计选择16位索引 + 16位版本 = 32位ID确保高效位操作
* 不是硬件限制,而是性能和内存效率的权衡
*/
private static readonly MAX_INDEX = 0xFFFF; // 65535
/**
* 最大世代限制16位
*/
private static readonly MAX_GENERATION = 0xFFFF; // 65535
/**
* 内存扩展块大小
* 当需要更多内存时,一次性预分配的索引数量
*/
private _expansionBlockSize: number;
/**
* 统计信息
*/
private _stats = {
totalAllocated: 0,
totalRecycled: 0,
currentActive: 0,
memoryExpansions: 0
};
/**
* 构造函数
*
* @param recycleDelay 延迟回收时间毫秒默认为100ms
* @param expansionBlockSize 内存扩展块大小默认为1024
*/
constructor(recycleDelay: number = 100, expansionBlockSize: number = 1024) {
this._recycleDelay = recycleDelay;
this._expansionBlockSize = expansionBlockSize;
// 预分配第一个块的世代信息
this._preAllocateGenerations(0, this._expansionBlockSize);
}
/**
* 获取一个可用的ID
*
* 返回一个32位ID高16位为世代版本低16位为索引。
*
* @returns 新分配的实体ID
* @throws {Error} 当达到索引限制时抛出错误
*/
public checkOut(): number {
// 处理延迟回收队列
this._processDelayedRecycle();
let index: number;
if (this._freeIndices.length > 0) {
// 重用回收的索引
index = this._freeIndices.pop()!;
} else {
// 分配新索引
if (this._nextAvailableIndex > IdentifierPool.MAX_INDEX) {
throw new Error(
`实体索引已达到框架设计限制 (${IdentifierPool.MAX_INDEX})。` +
'这意味着您已经分配了超过65535个不同的实体索引。' +
'这是16位索引设计的限制考虑优化实体回收策略或升级到64位ID设计。'
);
}
index = this._nextAvailableIndex++;
// 按需扩展世代存储
this._ensureGenerationCapacity(index);
}
const generation = this._generations.get(index) || 1;
this._stats.totalAllocated++;
this._stats.currentActive++;
return this._packId(index, generation);
}
/**
* 回收一个ID
*
* 验证ID的有效性后将其加入延迟回收队列。
* ID不会立即可重用而是在延迟时间后才真正回收。
*
* @param id 要回收的实体ID
* @returns 是否成功回收ID是否有效且未被重复回收
*/
public checkIn(id: number): boolean {
const index = this._unpackIndex(id);
const generation = this._unpackGeneration(id);
// 验证ID有效性
if (!this._isValidId(index, generation)) {
return false;
}
// 检查是否已经在待回收队列中
const alreadyPending = this._pendingRecycle.some(
(item) => item.index === index && item.generation === generation
);
if (alreadyPending) {
return false; // 已经在回收队列中,拒绝重复回收
}
// 加入延迟回收队列
this._pendingRecycle.push({
index,
generation,
timestamp: Date.now()
});
this._stats.currentActive--;
this._stats.totalRecycled++;
return true;
}
/**
* 验证ID是否有效
*
* 检查ID的索引和世代版本是否匹配当前状态。
*
* @param id 要验证的实体ID
* @returns ID是否有效
*/
public isValid(id: number): boolean {
const index = this._unpackIndex(id);
const generation = this._unpackGeneration(id);
return this._isValidId(index, generation);
}
/**
* 获取统计信息
*
* @returns 池的当前状态统计
*/
public getStats(): {
/** 已分配的总索引数 */
totalAllocated: number;
/** 总计回收次数 */
totalRecycled: number;
/** 当前活跃实体数 */
currentActive: number;
/** 当前空闲的索引数 */
currentlyFree: number;
/** 等待回收的ID数 */
pendingRecycle: number;
/** 理论最大实体数(设计限制) */
maxPossibleEntities: number;
/** 当前使用的最大索引 */
maxUsedIndex: number;
/** 内存使用(字节) */
memoryUsage: number;
/** 内存扩展次数 */
memoryExpansions: number;
/** 平均世代版本 */
averageGeneration: number;
/** 世代存储大小 */
generationStorageSize: number;
} {
// 计算平均世代版本
let totalGeneration = 0;
let generationCount = 0;
for (const [index, generation] of this._generations) {
if (index < this._nextAvailableIndex) {
totalGeneration += generation;
generationCount++;
}
}
const averageGeneration = generationCount > 0
? totalGeneration / generationCount
: 1;
return {
totalAllocated: this._stats.totalAllocated,
totalRecycled: this._stats.totalRecycled,
currentActive: this._stats.currentActive,
currentlyFree: this._freeIndices.length,
pendingRecycle: this._pendingRecycle.length,
maxPossibleEntities: IdentifierPool.MAX_INDEX + 1,
maxUsedIndex: this._nextAvailableIndex - 1,
memoryUsage: this._calculateMemoryUsage(),
memoryExpansions: this._stats.memoryExpansions,
averageGeneration: Math.round(averageGeneration * 100) / 100,
generationStorageSize: this._generations.size
};
}
/**
* 强制执行延迟回收处理
*
* 在某些情况下可能需要立即处理延迟回收队列,
* 比如内存压力大或者需要精确的统计信息时。
*/
public forceProcessDelayedRecycle(): void {
this._processDelayedRecycle(true);
}
/**
* 清理过期的延迟回收项
*
* 将超过延迟时间的回收项真正回收到空闲列表中。
*
* @param forceAll 是否强制处理所有延迟回收项
* @private
*/
private _processDelayedRecycle(forceAll: boolean = false): void {
if (this._pendingRecycle.length === 0) return;
const now = Date.now();
const readyToRecycle: typeof this._pendingRecycle = [];
const stillPending: typeof this._pendingRecycle = [];
// 分离已到期和未到期的项
for (const item of this._pendingRecycle) {
if (forceAll || now - item.timestamp >= this._recycleDelay) {
readyToRecycle.push(item);
} else {
stillPending.push(item);
}
}
// 处理到期的回收项
for (const item of readyToRecycle) {
// 再次验证ID有效性防止重复回收
if (this._isValidId(item.index, item.generation)) {
// 递增世代版本
let newGeneration = item.generation + 1;
// 防止世代版本溢出
if (newGeneration > IdentifierPool.MAX_GENERATION) {
newGeneration = 1; // 重置为1而不是0
}
this._generations.set(item.index, newGeneration);
// 添加到空闲列表
this._freeIndices.push(item.index);
}
}
// 更新待回收队列
this._pendingRecycle = stillPending;
}
/**
* 预分配世代信息
*
* @param startIndex 起始索引
* @param count 分配数量
* @private
*/
private _preAllocateGenerations(startIndex: number, count: number): void {
for (let i = 0; i < count; i++) {
const index = startIndex + i;
if (index <= IdentifierPool.MAX_INDEX) {
this._generations.set(index, 1);
}
}
this._stats.memoryExpansions++;
}
/**
* 确保指定索引的世代信息存在
*
* @param index 索引
* @private
*/
private _ensureGenerationCapacity(index: number): void {
if (!this._generations.has(index)) {
// 计算需要扩展的起始位置
const expansionStart = Math.floor(index / this._expansionBlockSize) * this._expansionBlockSize;
// 预分配一个块
this._preAllocateGenerations(expansionStart, this._expansionBlockSize);
}
}
/**
* 计算内存使用量
*
* @returns 内存使用字节数
* @private
*/
private _calculateMemoryUsage(): number {
const generationMapSize = this._generations.size * 16; // Map overhead + number pair
const freeIndicesSize = this._freeIndices.length * 8;
const pendingRecycleSize = this._pendingRecycle.length * 32;
return generationMapSize + freeIndicesSize + pendingRecycleSize;
}
/**
* 打包索引和世代为32位ID
*
* @param index 索引16位
* @param generation 世代版本16位
* @returns 打包后的32位ID
* @private
*/
private _packId(index: number, generation: number): number {
return (generation << 16) | index;
}
/**
* 从ID中解包索引
*
* @param id 32位ID
* @returns 索引部分16位
* @private
*/
private _unpackIndex(id: number): number {
return id & 0xFFFF;
}
/**
* 从ID中解包世代版本
*
* @param id 32位ID
* @returns 世代版本部分16位
* @private
*/
private _unpackGeneration(id: number): number {
return (id >>> 16) & 0xFFFF;
}
/**
* 内部ID有效性检查
*
* @param index 索引
* @param generation 世代版本
* @returns 是否有效
* @private
*/
private _isValidId(index: number, generation: number): boolean {
if (index < 0 || index >= this._nextAvailableIndex) {
return false;
}
const currentGeneration = this._generations.get(index);
return currentGeneration !== undefined && currentGeneration === generation;
}
}

View File

@@ -0,0 +1,343 @@
import { ComponentType } from '../Core/ComponentStorage';
import { getComponentTypeName } from '../Decorators';
/**
* 查询条件类型
*/
export type QueryCondition = {
all: ComponentType[];
any: ComponentType[];
none: ComponentType[];
tag?: number; // 按标签查询
name?: string; // 按名称查询
component?: ComponentType; // 单组件查询
matchNothing?: boolean; // 不匹配任何实体
}
/**
* 实体匹配条件描述符
*
* 用于描述实体查询条件,不执行实际查询
*
* @example
* ```typescript
* const matcher = Matcher.all(Position, Velocity)
* .any(Health, Shield)
* .none(Dead);
*
* // 获取查询条件
* const condition = matcher.getCondition();
* ```
*/
export class Matcher {
private readonly condition: QueryCondition = {
all: [],
any: [],
none: []
};
private constructor() {
// 私有构造函数,只能通过静态方法创建
}
/**
* 创建匹配器,要求所有指定的组件
* @param types 组件类型
*/
public static all(...types: ComponentType[]): Matcher {
const matcher = new Matcher();
return matcher.all(...types);
}
/**
* 创建匹配器,要求至少一个指定的组件
* @param types 组件类型
*/
public static any(...types: ComponentType[]): Matcher {
const matcher = new Matcher();
return matcher.any(...types);
}
/**
* 创建匹配器,排除指定的组件
* @param types 组件类型
*/
public static none(...types: ComponentType[]): Matcher {
const matcher = new Matcher();
return matcher.none(...types);
}
/**
* 创建按标签查询的匙配器
* @param tag 标签值
*/
public static byTag(tag: number): Matcher {
const matcher = new Matcher();
return matcher.withTag(tag);
}
/**
* 创建按名称查询的匙配器
* @param name 实体名称
*/
public static byName(name: string): Matcher {
const matcher = new Matcher();
return matcher.withName(name);
}
/**
* 创建单组件查询的匙配器
* @param componentType 组件类型
*/
public static byComponent(componentType: ComponentType): Matcher {
const matcher = new Matcher();
return matcher.withComponent(componentType);
}
/**
* 创建复杂查询构建器
*/
public static complex(): Matcher {
return new Matcher();
}
/**
* 创建空匙配器
*/
public static empty(): Matcher {
return new Matcher();
}
/**
* 创建不匹配任何实体的匹配器
* 用于只需要 onBegin/onEnd 生命周期方法但不需要处理实体的系统
*
* @example
* ```typescript
* // 创建一个只在帧开始时执行的系统
* class FrameBeginSystem extends EntitySystem {
* constructor() {
* super(Matcher.nothing());
* }
*
* protected onBegin(): void {
* // 每帧开始时执行
* }
*
* protected process(entities: readonly Entity[]): void {
* // 永远不会被调用,因为没有实体匹配
* }
* }
* ```
*/
public static nothing(): Matcher {
const matcher = new Matcher();
matcher.condition.matchNothing = true;
return matcher;
}
/**
* 必须包含所有指定组件
* @param types 组件类型
*/
public all(...types: ComponentType[]): Matcher {
this.condition.all.push(...types);
return this;
}
/**
* 必须包含至少一个指定组件
* @param types 组件类型
*/
public any(...types: ComponentType[]): Matcher {
this.condition.any.push(...types);
return this;
}
/**
* 不能包含任何指定组件
* @param types 组件类型
*/
public none(...types: ComponentType[]): Matcher {
this.condition.none.push(...types);
return this;
}
/**
* 排除指定组件(别名方法)
* @param types 组件类型
*/
public exclude(...types: ComponentType[]): Matcher {
return this.none(...types);
}
/**
* 至少包含其中之一(别名方法)
* @param types 组件类型
*/
public one(...types: ComponentType[]): Matcher {
return this.any(...types);
}
/**
* 按标签查询
* @param tag 标签值
*/
public withTag(tag: number): Matcher {
this.condition.tag = tag;
return this;
}
/**
* 按名称查询
* @param name 实体名称
*/
public withName(name: string): Matcher {
this.condition.name = name;
return this;
}
/**
* 单组件查询
* @param componentType 组件类型
*/
public withComponent(componentType: ComponentType): Matcher {
this.condition.component = componentType;
return this;
}
/**
* 移除标签条件
*/
public withoutTag(): Matcher {
delete this.condition.tag;
return this;
}
/**
* 移除名称条件
*/
public withoutName(): Matcher {
delete this.condition.name;
return this;
}
/**
* 移除单组件条件
*/
public withoutComponent(): Matcher {
delete this.condition.component;
return this;
}
/**
* 获取查询条件(只读)
*/
public getCondition(): Readonly<QueryCondition> {
return {
all: [...this.condition.all],
any: [...this.condition.any],
none: [...this.condition.none],
...(this.condition.tag !== undefined && { tag: this.condition.tag }),
...(this.condition.name !== undefined && { name: this.condition.name }),
...(this.condition.component !== undefined && { component: this.condition.component }),
...(this.condition.matchNothing && { matchNothing: true })
};
}
/**
* 检查是否为空条件
* 注意matchNothing 不算空条件,因为它是明确的"不匹配任何实体"语义
*/
public isEmpty(): boolean {
return this.condition.all.length === 0 &&
this.condition.any.length === 0 &&
this.condition.none.length === 0 &&
this.condition.tag === undefined &&
this.condition.name === undefined &&
this.condition.component === undefined &&
!this.condition.matchNothing;
}
/**
* 检查是否为"不匹配任何实体"的匹配器
*/
public isNothing(): boolean {
return this.condition.matchNothing === true;
}
/**
* 重置所有条件
*/
public reset(): Matcher {
this.condition.all.length = 0;
this.condition.any.length = 0;
this.condition.none.length = 0;
delete this.condition.tag;
delete this.condition.name;
delete this.condition.component;
delete this.condition.matchNothing;
return this;
}
/**
* 克隆匹配器
*/
public clone(): Matcher {
const cloned = new Matcher();
cloned.condition.all.push(...this.condition.all);
cloned.condition.any.push(...this.condition.any);
cloned.condition.none.push(...this.condition.none);
if (this.condition.tag !== undefined) {
cloned.condition.tag = this.condition.tag;
}
if (this.condition.name !== undefined) {
cloned.condition.name = this.condition.name;
}
if (this.condition.component !== undefined) {
cloned.condition.component = this.condition.component;
}
if (this.condition.matchNothing) {
cloned.condition.matchNothing = true;
}
return cloned;
}
/**
* 字符串表示
*/
public toString(): string {
if (this.condition.matchNothing) {
return 'Matcher[nothing]';
}
const parts: string[] = [];
if (this.condition.all.length > 0) {
parts.push(`all(${this.condition.all.map((t) => getComponentTypeName(t)).join(', ')})`);
}
if (this.condition.any.length > 0) {
parts.push(`any(${this.condition.any.map((t) => getComponentTypeName(t)).join(', ')})`);
}
if (this.condition.none.length > 0) {
parts.push(`none(${this.condition.none.map((t) => getComponentTypeName(t)).join(', ')})`);
}
if (this.condition.tag !== undefined) {
parts.push(`tag(${this.condition.tag})`);
}
if (this.condition.name !== undefined) {
parts.push(`name(${this.condition.name})`);
}
if (this.condition.component !== undefined) {
parts.push(`component(${getComponentTypeName(this.condition.component)})`);
}
return `Matcher[${parts.join(' & ')}]`;
}
}

View File

@@ -0,0 +1,310 @@
/**
* 稀疏集合实现
*
* 提供O(1)的插入、删除、查找操作,同时保持数据的紧凑存储。
* 使用密集数组存储实际数据,稀疏映射提供快速访问
*
* @template T 存储的数据类型
*
* @example
* ```typescript
* const sparseSet = new SparseSet<Entity>();
*
* sparseSet.add(entity1);
* sparseSet.add(entity2);
*
* if (sparseSet.has(entity1)) {
* sparseSet.remove(entity1);
* }
*
* sparseSet.forEach((entity, index) => {
* console.log(`Entity at index ${index}: ${entity.name}`);
* });
* ```
*/
export class SparseSet<T> {
/**
* 密集存储数组
*
* 连续存储所有有效数据,确保遍历时的缓存友好性。
*/
private _dense: T[] = [];
/**
* 稀疏映射表
*
* 将数据项映射到密集数组中的索引提供O(1)的查找性能。
*/
private _sparse = new Map<T, number>();
/**
* 添加元素到集合
*
* @param item 要添加的元素
* @returns 是否成功添加false表示元素已存在
*/
public add(item: T): boolean {
if (this._sparse.has(item)) {
return false; // 元素已存在
}
const index = this._dense.length;
this._dense.push(item);
this._sparse.set(item, index);
return true;
}
/**
* 从集合中移除元素
*
* 使用swap-and-pop技术保持数组紧凑性
* 1. 将要删除的元素与最后一个元素交换
* 2. 删除最后一个元素
* 3. 更新映射表
*
* @param item 要移除的元素
* @returns 是否成功移除false表示元素不存在
*/
public remove(item: T): boolean {
const index = this._sparse.get(item);
if (index === undefined) {
return false; // 元素不存在
}
const lastIndex = this._dense.length - 1;
// 如果不是最后一个元素,则与最后一个元素交换
if (index !== lastIndex) {
const lastItem = this._dense[lastIndex]!;
this._dense[index] = lastItem;
this._sparse.set(lastItem, index);
}
// 移除最后一个元素
this._dense.pop();
this._sparse.delete(item);
return true;
}
/**
* 检查元素是否存在于集合中
*
* @param item 要检查的元素
* @returns 元素是否存在
*/
public has(item: T): boolean {
return this._sparse.has(item);
}
/**
* 获取元素在密集数组中的索引
*
* @param item 要查询的元素
* @returns 索引如果元素不存在则返回undefined
*/
public getIndex(item: T): number | undefined {
return this._sparse.get(item);
}
/**
* 根据索引获取元素
*
* @param index 索引
* @returns 元素如果索引无效则返回undefined
*/
public getByIndex(index: number): T | undefined {
return this._dense[index];
}
/**
* 获取集合大小
*/
public get size(): number {
return this._dense.length;
}
/**
* 检查集合是否为空
*/
public get isEmpty(): boolean {
return this._dense.length === 0;
}
/**
* 遍历集合中的所有元素
*
* 保证遍历顺序与添加顺序一致(除非中间有删除操作)。
* 遍历性能优秀,因为数据在内存中连续存储。
*
* @param callback 遍历回调函数
*/
public forEach(callback: (item: T, index: number) => void): void {
for (let i = 0; i < this._dense.length; i++) {
callback(this._dense[i]!, i);
}
}
/**
* 映射集合中的所有元素
*
* @param callback 映射回调函数
* @returns 映射后的新数组
*/
public map<U>(callback: (item: T, index: number) => U): U[] {
const result: U[] = [];
for (let i = 0; i < this._dense.length; i++) {
result.push(callback(this._dense[i]!, i));
}
return result;
}
/**
* 过滤集合中的元素
*
* @param predicate 过滤条件
* @returns 满足条件的元素数组
*/
public filter(predicate: (item: T, index: number) => boolean): T[] {
const result: T[] = [];
for (let i = 0; i < this._dense.length; i++) {
if (predicate(this._dense[i]!, i)) {
result.push(this._dense[i]!);
}
}
return result;
}
/**
* 查找第一个满足条件的元素
*
* @param predicate 查找条件
* @returns 找到的元素如果没有则返回undefined
*/
public find(predicate: (item: T, index: number) => boolean): T | undefined {
for (let i = 0; i < this._dense.length; i++) {
if (predicate(this._dense[i]!, i)) {
return this._dense[i];
}
}
return undefined;
}
/**
* 检查是否存在满足条件的元素
*
* @param predicate 检查条件
* @returns 是否存在满足条件的元素
*/
public some(predicate: (item: T, index: number) => boolean): boolean {
for (let i = 0; i < this._dense.length; i++) {
if (predicate(this._dense[i]!, i)) {
return true;
}
}
return false;
}
/**
* 检查是否所有元素都满足条件
*
* @param predicate 检查条件
* @returns 是否所有元素都满足条件
*/
public every(predicate: (item: T, index: number) => boolean): boolean {
for (let i = 0; i < this._dense.length; i++) {
if (!predicate(this._dense[i]!, i)) {
return false;
}
}
return true;
}
/**
* 获取密集数组的只读副本
*
* 返回数组的浅拷贝,确保外部无法直接修改内部数据。
*/
public getDenseArray(): readonly T[] {
return [...this._dense];
}
/**
* 获取密集数组的直接引用(内部使用)
*
* 警告:直接修改返回的数组会破坏数据结构的完整性。
* 仅在性能关键场景下使用,并确保不会修改数组内容。
*/
public getDenseArrayUnsafe(): readonly T[] {
return this._dense;
}
/**
* 清空集合
*/
public clear(): void {
this._dense.length = 0;
this._sparse.clear();
}
/**
* 转换为数组
*/
public toArray(): T[] {
return [...this._dense];
}
/**
* 转换为Set
*/
public toSet(): Set<T> {
return new Set(this._dense);
}
/**
* 获取内存使用统计信息
*/
public getMemoryStats(): {
denseArraySize: number;
sparseMapSize: number;
totalMemory: number;
} {
const denseArraySize = this._dense.length * 8; // 估计每个引用8字节
const sparseMapSize = this._sparse.size * 16; // 估计每个Map条目16字节
return {
denseArraySize,
sparseMapSize,
totalMemory: denseArraySize + sparseMapSize
};
}
/**
* 验证数据结构的完整性
*
* 调试用方法,检查内部数据结构是否一致。
*/
public validate(): boolean {
// 检查大小一致性
if (this._dense.length !== this._sparse.size) {
return false;
}
// 检查映射关系的正确性
for (let i = 0; i < this._dense.length; i++) {
const item = this._dense[i]!;
const mappedIndex = this._sparse.get(item);
if (mappedIndex !== i) {
return false;
}
}
// 检查稀疏映射中的所有项都在密集数组中
for (const [item, index] of this._sparse) {
if (index >= this._dense.length || this._dense[index] !== item) {
return false;
}
}
return true;
}
}

View File

@@ -0,0 +1,10 @@
// ECS工具类导出
export { EntityList } from './EntityList';
export { EntityProcessorList } from './EntityProcessorList';
export { IdentifierPool } from './IdentifierPool';
export { Matcher } from './Matcher';
export { Bits } from './Bits';
export { BitMask64Utils } from './BigIntCompatibility';
export type { BitMask64Data } from './BigIntCompatibility';
export { SparseSet } from './SparseSet';
export { ComponentSparseSet } from './ComponentSparseSet';

View File

@@ -0,0 +1,531 @@
import { IScene } from './IScene';
import { Scene } from './Scene';
import { createLogger } from '../Utils/Logger';
import { PerformanceMonitor } from '../Utils/PerformanceMonitor';
import { ServiceContainer } from '../Core/ServiceContainer';
const logger = createLogger('World');
/**
* @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;
}
/**
* @zh World配置接口
* @en World configuration interface
*/
export type IWorldConfig = {
/**
* @zh World名称
* @en World name
*/
name?: string;
/**
* @zh 是否启用调试模式
* @en Enable debug mode
*/
debug?: boolean;
/**
* @zh 最大Scene数量限制
* @en Maximum number of scenes
*/
maxScenes?: number;
/**
* @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;
}
/**
* @zh World类 - ECS世界管理器
* @en World class - ECS world manager
*
* @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
* // @zh 创建游戏房间的World | @en Create World for game room
* const roomWorld = new World({ name: 'Room_001' });
*
* // @zh 注册World级别的服务 | @en Register World-level service
* roomWorld.services.registerSingleton(RoomManager);
*
* // @zh 在World中创建Scene | @en Create Scene in World
* const gameScene = roomWorld.createScene('game', new Scene());
* const uiScene = roomWorld.createScene('ui', new Scene());
*
* // @zh 在Scene中使用World级别的服务 | @en Use World-level service in Scene
* const roomManager = roomWorld.services.resolve(RoomManager);
*
* // @zh 更新整个World | @en Update entire World
* roomWorld.update(deltaTime);
* ```
*/
export class World {
public readonly name: string;
private readonly _config: IWorldConfig;
private readonly _scenes: Map<string, IScene> = new Map();
private readonly _activeScenes: Set<string> = new Set();
private readonly _globalSystems: IGlobalSystem[] = [];
private readonly _services: ServiceContainer;
private _isActive: boolean = false;
private _createdAt: number;
constructor(config: IWorldConfig = {}) {
this._config = {
name: 'World',
debug: false,
maxScenes: 10,
autoCleanup: true,
cleanupThresholdMs: 5 * 60 * 1000,
...config
};
this.name = this._config.name!;
this._createdAt = Date.now();
this._services = new ServiceContainer();
}
/**
* @zh World级别的服务容器用于管理World范围内的全局服务
* @en World-level service container for managing World-scoped global services
*/
public get services(): ServiceContainer {
return this._services;
}
/**
* @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() === '') {
throw new Error('Scene name不能为空');
}
if (this._scenes.has(sceneName)) {
throw new Error(`Scene name '${sceneName}' 已存在于World '${this.name}' 中`);
}
if (this._scenes.size >= this._config.maxScenes!) {
throw new Error(`World '${this.name}' 已达到最大Scene数量限制: ${this._config.maxScenes}`);
}
const scene = sceneInstance || (new Scene() as unknown as T);
if (this._config.debug) {
const performanceMonitor = new PerformanceMonitor();
performanceMonitor.enable();
scene.services.registerInstance(PerformanceMonitor, performanceMonitor);
}
(scene as { id?: string }).id = sceneName;
if (!scene.name) {
scene.name = sceneName;
}
this._scenes.set(sceneName, scene);
scene.initialize();
return 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);
if (!scene) {
return false;
}
if (this._activeScenes.has(sceneName)) {
this.setSceneActive(sceneName, false);
}
scene.end();
this._scenes.delete(sceneName);
logger.info(`从World '${this.name}' 中移除Scene: ${sceneName}`);
return true;
}
/**
* @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;
}
/**
* 获取所有Scene ID
*/
public getSceneIds(): string[] {
return Array.from(this._scenes.keys());
}
/**
* 获取所有Scene
*/
public getAllScenes(): IScene[] {
return Array.from(this._scenes.values());
}
/**
* 移除所有Scene
*/
public removeAllScenes(): void {
const sceneNames = Array.from(this._scenes.keys());
for (const sceneName of sceneNames) {
this.removeScene(sceneName);
}
logger.info(`从World '${this.name}' 中移除所有Scene`);
}
/**
* 设置Scene激活状态
*/
public setSceneActive(sceneName: string, active: boolean): void {
const scene = this._scenes.get(sceneName);
if (!scene) {
logger.warn(`Scene '${sceneName}' 不存在于World '${this.name}' 中`);
return;
}
if (active) {
this._activeScenes.add(sceneName);
scene.begin?.();
logger.debug(`在World '${this.name}' 中激活Scene: ${sceneName}`);
} else {
this._activeScenes.delete(sceneName);
logger.debug(`在World '${this.name}' 中停用Scene: ${sceneName}`);
}
}
/**
* 检查Scene是否激活
*/
public isSceneActive(sceneName: string): boolean {
return this._activeScenes.has(sceneName);
}
/**
* 获取活跃Scene数量
*/
public getActiveSceneCount(): number {
return this._activeScenes.size;
}
/**
* 添加全局System
* 全局System会在所有激活Scene之前更新
*/
public addGlobalSystem<T extends IGlobalSystem>(system: T): T {
if (this._globalSystems.includes(system)) {
return system;
}
this._globalSystems.push(system);
system.initialize?.();
logger.debug(`在World '${this.name}' 中添加全局System: ${system.name}`);
return system;
}
/**
* 移除全局System
*/
public removeGlobalSystem(system: IGlobalSystem): boolean {
const index = this._globalSystems.indexOf(system);
if (index === -1) {
return false;
}
this._globalSystems.splice(index, 1);
system.reset?.();
logger.debug(`从World '${this.name}' 中移除全局System: ${system.name}`);
return true;
}
/**
* 获取全局System
*/
public getGlobalSystem<T extends IGlobalSystem>(type: new (...args: any[]) => T): T | null {
for (const system of this._globalSystems) {
if (system instanceof type) {
return system as T;
}
}
return null;
}
/**
* 启动World
*/
public start(): void {
if (this._isActive) {
return;
}
this._isActive = true;
for (const system of this._globalSystems) {
system.initialize?.();
}
logger.info(`启动World: ${this.name}`);
}
/**
* 停止World
*/
public stop(): void {
if (!this._isActive) {
return;
}
for (const sceneName of this._activeScenes) {
this.setSceneActive(sceneName, false);
}
for (const system of this._globalSystems) {
system.reset?.();
}
this._isActive = false;
logger.info(`停止World: ${this.name}`);
}
/**
* @zh 更新World中的全局System
* @en Update global systems in World
*
* @internal Called by Core.update()
*/
public updateGlobalSystems(): void {
if (!this._isActive) {
return;
}
for (const system of this._globalSystems) {
system.update?.();
}
}
/**
* @zh 更新World中的所有激活Scene
* @en Update all active scenes in World
*
* @internal Called by Core.update()
*/
public updateScenes(): void {
if (!this._isActive) {
return;
}
for (const sceneName of this._activeScenes) {
const scene = this._scenes.get(sceneName);
scene?.update?.();
}
if (this._config.autoCleanup) {
this.cleanup();
}
}
/**
* 销毁World
*/
public destroy(): void {
logger.info(`销毁World: ${this.name}`);
this.stop();
for (const sceneName of Array.from(this._scenes.keys())) {
this.removeScene(sceneName);
}
for (const system of this._globalSystems) {
if (system.destroy) {
system.destroy();
} else {
system.reset?.();
}
}
this._globalSystems.length = 0;
this._services.clear();
this._scenes.clear();
this._activeScenes.clear();
}
/**
* 获取World状态
*/
public getStatus() {
return {
name: this.name,
isActive: this._isActive,
sceneCount: this._scenes.size,
activeSceneCount: this._activeScenes.size,
globalSystemCount: this._globalSystems.length,
createdAt: this._createdAt,
config: { ...this._config },
scenes: Array.from(this._scenes.keys()).map((sceneName) => ({
id: sceneName,
isActive: this._activeScenes.has(sceneName),
name: this._scenes.get(sceneName)?.name || sceneName
}))
};
}
/**
* 获取World统计信息
*/
public getStats() {
const stats = {
totalEntities: 0,
totalSystems: this._globalSystems.length,
memoryUsage: 0,
performance: {
averageUpdateTime: 0,
maxUpdateTime: 0
}
};
for (const scene of this._scenes.values()) {
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 _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 candidates = [...this._scenes.entries()]
.filter(([name, scene]) => this._isSceneCleanupCandidate(name, scene));
for (const [sceneName] of candidates) {
this.removeScene(sceneName);
logger.debug(`自动清理空Scene: ${sceneName} from World ${this.name}`);
}
}
/**
* 检查World是否激活
*/
public get isActive(): boolean {
return this._isActive;
}
/**
* 获取Scene数量
*/
public get sceneCount(): number {
return this._scenes.size;
}
/**
* 获取创建时间
*/
public get createdAt(): number {
return this._createdAt;
}
}

View File

@@ -0,0 +1,447 @@
import { World, IWorldConfig } from './World';
import { createLogger } from '../Utils/Logger';
import type { IService } from '../Core/ServiceContainer';
const logger = createLogger('WorldManager');
/**
* WorldManager配置接口
*/
export type IWorldManagerConfig = {
/**
* 最大World数量
*/
maxWorlds?: number;
/**
* 是否自动清理空World
*/
autoCleanup?: boolean;
/**
* 清理间隔(帧数)
*/
cleanupFrameInterval?: number;
/**
* 是否启用调试模式
*/
debug?: boolean;
}
/**
* World管理器 - 管理所有World实例
*
* WorldManager负责管理多个独立的World实例。
* 每个World都是独立的ECS环境可以包含多个Scene。
*
* 适用场景:
* - MMO游戏的多房间管理
* - 服务器端的多游戏实例
* - 需要完全隔离的多个游戏环境
*
* @example
* ```typescript
* // 创建WorldManager实例
* const worldManager = new WorldManager({
* maxWorlds: 100,
* autoCleanup: true
* });
*
* // 创建游戏房间World
* const room1 = worldManager.createWorld('room_001', {
* name: 'GameRoom_001',
* maxScenes: 5
* });
* room1.setActive(true);
*
* // 游戏循环
* function gameLoop(deltaTime: number) {
* Core.update(deltaTime);
* worldManager.updateAll(); // 更新所有活跃World
* }
* ```
*/
export class WorldManager implements IService {
private readonly _config: Required<IWorldManagerConfig>;
private readonly _worlds: Map<string, World> = new Map();
private _isRunning: boolean = false;
private _framesSinceCleanup: number = 0;
public constructor(config: IWorldManagerConfig = {}) {
this._config = {
maxWorlds: 50,
autoCleanup: true,
cleanupFrameInterval: 1800, // 1800帧
debug: false,
...config
};
// 默认启动运行状态
this._isRunning = true;
logger.info('WorldManager已初始化', {
maxWorlds: this._config.maxWorlds,
autoCleanup: this._config.autoCleanup,
cleanupFrameInterval: this._config.cleanupFrameInterval
});
}
/**
* 创建新World
*/
public createWorld(worldName: string, config?: IWorldConfig): World {
if (!worldName || typeof worldName !== 'string' || worldName.trim() === '') {
throw new Error('World name不能为空');
}
if (this._worlds.has(worldName)) {
throw new Error(`World name '${worldName}' 已存在`);
}
if (this._worlds.size >= this._config.maxWorlds!) {
throw new Error(`已达到最大World数量限制: ${this._config.maxWorlds}`);
}
// 优先级config.debug > WorldManager.debug > 默认
const worldConfig: IWorldConfig = {
name: worldName,
debug: config?.debug ?? this._config.debug ?? false,
...(config?.maxScenes !== undefined && { maxScenes: config.maxScenes }),
...(config?.autoCleanup !== undefined && { autoCleanup: config.autoCleanup })
};
const world = new World(worldConfig);
this._worlds.set(worldName, world);
return world;
}
/**
* 移除World
*/
public removeWorld(worldName: string): boolean {
const world = this._worlds.get(worldName);
if (!world) {
return false;
}
// 销毁World
world.destroy();
this._worlds.delete(worldName);
logger.info(`移除World: ${worldName}`);
return true;
}
/**
* 获取World
*/
public getWorld(worldName: string): World | null {
return this._worlds.get(worldName) || null;
}
/**
* 获取所有World ID
*/
public getWorldIds(): string[] {
return Array.from(this._worlds.keys());
}
/**
* 获取所有World
*/
public getAllWorlds(): World[] {
return Array.from(this._worlds.values());
}
/**
* 设置World激活状态
*/
public setWorldActive(worldName: string, active: boolean): void {
const world = this._worlds.get(worldName);
if (!world) {
logger.warn(`World '${worldName}' 不存在`);
return;
}
if (active) {
world.start();
logger.debug(`激活World: ${worldName}`);
} else {
world.stop();
logger.debug(`停用World: ${worldName}`);
}
}
/**
* 检查World是否激活
*/
public isWorldActive(worldName: string): boolean {
const world = this._worlds.get(worldName);
return world?.isActive ?? false;
}
/**
* 更新所有活跃的World
*
* 应该在每帧的游戏循环中调用。
* 会自动更新所有活跃World的全局系统和场景。
*
* @example
* ```typescript
* function gameLoop(deltaTime: number) {
* Core.update(deltaTime); // 更新全局服务
* worldManager.updateAll(); // 更新所有World
* }
* ```
*/
public updateAll(): void {
if (!this._isRunning) return;
for (const world of this._worlds.values()) {
if (world.isActive) {
// 更新World的全局System
world.updateGlobalSystems();
// 更新World中的所有Scene
world.updateScenes();
}
}
// 基于帧的自动清理
if (this._config.autoCleanup) {
this._framesSinceCleanup++;
if (this._framesSinceCleanup >= this._config.cleanupFrameInterval) {
this.cleanup();
this._framesSinceCleanup = 0; // 重置计数器
if (this._config.debug) {
logger.debug(`执行定期清理World (间隔: ${this._config.cleanupFrameInterval} 帧)`);
}
}
}
}
/**
* 获取所有激活的World
*/
public getActiveWorlds(): World[] {
const activeWorlds: World[] = [];
for (const world of this._worlds.values()) {
if (world.isActive) {
activeWorlds.push(world);
}
}
return activeWorlds;
}
/**
* 启动所有World
*/
public startAll(): void {
this._isRunning = true;
for (const world of this._worlds.values()) {
world.start();
}
logger.info('启动所有World');
}
/**
* 停止所有World
*/
public stopAll(): void {
this._isRunning = false;
for (const world of this._worlds.values()) {
world.stop();
}
logger.info('停止所有World');
}
/**
* 查找满足条件的World
*/
public findWorlds(predicate: (world: World) => boolean): World[] {
const results: World[] = [];
for (const world of this._worlds.values()) {
if (predicate(world)) {
results.push(world);
}
}
return results;
}
/**
* 根据名称查找World
*/
public findWorldByName(name: string): World | null {
for (const world of this._worlds.values()) {
if (world.name === name) {
return world;
}
}
return null;
}
/**
* 获取WorldManager统计信息
*/
public getStats() {
const stats = {
totalWorlds: this._worlds.size,
activeWorlds: this.activeWorldCount,
totalScenes: 0,
totalEntities: 0,
totalSystems: 0,
memoryUsage: 0,
isRunning: this._isRunning,
config: { ...this._config },
worlds: [] as any[]
};
for (const [worldName, world] of this._worlds) {
const worldStats = world.getStats();
stats.totalScenes += worldStats.totalSystems; // World的getStats可能需要调整
stats.totalEntities += worldStats.totalEntities;
stats.totalSystems += worldStats.totalSystems;
stats.worlds.push({
id: worldName,
name: world.name,
isActive: world.isActive,
sceneCount: world.sceneCount,
...worldStats
});
}
return stats;
}
/**
* 获取详细状态信息
*/
public getDetailedStatus() {
return {
...this.getStats(),
worlds: Array.from(this._worlds.entries()).map(([worldName, world]) => ({
id: worldName,
isActive: world.isActive,
status: world.getStatus()
}))
};
}
/**
* 清理空World
*/
public cleanup(): number {
const worldsToRemove: string[] = [];
for (const [worldName, world] of this._worlds) {
if (this.shouldCleanupWorld(world)) {
worldsToRemove.push(worldName);
}
}
for (const worldName of worldsToRemove) {
this.removeWorld(worldName);
}
if (worldsToRemove.length > 0) {
logger.debug(`清理了 ${worldsToRemove.length} 个World`);
}
return worldsToRemove.length;
}
/**
* 销毁WorldManager
*/
public destroy(): void {
logger.info('正在销毁WorldManager...');
// 停止所有World
this.stopAll();
// 销毁所有World
const worldNames = Array.from(this._worlds.keys());
for (const worldName of worldNames) {
this.removeWorld(worldName);
}
this._worlds.clear();
this._isRunning = false;
logger.info('WorldManager已销毁');
}
/**
* 实现 IService 接口的 dispose 方法
* 调用 destroy 方法进行清理
*/
public dispose(): void {
this.destroy();
}
/**
* 判断World是否应该被清理
* 清理策略:
* 1. World未激活
* 2. 没有Scene或所有Scene都是空的
* 3. 创建时间超过10分钟
*/
private shouldCleanupWorld(world: World): boolean {
if (world.isActive) {
return false;
}
const age = Date.now() - world.createdAt;
const isOldEnough = age > 10 * 60 * 1000; // 10分钟
if (world.sceneCount === 0) {
return isOldEnough;
}
// 检查是否所有Scene都是空的
const allScenes = world.getAllScenes();
const hasEntities = allScenes.some((scene) => scene.entities && scene.entities.count > 0);
return !hasEntities && isOldEnough;
}
/**
* 获取World总数
*/
public get worldCount(): number {
return this._worlds.size;
}
/**
* 获取激活World数量
*/
public get activeWorldCount(): number {
let count = 0;
for (const world of this._worlds.values()) {
if (world.isActive) count++;
}
return count;
}
/**
* 检查是否正在运行
*/
public get isRunning(): boolean {
return this._isRunning;
}
/**
* 获取配置
*/
public get config(): IWorldManagerConfig {
return { ...this._config };
}
}

Some files were not shown because too many files have changed in this diff Show More