TypeScript ECS (Entity-Component-System) 框架,专为游戏开发设计。
🤔 什么是 ECS? 不熟悉 ECS 架构?建议先阅读 ECS 架构基础 了解核心概念
- 🔧 完整的 TypeScript 支持 - 强类型检查和代码提示
- 📡 类型安全事件系统 - 事件装饰器和异步事件处理
- 🔍 查询系统 - 流式 API 和智能缓存
- ⚡ 性能优化 - 组件索引、Archetype 系统、脏标记
- 🎯 实体管理器 - 统一的实体生命周期管理
- 🧰 调试工具 - 内置性能监控和调试信息
📖 不熟悉这些概念? 查看我们的 技术概念详解 了解它们的作用和应用场景
npm install @esengine/ecs-framework
import { Core, Scene, Entity, Component, EntitySystem } from '@esengine/ecs-framework';
// 创建核心实例
const core = Core.create(true); // 调试模式
// 创建场景
const scene = new Scene();
Core.scene = scene;
class PositionComponent extends Component {
constructor(public x: number = 0, public y: number = 0) {
super();
}
}
class VelocityComponent extends Component {
constructor(public dx: number = 0, public dy: number = 0) {
super();
}
}
class HealthComponent extends Component {
constructor(
public maxHealth: number = 100,
public currentHealth: number = 100
) {
super();
}
}
// 基础实体创建
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");
class MovementSystem extends EntitySystem {
constructor() {
super();
}
public process(entities: Entity[]) {
for (const entity of entities) {
const position = entity.getComponent(PositionComponent);
const velocity = entity.getComponent(VelocityComponent);
if (position && velocity) {
position.x += velocity.dx;
position.y += velocity.dy;
}
}
}
}
// 添加系统到场景
scene.addEntityProcessor(new MovementSystem());
ECS框架需要在游戏引擎的更新循环中调用:
// 统一的API:传入deltaTime
Core.update(deltaTime);
不同平台的集成示例:
// Laya引擎
Laya.timer.frameLoop(1, this, () => {
const deltaTime = Laya.timer.delta / 1000; // 转换为秒
Core.update(deltaTime);
});
// Cocos Creator
update(deltaTime: number) {
Core.update(deltaTime);
}
// 原生浏览器环境
let lastTime = 0;
function gameLoop(currentTime: number) {
const deltaTime = lastTime > 0 ? (currentTime - lastTime) / 1000 : 0.016;
lastTime = currentTime;
Core.update(deltaTime);
requestAnimationFrame(gameLoop);
}
requestAnimationFrame(gameLoop);
EntityManager 提供了统一的实体管理接口:
import { EntityManager } from '@esengine/ecs-framework';
const entityManager = new EntityManager();
// 流式查询 API
const results = entityManager
.query()
.withAll(PositionComponent, VelocityComponent)
.withNone(HealthComponent)
.withTag(1)
.execute();
// 批量操作(使用Scene的方法)
const bullets = scene.createEntities(100, "bullet");
// 按标签查询
const enemies = entityManager.getEntitiesByTag(2);
类型安全的事件系统,编译时检查事件名和数据类型。
import { EventBus, ECSEventType } from '@esengine/ecs-framework';
const eventBus = entityManager.eventBus;
// 监听预定义事件
eventBus.onEntityCreated((data) => {
console.log(`实体创建: ${data.entityName}`);
});
eventBus.onComponentAdded((data) => {
console.log(`组件添加: ${data.componentType}`);
});
// 自定义事件
eventBus.emit('player:death', { playerId: 123, reason: 'fall' });
使用装饰器语法自动注册事件监听器,减少样板代码。
import { EventHandler, ECSEventType } from '@esengine/ecs-framework';
class GameSystem {
@EventHandler(ECSEventType.ENTITY_DESTROYED)
onEntityDestroyed(data: EntityDestroyedEventData) {
console.log('实体销毁:', data.entityName);
}
@EventHandler('player:levelup')
onPlayerLevelUp(data: { playerId: number; newLevel: number }) {
console.log(`玩家 ${data.playerId} 升级到 ${data.newLevel} 级`);
}
}
通过建立索引避免线性搜索,将查询复杂度从 O(n) 降低到 O(1)。
// 使用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、状态)
📋 详细选择指南参见 索引类型选择指南
将具有相同组件组合的实体分组,减少查询时的组件检查开销。
// 使用查询系统的Archetype功能
const querySystem = scene.querySystem;
// 查询统计
const stats = querySystem.getStats();
console.log('缓存命中率:', stats.hitRate);
追踪数据变化,只处理发生改变的实体,避免不必要的计算。
// 脏标记通过组件系统自动管理
// 组件变化时会自动标记为脏数据
// 查询系统会自动处理脏标记优化
const movingEntities = scene.querySystem.queryAll(PositionComponent, VelocityComponent);
💡 不确定何时使用这些优化? 查看 性能优化建议 了解适用场景
类 | 描述 |
---|---|
Core |
框架核心管理类 |
Scene |
场景容器,管理实体和系统 |
Entity |
实体对象,包含组件集合 |
Component |
组件基类 |
EntitySystem |
系统基类 |
EntityManager |
实体管理器 |
entityManager
.query()
.withAll(...components) // 包含所有指定组件
.withAny(...components) // 包含任意指定组件
.withNone(...components) // 不包含指定组件
.withTag(tag) // 包含指定标签
.withoutTag(tag) // 不包含指定标签
.execute() // 执行查询
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 优化 |
JavaScript引擎集成 | ✅ 专为JS引擎设计 | ✅ 通用设计 |
选择指南:
- 选择本框架:需要完整的游戏开发工具链和中文社区支持
- 选择 bitECS:需要极致性能和最小化设计
- 选择 Miniplex:主要用于 React 应用开发
ecs-framework/
├── src/
│ ├── ECS/ # ECS 核心系统
│ │ ├── Core/ # 核心管理器
│ │ ├── Systems/ # 系统类型
│ │ └── Utils/ # ECS 工具
│ ├── Types/ # TypeScript接口定义
│ └── Utils/ # 通用工具
├── docs/ # 文档
└── scripts/ # 构建脚本
- 📖 新手教程完整指南 - 完整学习路径,从零开始 ⭐ 强烈推荐
- 🚀 快速入门 - 详细的入门教程,包含Laya/Cocos/Node.js集成指南 ⭐ 平台集成必读
- 🧠 技术概念详解 - 通俗易懂的技术概念解释 ⭐ 推荐新手阅读
- 🎯 位掩码使用指南 - 位掩码概念、原理和高级使用技巧
- 💡 使用场景示例 - 不同类型游戏的具体应用案例
- 🔧 框架类型系统 - TypeScript接口设计和使用指南
- 🎭 实体管理指南 - 实体的创建和使用方法
- 🧩 组件设计指南 - 如何设计高质量组件 ⭐ 设计必读
- ⚙️ 系统详解指南 - 四种系统类型的详细使用
- 🎬 场景管理指南 - 场景切换和数据管理
- ⏰ 定时器系统指南 - 定时器的完整使用方法
- 核心 API 参考 - 完整的 API 使用说明
- 实体基础指南 - 实体的基本概念和操作
- EntityManager 指南 - 高性能查询和批量操作
- 事件系统指南 - 事件系统完整用法
- 查询系统指南 - 查询系统使用方法
- 性能优化指南 - 性能优化技术和策略
# 安装依赖
npm install
# 构建项目
npm run build
# 监听模式
npm run build:watch
# 清理构建文件
npm run clean
# 重新构建
npm run rebuild
框架提供内置性能统计:
// 场景统计
const sceneStats = scene.getStats();
console.log('性能统计:', {
实体数量: sceneStats.entityCount,
系统数量: sceneStats.processorCount
});
// 查询系统统计
const queryStats = scene.querySystem.getStats();
console.log('查询统计:', {
缓存命中率: queryStats.hitRate + '%',
查询次数: queryStats.queryCount
});
欢迎提交 Pull Request 和 Issue!
- Node.js >= 14.0.0
- TypeScript >= 4.0.0