2025-06-07 20:32:43 +08:00
|
|
|
|
# ECS Framework
|
|
|
|
|
|
|
2025-08-06 17:04:02 +08:00
|
|
|
|
[](https://git.io/typing-svg)
|
|
|
|
|
|
|
2025-07-18 14:59:00 +08:00
|
|
|
|
[](https://github.com/esengine/ecs-framework/actions)
|
2025-06-07 20:32:43 +08:00
|
|
|
|
[](https://badge.fury.io/js/%40esengine%2Fecs-framework)
|
2025-08-06 17:04:02 +08:00
|
|
|
|
[](https://www.typescriptlang.org/)
|
2025-06-07 20:32:43 +08:00
|
|
|
|
[](https://opensource.org/licenses/MIT)
|
2025-08-06 17:04:02 +08:00
|
|
|
|
[](https://github.com/esengine/ecs-framework/stargazers)
|
2025-06-07 20:32:43 +08:00
|
|
|
|
|
2025-06-10 13:12:14 +08:00
|
|
|
|
TypeScript ECS (Entity-Component-System) 框架,专为游戏开发设计。
|
2025-06-07 20:32:43 +08:00
|
|
|
|
|
2025-08-11 09:31:44 +08:00
|
|
|
|
## 项目特色
|
2025-06-07 20:32:43 +08:00
|
|
|
|
|
2025-08-06 17:04:02 +08:00
|
|
|
|
<div align="center">
|
|
|
|
|
|
|
|
|
|
|
|
[](https://store.cocos.com/app/detail/7823)
|
|
|
|
|
|
[](https://jq.qq.com/?_wv=1027&k=29w1Nud6)
|
|
|
|
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
## ECS 架构原理
|
2025-06-07 20:32:43 +08:00
|
|
|
|
|
2025-08-06 17:04:02 +08:00
|
|
|
|
<div align="center">
|
|
|
|
|
|
<img src="assets/svg/ecs-architecture.svg" alt="ECS 架构流程动画" />
|
|
|
|
|
|
</div>
|
2025-06-10 13:12:14 +08:00
|
|
|
|
|
2025-08-06 17:04:02 +08:00
|
|
|
|
ECS 是一种基于组合而非继承的软件架构模式:
|
|
|
|
|
|
- **Entity(实体)**: 游戏对象的唯一标识
|
|
|
|
|
|
- **Component(组件)**: 纯数据结构,描述实体属性
|
|
|
|
|
|
- **System(系统)**: 处理具有特定组件的实体
|
|
|
|
|
|
|
|
|
|
|
|
## 特性
|
|
|
|
|
|
|
|
|
|
|
|
- **完整的 TypeScript 支持** - 强类型检查和代码提示
|
|
|
|
|
|
- **高效查询系统** - 流式 API 和智能缓存
|
2025-08-15 12:58:55 +08:00
|
|
|
|
- **性能优化技术** - SparseSet索引、Archetype 系统、脏标记
|
2025-08-06 17:04:02 +08:00
|
|
|
|
- **事件系统** - 类型安全的事件处理
|
|
|
|
|
|
- **调试工具** - 内置性能监控和 [Cocos Creator 可视化调试插件](https://store.cocos.com/app/detail/7823)
|
2025-06-10 13:12:14 +08:00
|
|
|
|
|
|
|
|
|
|
## 安装
|
2025-06-07 20:32:43 +08:00
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
npm install @esengine/ecs-framework
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2025-06-10 13:12:14 +08:00
|
|
|
|
## 快速开始
|
2025-06-07 20:32:43 +08:00
|
|
|
|
|
2025-08-06 17:04:02 +08:00
|
|
|
|
### 1. 基础使用
|
2025-06-07 20:32:43 +08:00
|
|
|
|
|
|
|
|
|
|
```typescript
|
2025-08-14 18:38:09 +08:00
|
|
|
|
import { Core, Scene, Entity, Component, EntitySystem, ECSComponent, ECSSystem, Matcher, Time } from '@esengine/ecs-framework';
|
2025-06-07 20:32:43 +08:00
|
|
|
|
|
2025-08-06 17:04:02 +08:00
|
|
|
|
// 创建核心实例
|
|
|
|
|
|
const core = Core.create({ debug: true });
|
2025-06-10 13:12:14 +08:00
|
|
|
|
const scene = new Scene();
|
2025-08-12 11:08:27 +08:00
|
|
|
|
Core.setScene(scene);
|
2025-06-07 20:32:43 +08:00
|
|
|
|
|
2025-08-06 17:04:02 +08:00
|
|
|
|
// 定义组件
|
2025-08-14 18:44:04 +08:00
|
|
|
|
@ECSComponent('PositionComponent')
|
2025-06-09 13:25:10 +08:00
|
|
|
|
class PositionComponent extends Component {
|
2025-08-11 08:18:18 +08:00
|
|
|
|
public x: number = 0;
|
|
|
|
|
|
public y: number = 0;
|
|
|
|
|
|
|
2025-08-11 09:31:44 +08:00
|
|
|
|
constructor(x: number = 0, y: number = 0) {
|
2025-06-09 13:25:10 +08:00
|
|
|
|
super();
|
2025-08-11 08:18:18 +08:00
|
|
|
|
this.x = x;
|
|
|
|
|
|
this.y = y;
|
2025-06-07 20:32:43 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-06-09 13:25:10 +08:00
|
|
|
|
|
2025-08-14 18:45:24 +08:00
|
|
|
|
@ECSComponent('VelocityComponent')
|
2025-06-09 13:25:10 +08:00
|
|
|
|
class VelocityComponent extends Component {
|
2025-08-11 08:18:18 +08:00
|
|
|
|
public x: number = 0;
|
|
|
|
|
|
public y: number = 0;
|
|
|
|
|
|
|
2025-08-11 09:31:44 +08:00
|
|
|
|
constructor(x: number = 0, y: number = 0) {
|
2025-06-09 13:25:10 +08:00
|
|
|
|
super();
|
2025-08-11 08:18:18 +08:00
|
|
|
|
this.x = x;
|
|
|
|
|
|
this.y = y;
|
2025-06-09 13:25:10 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-08-06 17:04:02 +08:00
|
|
|
|
// 创建实体
|
|
|
|
|
|
const entity = scene.createEntity("Player");
|
|
|
|
|
|
entity.addComponent(new PositionComponent(100, 100));
|
|
|
|
|
|
entity.addComponent(new VelocityComponent(5, 0));
|
2025-07-30 14:14:04 +08:00
|
|
|
|
|
2025-08-06 17:04:02 +08:00
|
|
|
|
// 创建系统
|
2025-08-14 18:44:04 +08:00
|
|
|
|
@ECSSystem('MovementSystem')
|
2025-06-09 13:25:10 +08:00
|
|
|
|
class MovementSystem extends EntitySystem {
|
2025-08-11 08:18:18 +08:00
|
|
|
|
constructor() {
|
|
|
|
|
|
super(Matcher.all(PositionComponent, VelocityComponent));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
protected override process(entities: Entity[]) {
|
2025-06-09 13:25:10 +08:00
|
|
|
|
for (const entity of entities) {
|
2025-08-11 08:18:18 +08:00
|
|
|
|
const position = entity.getComponent(PositionComponent)!;
|
|
|
|
|
|
const velocity = entity.getComponent(VelocityComponent)!;
|
2025-06-09 13:25:10 +08:00
|
|
|
|
|
2025-08-11 08:18:18 +08:00
|
|
|
|
position.x += velocity.x * Time.deltaTime;
|
|
|
|
|
|
position.y += velocity.y * Time.deltaTime;
|
2025-06-09 13:25:10 +08:00
|
|
|
|
}
|
2025-06-07 20:32:43 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-06-08 10:20:51 +08:00
|
|
|
|
|
2025-06-09 13:25:10 +08:00
|
|
|
|
scene.addEntityProcessor(new MovementSystem());
|
2025-06-12 09:42:35 +08:00
|
|
|
|
|
2025-08-06 17:04:02 +08:00
|
|
|
|
// 游戏循环
|
2025-06-12 09:42:35 +08:00
|
|
|
|
Core.update(deltaTime);
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2025-08-14 18:38:09 +08:00
|
|
|
|
### 2. 类型装饰器
|
|
|
|
|
|
|
|
|
|
|
|
在代码压缩混淆后,类名会改变导致框架无法识别组件类型。使用装饰器确保稳定性:
|
|
|
|
|
|
|
|
|
|
|
|
```typescript
|
|
|
|
|
|
import { ECSComponent, ECSSystem } from '@esengine/ecs-framework';
|
|
|
|
|
|
|
|
|
|
|
|
// 组件装饰器
|
2025-08-14 18:44:04 +08:00
|
|
|
|
@ECSComponent('PositionComponent')
|
2025-08-14 18:38:09 +08:00
|
|
|
|
class PositionComponent extends Component {
|
|
|
|
|
|
public x: number = 0;
|
|
|
|
|
|
public y: number = 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-08-14 18:44:04 +08:00
|
|
|
|
@ECSComponent('VelocityComponent')
|
2025-08-14 18:38:09 +08:00
|
|
|
|
class VelocityComponent extends Component {
|
|
|
|
|
|
public x: number = 0;
|
|
|
|
|
|
public y: number = 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 系统装饰器
|
2025-08-14 18:44:04 +08:00
|
|
|
|
@ECSSystem('MovementSystem')
|
2025-08-14 18:38:09 +08:00
|
|
|
|
class MovementSystem extends EntitySystem {
|
|
|
|
|
|
constructor() {
|
|
|
|
|
|
super(Matcher.all(PositionComponent, VelocityComponent));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
protected override process(entities: Entity[]) {
|
|
|
|
|
|
// 处理逻辑
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2025-08-06 17:04:02 +08:00
|
|
|
|
## 高级特性
|
2025-06-12 09:42:35 +08:00
|
|
|
|
|
2025-08-06 17:04:02 +08:00
|
|
|
|
### 查询系统
|
2025-06-08 21:50:50 +08:00
|
|
|
|
|
2025-06-09 13:25:10 +08:00
|
|
|
|
```typescript
|
2025-08-14 18:38:09 +08:00
|
|
|
|
import { Matcher, ECSSystem } from '@esengine/ecs-framework';
|
2025-06-09 13:25:10 +08:00
|
|
|
|
|
2025-08-11 09:31:44 +08:00
|
|
|
|
// 使用Matcher和EntitySystem进行高效查询
|
2025-08-14 18:44:04 +08:00
|
|
|
|
@ECSSystem('QuerySystem')
|
2025-08-11 08:18:18 +08:00
|
|
|
|
class QuerySystem extends EntitySystem {
|
|
|
|
|
|
constructor() {
|
|
|
|
|
|
super(Matcher.all(PositionComponent, VelocityComponent).none(HealthComponent));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
protected override process(entities: Entity[]) {
|
|
|
|
|
|
// 处理匹配的实体
|
|
|
|
|
|
console.log(`Found ${entities.length} entities`);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-06-09 13:25:10 +08:00
|
|
|
|
|
2025-08-11 09:31:44 +08:00
|
|
|
|
// 更复杂的查询条件
|
2025-08-14 18:44:04 +08:00
|
|
|
|
@ECSSystem('CombatSystem')
|
2025-08-11 09:31:44 +08:00
|
|
|
|
class CombatSystem extends EntitySystem {
|
|
|
|
|
|
constructor() {
|
|
|
|
|
|
super(
|
|
|
|
|
|
Matcher
|
|
|
|
|
|
.all(PositionComponent, HealthComponent) // 必须有位置和血量
|
|
|
|
|
|
.any(WeaponComponent, MagicComponent) // 有武器或魔法
|
|
|
|
|
|
.none(DeadComponent) // 不能是死亡状态
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
protected override process(entities: Entity[]) {
|
|
|
|
|
|
// 处理战斗逻辑
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-06-10 13:12:14 +08:00
|
|
|
|
```
|
|
|
|
|
|
|
2025-08-06 17:04:02 +08:00
|
|
|
|
### 事件系统
|
2025-06-10 13:12:14 +08:00
|
|
|
|
|
|
|
|
|
|
```typescript
|
2025-08-11 09:31:44 +08:00
|
|
|
|
import { EventHandler, ECSEventType, IEntityEventData } from '@esengine/ecs-framework';
|
2025-06-10 13:12:14 +08:00
|
|
|
|
|
|
|
|
|
|
class GameSystem {
|
2025-06-09 13:25:10 +08:00
|
|
|
|
@EventHandler(ECSEventType.ENTITY_DESTROYED)
|
2025-08-11 09:31:44 +08:00
|
|
|
|
onEntityDestroyed(data: IEntityEventData) {
|
|
|
|
|
|
console.log('实体销毁:', data.entityName, '实体ID:', data.entityId);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@EventHandler(ECSEventType.ENTITY_CREATED)
|
|
|
|
|
|
onEntityCreated(data: IEntityEventData) {
|
|
|
|
|
|
console.log('实体创建:', data.entityName, '标签:', data.entityTag);
|
2025-06-09 13:25:10 +08:00
|
|
|
|
}
|
2025-06-10 13:12:14 +08:00
|
|
|
|
}
|
2025-06-07 20:32:43 +08:00
|
|
|
|
```
|
|
|
|
|
|
|
2025-08-06 17:04:02 +08:00
|
|
|
|
### SoA 存储优化
|
2025-06-10 13:12:14 +08:00
|
|
|
|
|
2025-08-06 17:04:02 +08:00
|
|
|
|
<div align="center">
|
|
|
|
|
|
<img src="assets/svg/soa-vs-aos.svg" alt="SoA vs AoS 数据结构对比" />
|
|
|
|
|
|
</div>
|
2025-06-10 13:12:14 +08:00
|
|
|
|
|
2025-08-06 17:04:02 +08:00
|
|
|
|
用于大规模实体处理:
|
2025-06-07 20:32:43 +08:00
|
|
|
|
|
|
|
|
|
|
```typescript
|
2025-08-06 17:04:02 +08:00
|
|
|
|
import { EnableSoA, Float32, Int32 } from '@esengine/ecs-framework';
|
2025-06-09 13:25:10 +08:00
|
|
|
|
|
2025-08-06 17:04:02 +08:00
|
|
|
|
@EnableSoA
|
|
|
|
|
|
class OptimizedTransformComponent extends Component {
|
|
|
|
|
|
@Float32 public x: number = 0;
|
|
|
|
|
|
@Float32 public y: number = 0;
|
|
|
|
|
|
@Float32 public rotation: number = 0;
|
|
|
|
|
|
}
|
2025-06-07 20:32:43 +08:00
|
|
|
|
```
|
|
|
|
|
|
|
2025-08-06 17:04:02 +08:00
|
|
|
|
**性能优势**:
|
2025-08-11 09:31:44 +08:00
|
|
|
|
- **缓存友好** - 连续内存访问,缓存命中率提升85%
|
|
|
|
|
|
- **批量处理** - 同类型数据处理速度提升2-3倍
|
|
|
|
|
|
- **热切换** - 开发期AoS便于调试,生产期SoA提升性能
|
|
|
|
|
|
- **自动优化** - `@EnableSoA`装饰器自动转换存储结构
|
2025-06-10 13:12:14 +08:00
|
|
|
|
|
2025-08-06 17:04:02 +08:00
|
|
|
|
## 平台集成
|
2025-06-10 13:12:14 +08:00
|
|
|
|
|
2025-08-06 17:04:02 +08:00
|
|
|
|
### Cocos Creator
|
2025-06-07 20:32:43 +08:00
|
|
|
|
|
|
|
|
|
|
```typescript
|
2025-08-06 17:04:02 +08:00
|
|
|
|
update(deltaTime: number) {
|
|
|
|
|
|
Core.update(deltaTime);
|
|
|
|
|
|
}
|
2025-06-10 13:12:14 +08:00
|
|
|
|
```
|
|
|
|
|
|
|
2025-08-06 17:04:02 +08:00
|
|
|
|
**专用调试插件**:
|
2025-08-11 09:31:44 +08:00
|
|
|
|
- [ECS 可视化调试插件](https://store.cocos.com/app/detail/7823) - 提供完整的可视化调试界面
|
|
|
|
|
|
- 实体查看器、组件编辑器、系统监控
|
|
|
|
|
|
- 性能分析和实时数据监控
|
2025-06-08 21:50:50 +08:00
|
|
|
|
|
2025-08-06 17:04:02 +08:00
|
|
|
|
### Laya 引擎
|
2025-06-10 13:12:14 +08:00
|
|
|
|
```typescript
|
2025-08-06 17:04:02 +08:00
|
|
|
|
Laya.timer.frameLoop(1, this, () => {
|
|
|
|
|
|
Core.update(Laya.timer.delta / 1000);
|
|
|
|
|
|
});
|
|
|
|
|
|
```
|
2025-06-10 13:12:14 +08:00
|
|
|
|
|
2025-08-06 17:04:02 +08:00
|
|
|
|
### 原生浏览器
|
|
|
|
|
|
```typescript
|
|
|
|
|
|
function gameLoop(currentTime: number) {
|
|
|
|
|
|
const deltaTime = (currentTime - lastTime) / 1000;
|
|
|
|
|
|
Core.update(deltaTime);
|
|
|
|
|
|
requestAnimationFrame(gameLoop);
|
|
|
|
|
|
}
|
2025-06-07 20:32:43 +08:00
|
|
|
|
```
|
|
|
|
|
|
|
2025-06-10 13:12:14 +08:00
|
|
|
|
|
|
|
|
|
|
## API 参考
|
|
|
|
|
|
|
|
|
|
|
|
### 核心类
|
|
|
|
|
|
|
|
|
|
|
|
| 类 | 描述 |
|
|
|
|
|
|
|---|---|
|
2025-08-06 17:04:02 +08:00
|
|
|
|
| `Core` | 框架核心管理 |
|
|
|
|
|
|
| `Scene` | 场景容器 |
|
|
|
|
|
|
| `Entity` | 实体对象 |
|
2025-06-10 13:12:14 +08:00
|
|
|
|
| `Component` | 组件基类 |
|
|
|
|
|
|
| `EntitySystem` | 系统基类 |
|
|
|
|
|
|
| `EntityManager` | 实体管理器 |
|
|
|
|
|
|
|
|
|
|
|
|
### 查询 API
|
2025-06-07 20:32:43 +08:00
|
|
|
|
|
|
|
|
|
|
```typescript
|
2025-08-11 09:31:44 +08:00
|
|
|
|
// Matcher API - 推荐方式,高效且类型安全
|
2025-08-11 08:18:18 +08:00
|
|
|
|
Matcher.all(...components) // 包含所有组件
|
2025-08-11 09:31:44 +08:00
|
|
|
|
Matcher.any(...components) // 包含任意组件
|
2025-08-11 08:18:18 +08:00
|
|
|
|
Matcher.none(...components) // 不包含组件
|
|
|
|
|
|
|
2025-08-11 09:31:44 +08:00
|
|
|
|
// 组合查询示例
|
|
|
|
|
|
Matcher
|
|
|
|
|
|
.all(PositionComponent, VelocityComponent) // 必须有这些组件
|
|
|
|
|
|
.any(PlayerComponent, AIComponent) // 其中之一
|
|
|
|
|
|
.none(DeadComponent, DisabledComponent); // 排除这些
|
2025-06-10 13:12:14 +08:00
|
|
|
|
```
|
2025-06-07 20:32:43 +08:00
|
|
|
|
|
2025-06-10 13:12:14 +08:00
|
|
|
|
## 文档
|
2025-06-07 20:32:43 +08:00
|
|
|
|
|
2025-08-06 17:04:02 +08:00
|
|
|
|
- [快速入门](docs/getting-started.md) - 详细教程和平台集成
|
|
|
|
|
|
- [技术概念](docs/concepts-explained.md) - ECS 架构和框架特性
|
|
|
|
|
|
- [组件设计](docs/component-design-guide.md) - 组件设计最佳实践
|
|
|
|
|
|
- [性能优化](docs/performance-optimization.md) - 性能优化技术
|
|
|
|
|
|
- [API 参考](docs/core-concepts.md) - 完整 API 文档
|
2025-06-07 20:32:43 +08:00
|
|
|
|
|
2025-06-10 13:12:14 +08:00
|
|
|
|
## 扩展库
|
2025-06-09 13:25:10 +08:00
|
|
|
|
|
2025-08-06 17:04:02 +08:00
|
|
|
|
- [路径寻找](https://github.com/esengine/ecs-astar) - A*、BFS、Dijkstra 算法
|
2025-06-10 13:12:14 +08:00
|
|
|
|
- [AI 系统](https://github.com/esengine/BehaviourTree-ai) - 行为树、效用 AI
|
|
|
|
|
|
|
|
|
|
|
|
## 社区
|
2025-06-09 13:25:10 +08:00
|
|
|
|
|
2025-06-10 13:12:14 +08:00
|
|
|
|
- QQ 群:[ecs游戏框架交流](https://jq.qq.com/?_wv=1027&k=29w1Nud6)
|
|
|
|
|
|
- GitHub:[提交 Issue](https://github.com/esengine/ecs-framework/issues)
|
2025-06-09 13:25:10 +08:00
|
|
|
|
|
2025-06-10 13:12:14 +08:00
|
|
|
|
## 许可证
|
2025-06-07 20:32:43 +08:00
|
|
|
|
|
2025-08-06 17:04:02 +08:00
|
|
|
|
[MIT](LICENSE)
|