文档及教程更新

This commit is contained in:
YHH
2025-06-10 13:12:14 +08:00
parent ef023d27bf
commit 0c8f232282
21 changed files with 5470 additions and 2017 deletions

502
README.md
View File

@@ -3,53 +3,45 @@
[![npm version](https://badge.fury.io/js/%40esengine%2Fecs-framework.svg)](https://badge.fury.io/js/%40esengine%2Fecs-framework)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
一个专业级的 TypeScript ECSEntity-Component-System框架,采用现代化架构设计,专为高性能游戏开发打造
TypeScript ECS (Entity-Component-System) 框架,专为游戏开发设计
## ✨ 核心特性
> 🤔 **什么是 ECS** 不熟悉 ECS 架构?建议先阅读 [ECS 架构基础](docs/concepts-explained.md#ecs-架构基础) 了解核心概念
- 🏗️ **现代化 ECS 架构** - 完整的实体组件系统,提供清晰的代码结构
- 📡 **类型安全事件系统** - 增强的事件总线,支持异步事件、优先级、批处理和装饰器
-**定时器管理系统** - 完整的定时器管理,支持延迟和重复任务
- 🔍 **智能查询系统** - 支持复杂的实体查询流式API设计
-**高性能优化** - 组件索引、Archetype系统、脏标记机制三重优化
- 🛠️ **开发者友好** - 完整的TypeScript支持丰富的调试工具
- 📦 **轻量级设计** - 最小化依赖,适用于各种游戏引擎
## 特性
## 📦 安装
- 🔧 **完整的 TypeScript 支持** - 强类型检查和代码提示
- 📡 **[类型安全事件系统](docs/concepts-explained.md#事件系统)** - 事件装饰器和异步事件处理
- 🔍 **[查询系统](docs/concepts-explained.md#实体管理)** - 流式 API 和智能缓存
-**[性能优化](docs/concepts-explained.md#性能优化技术)** - 组件索引、Archetype 系统、脏标记
- 🎯 **[实体管理器](docs/concepts-explained.md#实体管理)** - 统一的实体生命周期管理
- 🧰 **调试工具** - 内置性能监控和调试信息
> 📖 **不熟悉这些概念?** 查看我们的 [技术概念详解](docs/concepts-explained.md) 了解它们的作用和应用场景
## 安装
```bash
npm install @esengine/ecs-framework
```
## 🚀 快速开始
## 快速开始
### 1. 基础设置
### 基础设置
```typescript
import { Core, CoreEvents, Scene } from '@esengine/ecs-framework';
import { Core, Scene, Entity, Component, EntitySystem } from '@esengine/ecs-framework';
// 创建 Core 实例
const core = Core.create(true); // 开启调试模式
// 创建核心实例
const core = Core.create(true); // 调试模式
// 创建场景
class GameScene extends Scene {
public initialize() {
// 场景初始化逻辑
}
}
// 在游戏循环中更新框架
function gameLoop() {
Core.emitter.emit(CoreEvents.frameUpdated);
}
const scene = new Scene();
Core.scene = scene;
```
### 2. 创建实体和组件
### 定义组件
```typescript
import { Component, Entity } from '@esengine/ecs-framework';
// 定义组件
class PositionComponent extends Component {
constructor(public x: number = 0, public y: number = 0) {
super();
@@ -62,18 +54,37 @@ class VelocityComponent extends Component {
}
}
// 创建实体
const entity = scene.createEntity("Player");
entity.addComponent(new PositionComponent(100, 100));
entity.addComponent(new VelocityComponent(10, 0));
class HealthComponent extends Component {
constructor(
public maxHealth: number = 100,
public currentHealth: number = 100
) {
super();
}
}
```
### 3. 创建处理系统
### 创建实体
```typescript
import { EntitySystem } from '@esengine/ecs-framework';
// 基础实体创建
const player = scene.createEntity("Player");
player.addComponent(new PositionComponent(100, 100));
player.addComponent(new VelocityComponent(5, 0));
player.addComponent(new HealthComponent(100, 100));
// 批量创建实体
const enemies = scene.createEntities(50, "Enemy");
```
### 创建系统
```typescript
class MovementSystem extends EntitySystem {
constructor() {
super();
}
public process(entities: Entity[]) {
for (const entity of entities) {
const position = entity.getComponent(PositionComponent);
@@ -91,236 +102,297 @@ class MovementSystem extends EntitySystem {
scene.addEntityProcessor(new MovementSystem());
```
### 4. 实体查询和管理
### 游戏循环
```typescript
function gameLoop() {
// 更新场景
scene.update();
// 发送帧更新事件
Core.emitter.emit(CoreEvents.frameUpdated);
requestAnimationFrame(gameLoop);
}
gameLoop();
```
## 实体管理器
EntityManager 提供了统一的实体管理接口:
```typescript
import { EntityManager } from '@esengine/ecs-framework';
// 使用EntityManager进行高级查询
const entityManager = new EntityManager();
// 查询具有特定组件的实体
const movingEntities = entityManager
// 流式查询 API
const results = entityManager
.query()
.withAll(PositionComponent, VelocityComponent)
.withNone(HealthComponent)
.withTag(1)
.execute();
// 查询带标签的实体
const enemies = entityManager.getEntitiesByTag(1);
// 批量操作使用Scene的方法
const bullets = scene.createEntities(100, "bullet");
// 批量创建实体
const bullets = entityManager.createEntities(100, "bullet");
// 按标签查询
const enemies = entityManager.getEntitiesByTag(2);
```
### 5. 事件系统
## 事件系统
### [基础事件](docs/concepts-explained.md#类型安全事件)
类型安全的事件系统,编译时检查事件名和数据类型。
```typescript
import { EventBus, ECSEventType, EventHandler } from '@esengine/ecs-framework';
import { EventBus, ECSEventType } from '@esengine/ecs-framework';
// 获取事件总线
const eventBus = entityManager.eventBus;
// 监听实体创建事件
// 监听预定义事件
eventBus.onEntityCreated((data) => {
console.log(`Entity created: ${data.entityName}`);
console.log(`实体创建: ${data.entityName}`);
});
// 监听组件添加事件
eventBus.onComponentAdded((data) => {
console.log(`Component ${data.componentType} added to entity ${data.entityId}`);
console.log(`组件添加: ${data.componentType}`);
});
// 使用装饰器自动注册事件监听器
class GameManager {
@EventHandler(ECSEventType.ENTITY_DESTROYED)
onEntityDestroyed(data) {
console.log('Entity destroyed:', data.entityName);
}
}
// 自定义事件
eventBus.emit('player:levelup', { playerId: 123, newLevel: 5 });
eventBus.emit('player:death', { playerId: 123, reason: 'fall' });
```
## 🆚 框架对比
### [事件装饰器](docs/concepts-explained.md#事件装饰器)
与其他 TypeScript ECS 框架相比,我们的优势:
| 特性 | @esengine/ecs-framework | bitecs | ecsy | Miniplex |
|------|-------------------------|-------|------|----------|
| **TypeScript 支持** | ✅ 原生支持 | ✅ 完整支持 | ⚠️ 部分支持 | ✅ 原生支持 |
| **事件系统** | ✅ 类型安全+装饰器 | ❌ 无内置事件系统 | ⚠️ 基础事件 | ✅ 响应式事件 |
| **查询系统** | ✅ 智能查询+流式API | ✅ 高性能 | ✅ 基础查询 | ✅ 响应式查询 |
| **性能优化** | ✅ 多层优化系统 | ✅ 高性能优化 | ⚠️ 基础优化 | ✅ React集成优化 |
| **实体管理器** | ✅ 统一管理接口 | ❌ 无统一接口 | ✅ 基础管理 | ✅ 响应式管理 |
| **组件索引** | ✅ 哈希+位图索引 | ✅ 原生支持 | ❌ 无索引系统 | ✅ 自动索引 |
| **Archetype系统** | ✅ 内置支持 | ✅ 内置支持 | ❌ 无Archetype | ❌ 无Archetype |
| **脏标记系统** | ✅ 细粒度追踪 | ⚠️ 基础支持 | ❌ 无脏标记 | ✅ React级追踪 |
| **批量操作** | ✅ 全面的批量API | ✅ 批量支持 | ⚠️ 有限支持 | ⚠️ 有限支持 |
| **游戏引擎集成** | ✅ 通用设计 | ✅ 通用设计 | ✅ 通用设计 | ⚠️ 主要针对React |
| **学习曲线** | 🟢 中等 | 🟡 较陡峭 | 🟢 简单 | 🟡 需要React知识 |
| **社区生态** | 🟡 成长中 | 🟢 活跃 | 🟡 稳定 | 🟡 小众但精品 |
### 为什么选择我们?
**相比 bitecs**
- 更友好的 TypeScript API无需手动管理内存
- 完整的实体管理器,开发体验更佳
- 内置类型安全事件系统bitecs需要自己实现
- 多种索引系统可选,适应不同场景
**相比 ecsy**
- 现代化的性能优化系统组件索引、Archetype、脏标记
- 更完整的 TypeScript 类型定义
- 增强的事件系统,支持装饰器和异步事件
- 活跃的维护和功能更新
**相比 Miniplex**
- 不依赖 React 生态,可用于任何游戏引擎
- 专门针对游戏开发优化
- 更轻量级的核心设计
- 传统事件模式,更适合游戏开发习惯
## 📚 核心概念
### Entity实体
实体是游戏世界中的基本对象,可以挂载组件和运行系统。
使用装饰器语法自动注册事件监听器,减少样板代码。
```typescript
// 创建实体
const entity = scene.createEntity("Player");
import { EventHandler, ECSEventType } from '@esengine/ecs-framework';
// 设置实体属性
entity.tag = 1;
entity.updateOrder = 0;
entity.enabled = true;
// 批量创建实体
const entities = scene.createEntities(100, "Enemy");
```
### Component组件
组件存储数据,定义实体的属性和状态。
```typescript
import { Component } from '@esengine/ecs-framework';
class HealthComponent extends Component {
public maxHealth: number = 100;
public currentHealth: number = 100;
public takeDamage(damage: number) {
this.currentHealth = Math.max(0, this.currentHealth - damage);
if (this.currentHealth <= 0) {
this.entity.destroy();
}
class GameSystem {
@EventHandler(ECSEventType.ENTITY_DESTROYED)
onEntityDestroyed(data: EntityDestroyedEventData) {
console.log('实体销毁:', data.entityName);
}
}
// 添加组件到实体
entity.addComponent(new HealthComponent());
```
### System系统
系统处理具有特定组件的实体集合,实现游戏逻辑。
```typescript
import { EntitySystem, Entity } from '@esengine/ecs-framework';
class HealthSystem extends EntitySystem {
protected process(entities: Entity[]) {
for (const entity of entities) {
const health = entity.getComponent(HealthComponent);
if (health && health.currentHealth <= 0) {
// 处理实体死亡逻辑
entity.destroy();
}
}
@EventHandler('player:levelup')
onPlayerLevelUp(data: { playerId: number; newLevel: number }) {
console.log(`玩家 ${data.playerId} 升级到 ${data.newLevel}`);
}
}
```
## 🧪 测试
## 性能优化
```bash
# 运行所有测试
npm run test
### [组件索引](docs/concepts-explained.md#组件索引系统)
# 性能基准测试
npm run benchmark
通过建立索引避免线性搜索,将查询复杂度从 O(n) 降低到 O(1)。
```typescript
// 使用Scene的查询系统进行组件索引
const querySystem = scene.querySystem;
// 查询具有特定组件的实体
const entitiesWithPosition = querySystem.queryAll(PositionComponent).entities;
const entitiesWithVelocity = querySystem.queryAll(VelocityComponent).entities;
// 性能统计
const stats = querySystem.getStats();
console.log('查询效率:', stats.hitRate);
```
## 📖 文档
**索引类型选择:**
- **哈希索引** - 适合稳定的、大量的组件(如位置、生命值)
- **位图索引** - 适合频繁变化的组件如Buff、状态
- [快速入门](docs/getting-started.md) - 从零开始学习框架使用
- [EntityManager 使用指南](docs/entity-manager-example.md) - 详细了解实体管理器的高级功能
- [事件系统使用指南](docs/event-system-example.md) - 学习类型安全事件系统的完整用法
- [性能优化指南](docs/performance-optimization.md) - 深入了解三大性能优化系统
- [核心概念](docs/core-concepts.md) - 深入了解 ECS 架构和设计原理
- [查询系统使用指南](docs/query-system-usage.md) - 学习高性能查询系统的详细用法
> 📋 详细选择指南参见 [索引类型选择指南](docs/concepts-explained.md#索引类型选择指南)
## 🔗 扩展库
### [Archetype 系统](docs/concepts-explained.md#archetype-系统)
- [路径寻找库](https://github.com/esengine/ecs-astar) - A*、广度优先、Dijkstra、GOAP 算法
- [AI 系统](https://github.com/esengine/BehaviourTree-ai) - 行为树、效用 AI 系统
将具有相同组件组合的实体分组,减少查询时的组件检查开销。
## 🤝 贡献
```typescript
// 使用查询系统的Archetype功能
const querySystem = scene.querySystem;
欢迎提交 Issue 和 Pull Request
### 开发环境设置
```bash
# 克隆项目
git clone https://github.com/esengine/ecs-framework.git
cd ecs-framework
# 运行基准测试
node benchmark.js
# 开发构建 (在source目录)
cd source && npm install && npm run build
// 查询统计
const stats = querySystem.getStats();
console.log('缓存命中率:', stats.hitRate);
```
### 构建要求
### [脏标记系统](docs/concepts-explained.md#脏标记系统)
追踪数据变化,只处理发生改变的实体,避免不必要的计算。
```typescript
// 脏标记通过组件系统自动管理
// 组件变化时会自动标记为脏数据
// 查询系统会自动处理脏标记优化
const movingEntities = scene.querySystem.queryAll(PositionComponent, VelocityComponent);
```
> 💡 **不确定何时使用这些优化?** 查看 [性能优化建议](docs/concepts-explained.md#性能建议) 了解适用场景
## API 参考
### 核心类
| 类 | 描述 |
|---|---|
| `Core` | 框架核心管理类 |
| `Scene` | 场景容器,管理实体和系统 |
| `Entity` | 实体对象,包含组件集合 |
| `Component` | 组件基类 |
| `EntitySystem` | 系统基类 |
| `EntityManager` | 实体管理器 |
### 查询 API
```typescript
entityManager
.query()
.withAll(...components) // 包含所有指定组件
.withAny(...components) // 包含任意指定组件
.withNone(...components) // 不包含指定组件
.withTag(tag) // 包含指定标签
.withoutTag(tag) // 不包含指定标签
.execute() // 执行查询
```
### 事件类型
```typescript
enum ECSEventType {
ENTITY_CREATED = 'entity:created',
ENTITY_DESTROYED = 'entity:destroyed',
COMPONENT_ADDED = 'component:added',
COMPONENT_REMOVED = 'component:removed',
SYSTEM_ADDED = 'system:added',
SYSTEM_REMOVED = 'system:removed'
}
```
## 与其他框架对比
| 特性 | @esengine/ecs-framework | bitECS | Miniplex |
|------|-------------------------|--------|----------|
| TypeScript 支持 | ✅ 原生支持 | ✅ 完整支持 | ✅ 原生支持 |
| 事件系统 | ✅ 内置+装饰器 | ❌ 需自己实现 | ✅ 响应式 |
| 查询系统 | ✅ 流式 API | ✅ 函数式 | ✅ 响应式 |
| 实体管理器 | ✅ 统一接口 | ❌ 低级 API | ✅ 高级接口 |
| 性能优化 | ✅ 多重优化 | ✅ 极致性能 | ✅ React 优化 |
| 游戏引擎集成 | ✅ 通用设计 | ✅ 通用设计 | ⚠️ 主要 React |
**选择指南:**
- 选择本框架:需要完整的游戏开发工具链和中文社区支持
- 选择 bitECS需要极致性能和最小化设计
- 选择 Miniplex主要用于 React 应用开发
## 项目结构
```
ecs-framework/
├── src/
│ ├── ECS/ # ECS 核心系统
│ │ ├── Core/ # 核心管理器
│ │ ├── Systems/ # 系统类型
│ │ └── Utils/ # ECS 工具
│ ├── Types/ # TypeScript接口定义
│ └── Utils/ # 通用工具
├── docs/ # 文档
└── scripts/ # 构建脚本
```
## 文档
### 🎯 新手入门
- **[📖 新手教程完整指南](docs/beginner-tutorials.md)** - 完整学习路径,从零开始 ⭐ **强烈推荐**
- [🚀 快速入门](docs/getting-started.md) - 详细的入门教程
- [🧠 技术概念详解](docs/concepts-explained.md) - 通俗易懂的技术概念解释 ⭐ **推荐新手阅读**
- [🎯 位掩码使用指南](docs/bitmask-guide.md) - 位掩码概念、原理和高级使用技巧
- [💡 使用场景示例](docs/use-cases.md) - 不同类型游戏的具体应用案例
- [🔧 框架类型系统](docs/concepts-explained.md#框架类型系统) - TypeScript接口设计和使用指南
### 📚 核心功能
- [🎭 实体管理指南](docs/entity-guide.md) - 实体的创建和使用方法
- [🧩 组件设计指南](docs/component-design-guide.md) - 如何设计高质量组件 ⭐ **设计必读**
- [⚙️ 系统详解指南](docs/system-guide.md) - 四种系统类型的详细使用
- [🎬 场景管理指南](docs/scene-management-guide.md) - 场景切换和数据管理
- [⏰ 定时器系统指南](docs/timer-guide.md) - 定时器的完整使用方法
### API 参考
- [核心 API 参考](docs/core-concepts.md) - 完整的 API 使用说明
- [实体基础指南](docs/entity-guide.md) - 实体的基本概念和操作
- [EntityManager 指南](docs/entity-manager-example.md) - 高性能查询和批量操作
- [事件系统指南](docs/event-system-example.md) - 事件系统完整用法
- [查询系统指南](docs/query-system-usage.md) - 查询系统使用方法
### 性能相关
- [性能优化指南](docs/performance-optimization.md) - 性能优化技术和策略
## 构建
```bash
# 安装依赖
npm install
# 构建项目
npm run build
# 监听模式
npm run build:watch
# 清理构建文件
npm run clean
# 重新构建
npm run rebuild
```
## 性能监控
框架提供内置性能统计:
```typescript
// 场景统计
const sceneStats = scene.getStats();
console.log('性能统计:', {
实体数量: sceneStats.entityCount,
系统数量: sceneStats.processorCount
});
// 查询系统统计
const queryStats = scene.querySystem.getStats();
console.log('查询统计:', {
缓存命中率: queryStats.hitRate + '%',
查询次数: queryStats.queryCount
});
```
## 扩展库
- [路径寻找库](https://github.com/esengine/ecs-astar) - A*、BFS、Dijkstra 算法
- [AI 系统](https://github.com/esengine/BehaviourTree-ai) - 行为树、效用 AI
## 社区
- QQ 群:[ecs游戏框架交流](https://jq.qq.com/?_wv=1027&k=29w1Nud6)
- GitHub[提交 Issue](https://github.com/esengine/ecs-framework/issues)
## 贡献
欢迎提交 Pull Request 和 Issue
### 开发要求
- Node.js >= 14.0.0
- TypeScript >= 4.0.0
## 📄 许可证
## 许可证
本项目采用 [MIT](LICENSE) 许可证。
## 💬 交流群
加入 QQ 群讨论:[ecs游戏框架交流](https://jq.qq.com/?_wv=1027&k=29w1Nud6)
### 🚀 核心性能指标
```bash
实体创建: 640,000+ 个/秒
组件查询: O(1) 复杂度(使用索引)
内存优化: 30-50% 减少分配
批量操作: 显著提升处理效率
```
### 🎯 性能优化技术
- **组件索引系统**: 哈希和位图双重索引,支持 O(1) 查询
- **Archetype 系统**: 按组件组合分组,减少查询开销
- **脏标记机制**: 细粒度变更追踪,避免不必要的计算
- **批量操作 API**: 减少函数调用开销,提升大规模操作效率
- **智能缓存**: 查询结果缓存和延迟清理机制
### 🔧 性能建议
1. **大规模场景**: 使用批量API和组件索引
2. **频繁查询**: 启用Archetype系统进行快速筛选
3. **实时游戏**: 利用脏标记减少无效更新
4. **移动端**: 建议实体数量控制在20,000以内
运行 `npm run benchmark` 查看在您的环境中的具体性能表现。
---
**ECS Framework** - 让游戏开发更简单、更高效!
[MIT](LICENSE)

BIN
docs/beginner-tutorials.md Normal file

Binary file not shown.

431
docs/bitmask-guide.md Normal file
View File

@@ -0,0 +1,431 @@
# 位掩码使用指南
本文档详细解释ECS框架中位掩码的概念、原理和使用方法。
## 目录
1. [什么是位掩码](#什么是位掩码)
2. [位掩码的优势](#位掩码的优势)
3. [基础使用方法](#基础使用方法)
4. [高级位掩码操作](#高级位掩码操作)
5. [实际应用场景](#实际应用场景)
6. [性能优化技巧](#性能优化技巧)
## 什么是位掩码
### 基本概念
位掩码BitMask是一种使用二进制位来表示状态或属性的技术。在ECS框架中每个组件类型对应一个二进制位实体的组件组合可以用一个数字来表示。
### 简单例子
假设我们有以下组件:
- PositionComponent → 位置 0 (二进制: 001)
- VelocityComponent → 位置 1 (二进制: 010)
- HealthComponent → 位置 2 (二进制: 100)
那么一个同时拥有Position和Health组件的实体其位掩码就是
```
001 (Position) + 100 (Health) = 101 (二进制) = 5 (十进制)
```
### 可视化理解
```typescript
// 组件类型对应的位位置
PositionComponent 0 2^0 = 1 二进制: 001
VelocityComponent 1 2^1 = 2 二进制: 010
HealthComponent 2 2^2 = 4 二进制: 100
RenderComponent 3 2^3 = 8 二进制: 1000
// 实体的组件组合示例
实体A: Position + Velocity 001 + 010 = 011 () = 3 ()
实体B: Position + Health 001 + 100 = 101 () = 5 ()
实体C: Position + Velocity + Health 001 + 010 + 100 = 111 () = 7 ()
```
## 位掩码的优势
### 1. 极快的查询速度
```typescript
// 传统方式:需要遍历组件列表
function hasComponents(entity, componentTypes) {
for (const type of componentTypes) {
if (!entity.hasComponent(type)) {
return false;
}
}
return true;
}
// 位掩码方式:一次位运算即可
function hasComponentsMask(entityMask, requiredMask) {
return (entityMask & requiredMask) === requiredMask;
}
```
### 2. 内存效率
```typescript
// 一个bigint可以表示64个组件的组合状态
// 相比存储组件列表,内存使用量大大减少
const entity = scene.createEntity("Player");
entity.addComponent(new PositionComponent());
entity.addComponent(new HealthComponent());
// 获取位掩码(只是一个数字)
const mask = entity.componentMask; // bigint类型
console.log(`位掩码: ${mask}`); // 输出: 5 (二进制: 101)
```
### 3. 批量操作优化
```typescript
// 可以快速筛选大量实体
const entities = scene.getAllEntities();
const requiredMask = BigInt(0b101); // Position + Health
const filteredEntities = entities.filter(entity =>
(entity.componentMask & requiredMask) === requiredMask
);
```
## 基础使用方法
### 获取实体的位掩码
```typescript
import { Scene, Entity, Component } from '@esengine/ecs-framework';
// 创建组件
class PositionComponent extends Component {
constructor(public x: number = 0, public y: number = 0) {
super();
}
}
class HealthComponent extends Component {
constructor(public maxHealth: number = 100) {
super();
}
}
// 创建实体并添加组件
const scene = new Scene();
const entity = scene.createEntity("Player");
console.log(`初始位掩码: ${entity.componentMask}`); // 0
entity.addComponent(new PositionComponent(100, 200));
console.log(`添加Position后: ${entity.componentMask}`); // 可能是 1
entity.addComponent(new HealthComponent(100));
console.log(`添加Health后: ${entity.componentMask}`); // 可能是 5
// 查看二进制表示
console.log(`二进制表示: ${entity.componentMask.toString(2)}`);
```
### 手动检查位掩码
```typescript
// 检查实体是否拥有特定组件组合
function checkEntityComponents(entity: Entity) {
const mask = entity.componentMask;
// 将位掩码转换为二进制字符串查看
const binaryString = mask.toString(2).padStart(8, '0');
console.log(`实体组件状态: ${binaryString}`);
// 分析每一位
console.log(`位0 (Position): ${(mask & 1n) !== 0n ? '有' : '无'}`);
console.log(`位1 (Velocity): ${(mask & 2n) !== 0n ? '有' : '无'}`);
console.log(`位2 (Health): ${(mask & 4n) !== 0n ? '有' : '无'}`);
console.log(`位3 (Render): ${(mask & 8n) !== 0n ? '有' : '无'}`);
}
```
## 高级位掩码操作
### 使用BitMaskOptimizer
框架提供了BitMaskOptimizer类来简化位掩码操作
```typescript
import { BitMaskOptimizer } from '@esengine/ecs-framework';
// 获取优化器实例
const optimizer = BitMaskOptimizer.getInstance();
// 注册组件类型(建议在游戏初始化时进行)
optimizer.registerComponentType('PositionComponent');
optimizer.registerComponentType('VelocityComponent');
optimizer.registerComponentType('HealthComponent');
optimizer.registerComponentType('RenderComponent');
// 创建单个组件的掩码
const positionMask = optimizer.createSingleComponentMask('PositionComponent');
console.log(`Position掩码: ${positionMask} (二进制: ${positionMask.toString(2)})`);
// 创建组合掩码
const movementMask = optimizer.createCombinedMask(['PositionComponent', 'VelocityComponent']);
console.log(`Movement掩码: ${movementMask} (二进制: ${movementMask.toString(2)})`);
// 检查实体是否匹配掩码
const entity = scene.createEntity("TestEntity");
entity.addComponent(new PositionComponent());
entity.addComponent(new VelocityComponent());
const hasMovementComponents = optimizer.maskContainsAllComponents(
entity.componentMask,
['PositionComponent', 'VelocityComponent']
);
console.log(`实体拥有移动组件: ${hasMovementComponents}`);
```
### 位掩码分析工具
```typescript
// 分析位掩码的实用函数
class MaskAnalyzer {
private optimizer = BitMaskOptimizer.getInstance();
// 分析实体的组件组合
analyzeEntity(entity: Entity): void {
const mask = entity.componentMask;
const componentNames = this.optimizer.maskToComponentNames(mask);
const componentCount = this.optimizer.getComponentCount(mask);
console.log(`实体 ${entity.name} 分析:`);
console.log(`- 位掩码: ${mask} (二进制: ${mask.toString(2)})`);
console.log(`- 组件数量: ${componentCount}`);
console.log(`- 组件列表: ${componentNames.join(', ')}`);
}
// 比较两个实体的组件差异
compareEntities(entityA: Entity, entityB: Entity): void {
const maskA = entityA.componentMask;
const maskB = entityB.componentMask;
const commonMask = maskA & maskB;
const onlyInA = maskA & ~maskB;
const onlyInB = maskB & ~maskA;
console.log(`实体比较:`);
console.log(`- 共同组件: ${this.optimizer.maskToComponentNames(commonMask).join(', ')}`);
console.log(`- 仅在A中: ${this.optimizer.maskToComponentNames(onlyInA).join(', ')}`);
console.log(`- 仅在B中: ${this.optimizer.maskToComponentNames(onlyInB).join(', ')}`);
}
// 查找具有特定组件组合的实体
findEntitiesWithMask(entities: Entity[], requiredComponents: string[]): Entity[] {
const requiredMask = this.optimizer.createCombinedMask(requiredComponents);
return entities.filter(entity =>
(entity.componentMask & requiredMask) === requiredMask
);
}
}
// 使用示例
const analyzer = new MaskAnalyzer();
analyzer.analyzeEntity(entity);
```
## 实际应用场景
### 1. 高性能实体查询
```typescript
class GameSystem {
private optimizer = BitMaskOptimizer.getInstance();
private movementMask: bigint;
private combatMask: bigint;
constructor() {
// 预计算常用掩码
this.movementMask = this.optimizer.createCombinedMask([
'PositionComponent', 'VelocityComponent'
]);
this.combatMask = this.optimizer.createCombinedMask([
'PositionComponent', 'HealthComponent', 'WeaponComponent'
]);
}
// 快速查找移动实体
findMovingEntities(entities: Entity[]): Entity[] {
return entities.filter(entity =>
(entity.componentMask & this.movementMask) === this.movementMask
);
}
// 快速查找战斗单位
findCombatUnits(entities: Entity[]): Entity[] {
return entities.filter(entity =>
(entity.componentMask & this.combatMask) === this.combatMask
);
}
}
```
### 2. 实体分类和管理
```typescript
class EntityClassifier {
private optimizer = BitMaskOptimizer.getInstance();
// 定义实体类型掩码
private readonly ENTITY_TYPES = {
PLAYER: this.optimizer.createCombinedMask([
'PositionComponent', 'HealthComponent', 'InputComponent'
]),
ENEMY: this.optimizer.createCombinedMask([
'PositionComponent', 'HealthComponent', 'AIComponent'
]),
PROJECTILE: this.optimizer.createCombinedMask([
'PositionComponent', 'VelocityComponent', 'DamageComponent'
]),
PICKUP: this.optimizer.createCombinedMask([
'PositionComponent', 'PickupComponent'
])
};
// 根据组件组合判断实体类型
classifyEntity(entity: Entity): string {
const mask = entity.componentMask;
for (const [type, typeMask] of Object.entries(this.ENTITY_TYPES)) {
if ((mask & typeMask) === typeMask) {
return type;
}
}
return 'UNKNOWN';
}
// 批量分类实体
classifyEntities(entities: Entity[]): Map<string, Entity[]> {
const classified = new Map<string, Entity[]>();
for (const entity of entities) {
const type = this.classifyEntity(entity);
if (!classified.has(type)) {
classified.set(type, []);
}
classified.get(type)!.push(entity);
}
return classified;
}
}
```
## 性能优化技巧
### 1. 预计算常用掩码
```typescript
class MaskCache {
private optimizer = BitMaskOptimizer.getInstance();
// 预计算游戏中常用的组件组合
public readonly COMMON_MASKS = {
RENDERABLE: this.optimizer.createCombinedMask([
'PositionComponent', 'RenderComponent'
]),
MOVABLE: this.optimizer.createCombinedMask([
'PositionComponent', 'VelocityComponent'
]),
LIVING: this.optimizer.createCombinedMask([
'HealthComponent'
]),
INTERACTIVE: this.optimizer.createCombinedMask([
'PositionComponent', 'ColliderComponent'
])
};
constructor() {
// 预计算常用组合以提升性能
this.optimizer.precomputeCommonMasks([
['PositionComponent', 'RenderComponent'],
['PositionComponent', 'VelocityComponent'],
['PositionComponent', 'HealthComponent', 'WeaponComponent']
]);
}
}
```
### 2. 位掩码调试工具
```typescript
// 位掩码调试工具
class MaskDebugger {
private optimizer = BitMaskOptimizer.getInstance();
// 可视化位掩码
visualizeMask(mask: bigint, maxBits: number = 16): string {
const binary = mask.toString(2).padStart(maxBits, '0');
const components = this.optimizer.maskToComponentNames(mask);
let visualization = `位掩码: ${mask} (二进制: ${binary})\n`;
visualization += `组件: ${components.join(', ')}\n`;
visualization += `可视化: `;
for (let i = maxBits - 1; i >= 0; i--) {
const bit = (mask & (1n << BigInt(i))) !== 0n ? '1' : '0';
visualization += bit;
if (i % 4 === 0 && i > 0) visualization += ' ';
}
return visualization;
}
}
```
## 最佳实践
### 1. 组件注册
```typescript
// 在游戏初始化时注册所有组件类型
function initializeComponentTypes() {
const optimizer = BitMaskOptimizer.getInstance();
// 按使用频率注册(常用的组件分配较小的位位置)
optimizer.registerComponentType('PositionComponent'); // 位置0
optimizer.registerComponentType('VelocityComponent'); // 位置1
optimizer.registerComponentType('HealthComponent'); // 位置2
optimizer.registerComponentType('RenderComponent'); // 位置3
// ... 其他组件
}
```
### 2. 掩码缓存管理
```typescript
// 定期清理掩码缓存以避免内存泄漏
setInterval(() => {
const optimizer = BitMaskOptimizer.getInstance();
const stats = optimizer.getCacheStats();
// 如果缓存过大,清理一部分
if (stats.size > 1000) {
optimizer.clearCache();
console.log('位掩码缓存已清理');
}
}, 60000); // 每分钟检查一次
```
## 总结
位掩码是ECS框架中的核心优化技术它提供了
1. **极快的查询速度** - 位运算比遍历快数百倍
2. **内存效率** - 用一个数字表示复杂的组件组合
3. **批量操作优化** - 可以快速处理大量实体
4. **灵活的查询构建** - 支持复杂的组件组合查询
通过理解和正确使用位掩码,可以显著提升游戏的性能表现。记住要在游戏初始化时注册组件类型,预计算常用掩码,并合理管理缓存。

View File

@@ -0,0 +1,695 @@
# 组件设计最佳实践指南
组件是ECS架构的核心设计良好的组件是构建高质量游戏的基础。本指南将教你如何设计出清晰、高效、可维护的组件。
## 组件设计原则
### 1. 数据为主,逻辑为辅
**核心理念:** 组件主要存储数据,复杂逻辑放在系统中处理。
```typescript
// ✅ 好的设计:主要是数据
class HealthComponent extends Component {
public maxHealth: number;
public currentHealth: number;
public regenRate: number = 0;
public lastDamageTime: number = 0;
constructor(maxHealth: number = 100) {
super();
this.maxHealth = maxHealth;
this.currentHealth = maxHealth;
}
// 简单的辅助方法是可以的
isDead(): boolean {
return this.currentHealth <= 0;
}
getHealthPercentage(): number {
return this.currentHealth / this.maxHealth;
}
}
// ❌ 不好的设计:包含太多逻辑
class BadHealthComponent extends Component {
public maxHealth: number;
public currentHealth: number;
takeDamage(damage: number) {
this.currentHealth -= damage;
// 这些逻辑应该在系统中处理
if (this.currentHealth <= 0) {
this.entity.destroy(); // 销毁逻辑
this.playDeathSound(); // 音效逻辑
this.createDeathEffect(); // 特效逻辑
this.updatePlayerScore(100); // 分数逻辑
}
}
}
```
### 2. 单一职责原则
每个组件只负责一个方面的数据。
```typescript
// ✅ 好的设计:单一职责
class PositionComponent extends Component {
public x: number = 0;
public y: number = 0;
constructor(x: number = 0, y: number = 0) {
super();
this.x = x;
this.y = y;
}
}
class VelocityComponent extends Component {
public x: number = 0;
public y: number = 0;
public maxSpeed: number = 100;
constructor(x: number = 0, y: number = 0) {
super();
this.x = x;
this.y = y;
}
}
class RotationComponent extends Component {
public angle: number = 0;
public angularVelocity: number = 0;
constructor(angle: number = 0) {
super();
this.angle = angle;
}
}
// ❌ 不好的设计:职责混乱
class TransformComponent extends Component {
public x: number = 0;
public y: number = 0;
public velocityX: number = 0;
public velocityY: number = 0;
public angle: number = 0;
public scale: number = 1;
public health: number = 100; // 和变换无关
public ammo: number = 30; // 和变换无关
}
```
### 3. 组合优于继承
使用多个小组件组合,而不是大而全的组件继承。
```typescript
// ✅ 好的设计:组合方式
class Player {
constructor(scene: Scene) {
const player = scene.createEntity("Player");
// 通过组合不同组件实现功能
player.addComponent(new PositionComponent(100, 100));
player.addComponent(new VelocityComponent());
player.addComponent(new HealthComponent(100));
player.addComponent(new PlayerInputComponent());
player.addComponent(new WeaponComponent());
player.addComponent(new InventoryComponent());
return player;
}
}
// 创建不同类型的实体很容易
class Enemy {
constructor(scene: Scene) {
const enemy = scene.createEntity("Enemy");
// 复用相同的组件,但组合不同
enemy.addComponent(new PositionComponent(200, 200));
enemy.addComponent(new VelocityComponent());
enemy.addComponent(new HealthComponent(50));
enemy.addComponent(new AIComponent()); // 不同AI而不是玩家输入
enemy.addComponent(new WeaponComponent()); // 相同:都有武器
// 没有库存组件
return enemy;
}
}
// ❌ 不好的设计:继承方式
class GameObject {
public x: number;
public y: number;
public health: number;
// ... 很多属性
}
class PlayerGameObject extends GameObject {
public input: InputData;
public inventory: Item[];
// 强制继承了不需要的属性
}
class EnemyGameObject extends GameObject {
public ai: AIData;
// 继承了不需要的库存等属性
}
```
## 常见组件类型和设计
### 1. 数据组件Data Components
纯数据存储,没有或很少有方法。
```typescript
// 位置信息
class PositionComponent extends Component {
public x: number;
public y: number;
constructor(x: number = 0, y: number = 0) {
super();
this.x = x;
this.y = y;
}
// 简单的辅助方法
distanceTo(other: PositionComponent): number {
const dx = this.x - other.x;
const dy = this.y - other.y;
return Math.sqrt(dx * dx + dy * dy);
}
set(x: number, y: number) {
this.x = x;
this.y = y;
}
}
// 统计信息
class StatsComponent extends Component {
public strength: number = 10;
public agility: number = 10;
public intelligence: number = 10;
public vitality: number = 10;
// 计算派生属性
getMaxHealth(): number {
return this.vitality * 10;
}
getDamage(): number {
return this.strength * 2;
}
getMoveSpeed(): number {
return this.agility * 5;
}
}
// 渲染信息
class SpriteComponent extends Component {
public textureName: string;
public width: number;
public height: number;
public tint: number = 0xFFFFFF;
public alpha: number = 1.0;
public visible: boolean = true;
constructor(textureName: string, width: number = 0, height: number = 0) {
super();
this.textureName = textureName;
this.width = width;
this.height = height;
}
}
```
### 2. 标记组件Tag Components
用于标识实体状态或类型的空组件。
```typescript
// 标记组件通常不包含数据
class PlayerComponent extends Component {
// 空组件,仅用于标记这是玩家实体
}
class EnemyComponent extends Component {
// 空组件,仅用于标记这是敌人实体
}
class DeadComponent extends Component {
// 标记实体已死亡
public deathTime: number;
constructor() {
super();
this.deathTime = Time.totalTime;
}
}
class InvincibleComponent extends Component {
// 标记实体无敌状态
public duration: number;
constructor(duration: number = 2.0) {
super();
this.duration = duration;
}
}
// 使用标记组件进行查询
class GameSystem {
updatePlayers() {
// 只处理玩家实体
const players = this.scene.findEntitiesWithComponent(PlayerComponent);
// ...
}
updateEnemies() {
// 只处理敌人实体
const enemies = this.scene.findEntitiesWithComponent(EnemyComponent);
// ...
}
}
```
### 3. 行为组件Behavior Components
包含简单行为逻辑的组件。
```typescript
class WeaponComponent extends Component {
public damage: number;
public fireRate: number;
public ammo: number;
public maxAmmo: number;
public lastFireTime: number = 0;
constructor(damage: number = 10, fireRate: number = 0.5) {
super();
this.damage = damage;
this.fireRate = fireRate;
this.maxAmmo = 30;
this.ammo = this.maxAmmo;
}
canFire(): boolean {
return this.ammo > 0 &&
Time.totalTime - this.lastFireTime >= this.fireRate;
}
fire(): boolean {
if (this.canFire()) {
this.ammo--;
this.lastFireTime = Time.totalTime;
return true;
}
return false;
}
reload() {
this.ammo = this.maxAmmo;
}
getAmmoPercentage(): number {
return this.ammo / this.maxAmmo;
}
}
class InventoryComponent extends Component {
private items: Map<string, number> = new Map();
public maxCapacity: number = 20;
addItem(itemType: string, quantity: number = 1): boolean {
if (this.getTotalItems() + quantity > this.maxCapacity) {
return false;
}
const current = this.items.get(itemType) || 0;
this.items.set(itemType, current + quantity);
return true;
}
removeItem(itemType: string, quantity: number = 1): boolean {
const current = this.items.get(itemType) || 0;
if (current < quantity) {
return false;
}
const newAmount = current - quantity;
if (newAmount === 0) {
this.items.delete(itemType);
} else {
this.items.set(itemType, newAmount);
}
return true;
}
hasItem(itemType: string, quantity: number = 1): boolean {
const current = this.items.get(itemType) || 0;
return current >= quantity;
}
getTotalItems(): number {
let total = 0;
this.items.forEach(quantity => total += quantity);
return total;
}
getItems(): Map<string, number> {
return new Map(this.items); // 返回副本
}
}
```
## 组件通信和依赖
### 1. 组件间通信
组件间不应直接通信,通过系统或事件系统进行通信。
```typescript
// ✅ 好的设计:通过事件通信
class HealthComponent extends Component {
public currentHealth: number;
public maxHealth: number;
takeDamage(damage: number) {
this.currentHealth -= damage;
// 发送事件,让其他系统响应
Core.emitter.emit('health:damaged', {
entity: this.entity,
damage: damage,
remainingHealth: this.currentHealth
});
if (this.currentHealth <= 0) {
Core.emitter.emit('health:died', {
entity: this.entity
});
}
}
}
// 其他组件响应事件
class AnimationComponent extends Component {
onAddedToEntity() {
super.onAddedToEntity();
// 监听受伤事件
Core.emitter.addObserver('health:damaged', this.onDamaged, this);
}
onRemovedFromEntity() {
Core.emitter.removeObserver('health:damaged', this.onDamaged, this);
super.onRemovedFromEntity();
}
private onDamaged(data: any) {
if (data.entity === this.entity) {
this.playHurtAnimation();
}
}
}
// ❌ 不好的设计:直接依赖其他组件
class BadHealthComponent extends Component {
takeDamage(damage: number) {
this.currentHealth -= damage;
// 直接操作其他组件
const animation = this.entity.getComponent(AnimationComponent);
if (animation) {
animation.playHurtAnimation(); // 紧耦合
}
const sound = this.entity.getComponent(SoundComponent);
if (sound) {
sound.playHurtSound(); // 紧耦合
}
}
}
```
### 2. 可选依赖
有时组件需要其他组件配合工作,但应该优雅处理缺失的情况。
```typescript
class MovementComponent extends Component {
public speed: number = 100;
update() {
// 可选依赖:输入组件
const input = this.entity.getComponent(InputComponent);
const velocity = this.entity.getComponent(VelocityComponent);
if (input && velocity) {
// 根据输入设置速度
velocity.x = input.horizontal * this.speed;
velocity.y = input.vertical * this.speed;
}
// 可选依赖AI组件
const ai = this.entity.getComponent(AIComponent);
if (ai && velocity && !input) {
// AI控制移动如果没有输入
velocity.x = ai.moveDirection.x * this.speed;
velocity.y = ai.moveDirection.y * this.speed;
}
}
}
```
## 组件性能优化
### 1. 对象池优化
对于频繁创建/销毁的组件,使用对象池。
```typescript
class PooledBulletComponent extends Component {
public damage: number = 10;
public speed: number = 200;
public direction: { x: number; y: number } = { x: 0, y: 0 };
public lifetime: number = 5.0;
private currentLifetime: number = 0;
// 重置组件状态,用于对象池
reset() {
this.damage = 10;
this.speed = 200;
this.direction.set(0, 0);
this.lifetime = 5.0;
this.currentLifetime = 0;
}
// 配置子弹
configure(damage: number, speed: number, direction: { x: number; y: number }) {
this.damage = damage;
this.speed = speed;
this.direction = direction.copy();
}
update() {
this.currentLifetime += Time.deltaTime;
if (this.currentLifetime >= this.lifetime) {
// 生命周期结束,回收到对象池
BulletPool.release(this.entity);
}
}
}
// 对象池管理
class BulletPool {
private static pool: Entity[] = [];
static get(): Entity {
if (this.pool.length > 0) {
const bullet = this.pool.pop()!;
bullet.enabled = true;
return bullet;
} else {
return this.createBullet();
}
}
static release(bullet: Entity) {
bullet.enabled = false;
bullet.getComponent(PooledBulletComponent)?.reset();
this.pool.push(bullet);
}
private static createBullet(): Entity {
const bullet = Core.scene.createEntity("Bullet");
bullet.addComponent(new PooledBulletComponent());
bullet.addComponent(new PositionComponent());
bullet.addComponent(new VelocityComponent());
return bullet;
}
}
```
### 2. 数据紧凑性
保持组件数据紧凑,避免不必要的对象分配。
```typescript
// ✅ 好的设计:紧凑的数据结构
class ParticleComponent extends Component {
// 使用基本类型,避免对象分配
public x: number = 0;
public y: number = 0;
public velocityX: number = 0;
public velocityY: number = 0;
public life: number = 1.0;
public maxLife: number = 1.0;
public size: number = 1.0;
public color: number = 0xFFFFFF;
// 计算属性,避免存储冗余数据
get alpha(): number {
return this.life / this.maxLife;
}
}
// ❌ 不好的设计:过多对象分配
class BadParticleComponent extends Component {
public position: { x: number; y: number } = { x: 0, y: 0 }; // 对象分配
public velocity: { x: number; y: number } = { x: 0, y: 0 }; // 对象分配
public color: Color = new Color(); // 对象分配
public transform: Transform = new Transform(); // 对象分配
// 冗余数据
public alpha: number = 1.0;
public life: number = 1.0;
public maxLife: number = 1.0;
}
```
## 组件调试和测试
### 1. 调试友好的组件
```typescript
class DebugFriendlyComponent extends Component {
public someValue: number = 0;
private debugName: string;
constructor(debugName: string = "Unknown") {
super();
this.debugName = debugName;
}
// 提供有用的调试信息
toString(): string {
return `${this.constructor.name}(${this.debugName}): value=${this.someValue}`;
}
// 验证组件状态
validate(): boolean {
if (this.someValue < 0) {
console.warn(`${this} has invalid value: ${this.someValue}`);
return false;
}
return true;
}
// 获取调试信息
getDebugInfo(): any {
return {
name: this.debugName,
value: this.someValue,
entityId: this.entity?.id,
isValid: this.validate()
};
}
}
```
### 2. 单元测试
```typescript
// 组件测试示例
describe('HealthComponent', () => {
let healthComponent: HealthComponent;
beforeEach(() => {
healthComponent = new HealthComponent(100);
});
test('初始状态正确', () => {
expect(healthComponent.currentHealth).toBe(100);
expect(healthComponent.maxHealth).toBe(100);
expect(healthComponent.isDead()).toBe(false);
});
test('受伤功能正确', () => {
healthComponent.takeDamage(30);
expect(healthComponent.currentHealth).toBe(70);
expect(healthComponent.getHealthPercentage()).toBe(0.7);
});
test('死亡检测正确', () => {
healthComponent.takeDamage(100);
expect(healthComponent.isDead()).toBe(true);
});
});
```
## 常见问题和最佳实践
### Q: 组件应该有多大?
A: 组件应该尽可能小和专注。如果一个组件有超过10个字段考虑拆分。
### Q: 组件可以包含方法吗?
A: 可以,但应该是简单的辅助方法。复杂逻辑应该在系统中处理。
### Q: 如何处理组件之间的依赖?
A:
1. 优先使用组合而不是依赖
2. 通过事件系统通信
3. 在系统中处理组件间的协调
### Q: 什么时候使用继承?
A: 很少使用。只在有明确的"是一个"关系时使用,如:
```typescript
abstract class ColliderComponent extends Component {
abstract checkCollision(other: ColliderComponent): boolean;
}
class CircleColliderComponent extends ColliderComponent {
public radius: number;
checkCollision(other: ColliderComponent): boolean {
// 圆形碰撞检测
}
}
class BoxColliderComponent extends ColliderComponent {
public width: number;
public height: number;
checkCollision(other: ColliderComponent): boolean {
// 方形碰撞检测
}
}
```
遵循这些原则,你就能设计出高质量、易维护的组件系统!

665
docs/concepts-explained.md Normal file
View File

@@ -0,0 +1,665 @@
# 技术概念详解
本文档用通俗易懂的语言解释ECS框架中的关键技术概念帮助开发者理解这些技术的作用和应用场景。
## 目录
- [ECS 架构基础](#ecs-架构基础)
- [性能优化技术](#性能优化技术)
- [事件系统](#事件系统)
- [实体管理](#实体管理)
## ECS 架构基础
### 什么是 ECS
ECS (Entity-Component-System) 是一种编程架构模式,将游戏对象分解为三个独立的部分:
**传统面向对象方式:**
```typescript
// 传统继承方式 - 问题很多
class GameObject {
x: number; y: number;
render() { ... }
update() { ... }
}
class Player extends GameObject {
health: number;
shoot() { ... }
}
class Enemy extends Player { // 敌人需要射击但不需要玩家控制?
ai() { ... }
}
```
**ECS 方式:**
```typescript
// 数据和逻辑分离,灵活组合
const player = createEntity()
.add(PositionComponent) // 位置数据
.add(HealthComponent) // 生命值数据
.add(PlayerInputComponent) // 玩家输入标记
const enemy = createEntity()
.add(PositionComponent) // 复用位置数据
.add(HealthComponent) // 复用生命值数据
.add(AIComponent) // AI标记
// 系统处理具有特定组件的实体
MovementSystem.process([PositionComponent, VelocityComponent]);
```
### ECS 的优势
1. **灵活组合** - 像搭积木一样组装功能
2. **代码复用** - 组件可以在不同实体间复用
3. **性能优化** - 数据连续存储,缓存友好
4. **并行处理** - 系统间相互独立,可以并行执行
5. **易于测试** - 组件和系统可以独立测试
### 实际应用场景
**游戏开发中的例子:**
- **RPG游戏**玩家、NPC、怪物都有位置和生命值但只有玩家有输入组件
- **射击游戏**:子弹、玩家、敌人都有位置和碰撞体,但行为完全不同
- **策略游戏**:建筑、单位、资源都是实体,通过不同组件组合实现功能
## 性能优化技术
### 组件索引系统
**问题:** 没有索引时,查找组件需要遍历所有实体
```typescript
// 慢的方式:线性搜索 O(n)
function findEntitiesWithHealth() {
const result = [];
for (const entity of allEntities) { // 遍历10万个实体
if (entity.hasComponent(HealthComponent)) {
result.push(entity);
}
}
return result;
}
```
**解决方案:** 建立索引,直接访问
```typescript
// 快的方式:索引查找 O(1)
const healthIndex = componentIndex.get(HealthComponent);
const entitiesWithHealth = healthIndex.getEntities(); // 直接获取
```
**应用场景:**
- 频繁查询特定组件的实体
- 大规模实体场景(数千到数万个实体)
- 实时游戏中的系统更新
### 索引类型选择指南
框架提供两种索引类型,选择合适的类型对性能至关重要:
#### 🔸 哈希索引 (Hash Index)
**适用场景:**
- 实体数量较多(> 1000个
- 组件数据变化不频繁
- 需要快速查找特定实体
**优势:**
- 查询速度极快 O(1)
- 内存使用相对较少
- 适合大量实体
**缺点:**
- 添加/删除组件时有额外开销
- 不适合频繁变化的组件
```typescript
// 适合哈希索引的组件
componentIndex.setIndexType(PositionComponent, 'hash'); // 位置变化不频繁
componentIndex.setIndexType(HealthComponent, 'hash'); // 生命值组件稳定
componentIndex.setIndexType(PlayerComponent, 'hash'); // 玩家标记组件
```
#### 🔹 位图索引 (Bitmap Index)
**适用场景:**
- 组件频繁添加/删除
- 实体数量适中(< 10000个
- 需要批量操作
**优势:**
- 添加/删除组件极快
- 批量查询效率高
- 内存访问模式好
**缺点:**
- 随实体数量增长,内存占用增加
- 稀疏数据时效率降低
```typescript
// 适合位图索引的组件
componentIndex.setIndexType(BuffComponent, 'bitmap'); // Buff经常添加删除
componentIndex.setIndexType(TemporaryComponent, 'bitmap'); // 临时组件
componentIndex.setIndexType(StateComponent, 'bitmap'); // 状态组件变化频繁
```
#### 📊 选择决策表
| 考虑因素 | 哈希索引 (Hash) | 位图索引 (Bitmap) |
|---------|----------------|-------------------|
| **实体数量** | > 1,000 | < 10,000 |
| **组件变化频率** | 低频变化 | 高频变化 |
| **查询频率** | 高频查询 | 中等查询 |
| **内存使用** | 较少 | 随实体数增加 |
| **批量操作** | 一般 | 优秀 |
#### 🤔 快速决策流程
**第一步:判断组件变化频率**
- 组件经常添加/删除? → 选择 **位图索引**
- 组件相对稳定? → 继续第二步
**第二步:判断实体数量**
- 实体数量 > 1000 → 选择 **哈希索引**
- 实体数量 < 1000 → 选择 **位图索引**
**第三步:特殊情况**
- 需要频繁批量操作? → 选择 **位图索引**
- 内存使用很重要? → 选择 **哈希索引**
#### 🎮 实际游戏中的选择示例
**射击游戏:**
```typescript
// 稳定组件用哈希索引
componentIndex.setIndexType(PositionComponent, 'hash'); // 实体位置稳定存在
componentIndex.setIndexType(HealthComponent, 'hash'); // 生命值组件持续存在
componentIndex.setIndexType(WeaponComponent, 'hash'); // 武器组件不常变化
// 变化组件用位图索引
componentIndex.setIndexType(BuffComponent, 'bitmap'); // Buff频繁添加删除
componentIndex.setIndexType(ReloadingComponent, 'bitmap'); // 装弹状态临时组件
```
**策略游戏:**
```typescript
// 大量单位,核心组件用哈希
componentIndex.setIndexType(UnitComponent, 'hash'); // 单位类型稳定
componentIndex.setIndexType(OwnerComponent, 'hash'); // 所属玩家稳定
// 状态组件用位图
componentIndex.setIndexType(SelectedComponent, 'bitmap'); // 选中状态常变化
componentIndex.setIndexType(MovingComponent, 'bitmap'); // 移动状态变化
componentIndex.setIndexType(AttackingComponent, 'bitmap'); // 攻击状态临时
```
**RPG游戏**
```typescript
// 角色核心属性用哈希
componentIndex.setIndexType(StatsComponent, 'hash'); // 属性组件稳定
componentIndex.setIndexType(InventoryComponent, 'hash'); // 背包组件稳定
componentIndex.setIndexType(LevelComponent, 'hash'); // 等级组件稳定
// 临时状态用位图
componentIndex.setIndexType(StatusEffectComponent, 'bitmap'); // 状态效果变化
componentIndex.setIndexType(QuestComponent, 'bitmap'); // 任务状态变化
componentIndex.setIndexType(CombatComponent, 'bitmap'); // 战斗状态临时
```
#### ❌ 常见选择错误
**错误示例1大量实体使用位图索引**
```typescript
// ❌ 错误10万个单位用位图索引内存爆炸
const entityCount = 100000;
componentIndex.setIndexType(UnitComponent, 'bitmap'); // 内存占用过大!
// ✅ 正确:大量实体用哈希索引
componentIndex.setIndexType(UnitComponent, 'hash');
```
**错误示例2频繁变化组件用哈希索引**
```typescript
// ❌ 错误Buff频繁添加删除哈希索引效率低
componentIndex.setIndexType(BuffComponent, 'hash'); // 添加删除慢!
// ✅ 正确:变化频繁的组件用位图索引
componentIndex.setIndexType(BuffComponent, 'bitmap');
```
**错误示例3不考虑实际使用场景**
```typescript
// ❌ 错误:所有组件都用同一种索引
componentIndex.setIndexType(PositionComponent, 'hash');
componentIndex.setIndexType(BuffComponent, 'hash'); // 应该用bitmap
componentIndex.setIndexType(TemporaryComponent, 'hash'); // 应该用bitmap
// ✅ 正确:根据组件特性选择
componentIndex.setIndexType(PositionComponent, 'hash'); // 稳定组件
componentIndex.setIndexType(BuffComponent, 'bitmap'); // 变化组件
componentIndex.setIndexType(TemporaryComponent, 'bitmap'); // 临时组件
```
### Archetype 系统
**什么是 Archetype**
Archetype原型是具有相同组件组合的实体分组。
**没有 Archetype 的问题:**
```typescript
// 每次都要检查每个实体的组件组合
for (const entity of allEntities) {
if (entity.has(Position) && entity.has(Velocity) && !entity.has(Frozen)) {
// 处理移动
}
}
```
**Archetype 的解决方案:**
```typescript
// 实体按组件组合自动分组
const movableArchetype = [Position, Velocity, !Frozen];
const movableEntities = archetypeSystem.getEntities(movableArchetype);
// 直接处理,无需逐个检查
```
**应用场景:**
- 大量实体的游戏RTS、MMO
- 频繁的实体查询操作
- 批量处理相同类型的实体
### 脏标记系统
**什么是脏标记?**
脏标记Dirty Tracking追踪哪些数据发生了变化避免处理未变化的数据。
**没有脏标记的问题:**
```typescript
// 每帧都重新计算所有实体,即使它们没有移动
function renderSystem() {
for (const entity of entities) {
updateRenderPosition(entity); // 浪费计算
updateRenderRotation(entity); // 浪费计算
updateRenderScale(entity); // 浪费计算
}
}
```
**脏标记的解决方案:**
```typescript
// 只处理发生变化的实体
function renderSystem() {
const dirtyEntities = dirtyTracking.getDirtyEntities();
for (const entity of dirtyEntities) {
if (dirtyTracking.isDirty(entity, PositionComponent)) {
updateRenderPosition(entity); // 只在需要时计算
}
if (dirtyTracking.isDirty(entity, RotationComponent)) {
updateRenderRotation(entity);
}
}
dirtyTracking.clearDirtyFlags();
}
```
**应用场景:**
- 渲染系统优化(只更新变化的物体)
- 物理系统优化(只计算移动的物体)
- UI更新优化只刷新变化的界面元素
- 网络同步优化(只发送变化的数据)
**实际例子:**
```typescript
// 游戏中的应用
class MovementSystem {
process() {
// 玩家移动时标记为脏
if (playerInput.moved) {
dirtyTracking.markDirty(player, PositionComponent);
}
// 静止的敌人不会被标记为脏,渲染系统会跳过它们
}
}
```
## 事件系统
### 类型安全事件
**传统事件的问题:**
```typescript
// 类型不安全,容易出错
eventEmitter.emit('player_died', playerData);
eventEmitter.on('player_dead', handler); // 事件名拼写错误!
```
**类型安全事件的解决方案:**
```typescript
// 编译时检查,避免错误
enum GameEvents {
PLAYER_DIED = 'player:died',
LEVEL_COMPLETED = 'level:completed'
}
eventBus.emit(GameEvents.PLAYER_DIED, { playerId: 123 });
eventBus.on(GameEvents.PLAYER_DIED, (data) => {
// data 类型自动推断
});
```
### 事件装饰器
**什么是装饰器?**
装饰器让你用简单的语法自动注册事件监听器。
**传统方式:**
```typescript
class GameManager {
constructor() {
// 手动注册事件
eventBus.on('entity:created', this.onEntityCreated.bind(this));
eventBus.on('entity:destroyed', this.onEntityDestroyed.bind(this));
eventBus.on('component:added', this.onComponentAdded.bind(this));
}
onEntityCreated(data) { ... }
onEntityDestroyed(data) { ... }
onComponentAdded(data) { ... }
}
```
**装饰器方式:**
```typescript
class GameManager {
@EventHandler('entity:created')
onEntityCreated(data) { ... } // 自动注册
@EventHandler('entity:destroyed')
onEntityDestroyed(data) { ... } // 自动注册
@EventHandler('component:added')
onComponentAdded(data) { ... } // 自动注册
}
```
**应用场景:**
- 游戏状态管理
- UI更新响应
- 音效播放触发
- 成就系统检查
## 实体管理
### 实体生命周期
**创建实体的不同方式:**
```typescript
// 单个创建 - 适用于重要实体
const player = scene.createEntity("Player");
// 批量创建 - 适用于大量相似实体
const bullets = scene.createEntities(100, "Bullet");
// 延迟创建 - 避免性能峰值
// 分批创建大量实体以避免单帧卡顿
for (let i = 0; i < 100; i++) {
setTimeout(() => {
const batch = scene.createEntities(10, "Enemy");
// 配置批次实体...
}, i * 16); // 每16ms创建一批
}
```
### 查询系统
**流式API的优势**
```typescript
// 传统方式:复杂的条件判断
const result = [];
for (const entity of entities) {
if (entity.has(Position) &&
entity.has(Velocity) &&
!entity.has(Frozen) &&
entity.tag === EntityTag.ENEMY) {
result.push(entity);
}
}
// 流式API清晰表达意图
const result = entityManager
.query()
.withAll(Position, Velocity)
.withNone(Frozen)
.withTag(EntityTag.ENEMY)
.execute();
```
### 批量操作
**为什么需要批量操作?**
```typescript
// 慢的方式:逐个处理
for (let i = 0; i < 1000; i++) {
const bullet = createEntity();
bullet.addComponent(new PositionComponent());
bullet.addComponent(new VelocityComponent());
}
// 快的方式:批量处理
const bullets = scene.createEntities(1000, "Bullet");
bullets.forEach(bullet => {
bullet.addComponent(new PositionComponent());
bullet.addComponent(new VelocityComponent());
});
```
**应用场景:**
- 生成大量子弹/粒子
- 加载关卡时创建大量实体
- 清理场景时删除大量实体
## 性能建议
### 什么时候使用这些优化?
| 实体数量 | 推荐配置 | 说明 |
|---------|---------|------|
| < 1,000 | 默认配置 | 简单场景,不需要特殊优化 |
| 1,000 - 10,000 | 启用组件索引 | 中等规模,索引提升查询速度 |
| 10,000 - 50,000 | 启用Archetype | 大规模场景,分组优化 |
| > 50,000 | 全部优化 | 超大规模,需要所有优化技术 |
### 常见使用误区
**错误:过度优化**
```typescript
// 不要在小项目中使用所有优化
const entityManager = new EntityManager();
entityManager.enableAllOptimizations(); // 小项目不需要
```
**正确:按需优化**
```typescript
// 根据实际需求选择优化
if (entityCount > 10000) {
entityManager.enableArchetypeSystem();
}
if (hasFrequentQueries) {
entityManager.enableComponentIndex();
}
```
## 总结
这些技术概念可能看起来复杂,但它们解决的都是实际开发中的具体问题:
1. **ECS架构** - 让代码更灵活、可维护
2. **组件索引** - 让查询更快速
3. **Archetype系统** - 让批量操作更高效
4. **脏标记系统** - 让更新更智能
5. **事件系统** - 让组件间通信更安全
6. **实体管理** - 让大规模场景成为可能
从简单的场景开始,随着项目复杂度增加,逐步引入这些优化技术。
## 框架类型系统
### TypeScript接口设计
ECS框架采用了精简的TypeScript接口设计提供类型安全保障的同时保持实现的灵活性。
#### 核心接口
**IComponent接口**
```typescript
interface IComponent {
readonly id: number;
enabled: boolean;
updateOrder: number;
onAddedToEntity(): void;
onRemovedFromEntity(): void;
onEnabled(): void;
onDisabled(): void;
update(): void;
}
```
- 定义所有组件的基本契约
- Component基类实现此接口
- 确保组件生命周期方法的一致性
**ISystemBase接口**
```typescript
interface ISystemBase {
readonly systemName: string;
readonly entities: readonly any[];
updateOrder: number;
enabled: boolean;
initialize(): void;
update(): void;
lateUpdate?(): void;
}
```
- 为EntitySystem类提供类型约束
- 定义系统的核心执行方法
- 支持可选的延迟更新
**IEventBus接口**
```typescript
interface IEventBus {
emit<T>(eventType: string, data: T): void;
emitAsync<T>(eventType: string, data: T): Promise<void>;
on<T>(eventType: string, handler: (data: T) => void, config?: IEventListenerConfig): string;
// ... 其他事件方法
}
```
- 提供类型安全的事件系统契约
- 支持同步和异步事件处理
- EventBus类完整实现此接口
#### 事件数据接口
**事件数据层次结构**
```typescript
// 基础事件数据
interface IEventData {
timestamp: number;
source?: string;
eventId?: string;
}
// 实体相关事件
interface IEntityEventData extends IEventData {
entityId: number;
entityName?: string;
entityTag?: string;
}
// 组件相关事件
interface IComponentEventData extends IEntityEventData {
componentType: string;
component?: IComponent;
}
```
- 清晰的继承层次
- 类型安全的事件数据传递
- 便于事件处理器的实现
#### 类型别名
**ComponentType<T>**
```typescript
type ComponentType<T extends IComponent = IComponent> = new (...args: any[]) => T;
```
- 用于类型安全的组件操作
- 支持泛型约束
- 广泛用于实体和查询系统
### 设计原则
#### 1. 接口简化原则
- 只保留实际使用的接口
- 移除了未使用的复杂接口如IEntityManager、IEntityQueryBuilder等
- 减少认知负担,提高开发效率
#### 2. 实现灵活性原则
- 接口作为类型约束而非强制实现
- 允许具体类有更丰富的实现
- 保持向后兼容性
#### 3. 类型安全原则
- 编译时类型检查
- 泛型支持提供精确的类型推断
- 事件系统的完整类型安全
### 使用指南
#### 在项目中使用接口
```typescript
// 作为类型约束
function processComponent<T extends IComponent>(component: T) {
if (component.enabled) {
component.update();
}
}
// 作为参数类型
function registerSystem(system: ISystemBase) {
scene.addEntityProcessor(system);
}
// 作为泛型约束
function getComponent<T extends IComponent>(type: ComponentType<T>): T | null {
return entity.getComponent(type);
}
```
#### 扩展框架接口
```typescript
// 如果需要扩展组件接口
interface IAdvancedComponent extends IComponent {
priority: number;
category: string;
}
class AdvancedComponent extends Component implements IAdvancedComponent {
public priority: number = 0;
public category: string = "default";
// 实现基础接口方法
}
```
### 接口维护
当前的接口设计已经过精心清理,包含:
- **12个核心接口** - 涵盖组件、系统、事件等核心概念
- **0个冗余接口** - 移除了所有未使用的接口定义
- **完整的类型覆盖** - 为所有主要功能提供类型支持
这种设计确保了框架的类型安全性,同时保持了代码的简洁性和可维护性。

View File

@@ -1,6 +1,8 @@
# 核心概念
# 核心 API 参考
ECS Framework 基于 Entity-Component-System 架构模式,这是一种高度模块化和可扩展的游戏开发架构。本文档将详细介绍框架的核心概念
本文档详细介绍 ECS Framework 的核心 API 和使用方法
> 🤔 **不熟悉ECS概念** 建议先阅读 [技术概念详解](concepts-explained.md) 了解ECS架构基础和性能优化原理
## ECS 架构概述
@@ -90,7 +92,7 @@ entities.forEach(entity => {
scene.querySystem.clearCache(); // 手动清理缓存
// 获取性能统计
const stats = scene.getPerformanceStats();
const stats = scene.getStats();
console.log(`实体数量: ${stats.entityCount}`);
```
@@ -101,20 +103,8 @@ console.log(`实体数量: ${stats.entityCount}`);
### 实体的基本属性
```typescript
import { Vector2 } from '@esengine/ecs-framework';
const entity = scene.createEntity("MyEntity");
// 位置
entity.position = new Vector2(100, 200);
entity.position = entity.position.add(new Vector2(10, 0));
// 旋转(弧度)
entity.rotation = Math.PI / 4;
// 缩放
entity.scale = new Vector2(2, 2);
// 标签(用于分类)
entity.tag = 1;
@@ -126,6 +116,22 @@ entity.active = true;
// 更新顺序
entity.updateOrder = 10;
// 注意框架专注于ECS架构不提供Transform相关功能
// 位置、旋转、缩放等Transform功能需要通过组件实现
class TransformComponent extends Component {
public x: number = 0;
public y: number = 0;
public rotation: number = 0;
public scaleX: number = 1;
public scaleY: number = 1;
}
// 使用Transform组件
const transform = entity.addComponent(new TransformComponent());
transform.x = 100;
transform.y = 200;
transform.rotation = Math.PI / 4;
```
### 实体层级关系
@@ -278,20 +284,24 @@ class BulletComponent extends Component {
}
// 注册组件池
ComponentPoolManager.getInstance().registerPool(BulletComponent, 1000);
ComponentPoolManager.getInstance().registerPool(
'BulletComponent',
() => new BulletComponent(),
(bullet) => bullet.reset(),
1000
);
// 使用对象池获取组件
const bullet = ComponentPoolManager.getInstance().getComponent(BulletComponent);
entity.addComponent(bullet);
const bullet = ComponentPoolManager.getInstance().acquireComponent('BulletComponent');
if (bullet) {
entity.addComponent(bullet);
}
// 释放组件回对象池
ComponentPoolManager.getInstance().releaseComponent(bullet);
ComponentPoolManager.getInstance().releaseComponent('BulletComponent', bullet);
// 预热组件池
ComponentPoolManager.getInstance().preWarmPools({
BulletComponent: 1000,
EffectComponent: 500
});
// 预热所有组件池
ComponentPoolManager.getInstance().prewarmAll(100);
// 获取池统计
const stats = ComponentPoolManager.getInstance().getPoolStats();
@@ -305,7 +315,9 @@ console.log('组件池统计:', stats);
### 场景生命周期
```typescript
class GameScene extends es.Scene {
import { Scene } from '@esengine/ecs-framework';
class GameScene extends Scene {
public initialize() {
// 场景初始化,创建实体和系统
this.setupEntities();
@@ -365,8 +377,14 @@ console.log("系统数量:", stats.processorCount);
最常用的系统类型,处理实体集合:
```typescript
class MovementSystem extends es.EntitySystem {
protected process(entities: es.Entity[]) {
import { EntitySystem, Entity, Matcher } from '@esengine/ecs-framework';
class MovementSystem extends EntitySystem {
constructor() {
super(Matcher.empty().all(MovementComponent));
}
protected process(entities: Entity[]) {
for (const entity of entities) {
const movement = entity.getComponent(MovementComponent);
if (movement) {
@@ -382,12 +400,26 @@ class MovementSystem extends es.EntitySystem {
定期处理的系统:
```typescript
class HealthRegenerationSystem extends es.ProcessingSystem {
protected process(entities: es.Entity[]) {
for (const entity of entities) {
const health = entity.getComponent(HealthComponent);
import { ProcessingSystem, Time, Matcher } from '@esengine/ecs-framework';
class HealthRegenerationSystem extends ProcessingSystem {
constructor() {
super(Matcher.empty().all(HealthComponent));
}
public processSystem() {
// ProcessingSystem不处理具体实体而是执行全局逻辑
// 如果需要处理实体应该使用EntitySystem
this.regenerateAllPlayerHealth();
}
private regenerateAllPlayerHealth() {
// 通过场景查找所有玩家实体并恢复生命值
const players = this.scene.findEntitiesByTag(PlayerTag);
for (const player of players) {
const health = player.getComponent(HealthComponent);
if (health && health.currentHealth < health.maxHealth) {
health.currentHealth += 10 * es.Time.deltaTime;
health.currentHealth += 10 * Time.deltaTime;
}
}
}
@@ -399,12 +431,15 @@ class HealthRegenerationSystem extends es.ProcessingSystem {
按时间间隔执行的系统:
```typescript
class SpawnSystem extends es.IntervalSystem {
import { IntervalSystem, Matcher } from '@esengine/ecs-framework';
class SpawnSystem extends IntervalSystem {
constructor() {
super(3.0); // 每3秒执行一次
// IntervalSystem需要Matcher和间隔时间
super(Matcher.empty(), 3.0); // 每3秒执行一次
}
protected processSystem() {
protected process(entities: Entity[]) {
// 生成敌人
const enemy = this.scene.createEntity("Enemy");
enemy.addComponent(new EnemyComponent());
@@ -417,7 +452,13 @@ class SpawnSystem extends es.IntervalSystem {
被动系统,不自动处理实体:
```typescript
class CollisionSystem extends es.PassiveSystem {
import { PassiveSystem, Matcher } from '@esengine/ecs-framework';
class CollisionSystem extends PassiveSystem {
constructor() {
super(Matcher.empty());
}
public checkCollisions() {
// 手动调用的碰撞检测逻辑
}
@@ -429,54 +470,32 @@ class CollisionSystem extends es.PassiveSystem {
时间管理工具类,提供游戏时间相关功能:
```typescript
import { Time } from '@esengine/ecs-framework';
// 获取时间信息
console.log("帧时间:", es.Time.deltaTime);
console.log("总时间:", es.Time.totalTime);
console.log("帧数:", es.Time.frameCount);
console.log("时间缩放:", es.Time.timeScale);
console.log("帧时间:", Time.deltaTime);
console.log("总时间:", Time.totalTime);
console.log("帧数:", Time.frameCount);
console.log("时间缩放:", Time.timeScale);
// 设置时间缩放(慢动作效果)
es.Time.timeScale = 0.5;
Time.timeScale = 0.5;
// 检查时间间隔
if (es.Time.checkEvery(1.0, lastCheckTime)) {
if (Time.checkEvery(1.0, lastCheckTime)) {
// 每秒执行一次
}
```
## Vector2二维向量
二维向量类,提供数学运算:
```typescript
// 创建向量
const vec1 = new es.Vector2(10, 20);
const vec2 = es.Vector2.zero;
const vec3 = es.Vector2.one;
// 向量运算
const sum = vec1.add(vec2);
const diff = vec1.subtract(vec2);
const scaled = vec1.multiply(2);
const normalized = vec1.normalize();
// 向量属性
console.log("长度:", vec1.length);
console.log("长度平方:", vec1.lengthSquared);
// 静态方法
const distance = es.Vector2.distance(vec1, vec2);
const lerped = es.Vector2.lerp(vec1, vec2, 0.5);
const fromAngle = es.Vector2.fromAngle(Math.PI / 4);
```
## 性能监控
框架内置性能监控工具:
```typescript
import { PerformanceMonitor } from '@esengine/ecs-framework';
// 获取性能监控实例
const monitor = es.PerformanceMonitor.instance;
const monitor = PerformanceMonitor.instance;
// 查看性能数据
console.log("平均FPS:", monitor.averageFPS);
@@ -493,22 +512,46 @@ monitor.reset();
内存管理优化工具:
```typescript
// 创建对象池
class BulletPool extends es.Pool<Bullet> {
protected createObject(): Bullet {
return new Bullet();
import { Pool, IPoolable } from '@esengine/ecs-framework';
// 定义可池化的对象需要实现IPoolable接口
class Bullet implements IPoolable {
public x: number = 0;
public y: number = 0;
public speed: number = 0;
// 重置对象状态,准备重用
public reset(): void {
this.x = 0;
this.y = 0;
this.speed = 0;
}
}
const bulletPool = new BulletPool();
// 创建对象池
const bulletPool = new Pool<Bullet>(() => new Bullet(), 100);
// 预热对象池
bulletPool.warmUp(20);
// 使用对象池
const bullet = bulletPool.obtain();
// 使用bullet...
bullet.x = 100;
bullet.y = 200;
bullet.speed = 500;
// 使用完后归还到池中
bulletPool.free(bullet);
// 查看池统计信息
console.log(bulletPool.getStats());
// 清空对象池
bulletPool.clear();
// 使用静态方法(自动管理池)
const bullet2 = Pool.obtain(Bullet);
Pool.free(Bullet, bullet2);
```
## 最佳实践
@@ -539,57 +582,18 @@ bulletPool.clear();
## 高级性能优化功能
### 位掩码优化
### 查询系统优化
位掩码优化器可以预计算和缓存常用的组件掩码,提升查询性能。
框架内部已集成查询优化,无需手动配置。查询系统会自动使用最优的算法:
```typescript
import { BitMaskOptimizer } from '@esengine/ecs-framework';
// 查询系统会自动优化这些操作
const movingEntities = scene.querySystem.queryAll(PositionComponent, VelocityComponent);
const renderableEntities = scene.querySystem.queryAll(PositionComponent, RenderComponent);
const optimizer = BitMaskOptimizer.getInstance();
// 注册组件类型
optimizer.registerComponentType(PositionComponent);
optimizer.registerComponentType(VelocityComponent);
optimizer.registerComponentType(RenderComponent);
// 预计算常用掩码组合
optimizer.precomputeCommonMasks();
// 获取优化的掩码
const positionMask = optimizer.getComponentMask(PositionComponent);
const movementMask = optimizer.getCombinedMask([PositionComponent, VelocityComponent]);
// 掩码操作
const hasBothComponents = optimizer.hasAllComponents(entityMask, movementMask);
const hasAnyComponent = optimizer.hasAnyComponent(entityMask, movementMask);
// 获取掩码分析
const analysis = optimizer.analyzeMask(entityMask);
console.log('掩码包含的组件类型:', analysis.componentTypes);
```
### 延迟索引更新器
批量更新索引可以显著提升大规模实体操作的性能。
```typescript
import { IndexUpdateBatcher } from '@esengine/ecs-framework';
const batcher = new IndexUpdateBatcher((updates) => {
// 处理批量更新
console.log(`批量处理 ${updates.length} 个索引更新`);
});
// 配置批量大小和延迟
batcher.configure(100, 16); // 批量大小100延迟16ms
// 添加更新任务
batcher.addUpdate("add", entity, componentMask);
batcher.addUpdate("remove", entity, componentMask);
// 强制刷新
batcher.flush();
// 获取查询统计信息
const queryStats = scene.querySystem.getStats();
console.log('查询统计:', queryStats);
```
### 批量操作API
@@ -598,14 +602,8 @@ batcher.flush();
// 批量创建实体 - 最高性能
const entities = scene.createEntities(10000, "Bullets");
// 延迟缓存清理
entities.forEach(entity => {
scene.addEntity(entity, false); // 延迟清理
});
scene.querySystem.clearCache(); // 手动清理
// 批量查询优化
const movingEntities = scene.getEntitiesWithComponents([PositionComponent, VelocityComponent]);
const movingEntities = scene.querySystem.queryAll(PositionComponent, VelocityComponent).entities;
```
## 总结

View File

@@ -1,6 +1,8 @@
# 实体使用指南
# 实体基础指南
本指南详细介绍 ECS Framework 中实体Entity所有功能和使用方法。
本指南介绍实体Entity基本概念和基础使用方法。
> 📖 **需要高级实体管理?** 请参考 [EntityManager 指南](entity-manager-example.md) 了解高性能查询和批量操作
## 实体概述
@@ -155,7 +157,7 @@ if (entity.hasComponent(HealthComponent)) {
// 检查组件掩码(高性能)
const mask = entity.componentMask;
console.log(`组件掩码: ${mask.toString(2)}`);
console.log(`组件掩码: ${mask.toString(2)}`); // 二进制表示
```
### 移除组件
@@ -295,80 +297,20 @@ if (entity.isDestroyed) {
// 4. 从场景中移除
```
## 性能优化
# 高级特性请参考其他指南
### 组件缓存
> 📚 **更多功能:**
> - **高性能查询和批量操作** → [EntityManager 指南](entity-manager-example.md)
> - **性能优化技术** → [性能优化指南](performance-optimization.md)
> - **组件索引和缓存** → [技术概念详解](concepts-explained.md)
```typescript
// 预热组件缓存(提高后续访问性能)
entity.warmUpComponentCache();
// 清理组件缓存
entity.cleanupComponentCache();
// 获取缓存统计信息
const cacheStats = entity.getComponentCacheStats();
console.log(`缓存命中率: ${cacheStats.cacheStats.hitRate}`);
console.log(`组件访问统计:`, cacheStats.accessStats);
```
### 批量操作
```typescript
// 批量添加组件(比单个添加更高效)
const components = entity.addComponents([
new PositionComponent(0, 0),
new VelocityComponent(50, 0),
new HealthComponent(100)
]);
// 批量移除组件
const removed = entity.removeComponentsByTypes([
HealthComponent,
VelocityComponent
]);
```
## 调试和监控
### 调试信息
```typescript
// 获取详细的调试信息
const debugInfo = entity.getDebugInfo();
console.log("实体调试信息:", debugInfo);
// 调试信息包含:
// - 基本属性名称、ID、状态等
// - 组件信息(数量、类型、掩码等)
// - 层次结构信息(父子关系、深度等)
// - 性能统计(缓存命中率、访问统计等)
```
### 实体比较
```typescript
// 比较两个实体的优先级
const result = entity1.compareTo(entity2);
if (result < 0) {
// entity1 优先级更高
} else if (result > 0) {
// entity2 优先级更高
} else {
// 优先级相同
}
// 实体的字符串表示
console.log(entity.toString()); // "Entity[Player:1]"
```
## 最佳实践
## 基础最佳实践
### 1. 合理使用标签
```typescript
// 定义标签常量
const Tags = {
const EntityTags = {
PLAYER: 1,
ENEMY: 2,
PROJECTILE: 3,
@@ -376,70 +318,42 @@ const Tags = {
} as const;
// 使用标签进行分类
player.tag = Tags.PLAYER;
enemy.tag = Tags.ENEMY;
player.tag = EntityTags.PLAYER;
enemy.tag = EntityTags.ENEMY;
```
### 2. 优化更新顺序
```typescript
// 设置合理的更新顺序
player.updateOrder = 0; // 玩家最先更新
enemy.updateOrder = 1; // 敌人其次
projectile.updateOrder = 2; // 投射物最后
```
### 3. 合理使用层次结构
```typescript
// 创建复合实体
const tank = scene.createEntity("Tank");
const turret = scene.createEntity("Turret");
const barrel = scene.createEntity("Barrel");
// 建立层次关系
tank.addChild(turret);
turret.addChild(barrel);
// 这样可以通过控制父实体来影响整个层次结构
tank.active = false; // 整个坦克都会被停用
```
### 4. 组件缓存优化
```typescript
// 对于频繁访问的组件,预热缓存
entity.warmUpComponentCache();
// 定期清理不常用的缓存
setInterval(() => {
entity.cleanupComponentCache();
}, 5000);
```
### 5. 避免内存泄漏
### 2. 正确的销毁处理
```typescript
// 确保正确销毁实体
if (!entity.isDestroyed) {
entity.destroy(); // 自动移除组件和层次关系
}
// 检查实体状态
if (entity.isDestroyed) {
return; // 避免操作已销毁的实体
}
```
// 在适当的时候销毁不需要的实体
if (enemy.getComponent(HealthComponent)?.isDead()) {
enemy.destroy();
### 3. 组件生命周期
```typescript
// 正确添加组件
const health = entity.addComponent(new HealthComponent(100));
// 安全获取组件
const healthComp = entity.getComponent(HealthComponent);
if (healthComp && healthComp.currentHealth <= 0) {
entity.destroy();
}
```
## 常见问题
### Q: 实体可以在不同场景间移动吗
### Q: 实体如何实现位置、旋转等变换
A: 不可以。实体与场景紧密绑定,如果需要在场景间传递数据,应该序列化实体的组件数据,然后在新场景中重新创建。
### Q: 如何实现实体的位置、旋转、缩放?
A: 框架本身不提供这些属性,需要通过组件来实现:
A: 通过添加相应的组件:
```typescript
class TransformComponent extends Component {
@@ -448,35 +362,9 @@ class TransformComponent extends Component {
public scale = { x: 1, y: 1 };
}
const transform = entity.addComponent(new TransformComponent());
transform.position.x = 100;
transform.rotation = Math.PI / 4;
entity.addComponent(new TransformComponent());
```
### Q: 实体的更新顺序如何影响性能
### Q: 实体可以在场景间移动吗
A: 更新顺序主要影响游戏逻辑的执行顺序,对性能影响较小。但合理的更新顺序可以避免一些逻辑问题,比如确保输入处理在移动之前执行。
### Q: 如何处理大量实体的性能问题?
A:
1. 使用对象池重用实体
2. 合理使用组件缓存
3. 避免不必要的组件查询
4. 使用批量操作
5. 定期清理销毁的实体
```typescript
// 使用对象池
class EntityPool extends Pool<Entity> {
protected createObject(): Entity {
return scene.createEntity("PooledEntity");
}
protected resetObject(entity: Entity): void {
entity.removeAllComponents();
entity.active = true;
entity.enabled = true;
}
}
```
A: 不可以。实体与场景绑定,需要在新场景中重新创建。

View File

@@ -1,421 +1,370 @@
# EntityManager 使用指南
EntityManager 是 ECS Framework 的核心管理系统,提供统一的实体管理、高性能查询和自动优化功能
本文档详细介绍 EntityManager 的使用方法和最佳实践
## 快速开始
## 目录
### 创建实体管理器
1. [基础用法](#基础用法)
2. [查询系统](#查询系统)
3. [实体管理](#实体管理)
4. [性能监控](#性能监控)
5. [最佳实践](#最佳实践)
## 基础用法
### 创建 EntityManager
```typescript
import { EntityManager, Scene } from '@esengine/ecs-framework';
// 通常在游戏管理器中创建
// 创建场景和实体管理器
const scene = new Scene();
const entityManager = new EntityManager(scene);
```
const entityManager = new EntityManager();
### 基础实体操作
// 批量创建实体使用Scene方法
const enemies = scene.createEntities(50, "Enemy");
```typescript
// 创建单个实体
const player = entityManager.createEntity("Player");
player.addComponent(new PositionComponent(100, 100));
player.addComponent(new HealthComponent(100));
player.tag = "player";
// 批量创建实体
const enemies = entityManager.createEntities(50, "Enemy");
// 为实体添加组件
enemies.forEach((enemy, index) => {
enemy.addComponent(new PositionComponent(
Math.random() * 800,
Math.random() * 600
));
enemy.addComponent(new HealthComponent(30));
enemy.tag = "enemy";
enemy.addComponent(new HealthComponent(100));
enemy.addComponent(new VelocityComponent(
(Math.random() - 0.5) * 100,
(Math.random() - 0.5) * 100
));
enemy.tag = 2; // 敌人标签
});
// 销毁实体
entityManager.destroyEntity(player);
```
## 高性能查询系统
EntityManager 提供多种查询方式,自动选择最优的查询策略。
## 查询系统
### 基础查询
```typescript
// 通过ID查询O(1)
const entity = entityManager.getEntity(123);
// 通过名称查询O(1) 哈希查找)
const player = entityManager.getEntityByName("Player");
// 通过标签查询O(1) 索引查找)
const enemies = entityManager.getEntitiesByTag("enemy");
// 组件查询使用O(1)组件索引)
// 按组件类型查询
const healthEntities = entityManager.getEntitiesWithComponent(HealthComponent);
// 多组件查询使用Archetype优化
const movingEntities = entityManager.getEntitiesWithComponents([
PositionComponent,
VelocityComponent
]);
// 按标签查询
const enemies = entityManager.getEntitiesByTag(2);
const players = entityManager.getEntitiesByTag(1);
// 按名称查询
const boss = entityManager.getEntityByName("BossEnemy");
// 获取所有实体
const allEntities = entityManager.getAllEntities();
```
### 流式查询API
EntityManager 提供强大的流式查询构建器:
### 流式查询 API
```typescript
// 基础查询构建
const results = entityManager
// 复杂查询条件
const movingEnemies = entityManager
.query()
.withAll([PositionComponent, HealthComponent]) // 必须包含这些组件
.withoutTag("dead") // 不能有死亡标签
.active(true) // 必须激活
.withAll(PositionComponent, VelocityComponent, HealthComponent)
.withTag(2) // 敌人标签
.execute();
// 复杂条件查询
const livingEnemies = entityManager
// 查询活跃的玩家
const activePlayers = entityManager
.query()
.withAll([PositionComponent, HealthComponent])
.withTag("enemy")
.withoutTag("dead")
.where(entity => {
const health = entity.getComponent(HealthComponent);
return health && health.currentHealth > 0;
})
.withAll(PositionComponent)
.withTag(1) // 玩家标签
.active() // 只查询活跃实体
.execute();
// 查询变
const firstEnemy = entityManager
// 排除特定组件的实
const nonCombatEntities = entityManager
.query()
.withTag("enemy")
.first(); // 获取第一个匹配
const enemyCount = entityManager
.query()
.withTag("enemy")
.count(); // 获取数量
// 直接处理查询结果
entityManager
.query()
.withAll([HealthComponent])
.forEach(entity => {
const health = entity.getComponent(HealthComponent);
if (health.currentHealth <= 0) {
entity.addTag("dead");
}
});
```
### 高级查询选项
```typescript
// 组合条件查询
const combatUnits = entityManager
.query()
.withAll([PositionComponent, HealthComponent]) // AND条件
.withAny([WeaponComponent, MagicComponent]) // OR条件
.without([DeadComponent]) // NOT条件
.withTag("combatant")
.withoutTag("peaceful")
.active(true)
.enabled(true)
.withAll(PositionComponent)
.without(WeaponComponent, HealthComponent)
.execute();
// 使用自定义过滤器
// 自定义条件查询
const nearbyEnemies = entityManager
.query()
.withAll([PositionComponent])
.withTag("enemy")
.withAll(PositionComponent)
.withTag(2)
.where(entity => {
const pos = entity.getComponent(PositionComponent);
const distance = Math.sqrt(
Math.pow(pos.x - playerPos.x, 2) +
Math.pow(pos.y - playerPos.y, 2)
);
return distance < 100; // 距离玩家100像素内
return pos && Math.abs(pos.x - playerX) < 100;
})
.execute();
```
## 批量操作
## 实体管理
EntityManager 提供高效的批量操作方法:
### 创建和销毁实体
```typescript
// 遍历所有实体
entityManager.forEachEntity(entity => {
// 处理每个实体
if (entity.position.x < 0) {
entity.position.x = 0;
}
});
// 创建单个实体
const player = entityManager.createEntity("Player");
player.addComponent(new PositionComponent(400, 300));
player.addComponent(new HealthComponent(100));
player.tag = 1;
// 遍历特定组件的实体
entityManager.forEachEntityWithComponent(HealthComponent, (entity, health) => {
if (health.currentHealth <= 0) {
entity.addTag("dead");
entity.enabled = false;
}
});
// 销毁实体
entityManager.destroyEntity(player);
// 批量创建并配置实体
const bullets = entityManager.createEntities(100, "Bullet", (bullet, index) => {
bullet.addComponent(new PositionComponent(
100 + index * 10,
100
));
bullet.addComponent(new VelocityComponent(0, -200));
bullet.tag = "projectile";
});
// 按名称销毁
entityManager.destroyEntity("Enemy_1");
// 按ID销毁
entityManager.destroyEntity(123);
```
## 性能优化系统
EntityManager 内置了三个性能优化系统:
### 1. 组件索引系统
自动为组件查询提供O(1)性能:
### 实体查找
```typescript
// 获取组件索引统计
const componentIndex = entityManager.getComponentIndex();
const stats = componentIndex.getPerformanceStats();
// 按ID查找
const entity = entityManager.getEntity(123);
console.log('组件索引统计:', {
totalQueries: stats.totalQueries,
indexHits: stats.indexHits,
hitRate: (stats.indexHits / stats.totalQueries * 100).toFixed(2) + '%'
});
// 按名称查找
const player = entityManager.getEntityByName("Player");
// 手动优化(通常自动进行)
componentIndex.optimize();
// 检查实体是否存在
if (entity && !entity.isDestroyed) {
// 实体有效
}
```
### 2. Archetype系统
## 性能监控
按组件组合分组实体,优化批量查询:
### 基础统计
```typescript
// 获取Archetype统计
const archetypeSystem = entityManager.getArchetypeSystem();
const archetypeStats = archetypeSystem.getStatistics();
// 获取实体数量
console.log('总实体数:', entityManager.entityCount);
console.log('活跃实体数:', entityManager.activeEntityCount);
console.log('Archetype统计:', {
totalArchetypes: archetypeStats.totalArchetypes,
totalEntities: archetypeStats.totalEntities,
queryCacheSize: archetypeStats.queryCacheSize
// 获取场景统计
const sceneStats = scene.getStats();
console.log('场景统计:', {
实体数量: sceneStats.entityCount,
系统数量: sceneStats.processorCount
});
// 查看所有原型
console.log('当前原型:', archetypeSystem.getAllArchetypes());
// 获取查询系统统计
const queryStats = scene.querySystem.getStats();
console.log('查询统计:', queryStats);
```
### 3. 脏标记系统
## 最佳实践
追踪实体变更,避免不必要的更新:
### 1. 高效查询
```typescript
// 获取脏标记统计
const dirtyTracking = entityManager.getDirtyTrackingSystem();
const dirtyStats = dirtyTracking.getPerformanceStats();
console.log('脏标记统计:', {
totalMarks: dirtyStats.totalMarks,
batchesProcessed: dirtyStats.batchesProcessed,
listenersNotified: dirtyStats.listenersNotified
});
// 手动处理脏标记
dirtyTracking.processDirtyMarks();
```
## 实体管理器统计
获取EntityManager的综合性能数据
```typescript
const stats = entityManager.getStatistics();
console.log('EntityManager统计:', {
// 基础统计
entityCount: stats.entityCount,
activeEntityCount: stats.activeEntityCount,
// 查询统计
totalQueries: stats.totalQueries,
indexHits: stats.indexHits,
archetypeHits: stats.archetypeHits,
// 性能指标
averageQueryTime: stats.averageQueryTime,
hitRate: (stats.indexHits / stats.totalQueries * 100).toFixed(2) + '%'
});
```
## 系统优化和清理
```typescript
// 手动触发优化
entityManager.optimize();
// 内存清理
entityManager.cleanup();
// 压缩数据结构
entityManager.compact();
// 获取内存使用情况
const memoryStats = entityManager.getMemoryUsage();
console.log('内存使用:', {
entityIndexSize: memoryStats.entityIndex,
componentIndexSize: memoryStats.componentIndex,
archetypeSize: memoryStats.archetype
});
```
## 实际使用案例
### 游戏系统集成
```typescript
class MovementSystem extends EntitySystem {
private entityManager: EntityManager;
constructor(scene: Scene) {
super();
this.entityManager = new EntityManager(scene);
}
// ✅ 好的做法:缓存查询结果
class CombatSystem extends EntitySystem {
private cachedEnemies: Entity[] = [];
private lastUpdateFrame = 0;
protected process(entities: Entity[]): void {
// 使用高效查询获取移动实体
const movingEntities = this.entityManager
.query()
.withAll([PositionComponent, VelocityComponent])
.active(true)
.execute();
// 每5帧更新一次缓存
if (Time.frameCount - this.lastUpdateFrame > 5) {
this.cachedEnemies = this.entityManager
.query()
.withAll(PositionComponent, HealthComponent)
.withTag(2)
.execute();
this.lastUpdateFrame = Time.frameCount;
}
// 批量处理
movingEntities.forEach(entity => {
const position = entity.getComponent(PositionComponent);
const velocity = entity.getComponent(VelocityComponent);
position.x += velocity.dx * Time.deltaTime;
position.y += velocity.dy * Time.deltaTime;
// 使用缓存的结果
this.cachedEnemies.forEach(enemy => {
// 处理敌人逻辑
});
}
}
```
### 复杂查询示例
### 2. 批量操作
```typescript
// 战斗系统:查找攻击范围内的敌人
class CombatSystem {
private entityManager: EntityManager;
// ✅ 好的做法:批量创建和配置
function createBulletWave(count: number): Entity[] {
// 使用Scene的批量创建
const bullets = scene.createEntities(count, "Bullet");
// 批量配置组件
bullets.forEach((bullet, index) => {
const angle = (index / count) * Math.PI * 2;
bullet.addComponent(new PositionComponent(400, 300));
bullet.addComponent(new VelocityComponent(
Math.cos(angle) * 200,
Math.sin(angle) * 200
));
bullet.addComponent(new BulletComponent());
bullet.tag = 3; // 子弹标签
});
return bullets;
}
```
### 3. 内存管理
```typescript
// ✅ 好的做法:及时清理无用实体
class CleanupSystem extends EntitySystem {
protected process(entities: Entity[]): void {
// 清理超出边界的子弹
const bullets = this.entityManager.getEntitiesByTag(3);
bullets.forEach(bullet => {
const pos = bullet.getComponent(PositionComponent);
if (pos && (pos.x < -100 || pos.x > 900 || pos.y < -100 || pos.y > 700)) {
this.entityManager.destroyEntity(bullet);
}
});
// 清理死亡的敌人
const deadEnemies = this.entityManager
.query()
.withAll(HealthComponent)
.withTag(2)
.where(entity => {
const health = entity.getComponent(HealthComponent);
return health && health.currentHealth <= 0;
})
.execute();
deadEnemies.forEach(enemy => {
this.entityManager.destroyEntity(enemy);
});
}
}
```
### 4. 查询优化
```typescript
// ✅ 好的做法:使用合适的查询方法
class GameSystem extends EntitySystem {
findTargetsInRange(attacker: Entity, range: number): Entity[] {
const attackerPos = attacker.getComponent(PositionComponent);
if (!attackerPos) return [];
// 先按标签快速筛选,再按距离过滤
return this.entityManager
.query()
.withAll([PositionComponent, HealthComponent])
.withTag("enemy")
.withoutTag("dead")
.where(entity => {
const pos = entity.getComponent(PositionComponent);
.getEntitiesByTag(2) // 敌人标签
.filter(enemy => {
const enemyPos = enemy.getComponent(PositionComponent);
if (!enemyPos) return false;
const distance = Math.sqrt(
Math.pow(pos.x - attackerPos.x, 2) +
Math.pow(pos.y - attackerPos.y, 2)
Math.pow(attackerPos.x - enemyPos.x, 2) +
Math.pow(attackerPos.y - enemyPos.y, 2)
);
return distance <= range;
})
.execute();
}
// 优化版本:使用空间分区(如果实现了的话)
findTargetsInRangeOptimized(attacker: Entity, range: number): Entity[] {
// 首先通过空间查询缩小范围
const nearbyEntities = this.spatialIndex.queryRange(
attackerPos.x - range,
attackerPos.y - range,
attackerPos.x + range,
attackerPos.y + range
);
// 然后使用EntityManager进行精确过滤
return this.entityManager
.query()
.withAll([HealthComponent])
.withTag("enemy")
.withoutTag("dead")
.where(entity => nearbyEntities.includes(entity))
.execute();
});
}
}
```
## 性能建议
### 查询优化
1. **利用索引**: 优先使用组件查询和标签查询它们具有O(1)性能
2. **减少自定义过滤**: `where()`条件虽然灵活,但会降低性能
3. **缓存查询结果**: 对于不经常变化的查询结果,考虑缓存
## 完整示例
```typescript
// ✅ 推荐:使用索引查询
const enemies = entityManager.getEntitiesByTag("enemy");
import {
EntityManager,
Scene,
Entity,
Component,
EntitySystem,
Matcher
} from '@esengine/ecs-framework';
// ⚠️ 谨慎:自定义过滤
const enemies = entityManager
.query()
.where(entity => entity.name.includes("Enemy"))
.execute();
```
### 批量操作优化
```typescript
// ✅ 推荐:批量创建
const bullets = entityManager.createEntities(100, "Bullet");
// ❌ 避免:循环单独创建
for (let i = 0; i < 100; i++) {
entityManager.createEntity("Bullet");
// 组件定义
class PositionComponent extends Component {
constructor(public x: number = 0, public y: number = 0) {
super();
}
}
```
### 内存管理
```typescript
// 定期清理
setInterval(() => {
entityManager.cleanup();
}, 30000); // 每30秒清理一次
// 监控性能
const stats = entityManager.getStatistics();
if (stats.indexHits / stats.totalQueries < 0.8) {
console.warn('查询命中率较低,考虑优化查询策略');
class HealthComponent extends Component {
constructor(
public maxHealth: number = 100,
public currentHealth: number = 100
) {
super();
}
}
// 游戏管理器
class GameManager {
private scene: Scene;
private entityManager: EntityManager;
constructor() {
this.scene = new Scene();
this.entityManager = new EntityManager();
this.setupGame();
}
private setupGame(): void {
// 创建玩家
const player = this.entityManager.createEntity("Player");
player.addComponent(new PositionComponent(400, 300));
player.addComponent(new HealthComponent(100));
player.tag = 1;
// 创建敌人
const enemies = this.scene.createEntities(10, "Enemy");
enemies.forEach(enemy => {
enemy.addComponent(new PositionComponent(
Math.random() * 800,
Math.random() * 600
));
enemy.addComponent(new HealthComponent(50));
enemy.tag = 2;
});
// 添加系统
this.scene.addEntityProcessor(new HealthSystem());
}
public update(): void {
this.scene.update();
// 输出统计信息
console.log('实体数量:', this.entityManager.entityCount);
console.log('活跃实体数:', this.entityManager.activeEntityCount);
}
}
// 生命值系统
class HealthSystem extends EntitySystem {
constructor() {
super(Matcher.empty().all(HealthComponent));
}
protected process(entities: Entity[]): void {
const healthEntities = this.scene.querySystem.queryAll(HealthComponent);
healthEntities.entities.forEach(entity => {
const health = entity.getComponent(HealthComponent);
if (health && health.currentHealth <= 0) {
console.log(`实体 ${entity.name} 死亡`);
entity.destroy();
}
});
}
}
// 启动游戏
const game = new GameManager();
setInterval(() => game.update(), 16); // 60 FPS
```
## 总结
EntityManager 提供了:
EntityManager 提供了强大的实体管理功能
- **统一接口**: 所有实体操作通过一个管理器完成
- **自动优化**: 内置三个性能优化系统
- **灵活查询**: 从简单的ID查找到复杂的条件查询
- **性能监控**: 完整的统计和诊断信息
- **批量操作**: 高效的批量处理能力
- **创建管理**`createEntity()`, `destroyEntity()`
- **查询功能**`getEntitiesWithComponent()`, `getEntitiesByTag()`, `query()`
- **实体查找**`getEntity()`, `getEntityByName()`
- **统计信息**`entityCount`, `activeEntityCount`
通过合理使用EntityManager您可以构建高性能、可维护的ECS游戏系统
通过合理使用这些API可以构建高性能的游戏系统。记住要及时清理无用实体缓存频繁查询的结果并使用合适的查询方法来优化性能

View File

@@ -6,15 +6,18 @@
```
ecs-framework/
├── source/
│ ├── src/ # 源代码
│ │ ├── ECS/ # ECS核心系统
│ │ ├── Types/ # 类型定义
│ │ ├── Utils/ # 工具类
│ │ └── Testing/ # 测试文件
│ ├── scripts/ # 构建脚本
│ └── tsconfig.json # TypeScript配置
── docs/ # 文档
├── src/ # 源代码
│ ├── ECS/ # ECS核心系统
│ │ ├── Core/ # 核心管理器
│ │ ├── Systems/ # 系统类型
│ │ ├── Utils/ # ECS工具类
│ │ └── Components/# 组件类型
│ ├── Types/ # TypeScript接口定义精简版
│ └── Utils/ # 通用工具类
── docs/ # 文档
├── scripts/ # 构建脚本
├── bin/ # 编译输出
└── dist/ # 发布版本
```
## 安装和使用
@@ -31,14 +34,17 @@ npm install @esengine/ecs-framework
# 克隆项目
git clone https://github.com/esengine/ecs-framework.git
# 进入源码目录
cd ecs-framework/source
# 进入项目目录
cd ecs-framework
# 安装依赖
npm install
# 编译TypeScript
npm run build
# 或者使用监听模式开发
npm run build:watch
```
## 基础设置
@@ -80,22 +86,7 @@ class GameManager {
Core.scene = this.scene;
// 初始化实体管理器
this.entityManager = new EntityManager(this.scene);
// 初始化性能优化
this.setupPerformanceOptimizations();
}
private setupPerformanceOptimizations() {
// 启用组件索引(自动优化查询性能)
// EntityManager内部已自动启用
// 可选:手动配置优化系统
const componentIndex = this.entityManager.getComponentIndex();
const archetypeSystem = this.entityManager.getArchetypeSystem();
const dirtyTracking = this.entityManager.getDirtyTrackingSystem();
// 优化系统会自动工作,通常无需手动配置
this.entityManager = new EntityManager();
}
public update(deltaTime: number): void {
@@ -216,7 +207,7 @@ class HealthComponent extends Component {
EntityManager 是框架的核心功能,提供统一的实体管理和高性能查询接口。
### 1. 基础用法
### 基础实体操作
```typescript
// 获取EntityManager实例在GameManager中已创建
@@ -224,37 +215,22 @@ const entityManager = gameManager.getEntityManager();
// 创建单个实体
const player = entityManager.createEntity("Player");
player.addComponent(new PositionComponent(100, 100));
player.addComponent(new VelocityComponent(50, 0));
// 批量创建实体
const enemies = entityManager.createEntities(50, "Enemy");
enemies.forEach((enemy, index) => {
enemy.addComponent(new PositionComponent(
Math.random() * 800,
Math.random() * 600
));
enemy.addComponent(new HealthComponent(30));
enemy.tag = "enemy";
});
```
// 批量创建实体使用Scene方法
const enemies = scene.createEntities(50, "Enemy");
### 2. 高性能查询
```typescript
// 流式查询API - 支持复杂查询条件
// 查询操作
const movingEntities = entityManager
.query()
.withAll([PositionComponent, VelocityComponent])
.withoutTag("dead")
.active(true)
.withAll(PositionComponent, VelocityComponent)
.withNone(HealthComponent)
.execute();
// 快速组件查询使用O(1)索引)
// 组件查询
const healthEntities = entityManager.getEntitiesWithComponent(HealthComponent);
// 标签查询
const allEnemies = entityManager.getEntitiesByTag("enemy");
const allEnemies = entityManager.getEntitiesByTag(2);
// 名称查询
const specificEnemy = entityManager.getEntityByName("BossEnemy");
@@ -262,59 +238,41 @@ const specificEnemy = entityManager.getEntityByName("BossEnemy");
// 复合查询
const livingEnemies = entityManager
.query()
.withAll([PositionComponent, HealthComponent])
.withTag("enemy")
.withoutTag("dead")
.where(entity => {
const health = entity.getComponent(HealthComponent);
return health && health.currentHealth > 0;
})
.withAll(HealthComponent)
.withTag(2)
.execute();
```
### 3. 批量操作
### 实体遍历
```typescript
// 批量处理实体
entityManager.forEachEntity(entity => {
// 处理所有实体
if (entity.tag === "bullet" && entity.position.y < 0) {
entity.destroy();
}
// 遍历所有实体
const allEntities = entityManager.getAllEntities();
allEntities.forEach(entity => {
console.log(`实体: ${entity.name}, ID: ${entity.id}`);
});
// 批量处理特定组件的实体
entityManager.forEachEntityWithComponent(HealthComponent, (entity, health) => {
if (health.currentHealth <= 0) {
entity.addTag("dead");
entity.enabled = false;
}
// 遍历特定组件的实体
const healthEntities = entityManager.getEntitiesWithComponent(HealthComponent);
healthEntities.forEach(entity => {
const health = entity.getComponent(HealthComponent);
console.log(`${entity.name} 生命值: ${health.currentHealth}/${health.maxHealth}`);
});
// 获取统计信息
const stats = entityManager.getStatistics();
console.log(`总实体数: ${stats.entityCount}`);
console.log(`索引命中率: ${stats.indexHits}/${stats.totalQueries}`);
```
### 4. 性能优化功能
### 性能监控
```typescript
// 获取性能优化系统
const componentIndex = entityManager.getComponentIndex();
const archetypeSystem = entityManager.getArchetypeSystem();
const dirtyTracking = entityManager.getDirtyTrackingSystem();
// 获取场景统计
const sceneStats = scene.getStats();
console.log('实体数量:', sceneStats.entityCount);
console.log('系统数量:', sceneStats.processorCount);
// 查看性能统计
console.log('组件索引统计:', componentIndex.getPerformanceStats());
console.log('Archetype统计:', archetypeSystem.getStatistics());
console.log('脏标记统计:', dirtyTracking.getPerformanceStats());
// 获取查询系统统计
const queryStats = scene.querySystem.getStats();
console.log('查询统计:', queryStats);
// 手动优化(通常自动进行)
entityManager.optimize();
// 内存清理
entityManager.cleanup();
```
## 创建系统
@@ -322,45 +280,40 @@ entityManager.cleanup();
系统处理具有特定组件的实体集合,实现游戏逻辑。
```typescript
import { EntitySystem, Entity } from '@esengine/ecs-framework';
import { EntitySystem, Entity, Matcher, EntityManager } from '@esengine/ecs-framework';
class MovementSystem extends EntitySystem {
protected process(entities: Entity[]): void {
// 使用EntityManager进行高效查询
const entityManager = new EntityManager(this.scene);
const movingEntities = entityManager
.query()
.withAll([PositionComponent, VelocityComponent])
.execute();
constructor() {
super(Matcher.empty().all(PositionComponent, VelocityComponent));
}
movingEntities.forEach(entity => {
protected process(entities: Entity[]): void {
// 使用Scene的查询系统进行高效查询
const movingEntities = this.scene.querySystem.queryAll(PositionComponent, VelocityComponent);
movingEntities.entities.forEach(entity => {
const position = entity.getComponent(PositionComponent);
const velocity = entity.getComponent(VelocityComponent);
if (position && velocity) {
// 更新位置
position.x += velocity.x * 0.016; // 假设60FPS
position.y += velocity.y * 0.016;
// 边界检查
if (position.x < 0 || position.x > 800) {
velocity.x = -velocity.x;
}
if (position.y < 0 || position.y > 600) {
velocity.y = -velocity.y;
}
position.x += velocity.dx * Time.deltaTime;
position.y += velocity.dy * Time.deltaTime;
}
});
}
}
class HealthSystem extends EntitySystem {
protected process(entities: Entity[]): void {
const entityManager = new EntityManager(this.scene);
constructor() {
super(Matcher.empty().all(HealthComponent));
}
// 查找所有有生命值的实体
entityManager.forEachEntityWithComponent(HealthComponent, (entity, health) => {
if (health.isDead()) {
protected process(entities: Entity[]): void {
const healthEntities = this.scene.querySystem.queryAll(HealthComponent);
healthEntities.entities.forEach(entity => {
const health = entity.getComponent(HealthComponent);
if (health && health.currentHealth <= 0) {
entity.destroy();
}
});
@@ -392,15 +345,14 @@ Core.emitter.removeObserver(CoreEvents.frameUpdated, this.onFrameUpdate);
### 性能监控
```typescript
// 获取EntityManager性能统计
const stats = entityManager.getStatistics();
console.log(`总实体数: ${stats.entityCount}`);
console.log(`索引命中率: ${stats.indexHits}/${stats.totalQueries}`);
// 获取场景性能统计
const sceneStats = scene.getStats();
console.log(`总实体数: ${sceneStats.entityCount}`);
console.log(`系统数量: ${sceneStats.processorCount}`);
// 获取各优化系统统计
console.log('组件索引:', entityManager.getComponentIndex().getPerformanceStats());
console.log('Archetype:', entityManager.getArchetypeSystem().getStatistics());
console.log('脏标记:', entityManager.getDirtyTrackingSystem().getPerformanceStats());
// 获取查询系统统计
const queryStats = scene.querySystem.getStats();
console.log('查询统计:', queryStats);
```
## 简单示例
@@ -429,7 +381,7 @@ class SimpleGame {
this.scene.name = "GameScene";
Core.scene = this.scene;
this.entityManager = new EntityManager(this.scene);
this.entityManager = new EntityManager();
this.setupSystems();
}
@@ -457,7 +409,7 @@ class SimpleGame {
}
private createEnemies(count: number): Entity[] {
const enemies = this.entityManager.createEntities(count, "Enemy");
const enemies = this.scene.createEntities(count, "Enemy");
enemies.forEach((enemy, index) => {
enemy.addComponent(new PositionComponent(
@@ -493,7 +445,7 @@ game.start();
## 性能优化建议
### 1. 大规模实体处理
- 使用 `EntityManager.createEntities()` 批量创建实体
- 使用 `scene.createEntities()` 批量创建实体
- 利用组件索引系统进行高效查询
- 启用Archetype系统减少查询遍历
@@ -503,7 +455,7 @@ game.start();
- 利用脏标记系统避免不必要的更新
### 3. 性能监控
- 定期检查 `EntityManager.getStatistics()` 获取性能数据
- 定期检查 `scene.getStats()` 获取性能数据
- 监控组件索引命中率
- 使用框架提供的性能统计功能

View File

@@ -1,497 +1,425 @@
# 性能优化指南
ECS Framework 提供了多层性能优化系统,确保在各种规模的游戏中都能提供卓越的性能表现
本文档介绍ECS框架的性能优化技术和最佳实践
## 性能优化架构
## 目录
### 三大核心优化系统
1. [查询系统优化](#查询系统优化)
2. [实体管理优化](#实体管理优化)
3. [组件设计优化](#组件设计优化)
4. [系统设计优化](#系统设计优化)
5. [内存管理](#内存管理)
6. [性能监控](#性能监控)
1. **组件索引系统 (ComponentIndex)** - 提供 O(1) 组件查询性能
2. **Archetype系统** - 按组件组合分组实体,减少查询遍历
3. **脏标记系统 (DirtyTracking)** - 细粒度变更追踪,避免不必要更新
## 查询系统优化
这三个系统协同工作,为不同场景提供最优的性能表现。
## 性能基准
### 核心操作性能
```
实体创建: 640,000+ 个/秒
组件查询: O(1) 复杂度(使用索引)
内存优化: 30-50% 减少分配
批量操作: 显著提升处理效率
```
### 查询性能对比
| 查询类型 | 传统方式 | 使用索引 | 性能提升 |
|----------|----------|----------|----------|
| 单组件查询 | O(n) | O(1) | 1000x+ |
| 多组件查询 | O(n*m) | O(k) | 100x+ |
| 标签查询 | O(n) | O(1) | 1000x+ |
| 复合查询 | O(n*m*k) | O(min(k1,k2)) | 500x+ |
*n=实体数量, m=组件种类, k=匹配实体数量*
## 组件索引系统
### 索引类型选择
框架提供两种索引实现:
#### 哈希索引 (HashComponentIndex)
- **适用场景**: 通用查询,平衡的读写性能
- **优势**: O(1) 查询,较低内存开销
- **缺点**: 哈希冲突时性能下降
### 使用高效的查询方法
```typescript
// 自动选择最优索引类型
const componentIndex = entityManager.getComponentIndex();
// ✅ 推荐:使用标签查询(快速)
const enemies = entityManager.getEntitiesByTag(2);
// 手动配置哈希索引
componentIndex.setIndexType(HealthComponent, 'hash');
```
#### 位图索引 (BitmapComponentIndex)
- **适用场景**: 大规模实体,频繁的组合查询
- **优势**: 超快的 AND/OR 操作,空间压缩
- **缺点**: 更新成本较高,内存开销随实体数量增长
```typescript
// 配置位图索引用于大规模查询
componentIndex.setIndexType(PositionComponent, 'bitmap');
```
### 智能索引管理
ComponentIndexManager 会根据使用模式自动优化:
```typescript
// 获取索引性能统计
const stats = componentIndex.getPerformanceStats();
console.log('索引性能:', {
queriesPerSecond: stats.queriesPerSecond,
hitRate: stats.hitRate,
indexType: stats.recommendedType
});
// 自动优化索引类型
componentIndex.optimize(); // 根据使用模式切换索引类型
```
## Archetype系统优化
### 原型分组策略
Archetype系统将实体按组件组合分组实现快速批量操作
```typescript
// 获取Archetype统计
const archetypeSystem = entityManager.getArchetypeSystem();
const stats = archetypeSystem.getStatistics();
console.log('Archetype优化:', {
totalArchetypes: stats.totalArchetypes, // 原型数量
avgEntitiesPerArchetype: stats.averageEntitiesPerArchetype,
queryCacheHits: stats.queryCacheHits // 缓存命中次数
});
```
### 查询缓存机制
```typescript
// 启用查询缓存(默认开启)
archetypeSystem.enableQueryCache(true);
// 缓存大小限制(避免内存泄漏)
archetypeSystem.setMaxCacheSize(1000);
// 清理过期缓存
archetypeSystem.cleanCache();
```
### 最佳实践
1. **组件设计**: 避免创建过多单独的原型
2. **批量操作**: 利用原型批量处理相同组件组合的实体
3. **缓存管理**: 定期清理查询缓存
```typescript
// ✅ 好的设计:复用组件组合
class MovementSystem extends EntitySystem {
process() {
// 一次查询处理所有移动实体
const movingEntities = this.entityManager
.query()
.withAll([PositionComponent, VelocityComponent])
.execute(); // 利用Archetype快速获取
// 批量处理
movingEntities.forEach(entity => {
// 更新逻辑
});
}
}
// ❌ 避免:频繁查询不同组合
class BadSystem extends EntitySystem {
process() {
// 多次小查询无法充分利用Archetype
const players = this.queryPlayers();
const enemies = this.queryEnemies();
const bullets = this.queryBullets();
}
}
```
## 脏标记系统优化
### 脏标记类型
系统提供细粒度的脏标记追踪:
```typescript
enum DirtyType {
COMPONENT_ADDED, // 组件添加
COMPONENT_REMOVED, // 组件移除
COMPONENT_MODIFIED, // 组件修改
ENTITY_ENABLED, // 实体启用
ENTITY_DISABLED, // 实体禁用
TAG_ADDED, // 标签添加
TAG_REMOVED // 标签移除
}
```
### 批量处理配置
```typescript
const dirtyTracking = entityManager.getDirtyTrackingSystem();
// 配置批量处理参数
dirtyTracking.configure({
batchSize: 100, // 每批处理100个脏标记
timeSliceMs: 16, // 每帧最多处理16ms
processingInterval: 1 // 每帧处理一次
});
// 监听脏标记事件
dirtyTracking.addListener(DirtyType.COMPONENT_MODIFIED, (entity, component) => {
// 响应组件修改
this.invalidateRenderCache(entity);
}, { priority: 10 });
```
### 性能监控
```typescript
const dirtyStats = dirtyTracking.getPerformanceStats();
console.log('脏标记性能:', {
totalMarks: dirtyStats.totalMarks,
batchesProcessed: dirtyStats.batchesProcessed,
averageBatchTime: dirtyStats.averageBatchTime,
queueSize: dirtyStats.currentQueueSize
});
```
## 查询优化策略
### 查询层次选择
根据查询复杂度选择最优方法:
```typescript
// 1. 简单查询:直接使用索引
// ✅ 推荐:使用组件查询
const healthEntities = entityManager.getEntitiesWithComponent(HealthComponent);
// 2. 双组件查询使用Archetype
const movingEntities = entityManager.getEntitiesWithComponents([
PositionComponent,
VelocityComponent
]);
// ✅ 推荐使用Scene的查询系统
const movingEntities = scene.querySystem.queryAll(PositionComponent, VelocityComponent);
// 3. 复杂查询:组合使用
const combatants = entityManager
// ⚠️ 谨慎:自定义条件查询(较慢)
const nearbyEnemies = entityManager
.query()
.withAll([PositionComponent, HealthComponent]) // Archetype预筛选
.withTag("combat") // 索引过滤
.where(entity => { // 自定义精确过滤
const health = entity.getComponent(HealthComponent);
return health.currentHealth > health.maxHealth * 0.3;
.withAll(PositionComponent)
.where(entity => {
const pos = entity.getComponent(PositionComponent);
return pos && Math.abs(pos.x - playerX) < 100;
})
.execute();
```
### 查询缓存策略
### 查询结果缓存
```typescript
class CombatSystem extends EntitySystem {
class OptimizedCombatSystem extends EntitySystem {
private cachedEnemies: Entity[] = [];
private lastEnemyCacheUpdate = 0;
private lastCacheUpdate = 0;
private cacheInterval = 5; // 每5帧更新一次
process() {
const currentTime = performance.now();
// 每200ms更新一次敌人缓存
if (currentTime - this.lastEnemyCacheUpdate > 200) {
this.cachedEnemies = this.entityManager
.getEntitiesByTag("enemy");
this.lastEnemyCacheUpdate = currentTime;
protected process(entities: Entity[]): void {
// 缓存查询结果
if (Time.frameCount - this.lastCacheUpdate >= this.cacheInterval) {
this.cachedEnemies = this.entityManager.getEntitiesByTag(2);
this.lastCacheUpdate = Time.frameCount;
}
// 使用缓存的结果
this.processCombat(this.cachedEnemies);
}
}
```
## 内存优化
### 内存使用监控
```typescript
// 获取各系统内存使用情况
const memoryStats = entityManager.getMemoryUsage();
console.log('内存使用情况:', {
entityIndex: memoryStats.entityIndex, // 实体索引
componentIndex: memoryStats.componentIndex, // 组件索引
archetype: memoryStats.archetype, // 原型系统
dirtyTracking: memoryStats.dirtyTracking, // 脏标记
total: memoryStats.total
});
```
### 内存清理策略
```typescript
// 定期内存清理
setInterval(() => {
entityManager.cleanup(); // 清理无效引用
entityManager.compact(); // 压缩数据结构
}, 30000); // 每30秒清理一次
// 游戏场景切换时的深度清理
function switchScene() {
entityManager.destroyAllEntities();
entityManager.cleanup();
entityManager.compact();
// 重置优化系统
entityManager.getComponentIndex().reset();
entityManager.getArchetypeSystem().clearCache();
entityManager.getDirtyTrackingSystem().clear();
}
```
## 实战优化案例
### 大规模射击游戏优化
```typescript
class BulletSystem extends EntitySystem {
private bulletPool: Entity[] = [];
private maxBullets = 1000;
constructor(entityManager: EntityManager) {
super();
this.prewarmBulletPool();
}
private prewarmBulletPool() {
// 预创建子弹池
this.bulletPool = this.entityManager.createEntities(
this.maxBullets,
"Bullet"
);
// 初始化为非激活状态
this.bulletPool.forEach(bullet => {
bullet.enabled = false;
bullet.addComponent(new PositionComponent());
bullet.addComponent(new VelocityComponent());
bullet.addComponent(new BulletComponent());
this.cachedEnemies.forEach(enemy => {
this.processEnemy(enemy);
});
}
public spawnBullet(x: number, y: number, vx: number, vy: number): Entity | null {
// 从池中获取非激活子弹(使用索引快速查询)
const availableBullet = this.entityManager
.query()
.withAll([BulletComponent])
.active(false)
.first();
if (availableBullet) {
// 重用现有子弹
const pos = availableBullet.getComponent(PositionComponent);
const vel = availableBullet.getComponent(VelocityComponent);
pos.x = x; pos.y = y;
vel.x = vx; vel.y = vy;
availableBullet.enabled = true;
return availableBullet;
}
return null; // 池已满
}
process() {
// 批量处理所有激活的子弹
this.entityManager.forEachEntityWithComponent(
BulletComponent,
(entity, bullet) => {
if (!entity.enabled) return;
// 更新位置
const pos = entity.getComponent(PositionComponent);
const vel = entity.getComponent(VelocityComponent);
pos.x += vel.x * Time.deltaTime;
pos.y += vel.y * Time.deltaTime;
// 边界检查,回收到池中
if (pos.x < 0 || pos.x > 800 || pos.y < 0 || pos.y > 600) {
entity.enabled = false; // 回收而不是销毁
}
}
);
private processEnemy(enemy: Entity): void {
// 处理敌人逻辑
}
}
```
### AI系统性能优化
## 实体管理优化
### 批量创建实体
```typescript
class AISystem extends EntitySystem {
private spatialGrid: SpatialGrid;
private updateFrequency = 60; // 60Hz更新频率
private lastUpdate = 0;
// ✅ 推荐使用Scene的批量创建
function createEnemyWave(count: number): Entity[] {
const enemies = scene.createEntities(count, "Enemy");
process() {
const currentTime = performance.now();
// 批量配置组件
enemies.forEach((enemy, index) => {
enemy.addComponent(new PositionComponent(
Math.random() * 800,
Math.random() * 600
));
enemy.addComponent(new HealthComponent(100));
enemy.addComponent(new AIComponent());
enemy.tag = 2; // 敌人标签
});
// 控制更新频率
if (currentTime - this.lastUpdate < 1000 / this.updateFrequency) {
return;
return enemies;
}
// ❌ 避免:循环单独创建
function createEnemyWaveSlow(count: number): Entity[] {
const enemies: Entity[] = [];
for (let i = 0; i < count; i++) {
const enemy = entityManager.createEntity(`Enemy_${i}`);
enemy.addComponent(new PositionComponent());
enemy.addComponent(new HealthComponent());
enemies.push(enemy);
}
return enemies;
}
```
### 实体复用策略
```typescript
// 使用简单的实体复用策略
class EntityReusableManager {
private inactiveEntities: Entity[] = [];
private scene: Scene;
constructor(scene: Scene) {
this.scene = scene;
}
// 预创建实体
preCreateEntities(count: number, entityName: string): void {
const entities = this.scene.createEntities(count, entityName);
entities.forEach(entity => {
entity.enabled = false; // 禁用但不销毁
this.inactiveEntities.push(entity);
});
}
// 获取可复用实体
getReusableEntity(): Entity | null {
if (this.inactiveEntities.length > 0) {
const entity = this.inactiveEntities.pop()!;
entity.enabled = true;
return entity;
}
return null;
}
// 回收实体
recycleEntity(entity: Entity): void {
entity.enabled = false;
entity.removeAllComponents();
this.inactiveEntities.push(entity);
}
}
```
## 组件设计优化
### 数据局部性优化
```typescript
// ✅ 推荐:紧凑的数据结构
class OptimizedPositionComponent extends Component {
public x: number = 0;
public y: number = 0;
public z: number = 0;
// 避免对象分配
public setPosition(x: number, y: number, z: number = 0): void {
this.x = x;
this.y = y;
this.z = z;
}
}
// ❌ 避免:过多对象分配
class SlowPositionComponent extends Component {
public position: { x: number; y: number; z: number } = { x: 0, y: 0, z: 0 };
public velocity: { x: number; y: number; z: number } = { x: 0, y: 0, z: 0 };
public acceleration: { x: number; y: number; z: number } = { x: 0, y: 0, z: 0 };
}
```
### 组件池化
```typescript
// 使用框架内置的组件池
ComponentPoolManager.getInstance().registerPool(
'BulletComponent',
() => new BulletComponent(),
(bullet) => bullet.reset(),
1000
);
// 获取组件
const bullet = ComponentPoolManager.getInstance().acquireComponent('BulletComponent');
if (bullet) {
entity.addComponent(bullet);
}
// 释放组件
ComponentPoolManager.getInstance().releaseComponent('BulletComponent', bullet);
```
## 系统设计优化
### 系统更新顺序优化
```typescript
class OptimizedGameManager {
private scene: Scene;
constructor() {
this.scene = new Scene();
this.setupSystems();
}
private setupSystems(): void {
// 按依赖关系排序系统
this.scene.addEntityProcessor(new InputSystem()).updateOrder = 10;
this.scene.addEntityProcessor(new MovementSystem()).updateOrder = 20;
this.scene.addEntityProcessor(new CollisionSystem()).updateOrder = 30;
this.scene.addEntityProcessor(new RenderSystem()).updateOrder = 40;
this.scene.addEntityProcessor(new CleanupSystem()).updateOrder = 50;
}
}
```
### 时间分片处理
```typescript
class TimeSlicedAISystem extends EntitySystem {
private aiEntities: Entity[] = [];
private currentIndex = 0;
private entitiesPerFrame = 10;
protected process(entities: Entity[]): void {
// 获取所有AI实体
if (this.aiEntities.length === 0) {
this.aiEntities = this.entityManager.getEntitiesByTag(3); // AI标签
}
// 使用空间分区优化邻居查询
const aiEntities = this.entityManager
// 每帧只处理部分实体
const endIndex = Math.min(
this.currentIndex + this.entitiesPerFrame,
this.aiEntities.length
);
for (let i = this.currentIndex; i < endIndex; i++) {
this.processAI(this.aiEntities[i]);
}
// 更新索引
this.currentIndex = endIndex;
if (this.currentIndex >= this.aiEntities.length) {
this.currentIndex = 0;
this.aiEntities = []; // 重新获取实体列表
}
}
private processAI(entity: Entity): void {
// AI处理逻辑
}
}
```
## 内存管理
### 及时清理无用实体
```typescript
class CleanupSystem extends EntitySystem {
protected process(entities: Entity[]): void {
// 清理超出边界的子弹
const bullets = this.entityManager.getEntitiesByTag(4); // 子弹标签
bullets.forEach(bullet => {
const pos = bullet.getComponent(PositionComponent);
if (pos && this.isOutOfBounds(pos)) {
this.entityManager.destroyEntity(bullet);
}
});
// 清理死亡的实体
const deadEntities = this.entityManager
.query()
.withAll([PositionComponent, AIComponent])
.active(true)
.withAll(HealthComponent)
.where(entity => {
const health = entity.getComponent(HealthComponent);
return health && health.currentHealth <= 0;
})
.execute();
// 分批处理AI实体
const batchSize = 50;
for (let i = 0; i < aiEntities.length; i += batchSize) {
const batch = aiEntities.slice(i, i + batchSize);
this.processBatch(batch);
// 时间片控制,避免单帧卡顿
if (performance.now() - currentTime > 10) { // 10ms时间片
break; // 下一帧继续处理
}
}
this.lastUpdate = currentTime;
}
private processBatch(entities: Entity[]) {
entities.forEach(entity => {
const pos = entity.getComponent(PositionComponent);
const ai = entity.getComponent(AIComponent);
// 空间查询优化邻居搜索
const neighbors = this.spatialGrid.queryRadius(pos.x, pos.y, ai.sightRange);
// AI决策逻辑
ai.update(neighbors);
deadEntities.forEach(entity => {
this.entityManager.destroyEntity(entity);
});
}
private isOutOfBounds(pos: PositionComponent): boolean {
return pos.x < -100 || pos.x > 900 || pos.y < -100 || pos.y > 700;
}
}
```
## 性能监控工具
### 实时性能仪表板
### 实体复用管理
```typescript
class PerformanceDashboard {
private stats: any = {};
private updateInterval = 1000; // 1秒更新一次
class GameEntityManager {
private bulletManager: EntityReusableManager;
private effectManager: EntityReusableManager;
constructor(private entityManager: EntityManager) {
setInterval(() => this.updateStats(), this.updateInterval);
constructor(scene: Scene) {
this.bulletManager = new EntityReusableManager(scene);
this.effectManager = new EntityReusableManager(scene);
// 预创建实体
this.bulletManager.preCreateEntities(100, "Bullet");
this.effectManager.preCreateEntities(50, "Effect");
}
private updateStats() {
this.stats = {
// 基础统计
entities: this.entityManager.getStatistics(),
// 组件索引
componentIndex: this.entityManager.getComponentIndex().getPerformanceStats(),
// Archetype系统
archetype: this.entityManager.getArchetypeSystem().getStatistics(),
// 脏标记系统
dirtyTracking: this.entityManager.getDirtyTrackingSystem().getPerformanceStats(),
// 内存使用
memory: this.entityManager.getMemoryUsage(),
// 计算性能指标
performance: this.calculatePerformanceMetrics()
};
this.displayStats();
createBullet(): Entity | null {
const bullet = this.bulletManager.getReusableEntity();
if (bullet) {
bullet.addComponent(new BulletComponent());
bullet.addComponent(new PositionComponent());
bullet.addComponent(new VelocityComponent());
}
return bullet;
}
private calculatePerformanceMetrics() {
const componentStats = this.stats.componentIndex;
const archetypeStats = this.stats.archetype;
return {
queryHitRate: componentStats.hitRate,
archetypeEfficiency: archetypeStats.averageEntitiesPerArchetype,
memoryEfficiency: this.stats.memory.compressionRatio,
overallPerformance: this.calculateOverallScore()
};
}
private displayStats() {
console.log('=== ECS性能仪表板 ===');
console.log('查询命中率:', this.stats.performance.queryHitRate.toFixed(2) + '%');
console.log('内存使用:', (this.stats.memory.total / 1024 / 1024).toFixed(2) + 'MB');
console.log('整体性能评分:', this.stats.performance.overallPerformance.toFixed(1) + '/10');
destroyBullet(bullet: Entity): void {
this.bulletManager.recycleEntity(bullet);
}
}
```
## 优化检查清单
## 性能监控
### 开发阶段
### 基础性能统计
- [ ] 使用EntityManager而不是直接操作Scene
- [ ] 优先使用组件查询和标签查询
- [ ] 设计合理的组件组合,避免过度碎片化
- [ ] 实现对象池机制减少频繁创建/销毁
```typescript
class PerformanceMonitor {
private scene: Scene;
private entityManager: EntityManager;
### 运行时优化
constructor(scene: Scene, entityManager: EntityManager) {
this.scene = scene;
this.entityManager = entityManager;
}
- [ ] 监控查询命中率保持在80%以上
- [ ] 控制Archetype数量避免过度分散
- [ ] 配置适当的脏标记批量处理参数
- [ ] 定期进行内存清理和数据压缩
public getPerformanceReport(): any {
return {
// 实体统计
entities: {
total: this.entityManager.entityCount,
active: this.entityManager.activeEntityCount
},
### 性能监控
// 场景统计
scene: this.scene.getStats(),
- [ ] 定期检查性能统计数据
- [ ] 监控内存使用趋势
- [ ] 设置性能预警阈值
- [ ] 在不同设备上进行性能测试
// 查询系统统计
querySystem: this.scene.querySystem.getStats(),
通过系统性地应用这些优化策略您可以构建出在各种规模下都能提供卓越性能的ECS游戏系统。
// 内存使用
memory: {
used: (performance as any).memory?.usedJSHeapSize || 0,
total: (performance as any).memory?.totalJSHeapSize || 0
}
};
}
public logPerformance(): void {
const report = this.getPerformanceReport();
console.log('性能报告:', report);
}
}
```
### 帧率监控
```typescript
class FPSMonitor {
private frameCount = 0;
private lastTime = performance.now();
private fps = 0;
public update(): void {
this.frameCount++;
const currentTime = performance.now();
if (currentTime - this.lastTime >= 1000) {
this.fps = this.frameCount;
this.frameCount = 0;
this.lastTime = currentTime;
if (this.fps < 30) {
console.warn(`低帧率警告: ${this.fps} FPS`);
}
}
}
public getFPS(): number {
return this.fps;
}
}
```
## 最佳实践总结
### 查询优化
1. 优先使用标签查询和组件查询
2. 缓存频繁使用的查询结果
3. 避免过度使用自定义条件查询
4. 合理设置查询缓存更新频率
### 实体管理
1. 使用批量创建方法
2. 实现实体池化减少GC压力
3. 及时清理无用实体
4. 合理设置实体标签
### 组件设计
1. 保持组件数据紧凑
2. 避免在组件中分配大量对象
3. 使用组件池化
4. 分离数据和行为
### 系统设计
1. 合理安排系统更新顺序
2. 对重计算任务使用时间分片
3. 避免在系统中进行复杂查询
4. 缓存系统间的共享数据
### 内存管理
1. 定期清理无用实体和组件
2. 使用对象池减少GC
3. 监控内存使用情况
4. 避免内存泄漏
通过遵循这些最佳实践可以显著提升ECS框架的性能表现。

View File

@@ -1,306 +0,0 @@
# ECS框架性能基准
本文档展示了ECS框架的真实性能数据和瓶颈分析。
## 🚀 快速测试
```bash
# 快速性能基准测试
npm run benchmark
# 完整性能测试
npm run test:performance
# 单元测试
npm run test:unit
```
## 📊 性能基准数据
> 测试环境: Node.js, Windows 10, 现代桌面CPU
### 1. 实体创建性能
| 实体数量 | 创建时间 | 创建速度 | 每个实体耗时 | 性能等级 |
|---------|---------|---------|-------------|---------|
| 1,000 | 1.56ms | 640,697个/秒 | 0.0016ms | 🚀 极致 |
| 5,000 | 19.47ms | 256,805个/秒 | 0.0039ms | 🚀 极致 |
| 10,000 | 39.94ms | 250,345个/秒 | 0.0040ms | 🚀 极致 |
| 50,000 | 258.17ms | 193,673个/秒 | 0.0052ms | ✅ 优秀 |
| 100,000 | 463.04ms | 215,963个/秒 | 0.0046ms | ✅ 优秀 |
| 500,000 | 3,087ms | 161,990个/秒 | 0.0062ms | ✅ 优秀 |
**结论**: 🚀 实体创建性能达到极致水平大规模创建50万实体仅需3秒
### 2. 性能瓶颈分析 (500,000个实体)
**当前瓶颈分布**:
```
实体创建: 46.3% (1,429ms)
组件添加: 53.5% (1,651ms) ← 主要瓶颈
标签分配: 0.2% (7ms)
```
**特征**: 框架实现了均衡的性能分布,各部分开销相对合理
### 3. 组件添加性能详细分析
| 组件类型 | 添加速度 | 平均耗时 | 性能等级 |
|---------|---------|---------|---------|
| PositionComponent | 596,929组件/秒 | 0.0017ms | 🚀 极致 |
| VelocityComponent | 1,186,770组件/秒 | 0.0008ms | 🚀 极致 |
| HealthComponent | 841,982组件/秒 | 0.0012ms | 🚀 极致 |
| RenderComponent | 763,351组件/秒 | 0.0013ms | 🚀 极致 |
| AIComponent | 185,964组件/秒 | 0.0054ms | ✅ 优秀 |
### 4. 优化技术性能影响
| 优化技术 | 性能提升 | 内存影响 | 适用场景 |
|---------|---------|---------|---------|
| 组件对象池 | 30-50% | 减少分配 | 频繁创建/销毁 |
| 位掩码优化器 | 20-40% | 缓存开销 | 大量查询操作 |
| 批量操作 | 显著提升 | 无明显影响 | 大规模实体创建 |
| 延迟索引更新 | 60-80% | 临时内存增加 | 批量实体操作 |
| 索引去重优化 | 避免O(n) | 轻微内存增加 | 防止重复实体 |
### 5. 查询系统性能
#### 5.1 基础查询性能
| 查询类型 | 查询速度 | 每次查询耗时 | 性能等级 |
|---------|---------|-------------|---------|
| 单组件查询 | 12,178次/秒 | 0.082ms | ✅ 优秀 |
| 多组件查询 | 9,439次/秒 | 0.106ms | ✅ 优秀 |
| 复合查询 | 7,407次/秒 | 0.135ms | ✅ 良好 |
#### 5.2 缓存查询性能
| 缓存状态 | 访问速度 | 性能特征 |
|---------|---------|---------|
| 缓存命中 | 零延迟 | 🚀 即时响应 |
| 缓存未命中 | 标准查询 | ✅ 自动构建 |
| 缓存清理 | 批量延迟 | 🔧 优化策略 |
### 6. 新功能性能基准
#### 6.1 组件对象池性能
```
📊 对象池 vs 直接创建 (10,000次操作)
对象池获取: 1.65ms (6,060,606次/秒)
直接创建: 1.51ms (6,622,516次/秒)
⚠️ 小规模测试中对象池可能略慢,但在大规模应用中:
- 减少30-50%的内存分配
- 避免垃圾回收压力
- 提升长期运行稳定性
```
#### 6.2 位掩码优化器性能
```
🔥 位掩码操作性能 (100,000次操作)
单个掩码创建: 20.00ms (5,000,000次/秒)
组合掩码创建: 53.69ms (1,862,285次/秒)
缓存掩码访问: <1ms (近零延迟)
```
## 🎯 性能扩展性分析
### 实体创建扩展性
```
📈 创建速度趋势分析
1K-10K实体: 250,000-640,000 实体/秒 (优秀)
10K-100K实体: 200,000-250,000 实体/秒 (良好)
100K-500K实体: 160,000-220,000 实体/秒 (稳定)
结论: 性能随规模稳定下降,无突然性能悬崖
```
### 内存使用效率
| 实体数量 | 内存使用 | 每实体内存 | 内存效率 |
|---------|---------|-----------|---------|
| 1,000 | 3.5MB | 3.5KB | 🚀 极致 |
| 5,000 | 7.1MB | 1.4KB | 🚀 极致 |
| 10,000 | 20.8MB | 2.1KB | ✅ 优秀 |
| 50,000 | ~100MB | ~2KB | ✅ 优秀 |
## 💡 性能优化建议
### 1. 实体创建最佳实践
**✅ 推荐做法**:
```typescript
// 使用批量创建API
const entities = scene.createEntities(10000, "Enemies");
// 延迟缓存清理
entities.forEach(entity => {
scene.addEntity(entity, false); // 延迟清理
});
scene.querySystem.clearCache(); // 手动清理
```
**❌ 避免做法**:
```typescript
// 避免循环单个创建
for (let i = 0; i < 10000; i++) {
scene.createEntity("Enemy" + i); // 每次触发缓存清理
}
```
### 2. 组件池优化策略
**预热策略**:
```typescript
// 预热常用组件池
ComponentPoolManager.getInstance().preWarmPools({
BulletComponent: 2000, // 子弹大量创建
EffectComponent: 1000, // 特效频繁使用
PickupComponent: 500 // 道具适量缓存
});
```
**使用模式**:
```typescript
// 高效的组件复用
const bullet = ComponentPoolManager.getInstance().getComponent(BulletComponent);
bullet.reset(); // 重置状态
entity.addComponent(bullet);
// 销毁时释放到池
ComponentPoolManager.getInstance().releaseComponent(bullet);
```
### 3. 查询优化策略
**缓存策略**:
```typescript
// 缓存频繁查询结果
class MovementSystem extends EntitySystem {
private cachedMovingEntities: Entity[];
private lastCacheFrame: number = 0;
protected process(entities: Entity[]) {
// 每5帧更新一次缓存
if (Time.frameCount - this.lastCacheFrame > 5) {
this.cachedMovingEntities = scene.getEntitiesWithComponents([Position, Velocity]);
this.lastCacheFrame = Time.frameCount;
}
// 使用缓存结果
this.processMovement(this.cachedMovingEntities);
}
}
```
### 4. 不同规模应用建议
#### 小型游戏 (< 5,000实体)
- ✅ 可以随意使用所有功能
- ✅ 不需要特殊优化
- ✅ 专注于游戏逻辑开发
#### 中型游戏 (5,000-50,000实体)
- ✅ 使用批量操作API
- ✅ 启用组件对象池
- ⚠️ 注意查询频率
#### 大型游戏 (50,000+实体)
- 🚀 必须使用批量操作
- 🚀 必须启用对象池
- 🚀 必须缓存查询结果
- 🚀 考虑分区处理
## 🌍 平台性能对比
### Windows 桌面端 (测试平台)
- **实体创建**: 640,697实体/秒
- **组件操作**: 596,929组件/秒
- **推荐实体数**: ≤ 200,000
### 预估其他平台性能
| 平台类型 | 预估性能比例 | 推荐实体数 | 特殊注意 |
|---------|-------------|-----------|---------|
| macOS桌面 | 90-100% | ≤ 180,000 | 内存管理优秀 |
| Linux桌面 | 95-105% | ≤ 200,000 | 性能最优 |
| Chrome浏览器 | 60-80% | ≤ 100,000 | V8引擎优化 |
| Firefox浏览器 | 50-70% | ≤ 80,000 | SpiderMonkey限制 |
| Safari浏览器 | 55-75% | ≤ 90,000 | JavaScriptCore |
| Node.js服务器 | 100-110% | ≤ 500,000 | 服务器级性能 |
| Android Chrome | 30-50% | ≤ 30,000 | 移动端限制 |
| iOS Safari | 40-60% | ≤ 40,000 | iOS优化较好 |
## 🔬 测试环境详情
### 硬件环境
- **操作系统**: Windows 10 (Build 26100)
- **处理器**: 现代桌面CPU
- **内存**: 充足RAM
- **存储**: SSD高速存储
### 软件环境
- **Node.js**: v16+
- **TypeScript**: v5.8.3
- **ECS框架版本**: v2.0.6
- **测试工具**: 内置基准测试套件
### 测试方法
- **实体配置**: 位置、速度、生命值、渲染、AI组件随机分配
- **测试迭代**: 多次测试取平均值
- **内存监控**: 实时内存使用情况
- **性能指标**: performance.now()高精度计时
## 📋 性能测试清单
### 运行完整性能测试
```bash
# 1. 快速基准测试 (2-3分钟)
npm run benchmark
# 2. 完整性能测试 (10-15分钟)
npm run test:performance
# 3. 单元测试验证 (30秒)
npm run test:unit
# 4. 所有测试 (15-20分钟)
npm run test
```
### 自定义性能测试
```typescript
import { runEntityCreationBenchmark } from '@esengine/ecs-framework/Testing/Performance/benchmark';
// 自定义规模测试
await runEntityCreationBenchmark([1000, 5000, 10000]);
// 组件性能测试
await runComponentPerformanceTest();
// 查询性能测试
await runQueryPerformanceTest();
```
## 🏆 性能总结
### 🎯 核心能力
1. **实体创建速度**: 最高64万实体/秒
2. **大规模处理**: 50万实体仅需3秒创建
3. **均衡性能**: 各组件开销分布合理
4. **扩展性**: 性能随规模线性下降,无突然悬崖
### 🔧 技术特点
1. **批量操作架构** - 大幅减少单次操作开销
2. **智能缓存策略** - 延迟清理机制
3. **索引系统优化** - 避免O(n)操作
4. **内存管理优化** - 对象池和位掩码缓存
### 🌟 实际应用价值
- **小型游戏**: 性能过剩,专注玩法
- **中型游戏**: 性能充足,适度优化
- **大型游戏**: 需要优化策略,但完全可行
- **服务器端**: 可处理大规模实体管理
---
**结论**: ECS框架达到了产品级性能标准能够满足从休闲小游戏到复杂RTS游戏的各种需求。框架层面的性能已经充分优化为开发者提供了坚实的性能基础。

View File

@@ -36,7 +36,7 @@ const noneResult = querySystem.queryNone(DeadComponent);
```typescript
// 类型安全的查询,返回实体和对应的组件
const typedResult = querySystem.queryAllTyped(PositionComponent, VelocityComponent);
const typedResult = querySystem.queryAll(PositionComponent, VelocityComponent);
for (let i = 0; i < typedResult.entities.length; i++) {
const entity = typedResult.entities[i];
const [position, velocity] = typedResult.components[i];
@@ -158,9 +158,6 @@ querySystem.warmUpCache(commonQueries);
### 2. 索引优化
```typescript
// 自动优化索引配置
querySystem.optimizeIndexes();
// 获取性能统计
const stats = querySystem.getStats();
console.log(`缓存命中率: ${(stats.hitRate * 100).toFixed(1)}%`);
@@ -205,10 +202,14 @@ console.log(`新增: ${diff.added.length}, 移除: ${diff.removed.length}`);
### 移动系统示例
```typescript
import { EntitySystem } from '@esengine/ecs-framework';
import { EntitySystem, Entity, Matcher } from '@esengine/ecs-framework';
class MovementSystem extends EntitySystem {
public update(): void {
constructor() {
super(Matcher.empty().all(PositionComponent, VelocityComponent));
}
protected process(entities: Entity[]): void {
// 查询所有可移动的实体
const movableEntities = this.scene.querySystem.queryTwoComponents(
PositionComponent,
@@ -236,7 +237,11 @@ class MovementSystem extends EntitySystem {
```typescript
class CollisionSystem extends EntitySystem {
public update(): void {
constructor() {
super(Matcher.empty().all(PositionComponent, ColliderComponent));
}
protected process(entities: Entity[]): void {
// 获取所有具有碰撞器的实体
const collidableEntities = this.scene.querySystem.queryTwoComponents(
PositionComponent,
@@ -276,7 +281,11 @@ class CollisionSystem extends EntitySystem {
```typescript
class HealthSystem extends EntitySystem {
public update(): void {
constructor() {
super(Matcher.empty().all(HealthComponent));
}
protected process(entities: Entity[]): void {
// 查询所有具有生命值的实体
const healthEntities = this.scene.querySystem.queryComponentTyped(HealthComponent);
const deadEntities: Entity[] = [];

View File

@@ -0,0 +1,734 @@
# 场景管理完整指南
场景Scene是ECS框架中管理游戏对象和系统的核心容器。本指南将详细介绍如何有效地使用场景来构建和管理你的游戏。
## 场景基础概念
### 什么是场景?
场景是一个完整的游戏世界容器,它包含:
- 🎮 **实体集合** - 所有游戏对象
- ⚙️ **系统集合** - 处理游戏逻辑的系统
- 📊 **事件系统** - 场景内的事件通信
- 🔍 **查询系统** - 高效的实体查询
- 📈 **性能监控** - 场景级别的性能统计
```typescript
import { Scene, Core } from '@esengine/ecs-framework';
// 创建场景
const gameScene = new Scene();
// 设置为当前活动场景
Core.scene = gameScene;
```
### 场景的生命周期
```typescript
class GameScene extends Scene {
// 场景开始时调用
onStart() {
console.log("场景开始");
this.initializeScene();
}
// 场景更新时调用(每帧)
update() {
super.update(); // 调用父类更新
// 自定义更新逻辑
this.updateGameLogic();
}
// 场景结束时调用
onDestroy() {
console.log("场景结束");
this.cleanup();
super.onDestroy();
}
}
```
## 基础场景操作
### 1. 创建和配置场景
```typescript
class MenuScene extends Scene {
private backgroundMusic: AudioClip;
onStart() {
this.setupUI();
this.setupSystems();
this.setupInput();
this.playBackgroundMusic();
}
private setupUI() {
// 创建菜单UI实体
const titleEntity = this.createEntity("Title");
titleEntity.addComponent(new TextComponent("我的游戏", 48));
titleEntity.addComponent(new PositionComponent(400, 100));
const startButton = this.createEntity("StartButton");
startButton.addComponent(new ButtonComponent("开始游戏"));
startButton.addComponent(new PositionComponent(400, 300));
const settingsButton = this.createEntity("SettingsButton");
settingsButton.addComponent(new ButtonComponent("设置"));
settingsButton.addComponent(new PositionComponent(400, 400));
const exitButton = this.createEntity("ExitButton");
exitButton.addComponent(new ButtonComponent("退出"));
exitButton.addComponent(new PositionComponent(400, 500));
}
private setupSystems() {
// 添加UI相关系统
this.addEntityProcessor(new UIRenderSystem());
this.addEntityProcessor(new ButtonClickSystem());
this.addEntityProcessor(new MenuTransitionSystem());
}
private setupInput() {
// 监听按钮点击事件
this.eventBus.on('button:clicked', this.onButtonClicked, this);
}
private onButtonClicked(data: { buttonName: string }) {
switch (data.buttonName) {
case "开始游戏":
this.transitionToGame();
break;
case "设置":
this.showSettings();
break;
case "退出":
this.exitGame();
break;
}
}
private transitionToGame() {
// 切换到游戏场景
const gameScene = new GameScene();
Core.scene = gameScene;
}
}
```
### 2. 游戏主场景
```typescript
class GameScene extends Scene {
private player: Entity;
private enemySpawner: Entity;
private ui: Entity;
onStart() {
this.setupWorld();
this.setupPlayer();
this.setupEnemies();
this.setupSystems();
this.setupUI();
}
private setupWorld() {
// 创建背景
const background = this.createEntity("Background");
background.addComponent(new SpriteComponent("background.png"));
background.addComponent(new PositionComponent(0, 0));
// 创建边界
this.createWorldBounds();
}
private setupPlayer() {
this.player = this.createEntity("Player");
this.player.addComponent(new PositionComponent(400, 300));
this.player.addComponent(new VelocityComponent());
this.player.addComponent(new HealthComponent(100));
this.player.addComponent(new SpriteComponent("player.png"));
this.player.addComponent(new PlayerInputComponent());
this.player.addComponent(new WeaponComponent());
this.player.tag = EntityTags.PLAYER;
}
private setupEnemies() {
this.enemySpawner = this.createEntity("EnemySpawner");
this.enemySpawner.addComponent(new SpawnerComponent());
this.enemySpawner.addComponent(new PositionComponent(0, 0));
}
private setupSystems() {
// 输入系统
this.addEntityProcessor(new PlayerInputSystem()).updateOrder = 0;
// 游戏逻辑系统
this.addEntityProcessor(new MovementSystem()).updateOrder = 10;
this.addEntityProcessor(new AISystem()).updateOrder = 15;
this.addEntityProcessor(new WeaponSystem()).updateOrder = 20;
this.addEntityProcessor(new CollisionSystem()).updateOrder = 30;
this.addEntityProcessor(new HealthSystem()).updateOrder = 40;
// 生成和清理系统
this.addEntityProcessor(new EnemySpawnSystem()).updateOrder = 50;
this.addEntityProcessor(new EntityCleanupSystem()).updateOrder = 60;
// 渲染系统
this.addEntityProcessor(new RenderSystem()).updateOrder = 100;
this.addEntityProcessor(new UIRenderSystem()).updateOrder = 110;
// 特效和音频系统
this.addEntityProcessor(new ParticleSystem()).updateOrder = 120;
this.addEntityProcessor(new AudioSystem()).updateOrder = 130;
}
private setupUI() {
this.ui = this.createEntity("GameUI");
this.ui.addComponent(new HealthBarComponent());
this.ui.addComponent(new ScoreDisplayComponent());
this.ui.addComponent(new AmmoDisplayComponent());
}
private createWorldBounds() {
// 创建世界边界,防止实体跑出屏幕
const bounds = [
{ x: 0, y: 0, width: 10, height: 600 }, // 左边界
{ x: 790, y: 0, width: 10, height: 600 }, // 右边界
{ x: 0, y: 0, width: 800, height: 10 }, // 上边界
{ x: 0, y: 590, width: 800, height: 10 } // 下边界
];
bounds.forEach((bound, index) => {
const wall = this.createEntity(`Wall_${index}`);
wall.addComponent(new PositionComponent(bound.x, bound.y));
wall.addComponent(new ColliderComponent(bound.width, bound.height));
wall.addComponent(new WallComponent());
wall.tag = EntityTags.WALL;
});
}
}
```
## 场景切换和管理
### 1. 场景管理器
> **注意:** 以下的 SceneManager、TransitionManager 等是自定义的场景管理类示例不是ECS框架提供的内置API。你可以基于这些示例实现自己的场景管理系统。
```typescript
enum SceneType {
MENU = "menu",
GAME = "game",
PAUSE = "pause",
GAME_OVER = "game_over",
SETTINGS = "settings"
}
// 自定义场景管理器(示例实现)
class SceneManager {
private static instance: SceneManager;
private currentScene: Scene | null = null;
private previousScene: Scene | null = null;
private sceneHistory: Scene[] = [];
static getInstance(): SceneManager {
if (!this.instance) {
this.instance = new SceneManager();
}
return this.instance;
}
switchToScene(sceneType: SceneType, data?: any) {
// 保存当前场景到历史
if (this.currentScene) {
this.previousScene = this.currentScene;
this.sceneHistory.push(this.currentScene);
this.currentScene.onDestroy();
}
// 创建新场景
this.currentScene = this.createScene(sceneType, data);
Core.scene = this.currentScene;
console.log(`切换到场景: ${sceneType}`);
}
goBack(): boolean {
if (this.sceneHistory.length > 0) {
const previousScene = this.sceneHistory.pop()!;
if (this.currentScene) {
this.currentScene.onDestroy();
}
this.currentScene = previousScene;
Core.scene = this.currentScene;
return true;
}
return false;
}
pushScene(sceneType: SceneType, data?: any) {
// 暂停当前场景,不销毁
if (this.currentScene) {
this.previousScene = this.currentScene;
this.sceneHistory.push(this.currentScene);
this.pauseScene(this.currentScene);
}
this.currentScene = this.createScene(sceneType, data);
Core.scene = this.currentScene;
}
popScene() {
if (this.sceneHistory.length > 0) {
if (this.currentScene) {
this.currentScene.onDestroy();
}
this.currentScene = this.sceneHistory.pop()!;
this.resumeScene(this.currentScene);
Core.scene = this.currentScene;
}
}
private createScene(sceneType: SceneType, data?: any): Scene {
switch (sceneType) {
case SceneType.MENU:
return new MenuScene();
case SceneType.GAME:
return new GameScene(data);
case SceneType.PAUSE:
return new PauseScene();
case SceneType.GAME_OVER:
return new GameOverScene(data);
case SceneType.SETTINGS:
return new SettingsScene();
default:
throw new Error(`Unknown scene type: ${sceneType}`);
}
}
private pauseScene(scene: Scene) {
// 暂停场景的所有系统
scene.systems.forEach(system => {
system.enabled = false;
});
}
private resumeScene(scene: Scene) {
// 恢复场景的所有系统
scene.systems.forEach(system => {
system.enabled = true;
});
}
}
// 使用场景管理器
const sceneManager = SceneManager.getInstance();
// 切换场景
sceneManager.switchToScene(SceneType.MENU);
// 推入场景(用于暂停菜单等)
sceneManager.pushScene(SceneType.PAUSE);
// 弹出场景(返回游戏)
sceneManager.popScene();
```
### 2. 场景转场效果
```typescript
class TransitionManager {
private isTransitioning: boolean = false;
async fadeTransition(fromScene: Scene, toScene: Scene, duration: number = 1.0) {
if (this.isTransitioning) return;
this.isTransitioning = true;
// 创建转场覆盖层
const overlay = this.createFadeOverlay();
// 淡出当前场景
await this.fadeOut(overlay, duration / 2);
// 切换场景
fromScene.onDestroy();
Core.scene = toScene;
// 淡入新场景
await this.fadeIn(overlay, duration / 2);
// 清理覆盖层
overlay.destroy();
this.isTransitioning = false;
}
async slideTransition(fromScene: Scene, toScene: Scene, direction: 'left' | 'right' | 'up' | 'down') {
if (this.isTransitioning) return;
this.isTransitioning = true;
// 实现滑动转场效果
const slideDistance = this.getSlideDistance(direction);
// 移动当前场景
await this.slideScene(fromScene, slideDistance);
// 切换场景
fromScene.onDestroy();
Core.scene = toScene;
// 从相反方向滑入新场景
await this.slideScene(toScene, -slideDistance);
this.isTransitioning = false;
}
private createFadeOverlay(): Entity {
const overlay = Core.scene.createEntity("TransitionOverlay");
overlay.addComponent(new SpriteComponent("black_pixel.png"));
overlay.addComponent(new PositionComponent(0, 0));
const sprite = overlay.getComponent(SpriteComponent);
sprite.width = 800;
sprite.height = 600;
sprite.alpha = 0;
return overlay;
}
}
```
## 场景数据管理
### 1. 场景间数据传递
```typescript
interface GameData {
score: number;
level: number;
playerName: string;
difficulty: string;
}
class GameScene extends Scene {
private gameData: GameData;
constructor(data?: GameData) {
super();
this.gameData = data || {
score: 0,
level: 1,
playerName: "Player",
difficulty: "normal"
};
}
onStart() {
super.onStart();
// 根据传入数据配置场景
this.setupPlayerWithData();
this.setupLevelWithDifficulty();
}
private setupPlayerWithData() {
const player = this.createEntity("Player");
player.addComponent(new NameComponent(this.gameData.playerName));
player.addComponent(new ScoreComponent(this.gameData.score));
// ... 其他组件
}
private setupLevelWithDifficulty() {
const difficultySettings = {
easy: { enemySpawnRate: 2.0, enemyHealth: 50 },
normal: { enemySpawnRate: 1.5, enemyHealth: 75 },
hard: { enemySpawnRate: 1.0, enemyHealth: 100 }
};
const settings = difficultySettings[this.gameData.difficulty];
const spawner = this.createEntity("EnemySpawner");
const spawnerComp = new SpawnerComponent();
spawnerComp.spawnInterval = settings.enemySpawnRate;
spawnerComp.enemyHealth = settings.enemyHealth;
spawner.addComponent(spawnerComp);
}
// 游戏结束时传递数据到下一个场景
gameOver() {
const finalScore = this.getPlayerScore();
const sceneManager = SceneManager.getInstance();
sceneManager.switchToScene(SceneType.GAME_OVER, {
score: finalScore,
level: this.gameData.level,
playerName: this.gameData.playerName
});
}
}
class GameOverScene extends Scene {
constructor(private gameData: GameData) {
super();
}
onStart() {
this.displayResults();
this.setupRestartButton();
}
private displayResults() {
const scoreText = this.createEntity("ScoreText");
scoreText.addComponent(new TextComponent(`最终分数: ${this.gameData.score}`));
scoreText.addComponent(new PositionComponent(400, 200));
const levelText = this.createEntity("LevelText");
levelText.addComponent(new TextComponent(`到达关卡: ${this.gameData.level}`));
levelText.addComponent(new PositionComponent(400, 250));
}
}
```
### 2. 持久化数据管理
```typescript
class SaveManager {
private static SAVE_KEY = "game_save_data";
static saveScene(scene: Scene): void {
const saveData = {
playerData: this.extractPlayerData(scene),
sceneState: this.extractSceneState(scene),
timestamp: Date.now()
};
localStorage.setItem(this.SAVE_KEY, JSON.stringify(saveData));
console.log("游戏已保存");
}
static loadScene(): Scene | null {
const saveDataStr = localStorage.getItem(this.SAVE_KEY);
if (!saveDataStr) return null;
try {
const saveData = JSON.parse(saveDataStr);
return this.recreateScene(saveData);
} catch (error) {
console.error("读取存档失败:", error);
return null;
}
}
private static extractPlayerData(scene: Scene): any {
const player = scene.findEntitiesWithTag(EntityTags.PLAYER)[0];
if (!player) return null;
return {
position: player.getComponent(PositionComponent),
health: player.getComponent(HealthComponent),
inventory: player.getComponent(InventoryComponent)?.getItems(),
score: player.getComponent(ScoreComponent)?.score
};
}
private static extractSceneState(scene: Scene): any {
return {
enemies: this.extractEnemiesData(scene),
items: this.extractItemsData(scene),
level: this.getCurrentLevel(scene)
};
}
private static recreateScene(saveData: any): Scene {
const scene = new GameScene();
// 重建玩家
this.recreatePlayer(scene, saveData.playerData);
// 重建场景状态
this.recreateSceneState(scene, saveData.sceneState);
return scene;
}
}
// 自动保存系统
class AutoSaveSystem extends IntervalSystem {
constructor() {
super(30.0); // 每30秒自动保存
}
processSystem() {
SaveManager.saveScene(this.scene);
}
}
```
## 场景性能优化
### 1. 实体管理优化
```typescript
class OptimizedScene extends Scene {
private activeEntities: Set<Entity> = new Set();
private inactiveEntities: Set<Entity> = new Set();
createEntity(name?: string): Entity {
const entity = super.createEntity(name);
this.activeEntities.add(entity);
return entity;
}
destroyEntity(entity: Entity) {
this.activeEntities.delete(entity);
super.destroyEntity(entity);
}
// 暂时禁用实体而不销毁
deactivateEntity(entity: Entity) {
if (this.activeEntities.has(entity)) {
this.activeEntities.delete(entity);
this.inactiveEntities.add(entity);
entity.enabled = false;
}
}
// 重新激活实体
activateEntity(entity: Entity) {
if (this.inactiveEntities.has(entity)) {
this.inactiveEntities.delete(entity);
this.activeEntities.add(entity);
entity.enabled = true;
}
}
// 只更新活跃实体
update() {
for (const entity of this.activeEntities) {
if (entity.enabled) {
entity.update();
}
}
this.updateEntitySystems();
}
// 批量操作
deactivateAllEnemies() {
const enemies = this.findEntitiesWithTag(EntityTags.ENEMY);
enemies.forEach(enemy => this.deactivateEntity(enemy));
}
activateAllEnemies() {
const enemies = Array.from(this.inactiveEntities)
.filter(entity => entity.hasTag(EntityTags.ENEMY));
enemies.forEach(enemy => this.activateEntity(enemy));
}
}
```
### 2. 系统性能监控
```typescript
class PerformanceMonitoredScene extends Scene {
private systemPerformance: Map<string, number[]> = new Map();
addEntityProcessor<T extends EntitySystem>(system: T): T {
const wrappedSystem = this.wrapSystemWithMonitoring(system);
return super.addEntityProcessor(wrappedSystem);
}
private wrapSystemWithMonitoring<T extends EntitySystem>(system: T): T {
const originalUpdate = system.update.bind(system);
const systemName = system.constructor.name;
system.update = () => {
const startTime = performance.now();
originalUpdate();
const endTime = performance.now();
this.recordSystemPerformance(systemName, endTime - startTime);
};
return system;
}
private recordSystemPerformance(systemName: string, duration: number) {
if (!this.systemPerformance.has(systemName)) {
this.systemPerformance.set(systemName, []);
}
const records = this.systemPerformance.get(systemName)!;
records.push(duration);
// 只保留最近100次记录
if (records.length > 100) {
records.shift();
}
}
getPerformanceReport(): any {
const report = {};
this.systemPerformance.forEach((durations, systemName) => {
const avgDuration = durations.reduce((a, b) => a + b, 0) / durations.length;
const maxDuration = Math.max(...durations);
const minDuration = Math.min(...durations);
report[systemName] = {
average: avgDuration.toFixed(2) + 'ms',
max: maxDuration.toFixed(2) + 'ms',
min: minDuration.toFixed(2) + 'ms',
samples: durations.length
};
});
return report;
}
// 定期输出性能报告
private performanceReportTimer() {
Core.schedule(5.0, true, this, () => {
console.table(this.getPerformanceReport());
});
}
}
```
## 常见问题和最佳实践
### Q: 何时创建新场景?
A:
- 游戏的不同阶段(菜单、游戏、设置)
- 不同的关卡
- 需要完全不同系统配置的情况
- 需要清理大量实体时
### Q: 场景切换时如何保持数据?
A:
1. 使用场景构造函数传递数据
2. 使用全局数据管理器
3. 使用本地存储进行持久化
### Q: 如何优化场景性能?
A:
1. 合理使用实体的启用/禁用
2. 监控系统性能
3. 批量操作实体
4. 使用对象池减少垃圾回收
### Q: 多个场景可以同时存在吗?
A: 框架同时只支持一个活跃场景,但可以通过场景栈实现多场景管理(如暂停菜单)。
通过合理使用场景系统,你可以构建出结构清晰、性能优良的游戏架构!

515
docs/system-guide.md Normal file
View File

@@ -0,0 +1,515 @@
# 系统System详解指南
系统是ECS架构中的"S",负责处理拥有特定组件的实体。本指南详细介绍框架中的各种系统类型及其使用方法。
## 系统基础概念
### 什么是系统?
系统是处理游戏逻辑的地方,它们:
- 🎯 **专注单一职责** - 每个系统只处理一种类型的逻辑
- 🔄 **自动执行** - 系统会在每帧自动被调用
- 📊 **基于组件过滤** - 只处理包含特定组件的实体
-**高性能** - 利用ECS的数据局部性优势
### 系统的工作原理
```typescript
// 系统的基本工作流程:
// 1. 查询符合条件的实体
// 2. 遍历这些实体
// 3. 读取/修改实体的组件数据
// 4. 执行游戏逻辑
class MovementSystem extends EntitySystem {
process(entities: Entity[]) {
for (const entity of entities) {
const position = entity.getComponent(PositionComponent);
const velocity = entity.getComponent(VelocityComponent);
// 更新位置 = 当前位置 + 速度 * 时间
position.x += velocity.x * Time.deltaTime;
position.y += velocity.y * Time.deltaTime;
}
}
}
```
## 系统类型详解
### 1. EntitySystem - 基础系统
最常用的系统类型,每帧处理所有符合条件的实体。
```typescript
import { EntitySystem, Entity, Matcher } from '@esengine/ecs-framework';
class HealthSystem extends EntitySystem {
constructor() {
// 使用Matcher指定需要的组件
super(Matcher.empty().all(HealthComponent));
}
// 主要处理逻辑
protected process(entities: Entity[]) {
for (const entity of entities) {
const health = entity.getComponent(HealthComponent);
// 处理生命值逻辑
if (health.currentHealth <= 0) {
this.handleDeath(entity);
} else if (health.currentHealth < health.maxHealth) {
this.handleRegeneration(health);
}
}
}
private handleDeath(entity: Entity) {
// 添加死亡标记
entity.addComponent(new DeadComponent());
// 触发死亡事件
Core.emitter.emit('entity:died', {
entityId: entity.id,
entityName: entity.name
});
}
private handleRegeneration(health: HealthComponent) {
// 缓慢恢复生命值
health.currentHealth += health.regenRate * Time.deltaTime;
health.currentHealth = Math.min(health.currentHealth, health.maxHealth);
}
}
```
**适用场景:**
- 移动系统
- 渲染系统
- 碰撞检测系统
- AI系统
### 2. ProcessingSystem - 简化处理系统
不需要处理具体实体,主要用于执行全局逻辑或不依赖特定实体的系统处理。
```typescript
import { ProcessingSystem, Matcher } from '@esengine/ecs-framework';
class GameLogicSystem extends ProcessingSystem {
constructor() {
// ProcessingSystem可以不指定Matcher或使用空Matcher
super(Matcher.empty());
}
// 处理系统逻辑(每帧执行)
public processSystem() {
// 执行全局游戏逻辑
this.updateGameState();
this.checkWinConditions();
this.updateUI();
}
private updateGameState() {
// 更新游戏状态逻辑
console.log("更新游戏状态");
}
private checkWinConditions() {
// 检查胜利条件
const players = this.scene.findEntitiesByTag(EntityTags.PLAYER);
const enemies = this.scene.findEntitiesByTag(EntityTags.ENEMY);
if (enemies.length === 0) {
this.triggerVictory();
} else if (players.length === 0) {
this.triggerGameOver();
}
}
private updateUI() {
// 更新UI显示
const gameTime = Time.totalTime;
console.log(`游戏时间: ${gameTime.toFixed(1)}`);
}
}
private processIdle(entity: Entity, ai: AIComponent) {
ai.idleTimer += Time.deltaTime;
if (ai.idleTimer >= ai.idleTime) {
ai.state = AIState.PATROL;
ai.idleTimer = 0;
}
// 检查附近是否有玩家
const nearbyPlayer = this.findNearbyPlayer(entity, ai.detectionRange);
if (nearbyPlayer) {
ai.state = AIState.CHASE;
ai.target = nearbyPlayer;
}
}
private processPatrol(entity: Entity, ai: AIComponent, position: PositionComponent) {
// 简单的来回巡逻
if (!ai.patrolTarget) {
ai.patrolTarget = this.getNextPatrolPoint(ai);
}
const direction = ai.patrolTarget.subtract(position);
const distance = direction.length();
if (distance < 10) {
ai.patrolTarget = this.getNextPatrolPoint(ai);
} else {
const normalized = direction.normalize();
position.x += normalized.x * ai.moveSpeed * Time.deltaTime;
position.y += normalized.y * ai.moveSpeed * Time.deltaTime;
}
}
}
```
**适用场景:**
- 全局游戏逻辑系统
- 胜负判断系统
- UI更新系统
- 不依赖特定实体的处理
### 3. IntervalSystem - 间隔执行系统
不是每帧都执行,而是按指定间隔执行的系统,适合不需要高频更新的逻辑。
```typescript
import { IntervalSystem, Matcher } from '@esengine/ecs-framework';
class SpawnSystem extends IntervalSystem {
private spawnPoints: { x: number; y: number }[] = [
{ x: 100, y: 100 },
{ x: 700, y: 100 },
{ x: 400, y: 500 }
];
// 每2秒执行一次
constructor() {
// IntervalSystem需要指定Matcher和间隔时间
super(Matcher.empty().all(SpawnerComponent), 2.0);
}
// 间隔执行的逻辑重写process方法
protected process(entities: Entity[]) {
// entities就是匹配的生成器实体
for (const spawner of entities) {
const spawnerComp = spawner.getComponent(SpawnerComponent);
if (this.shouldSpawn(spawnerComp)) {
this.spawnEnemy(spawner, spawnerComp);
}
}
}
private shouldSpawn(spawner: SpawnerComponent): boolean {
// 检查是否应该生成
const currentEnemyCount = this.getCurrentEnemyCount();
return currentEnemyCount < spawner.maxEnemies &&
Math.random() < spawner.spawnChance;
}
private spawnEnemy(spawnerEntity: Entity, spawner: SpawnerComponent) {
// 随机选择生成点
const spawnPoint = this.spawnPoints[
Math.floor(Math.random() * this.spawnPoints.length)
];
// 创建敌人实体
const enemy = this.scene.createEntity("Enemy");
enemy.addComponent(new PositionComponent(spawnPoint.x, spawnPoint.y));
enemy.addComponent(new HealthComponent(50));
enemy.addComponent(new AIComponent());
enemy.addComponent(new VelocityComponent(0, 0));
enemy.tag = EntityTags.ENEMY;
// 更新生成器统计
spawner.spawnedCount++;
spawner.lastSpawnTime = Time.totalTime;
// 发送生成事件
Core.emitter.emit('enemy:spawned', {
enemyId: enemy.id,
spawnPoint: spawnPoint,
spawnerEntity: spawnerEntity.id
});
}
}
```
**适用场景:**
- 敌人生成系统
- 自动保存系统
- 资源回收系统
- 定期数据同步
### 4. PassiveSystem - 被动系统
不主动遍历实体,而是响应事件的系统。
```typescript
import { PassiveSystem, Matcher, Core } from '@esengine/ecs-framework';
class ScoreSystem extends PassiveSystem {
private score: number = 0;
private multiplier: number = 1;
private combo: number = 0;
constructor() {
// PassiveSystem也需要Matcher即使不使用
super(Matcher.empty());
}
initialize() {
super.initialize();
// 监听游戏事件使用Core.emitter
Core.emitter.addObserver('enemy:killed', this.onEnemyKilled, this);
Core.emitter.addObserver('item:collected', this.onItemCollected, this);
Core.emitter.addObserver('combo:broken', this.onComboBroken, this);
}
// PassiveSystem被移除时清理
destroy() {
// 清理事件监听
Core.emitter.removeObserver('enemy:killed', this.onEnemyKilled, this);
Core.emitter.removeObserver('item:collected', this.onItemCollected, this);
Core.emitter.removeObserver('combo:broken', this.onComboBroken, this);
}
private onEnemyKilled(data: { enemyType: string; position: { x: number; y: number } }) {
// 根据敌人类型给分
let baseScore = this.getScoreForEnemyType(data.enemyType);
// 连击奖励
this.combo++;
if (this.combo > 3) {
this.multiplier = Math.min(this.combo * 0.1, 3.0); // 最多3倍
}
const finalScore = Math.floor(baseScore * this.multiplier);
this.addScore(finalScore);
// 显示分数奖励
this.showScorePopup(data.position, finalScore);
}
private addScore(points: number) {
this.score += points;
// 发送分数更新事件
Core.emitter.emit('score:updated', {
score: this.score,
points: points,
multiplier: this.multiplier,
combo: this.combo
});
}
}
```
**适用场景:**
- 分数统计系统
- 音效播放系统
- UI更新系统
- 成就系统
## 系统管理和注册
### 在场景中添加系统
```typescript
import { Scene, Core } from '@esengine/ecs-framework';
const scene = new Scene();
// 添加各种系统使用addEntityProcessor方法
scene.addEntityProcessor(new MovementSystem());
scene.addEntityProcessor(new GameLogicSystem());
scene.addEntityProcessor(new SpawnSystem());
scene.addEntityProcessor(new ScoreSystem());
// 设置系统的执行优先级
const movementSystem = scene.getEntityProcessor(MovementSystem);
if (movementSystem) {
movementSystem.updateOrder = 10; // 数值越小越先执行
}
const renderSystem = scene.getEntityProcessor(RenderSystem);
if (renderSystem) {
renderSystem.updateOrder = 100; // 渲染系统最后执行
}
// 设置为当前场景
Core.scene = scene;
```
### 系统的启用和禁用
```typescript
// 暂时禁用某个系统
const gameLogicSystem = scene.getEntityProcessor(GameLogicSystem);
if (gameLogicSystem) {
gameLogicSystem.enabled = false;
}
// 重新启用
if (gameLogicSystem) {
gameLogicSystem.enabled = true;
}
// 移除系统
scene.removeEntityProcessor(gameLogicSystem);
```
## 系统设计最佳实践
### 1. 单一职责原则
```typescript
// ✅ 好的设计:每个系统只负责一件事
class MovementSystem extends EntitySystem {
// 只负责移动
}
class CollisionSystem extends EntitySystem {
// 只负责碰撞检测
}
class RenderSystem extends EntitySystem {
// 只负责渲染
}
// ❌ 不好的设计:一个系统做太多事情
class GameplaySystem extends EntitySystem {
// 既处理移动,又处理碰撞,还处理渲染...
}
```
### 2. 合理的系统执行顺序
```typescript
// 设置合理的执行顺序
scene.addEntityProcessor(new InputSystem()).updateOrder = 0; // 输入最先
scene.addEntityProcessor(new GameLogicSystem()).updateOrder = 10; // 游戏逻辑
scene.addEntityProcessor(new MovementSystem()).updateOrder = 20; // 移动计算
scene.addEntityProcessor(new CollisionSystem()).updateOrder = 30; // 碰撞检测
scene.addEntityProcessor(new HealthSystem()).updateOrder = 40; // 生命值处理
scene.addEntityProcessor(new RenderSystem()).updateOrder = 100; // 渲染最后
```
### 3. 系统间通信
```typescript
// 使用事件进行系统间通信
class CollisionSystem extends EntitySystem {
process(entities: Entity[]) {
// ... 碰撞检测逻辑
if (collision) {
// 发送碰撞事件,让其他系统响应
Core.emitter.emit('collision:detected', {
entity1: collider1,
entity2: collider2,
collisionPoint: point
});
}
}
}
class HealthSystem extends PassiveSystem {
onAddedToScene() {
// 监听碰撞事件
Core.emitter.addObserver('collision:detected', this.onCollision, this);
}
private onCollision(data: CollisionEventData) {
// 处理碰撞伤害
if (data.entity1.hasComponent(HealthComponent)) {
const health = data.entity1.getComponent(HealthComponent);
health.takeDamage(10);
}
}
}
```
### 4. 性能优化
```typescript
class OptimizedMovementSystem extends EntitySystem {
private lastUpdateTime: number = 0;
private readonly UPDATE_INTERVAL = 16; // 60FPS
process(entities: Entity[]) {
const currentTime = Time.totalTime;
// 限制更新频率
if (currentTime - this.lastUpdateTime < this.UPDATE_INTERVAL) {
return;
}
// 批量处理
this.processBatch(entities);
this.lastUpdateTime = currentTime;
}
private processBatch(entities: Entity[]) {
// 使用for循环而不是forEach性能更好
for (let i = 0; i < entities.length; i++) {
const entity = entities[i];
// 处理逻辑...
}
}
}
```
## 常见问题
### Q: 系统的执行顺序重要吗?
A: 非常重要!合理的执行顺序可以避免逻辑错误:
```typescript
// 正确顺序:
// 1. 输入系统(收集玩家输入)
// 2. AI系统敌人决策
// 3. 移动系统(更新位置)
// 4. 碰撞系统(检测碰撞)
// 5. 渲染系统(显示画面)
```
### Q: 什么时候使用哪种系统类型?
A:
- **EntitySystem** - 大部分游戏逻辑移动、AI、碰撞等
- **ProcessingSystem** - 复杂的单实体处理复杂AI、粒子系统
- **IntervalSystem** - 不需要每帧执行的逻辑(生成器、自动保存)
- **PassiveSystem** - 事件响应系统分数、音效、UI更新
### Q: 系统可以访问其他系统吗?
A: 不建议直接访问。推荐使用事件系统进行系统间通信,保持松耦合。
### Q: 如何调试系统性能?
A: 使用框架内置的性能监控:
```typescript
const monitor = PerformanceMonitor.instance;
monitor.startFrame('MovementSystem');
// 系统逻辑...
monitor.endFrame('MovementSystem');
// 查看性能报告
console.log(monitor.getReport());
```
通过合理使用这些系统类型,你可以构建出高性能、易维护的游戏逻辑!

651
docs/timer-guide.md Normal file
View File

@@ -0,0 +1,651 @@
# 定时器系统使用指南
定时器系统是游戏开发中的重要工具用于处理延迟执行、重复任务、倒计时等功能。本指南详细介绍如何使用ECS框架的定时器系统。
## 定时器基础概念
### 什么是定时器?
定时器允许你:
-**延迟执行** - 在指定时间后执行某个操作
- 🔄 **重复执行** - 定期重复执行某个操作
- 🛑 **取消执行** - 在执行前取消定时器
- 🎯 **精确控制** - 精确控制执行时机
### 定时器的优势
相比直接在游戏循环中计时,定时器系统提供:
- 🧹 **自动管理** - 自动处理定时器的生命周期
- 🎮 **游戏时间控制** - 支持游戏暂停、时间缩放
- 💾 **内存优化** - 自动回收完成的定时器
- 🔧 **易于使用** - 简单的API调用
## 基础定时器使用
### 1. 简单延迟执行
```typescript
import { Core, Timer } from '@esengine/ecs-framework';
// 3秒后执行一次
Core.schedule(3.0, false, this, (timer) => {
console.log("3秒钟到了");
});
// 实际游戏例子:延迟显示提示
class GameTutorial {
startTutorial() {
// 2秒后显示第一个提示
Core.schedule(2.0, false, this, () => {
this.showTip("欢迎来到游戏世界!");
});
// 5秒后显示移动提示
Core.schedule(5.0, false, this, () => {
this.showTip("使用WASD键移动角色");
});
// 8秒后显示攻击提示
Core.schedule(8.0, false, this, () => {
this.showTip("按空格键攻击敌人");
});
}
private showTip(message: string) {
// 显示提示的逻辑
console.log(`提示: ${message}`);
}
}
```
### 2. 重复执行
```typescript
// 每1秒执行一次持续执行
const repeatTimer = Core.schedule(1.0, true, this, (timer) => {
console.log("每秒执行一次");
});
// 实际游戏例子:生命值恢复
class HealthRegeneration {
private regenTimer: ITimer;
startRegeneration(entity: Entity) {
const health = entity.getComponent(HealthComponent);
// 每2秒恢复5点生命值
this.regenTimer = Core.schedule(2.0, true, this, () => {
if (health.currentHealth < health.maxHealth) {
health.currentHealth += 5;
health.currentHealth = Math.min(health.currentHealth, health.maxHealth);
console.log(`生命值恢复:${health.currentHealth}/${health.maxHealth}`);
// 满血时停止恢复
if (health.currentHealth >= health.maxHealth) {
this.stopRegeneration();
}
}
});
}
stopRegeneration() {
if (this.regenTimer) {
this.regenTimer.stop();
this.regenTimer = null;
}
}
}
```
### 3. 获取定时器引用进行控制
```typescript
import { ITimer } from '@esengine/ecs-framework';
class BombTimer {
private bombTimer: ITimer;
private explosionTime: number = 5.0;
startBomb(position: { x: number; y: number }) {
console.log("炸弹已放置5秒后爆炸...");
// 创建定时器并保存引用
this.bombTimer = Core.schedule(this.explosionTime, false, this, () => {
this.explode(position);
});
}
defuseBomb() {
if (this.bombTimer && !this.bombTimer.isDone) {
// 拆除炸弹
this.bombTimer.stop();
console.log("炸弹已被拆除!");
}
}
getRemainingTime(): number {
if (this.bombTimer && !this.bombTimer.isDone) {
return this.explosionTime - this.bombTimer.elapsedTime;
}
return 0;
}
private explode(position: { x: number; y: number }) {
console.log("💥 炸弹爆炸了!");
// 爆炸效果逻辑...
}
}
```
## 高级定时器功能
### 1. 定时器链 - 顺序执行多个任务
```typescript
class CutsceneManager {
playCutscene() {
// 第一个镜头2秒
Core.schedule(2.0, false, this, () => {
this.showScene("开场镜头");
// 第二个镜头3秒后
Core.schedule(3.0, false, this, () => {
this.showScene("角色登场");
// 第三个镜头2秒后
Core.schedule(2.0, false, this, () => {
this.showScene("背景介绍");
// 结束1秒后
Core.schedule(1.0, false, this, () => {
this.endCutscene();
});
});
});
});
}
private showScene(sceneName: string) {
console.log(`播放场景: ${sceneName}`);
}
private endCutscene() {
console.log("过场动画结束,开始游戏!");
}
}
// 更优雅的链式写法
class ImprovedCutsceneManager {
playCutscene() {
this.scheduleSequence([
{ delay: 2.0, action: () => this.showScene("开场镜头") },
{ delay: 3.0, action: () => this.showScene("角色登场") },
{ delay: 2.0, action: () => this.showScene("背景介绍") },
{ delay: 1.0, action: () => this.endCutscene() }
]);
}
private scheduleSequence(sequence: Array<{delay: number, action: () => void}>) {
let currentDelay = 0;
sequence.forEach(step => {
currentDelay += step.delay;
Core.schedule(currentDelay, false, this, step.action);
});
}
}
```
### 2. 条件定时器 - 满足条件时执行
```typescript
class ConditionalTimer {
waitForCondition(
condition: () => boolean,
action: () => void,
checkInterval: number = 0.1,
timeout: number = 10.0
) {
let timeElapsed = 0;
const checkTimer = Core.schedule(checkInterval, true, this, () => {
timeElapsed += checkInterval;
if (condition()) {
// 条件满足,执行动作并停止检查
checkTimer.stop();
action();
} else if (timeElapsed >= timeout) {
// 超时,停止检查
checkTimer.stop();
console.log("等待条件超时");
}
});
}
}
// 使用例子
class WaitForPlayerExample {
waitForPlayerToReachGoal() {
const player = this.getPlayer();
const goalPosition = { x: 500, y: 300 };
this.waitForCondition(
// 条件:玩家到达目标位置
() => {
const playerPos = player.getComponent(PositionComponent);
return playerPos.distanceTo(goalPosition) < 50;
},
// 动作:触发下一关
() => {
console.log("玩家到达目标!开始下一关");
this.loadNextLevel();
},
0.1, // 每0.1秒检查一次
30.0 // 30秒后超时
);
}
}
```
### 3. 可暂停的定时器
```typescript
class PausableTimer {
private timers: ITimer[] = [];
private isPaused: boolean = false;
schedule(delay: number, repeat: boolean, callback: () => void): ITimer {
const timer = Core.schedule(delay, repeat, this, callback);
this.timers.push(timer);
return timer;
}
pauseAll() {
this.isPaused = true;
this.timers.forEach(timer => {
if (!timer.isDone) {
timer.stop();
}
});
}
resumeAll() {
if (!this.isPaused) return;
this.isPaused = false;
// 重新启动所有未完成的定时器
// 注意:这是简化实现,实际需要保存剩余时间
this.timers = this.timers.filter(timer => !timer.isDone);
}
clearAll() {
this.timers.forEach(timer => timer.stop());
this.timers = [];
}
}
// 游戏暂停系统
class GamePauseSystem {
private gameTimers: PausableTimer = new PausableTimer();
private isGamePaused: boolean = false;
pauseGame() {
if (this.isGamePaused) return;
this.isGamePaused = true;
this.gameTimers.pauseAll();
// 显示暂停菜单
this.showPauseMenu();
}
resumeGame() {
if (!this.isGamePaused) return;
this.isGamePaused = false;
this.gameTimers.resumeAll();
// 隐藏暂停菜单
this.hidePauseMenu();
}
scheduleGameTimer(delay: number, repeat: boolean, callback: () => void) {
return this.gameTimers.schedule(delay, repeat, callback);
}
}
```
## 实际游戏应用示例
### 1. Buff/Debuff 系统
```typescript
class BuffSystem {
applyBuff(entity: Entity, buffType: string, duration: number) {
const buff = new BuffComponent(buffType, duration);
entity.addComponent(buff);
// 应用Buff效果
this.applyBuffEffect(entity, buffType);
// 设置定时器移除Buff
Core.schedule(duration, false, this, () => {
if (!entity.isDestroyed && entity.hasComponent(BuffComponent)) {
this.removeBuff(entity, buffType);
}
});
console.log(`应用了 ${buffType} Buff持续时间 ${duration}`);
}
private applyBuffEffect(entity: Entity, buffType: string) {
const stats = entity.getComponent(StatsComponent);
switch (buffType) {
case 'speed_boost':
stats.moveSpeed *= 1.5;
break;
case 'damage_boost':
stats.damage *= 2.0;
break;
case 'invincible':
entity.addComponent(new InvincibleComponent());
break;
}
}
private removeBuff(entity: Entity, buffType: string) {
const buff = entity.getComponent(BuffComponent);
if (buff && buff.buffType === buffType) {
entity.removeComponent(buff);
this.removeBuffEffect(entity, buffType);
console.log(`${buffType} Buff 已过期`);
}
}
}
```
### 2. 技能冷却系统
```typescript
class SkillSystem {
private cooldowns: Map<string, number> = new Map();
useSkill(player: Entity, skillName: string): boolean {
// 检查冷却
if (this.isOnCooldown(skillName)) {
const remainingTime = this.getCooldownRemaining(skillName);
console.log(`技能冷却中,还需 ${remainingTime.toFixed(1)}`);
return false;
}
// 执行技能
this.executeSkill(player, skillName);
// 启动冷却
const cooldownTime = this.getSkillCooldown(skillName);
this.startCooldown(skillName, cooldownTime);
return true;
}
private startCooldown(skillName: string, duration: number) {
const endTime = Time.totalTime + duration;
this.cooldowns.set(skillName, endTime);
// 设置定时器清理冷却
Core.schedule(duration, false, this, () => {
this.cooldowns.delete(skillName);
console.log(`技能 ${skillName} 冷却完成!`);
});
}
private isOnCooldown(skillName: string): boolean {
const endTime = this.cooldowns.get(skillName);
return endTime !== undefined && Time.totalTime < endTime;
}
private getCooldownRemaining(skillName: string): number {
const endTime = this.cooldowns.get(skillName);
return endTime ? Math.max(0, endTime - Time.totalTime) : 0;
}
private executeSkill(player: Entity, skillName: string) {
switch (skillName) {
case 'fireball':
this.castFireball(player);
break;
case 'heal':
this.castHeal(player);
break;
case 'dash':
this.performDash(player);
break;
}
}
private getSkillCooldown(skillName: string): number {
const cooldowns = {
'fireball': 3.0,
'heal': 10.0,
'dash': 5.0
};
return cooldowns[skillName] || 1.0;
}
}
```
### 3. 关卡时间限制
```typescript
class LevelTimer {
private timeLimit: number;
private timeRemaining: number;
private timerActive: boolean = false;
private updateTimer: ITimer;
startLevel(timeLimitSeconds: number) {
this.timeLimit = timeLimitSeconds;
this.timeRemaining = timeLimitSeconds;
this.timerActive = true;
// 每秒更新倒计时
this.updateTimer = Core.schedule(1.0, true, this, () => {
this.updateCountdown();
});
console.log(`关卡开始!时间限制:${timeLimitSeconds}`);
}
private updateCountdown() {
if (!this.timerActive) return;
this.timeRemaining--;
// 更新UI显示
this.updateTimerUI(this.timeRemaining);
// 时间警告
if (this.timeRemaining === 30) {
console.log("⚠️ 警告还剩30秒");
this.playWarningSound();
} else if (this.timeRemaining === 10) {
console.log("🚨 紧急还剩10秒");
this.playUrgentSound();
}
// 时间到
if (this.timeRemaining <= 0) {
this.timeUp();
}
}
private timeUp() {
this.timerActive = false;
this.updateTimer.stop();
console.log("⏰ 时间到!游戏结束");
// 触发游戏结束
Core.emitter.emit('level:timeout');
}
completeLevel() {
if (this.timerActive) {
this.timerActive = false;
this.updateTimer.stop();
const completionTime = this.timeLimit - this.timeRemaining;
console.log(`🎉 关卡完成!用时:${completionTime}`);
// 根据剩余时间给予奖励
this.calculateTimeBonus(this.timeRemaining);
}
}
private calculateTimeBonus(timeLeft: number) {
const bonus = Math.floor(timeLeft * 10); // 每秒剩余10分
if (bonus > 0) {
console.log(`时间奖励:${bonus}`);
Core.emitter.emit('score:time_bonus', { bonus });
}
}
getTimeRemaining(): number {
return this.timeRemaining;
}
getTimeRemainingFormatted(): string {
const minutes = Math.floor(this.timeRemaining / 60);
const seconds = this.timeRemaining % 60;
return `${minutes}:${seconds.toString().padStart(2, '0')}`;
}
}
```
## 定时器性能优化
### 1. 定时器池化
```typescript
class TimerPool {
private static instance: TimerPool;
private timerPool: ITimer[] = [];
static getInstance(): TimerPool {
if (!this.instance) {
this.instance = new TimerPool();
}
return this.instance;
}
getTimer(): ITimer {
return this.timerPool.pop() || this.createTimer();
}
releaseTimer(timer: ITimer) {
timer.stop();
this.timerPool.push(timer);
}
private createTimer(): ITimer {
// 创建新定时器的逻辑
return new Timer();
}
}
```
### 2. 批量定时器管理
```typescript
class BatchTimerManager {
private timers: Set<ITimer> = new Set();
scheduleMany(configs: Array<{delay: number, repeat: boolean, callback: () => void}>) {
return configs.map(config => {
const timer = Core.schedule(config.delay, config.repeat, this, config.callback);
this.timers.add(timer);
return timer;
});
}
stopAll() {
this.timers.forEach(timer => timer.stop());
this.timers.clear();
}
cleanup() {
// 清理已完成的定时器
this.timers.forEach(timer => {
if (timer.isDone) {
this.timers.delete(timer);
}
});
}
}
```
## 常见问题和最佳实践
### Q: 定时器会自动清理吗?
A: 是的,完成的定时器会自动清理。但如果需要提前停止,记得调用 `timer.stop()`
### Q: 定时器会受到游戏暂停影响吗?
A: 定时器使用游戏时间,如果实现了时间缩放功能,定时器会相应调整。
### Q: 如何实现精确的帧同步定时器?
A: 使用帧计数而不是时间:
```typescript
class FrameTimer {
private frameCount: number = 0;
private targetFrame: number;
scheduleFrames(frames: number, callback: () => void) {
this.targetFrame = this.frameCount + frames;
const checkFrame = () => {
this.frameCount++;
if (this.frameCount >= this.targetFrame) {
callback();
} else {
requestAnimationFrame(checkFrame);
}
};
requestAnimationFrame(checkFrame);
}
}
```
### Q: 如何避免定时器内存泄漏?
A:
1. 及时停止不需要的定时器
2. 在对象销毁时清理所有定时器
3. 使用弱引用避免循环引用
```typescript
class SafeTimerUser {
private timers: ITimer[] = [];
scheduleTimer(delay: number, callback: () => void) {
const timer = Core.schedule(delay, false, this, callback);
this.timers.push(timer);
return timer;
}
destroy() {
// 清理所有定时器
this.timers.forEach(timer => timer.stop());
this.timers = [];
}
}
```
定时器是游戏开发中非常有用的工具,合理使用可以让你的游戏逻辑更加优雅和高效!

600
docs/use-cases.md Normal file
View File

@@ -0,0 +1,600 @@
# ECS框架使用场景示例
本文档展示ECS框架在不同类型游戏中的具体应用案例。
## 目录
1. [小型休闲游戏](#小型休闲游戏)
2. [中型动作游戏](#中型动作游戏)
3. [大型策略游戏](#大型策略游戏)
4. [MMO游戏](#mmo游戏)
## 小型休闲游戏
### 场景:简单的飞机大战游戏
```typescript
import {
Scene,
EntityManager,
Entity,
Component,
EntitySystem,
Matcher
} from '@esengine/ecs-framework';
// 组件定义
class PositionComponent extends Component {
constructor(public x: number = 0, public y: number = 0) {
super();
}
}
class VelocityComponent extends Component {
constructor(public x: number = 0, public y: number = 0) {
super();
}
}
class PlayerComponent extends Component {}
class EnemyComponent extends Component {}
class BulletComponent extends Component {}
// 游戏管理器
class PlaneWarGame {
private scene: Scene;
private entityManager: EntityManager;
constructor() {
this.scene = new Scene();
this.entityManager = new EntityManager();
this.setupGame();
}
private setupGame(): void {
// 创建玩家
const player = this.entityManager.createEntity("Player");
player.addComponent(new PositionComponent(400, 500));
player.addComponent(new VelocityComponent(0, 0));
player.addComponent(new PlayerComponent());
player.tag = 1; // 玩家标签
// 创建敌人
this.spawnEnemies(5);
// 添加系统
this.scene.addEntityProcessor(new MovementSystem());
this.scene.addEntityProcessor(new CollisionSystem());
this.scene.addEntityProcessor(new CleanupSystem());
}
private spawnEnemies(count: number): void {
const enemies = this.scene.createEntities(count, "Enemy");
enemies.forEach((enemy, index) => {
enemy.addComponent(new PositionComponent(
Math.random() * 800,
-50
));
enemy.addComponent(new VelocityComponent(0, 100));
enemy.addComponent(new EnemyComponent());
enemy.tag = 2; // 敌人标签
});
}
public update(): void {
this.scene.update();
}
}
// 移动系统
class MovementSystem extends EntitySystem {
constructor() {
super(Matcher.empty().all(PositionComponent, VelocityComponent));
}
protected process(entities: Entity[]): void {
const movingEntities = this.scene.querySystem.queryAll(
PositionComponent,
VelocityComponent
);
movingEntities.entities.forEach(entity => {
const pos = entity.getComponent(PositionComponent);
const vel = entity.getComponent(VelocityComponent);
pos.x += vel.x * Time.deltaTime;
pos.y += vel.y * Time.deltaTime;
});
}
}
## 中型动作游戏
### 场景2D平台跳跃游戏
```typescript
// 更复杂的组件
class HealthComponent extends Component {
constructor(
public maxHealth: number = 100,
public currentHealth: number = 100
) {
super();
}
}
class AnimationComponent extends Component {
constructor(
public currentAnimation: string = "idle",
public frameIndex: number = 0,
public frameTime: number = 0
) {
super();
}
}
class PhysicsComponent extends Component {
constructor(
public mass: number = 1,
public friction: number = 0.8,
public isGrounded: boolean = false
) {
super();
}
}
// 平台游戏管理器
class PlatformGame {
private scene: Scene;
private entityManager: EntityManager;
constructor() {
this.scene = new Scene();
this.entityManager = new EntityManager();
this.setupGame();
}
private setupGame(): void {
// 创建玩家
this.createPlayer();
// 创建敌人
this.createEnemies(10);
// 创建平台
this.createPlatforms();
// 添加系统(按更新顺序)
this.scene.addEntityProcessor(new InputSystem()).updateOrder = 10;
this.scene.addEntityProcessor(new PhysicsSystem()).updateOrder = 20;
this.scene.addEntityProcessor(new AnimationSystem()).updateOrder = 30;
this.scene.addEntityProcessor(new CombatSystem()).updateOrder = 40;
this.scene.addEntityProcessor(new RenderSystem()).updateOrder = 50;
}
private createPlayer(): void {
const player = this.entityManager.createEntity("Player");
player.addComponent(new PositionComponent(100, 300));
player.addComponent(new VelocityComponent(0, 0));
player.addComponent(new HealthComponent(100));
player.addComponent(new AnimationComponent("idle"));
player.addComponent(new PhysicsComponent(1, 0.8));
player.tag = 1;
}
private createEnemies(count: number): void {
const enemies = this.scene.createEntities(count, "Enemy");
enemies.forEach((enemy, index) => {
enemy.addComponent(new PositionComponent(
200 + index * 100,
300
));
enemy.addComponent(new VelocityComponent(0, 0));
enemy.addComponent(new HealthComponent(50));
enemy.addComponent(new AnimationComponent("patrol"));
enemy.addComponent(new PhysicsComponent(0.8, 0.9));
enemy.tag = 2;
});
}
private createPlatforms(): void {
const platforms = this.scene.createEntities(5, "Platform");
platforms.forEach((platform, index) => {
platform.addComponent(new PositionComponent(
index * 200,
400 + Math.random() * 100
));
platform.tag = 3; // 平台标签
});
}
}
## 大型策略游戏
### 场景:即时战略游戏
```typescript
// 策略游戏组件
class UnitComponent extends Component {
constructor(
public unitType: string,
public playerId: number,
public level: number = 1
) {
super();
}
}
class AIComponent extends Component {
constructor(
public state: string = "idle",
public target: Entity | null = null,
public lastDecisionTime: number = 0
) {
super();
}
}
class ResourceComponent extends Component {
constructor(
public gold: number = 0,
public wood: number = 0,
public food: number = 0
) {
super();
}
}
// 策略游戏管理器
class StrategyGame {
private scene: Scene;
private entityManager: EntityManager;
private players: Map<number, Entity> = new Map();
constructor() {
this.scene = new Scene();
this.entityManager = new EntityManager();
this.setupGame();
}
private setupGame(): void {
// 创建玩家
this.createPlayers(4);
// 为每个玩家创建初始单位
this.players.forEach((player, playerId) => {
this.createInitialUnits(playerId, 10);
});
// 添加系统
this.scene.addEntityProcessor(new AISystem()).updateOrder = 10;
this.scene.addEntityProcessor(new CombatSystem()).updateOrder = 20;
this.scene.addEntityProcessor(new ResourceSystem()).updateOrder = 30;
this.scene.addEntityProcessor(new UnitManagementSystem()).updateOrder = 40;
}
private createPlayers(count: number): void {
for (let i = 0; i < count; i++) {
const player = this.entityManager.createEntity(`Player_${i}`);
player.addComponent(new ResourceComponent(1000, 500, 100));
player.tag = 10 + i; // 玩家标签从10开始
this.players.set(i, player);
}
}
private createInitialUnits(playerId: number, count: number): void {
const units = this.scene.createEntities(count, `Unit_${playerId}`);
units.forEach((unit, index) => {
unit.addComponent(new PositionComponent(
playerId * 200 + Math.random() * 100,
playerId * 200 + Math.random() * 100
));
unit.addComponent(new UnitComponent("warrior", playerId));
unit.addComponent(new HealthComponent(100));
unit.addComponent(new AIComponent());
unit.tag = 20 + playerId; // 单位标签
});
}
// 批量单位操作
public createArmy(playerId: number, unitType: string, count: number): Entity[] {
const units = this.scene.createEntities(count, `${unitType}_${playerId}`);
// 批量配置组件
units.forEach(unit => {
unit.addComponent(new UnitComponent(unitType, playerId));
unit.addComponent(new HealthComponent(100));
unit.addComponent(new PositionComponent(
Math.random() * 1000,
Math.random() * 1000
));
unit.tag = 20 + playerId;
});
return units;
}
// 查询玩家的所有单位
public getPlayerUnits(playerId: number): Entity[] {
return this.entityManager
.query()
.withAll(UnitComponent)
.withTag(20 + playerId)
.execute();
}
// 查询特定类型的单位
public getUnitsByType(unitType: string): Entity[] {
return this.entityManager
.query()
.withAll(UnitComponent)
.where(entity => {
const unit = entity.getComponent(UnitComponent);
return unit && unit.unitType === unitType;
})
.execute();
}
}
// AI系统
class AISystem extends EntitySystem {
constructor() {
super(Matcher.empty().all(AIComponent, UnitComponent));
}
protected process(entities: Entity[]): void {
const aiUnits = this.entityManager
.query()
.withAll(AIComponent, UnitComponent)
.execute();
aiUnits.forEach(unit => {
this.processAI(unit);
});
}
private processAI(unit: Entity): void {
const ai = unit.getComponent(AIComponent);
const unitComp = unit.getComponent(UnitComponent);
if (!ai || !unitComp) return;
// 简单AI逻辑
switch (ai.state) {
case "idle":
this.findTarget(unit);
break;
case "attack":
this.attackTarget(unit);
break;
case "move":
this.moveToTarget(unit);
break;
}
}
private findTarget(unit: Entity): void {
const unitComp = unit.getComponent(UnitComponent);
if (!unitComp) return;
// 查找敌方单位
const enemies = this.entityManager
.query()
.withAll(UnitComponent)
.where(entity => {
const enemyUnit = entity.getComponent(UnitComponent);
return enemyUnit && enemyUnit.playerId !== unitComp.playerId;
})
.execute();
if (enemies.length > 0) {
const ai = unit.getComponent(AIComponent);
if (ai) {
ai.target = enemies[0];
ai.state = "attack";
}
}
}
private attackTarget(unit: Entity): void {
// 攻击逻辑
}
private moveToTarget(unit: Entity): void {
// 移动逻辑
}
}
## MMO游戏
### 场景:大型多人在线游戏
```typescript
// MMO特有组件
class NetworkComponent extends Component {
constructor(
public playerId: string,
public isLocal: boolean = false,
public lastSyncTime: number = 0
) {
super();
}
}
class InventoryComponent extends Component {
public items: Map<string, number> = new Map();
addItem(itemId: string, count: number): void {
const current = this.items.get(itemId) || 0;
this.items.set(itemId, current + count);
}
}
class GuildComponent extends Component {
constructor(
public guildId: string,
public rank: string = "member"
) {
super();
}
}
// MMO游戏管理器
class MMOGame {
private scene: Scene;
private entityManager: EntityManager;
private localPlayerId: string;
constructor(localPlayerId: string) {
this.scene = new Scene();
this.entityManager = new EntityManager();
this.localPlayerId = localPlayerId;
this.setupGame();
}
private setupGame(): void {
// 添加MMO特有系统
this.scene.addEntityProcessor(new NetworkSyncSystem()).updateOrder = 5;
this.scene.addEntityProcessor(new PlayerSystem()).updateOrder = 10;
this.scene.addEntityProcessor(new NPCSystem()).updateOrder = 15;
this.scene.addEntityProcessor(new GuildSystem()).updateOrder = 20;
this.scene.addEntityProcessor(new InventorySystem()).updateOrder = 25;
}
// 创建玩家角色
public createPlayer(playerId: string, isLocal: boolean = false): Entity {
const player = this.entityManager.createEntity(`Player_${playerId}`);
player.addComponent(new PositionComponent(0, 0));
player.addComponent(new HealthComponent(1000));
player.addComponent(new NetworkComponent(playerId, isLocal));
player.addComponent(new InventoryComponent());
player.tag = isLocal ? 1 : 2; // 本地玩家标签1远程玩家标签2
return player;
}
// 批量创建NPC
public createNPCs(count: number): Entity[] {
const npcs = this.scene.createEntities(count, "NPC");
npcs.forEach((npc, index) => {
npc.addComponent(new PositionComponent(
Math.random() * 2000,
Math.random() * 2000
));
npc.addComponent(new HealthComponent(500));
npc.addComponent(new AIComponent("patrol"));
npc.tag = 3; // NPC标签
});
return npcs;
}
// 查询附近的玩家
public getNearbyPlayers(centerX: number, centerY: number, radius: number): Entity[] {
return this.entityManager
.query()
.withAll(PositionComponent, NetworkComponent)
.where(entity => {
const pos = entity.getComponent(PositionComponent);
if (!pos) return false;
const distance = Math.sqrt(
Math.pow(pos.x - centerX, 2) +
Math.pow(pos.y - centerY, 2)
);
return distance <= radius;
})
.execute();
}
// 查询公会成员
public getGuildMembers(guildId: string): Entity[] {
return this.entityManager
.query()
.withAll(GuildComponent, NetworkComponent)
.where(entity => {
const guild = entity.getComponent(GuildComponent);
return guild && guild.guildId === guildId;
})
.execute();
}
// 获取在线玩家统计
public getOnlinePlayerStats(): any {
const allPlayers = this.entityManager.getEntitiesWithComponent(NetworkComponent);
const localPlayers = this.entityManager.getEntitiesByTag(1);
const remotePlayers = this.entityManager.getEntitiesByTag(2);
return {
total: allPlayers.length,
local: localPlayers.length,
remote: remotePlayers.length
};
}
}
// 网络同步系统
class NetworkSyncSystem extends EntitySystem {
constructor() {
super(Matcher.empty().all(NetworkComponent));
}
protected process(entities: Entity[]): void {
const networkEntities = this.entityManager.getEntitiesWithComponent(NetworkComponent);
networkEntities.forEach(entity => {
const network = entity.getComponent(NetworkComponent);
if (!network || network.isLocal) return;
// 同步远程实体数据
this.syncRemoteEntity(entity);
});
}
private syncRemoteEntity(entity: Entity): void {
// 网络同步逻辑
const network = entity.getComponent(NetworkComponent);
if (!network) return;
const currentTime = Date.now();
if (currentTime - network.lastSyncTime > 100) { // 100ms同步一次
// 发送同步数据
network.lastSyncTime = currentTime;
}
}
}
## 性能优化建议
### 小型游戏(< 1000实体
- 使用简单的查询方法
- 不需要复杂的优化
- 重点关注代码可读性
### 中型游戏1000-10000实体
- 使用标签查询优化性能
- 实现基础的对象池
- 缓存频繁查询的结果
### 大型游戏10000-100000实体
- 使用时间分片处理大量实体
- 实现空间分区优化邻近查询
- 使用批量操作减少单次调用开销
### MMO游戏100000+实体)
- 实现分区管理,只处理相关区域的实体
- 使用异步处理避免阻塞主线程
- 实现智能缓存和预加载机制
## 总结
ECS框架的灵活性使其能够适应各种规模的游戏开发需求
1. **小型游戏**:简单直接,快速开发
2. **中型游戏**:平衡性能和复杂度
3. **大型游戏**:充分利用优化特性
4. **MMO游戏**:处理海量实体和复杂交互
选择合适的架构模式和优化策略可以让ECS框架在不同场景下都发挥最佳性能。

View File

@@ -181,8 +181,8 @@ class ComponentCache {
* // 获取组件
* const health = entity.getComponent(HealthComponent);
*
* // 设置位置
* entity.position = new Vector2(100, 200);
* // 添加位置组件
* entity.addComponent(new PositionComponent(100, 200));
*
* // 添加子实体
* const weapon = new Entity("Weapon", 2);

View File

@@ -6,7 +6,6 @@ import { EntitySystem } from './Systems/EntitySystem';
import { ComponentStorageManager } from './Core/ComponentStorage';
import { QuerySystem } from './Core/QuerySystem';
import { TypeSafeEventSystem, GlobalEventSystem } from './Core/EventSystem';
import type { IScene } from '../Types';
/**
* 游戏场景类

View File

@@ -2,27 +2,6 @@
* 框架核心类型定义
*/
/** 更新顺序比较器接口 */
export interface IUpdateOrderComparable {
updateOrder: number;
}
/** 日志类型枚举 */
export enum LogType {
Error = 0,
Assert = 1,
Warning = 2,
Log = 3,
Exception = 4
}
/** 组件变换类型枚举 */
export enum ComponentTransform {
Position = 1,
Scale = 2,
Rotation = 4
}
/**
* 组件接口
*
@@ -51,136 +30,14 @@ export interface IComponent {
}
/**
* 实体接口
* 系统基础接口
*
* 定义实体的基本契约,所有实体都应该实现此接口
*/
export interface IEntity {
/** 实体唯一标识符 */
readonly id: string | number;
/** 实体名称 */
name: string;
/** 实体激活状态 */
active: boolean;
/** 实体启用状态 */
enabled: boolean;
/** 实体是否已销毁 */
readonly isDestroyed: boolean;
/** 更新顺序 */
updateOrder: number;
/** 实体标签 */
tag: number;
/** 添加组件 */
addComponent<T extends IComponent>(component: T): T;
/** 创建并添加组件 */
createComponent<T extends IComponent>(componentType: ComponentType<T>, ...args: any[]): T;
/** 获取组件 */
getComponent<T extends IComponent>(type: ComponentType<T>): T | null;
/** 获取或创建组件 */
getOrCreateComponent<T extends IComponent>(type: ComponentType<T>, ...args: any[]): T;
/** 移除组件 */
removeComponent(component: IComponent): void;
/** 通过类型移除组件 */
removeComponentByType<T extends IComponent>(type: ComponentType<T>): T | null;
/** 检查是否有组件 */
hasComponent<T extends IComponent>(type: ComponentType<T>): boolean;
/** 获取所有指定类型的组件 */
getComponents<T extends IComponent>(type: ComponentType<T>): T[];
/** 添加子实体 */
addChild(child: IEntity): IEntity;
/** 移除子实体 */
removeChild(child: IEntity): boolean;
/** 查找子实体 */
findChild(name: string, recursive?: boolean): IEntity | null;
/** 更新实体 */
update(): void;
/** 销毁实体 */
destroy(): void;
}
/**
* 实体基础接口(向后兼容)
*
* 为现有的Entity类提供更灵活的类型定义
*/
export interface IEntityBase {
/** 实体唯一标识符 */
readonly id: string | number;
/** 实体名称 */
name: string;
/** 实体激活状态 */
active: boolean;
/** 实体启用状态 */
enabled: boolean;
/** 实体是否已销毁 */
readonly isDestroyed: boolean;
/** 更新顺序 */
updateOrder: number;
/** 实体标签 */
tag: number;
/** 添加组件(泛型版本) */
addComponent<T>(component: T): T;
/** 获取组件(泛型版本) */
getComponent<T>(type: ComponentClass<T>): T | null;
/** 检查是否有组件(泛型版本) */
hasComponent<T>(type: ComponentClass<T>): boolean;
/** 添加子实体 */
addChild(child: any): any;
/** 移除子实体 */
removeChild(child: any): boolean;
/** 查找子实体 */
findChild(name: string, recursive?: boolean): any;
/** 更新实体 */
update(): void;
/** 销毁实体 */
destroy(): void;
}
/**
* 系统接口
*
* 定义系统的基本契约,所有系统都应该实现此接口
*/
export interface ISystem {
/** 系统名称 */
readonly systemName: string;
/** 系统处理的实体列表 */
readonly entities: readonly IEntity[];
/** 更新顺序/优先级 */
updateOrder: number;
/** 系统启用状态 */
enabled: boolean;
/** 系统初始化 */
initialize(): void;
/** 更新系统(主要处理阶段) */
update(): void;
/** 延迟更新系统 */
lateUpdate?(): void;
/** 当实体添加到系统时的回调 */
onEntityAdded?(entity: IEntity): void;
/** 当实体从系统移除时的回调 */
onEntityRemoved?(entity: IEntity): void;
/** 当实体组件发生变化时的回调 */
onEntityChanged?(entity: IEntity): void;
}
/**
* 系统基础接口(向后兼容)
*
* 为现有的EntitySystem类提供更灵活的类型定义
* 为现有的EntitySystem类提供类型定义
*/
export interface ISystemBase {
/** 系统名称 */
readonly systemName: string;
/** 系统处理的实体列表(泛型版本) */
/** 系统处理的实体列表 */
readonly entities: readonly any[];
/** 更新顺序/优先级 */
updateOrder: number;
@@ -195,39 +52,6 @@ export interface ISystemBase {
lateUpdate?(): void;
}
/**
* 场景接口
*
* 定义场景的基本契约
*/
export interface IScene {
/** 场景名称 */
name: string;
/** 场景中的实体列表 */
readonly entities: readonly IEntity[];
/** 场景中的系统列表 */
readonly systems: readonly ISystem[];
/** 创建实体 */
createEntity(name: string): IEntity;
/** 添加实体到场景 */
addEntity(entity: IEntity): void;
/** 从场景移除实体 */
removeEntity(entity: IEntity): void;
/** 查找实体 */
findEntity(name: string): IEntity | null;
/** 添加系统到场景 */
addSystem<T extends ISystem>(system: T): T;
/** 从场景移除系统 */
removeSystem(system: ISystem): void;
/** 获取系统 */
getSystem<T extends ISystem>(systemType: new (...args: any[]) => T): T | null;
/** 更新场景 */
update(): void;
}
/**
* 组件类型定义
*
@@ -235,170 +59,6 @@ export interface IScene {
*/
export type ComponentType<T extends IComponent = IComponent> = new (...args: any[]) => T;
/**
* 原始组件类型(向后兼容)
*
* 用于与现有Component类的兼容
*/
export type ComponentClass<T = any> = new (...args: any[]) => T;
/**
* 实体查询匹配器接口
*
* 用于查询符合特定条件的实体
*/
export interface IMatcher {
/** 必须包含的组件类型 */
all(...componentTypes: ComponentType[]): IMatcher;
/** 至少包含其中一个组件类型 */
any(...componentTypes: ComponentType[]): IMatcher;
/** 不能包含的组件类型 */
exclude(...componentTypes: ComponentType[]): IMatcher;
/** 检查实体是否匹配 */
isInterestedEntity(entity: IEntity): boolean;
}
/**
* 性能监控接口
*/
export interface IPerformanceData {
/** 执行时间(毫秒) */
executionTime: number;
/** 调用次数 */
callCount: number;
/** 平均执行时间 */
averageTime: number;
/** 最大执行时间 */
maxTime: number;
/** 最小执行时间 */
minTime: number;
}
/**
* 生命周期管理接口
*/
export interface ILifecycle {
/** 初始化 */
initialize?(): void;
/** 启动 */
start?(): void;
/** 更新 */
update?(): void;
/** 延迟更新 */
lateUpdate?(): void;
/** 停止 */
stop?(): void;
/** 销毁 */
destroy?(): void;
}
/**
* 实体管理器接口
*
* 提供统一的实体管理和查询机制,支持高效的实体操作
*/
export interface IEntityManager {
/** 所有实体数量 */
readonly entityCount: number;
/** 激活的实体数量 */
readonly activeEntityCount: number;
/** 创建实体 */
createEntity(name?: string): IEntity;
/** 销毁实体 */
destroyEntity(entity: IEntity | string | number): boolean;
/** 批量销毁实体 */
destroyEntities(entities: (IEntity | string | number)[]): number;
/** 销毁所有实体 */
destroyAllEntities(): number;
/** 根据ID获取实体 */
getEntity(id: string | number): IEntity | null;
/** 根据名称获取实体 */
getEntityByName(name: string): IEntity | null;
/** 根据名称获取所有实体 */
getEntitiesByName(name: string): IEntity[];
/** 根据标签获取实体 */
getEntitiesByTag(tag: number): IEntity[];
/** 获取所有实体 */
getAllEntities(): IEntity[];
/** 获取所有激活的实体 */
getActiveEntities(): IEntity[];
/** 获取拥有指定组件的实体 */
getEntitiesWithComponent<T extends IComponent>(componentType: ComponentType<T>): IEntity[];
/** 获取拥有指定组件的实体及其组件 */
getEntitiesWithComponentData<T extends IComponent>(componentType: ComponentType<T>): Array<{entity: IEntity, component: T}>;
/** 获取拥有所有指定组件的实体 */
getEntitiesWithComponents(...componentTypes: ComponentType[]): IEntity[];
/** 获取拥有任一指定组件的实体 */
getEntitiesWithAnyComponent(...componentTypes: ComponentType[]): IEntity[];
/** 获取不包含指定组件的实体 */
getEntitiesWithoutComponent<T extends IComponent>(componentType: ComponentType<T>): IEntity[];
/** 对所有实体执行操作 */
forEachEntity(action: (entity: IEntity) => void): void;
/** 对符合条件的实体执行操作 */
forEachEntityWhere(predicate: (entity: IEntity) => boolean, action: (entity: IEntity) => void): void;
/** 对拥有指定组件的实体执行操作 */
forEachEntityWithComponent<T extends IComponent>(
componentType: ComponentType<T>,
action: (entity: IEntity, component: T) => void
): void;
/** 查找第一个符合条件的实体 */
findEntity(predicate: (entity: IEntity) => boolean): IEntity | null;
/** 查找所有符合条件的实体 */
findEntities(predicate: (entity: IEntity) => boolean): IEntity[];
/** 获取统计信息 */
getStatistics(): {
totalEntities: number;
activeEntities: number;
destroyedEntities: number;
entitiesByTag: Map<number, number>;
componentsCount: Map<string, number>;
};
/** 清理已销毁的实体 */
cleanup(): number;
/** 压缩存储空间 */
compact(): void;
}
/**
* 实体查询构建器接口
*
* 提供流式API构建复杂的实体查询条件
*/
export interface IEntityQueryBuilder {
/** 必须包含所有指定组件 */
withAll(...componentTypes: ComponentType[]): IEntityQueryBuilder;
/** 必须包含任一指定组件 */
withAny(...componentTypes: ComponentType[]): IEntityQueryBuilder;
/** 必须不包含指定组件 */
without(...componentTypes: ComponentType[]): IEntityQueryBuilder;
/** 必须包含指定标签 */
withTag(tag: number): IEntityQueryBuilder;
/** 必须不包含指定标签 */
withoutTag(tag: number): IEntityQueryBuilder;
/** 必须处于激活状态 */
active(): IEntityQueryBuilder;
/** 必须处于启用状态 */
enabled(): IEntityQueryBuilder;
/** 自定义过滤条件 */
where(predicate: (entity: IEntity) => boolean): IEntityQueryBuilder;
/** 执行查询,返回所有匹配的实体 */
execute(): IEntity[];
/** 执行查询,返回第一个匹配的实体 */
first(): IEntity | null;
/** 执行查询,返回匹配实体的数量 */
count(): number;
/** 对查询结果执行操作 */
forEach(action: (entity: IEntity) => void): void;
}
/**
* 事件总线接口
* 提供类型安全的事件发布订阅机制

View File

@@ -16,6 +16,20 @@ export class Timer implements ITimer{
return this.context as T;
}
/**
* 定时器是否已完成
*/
public get isDone(): boolean {
return this._isDone;
}
/**
* 定时器已运行的时间
*/
public get elapsedTime(): number {
return this._elapsedTime;
}
public reset(): void {
this._elapsedTime = 0;
}

View File

@@ -1 +0,0 @@