优化matcher内部实现改为querysystem
完善type类型 更新文档
This commit is contained in:
@@ -7,22 +7,22 @@
|
||||
### 第一阶段:基础入门(必读)
|
||||
|
||||
#### 1. [快速开始](getting-started.md)
|
||||
- 🚀 **5分钟入门** - 创建你的第一个ECS游戏
|
||||
- **5分钟入门** - 创建你的第一个ECS游戏
|
||||
- 📦 **环境搭建** - 安装和配置框架
|
||||
- 🎮 **第一个游戏** - 完整的示例游戏
|
||||
- 🔧 **基础API** - 核心功能介绍
|
||||
- **第一个游戏** - 完整的示例游戏
|
||||
- **基础API** - 核心功能介绍
|
||||
|
||||
#### 2. [核心概念](core-concepts.md)
|
||||
- 🏗️ **ECS架构** - 实体、组件、系统的关系
|
||||
- 📊 **API参考** - 核心类和方法
|
||||
- 🎯 **最佳实践** - 代码规范和设计模式
|
||||
- 🔍 **查询系统** - 如何高效查找实体
|
||||
- **API参考** - 核心类和方法
|
||||
- **最佳实践** - 代码规范和设计模式
|
||||
- **查询系统** - 如何高效查找实体
|
||||
|
||||
#### 3. [概念详解](concepts-explained.md) ⭐️**新手必读**
|
||||
#### 3. [概念详解](concepts-explained.md) ️**新手必读**
|
||||
- 🤔 **通俗解释** - 用简单语言解释复杂概念
|
||||
- 📈 **性能优化技术** - 组件索引、Archetype、脏标记
|
||||
- 🎯 **索引选择指南** - 何时使用哈希索引vs位图索引
|
||||
- 🎮 **应用场景** - 不同游戏类型的选择建议
|
||||
- **性能优化技术** - 组件索引、Archetype、脏标记
|
||||
- **索引选择指南** - 何时使用哈希索引vs位图索引
|
||||
- **应用场景** - 不同游戏类型的选择建议
|
||||
|
||||
### 第二阶段:核心功能掌握
|
||||
|
||||
@@ -30,21 +30,21 @@
|
||||
- 🎭 **实体基础** - 什么是实体,如何创建和使用
|
||||
- 🏷️ **标签系统** - 实体分类和查找
|
||||
- 🔄 **生命周期** - 实体的创建、更新、销毁
|
||||
- 📝 **简单示例** - 玩家、敌人、道具实体
|
||||
- **简单示例** - 玩家、敌人、道具实体
|
||||
|
||||
#### 5. [组件设计最佳实践](component-design-guide.md) ⭐️**设计必读**
|
||||
#### 5. [组件设计最佳实践](component-design-guide.md) ️**设计必读**
|
||||
- 🧩 **组件设计原则** - 单一职责、数据为主
|
||||
- 📦 **组件类型** - 数据组件、标记组件、行为组件
|
||||
- 🔗 **组件通信** - 如何让组件协同工作
|
||||
- 🎯 **性能优化** - 对象池和数据紧凑性
|
||||
- **性能优化** - 对象池和数据紧凑性
|
||||
- 🧪 **测试和调试** - 如何测试你的组件
|
||||
|
||||
#### 6. [系统详解指南](system-guide.md) ⭐️**逻辑必读**
|
||||
#### 6. [系统详解指南](system-guide.md) ️**逻辑必读**
|
||||
- ⚙️ **四种系统类型** - EntitySystem、ProcessingSystem、IntervalSystem、PassiveSystem
|
||||
- 🎯 **使用场景** - 什么时候用哪种系统
|
||||
- **使用场景** - 什么时候用哪种系统
|
||||
- 📋 **执行顺序** - 系统间的依赖关系
|
||||
- 🔄 **系统通信** - 事件驱动的松耦合设计
|
||||
- 🚀 **性能优化** - 批量处理和频率控制
|
||||
- **性能优化** - 批量处理和频率控制
|
||||
|
||||
### 第三阶段:高级功能应用
|
||||
|
||||
@@ -52,57 +52,57 @@
|
||||
- 🎬 **场景概念** - 什么是场景,如何组织游戏世界
|
||||
- 🔄 **场景切换** - 菜单、游戏、暂停场景的切换
|
||||
- 💾 **数据传递** - 场景间如何传递数据
|
||||
- 🎮 **实际应用** - 完整的游戏场景设计
|
||||
- 📈 **性能优化** - 场景级别的性能监控
|
||||
- **实际应用** - 完整的游戏场景设计
|
||||
- **性能优化** - 场景级别的性能监控
|
||||
|
||||
#### 8. [定时器系统指南](timer-guide.md)
|
||||
- ⏰ **定时器基础** - 延迟执行、重复执行
|
||||
- 🔗 **定时器链** - 顺序执行多个任务
|
||||
- 🎯 **条件定时器** - 等待特定条件满足
|
||||
- **条件定时器** - 等待特定条件满足
|
||||
- ⏸️ **可暂停定时器** - 游戏暂停功能
|
||||
- 🎮 **游戏应用** - Buff系统、技能冷却、关卡限时
|
||||
- **游戏应用** - Buff系统、技能冷却、关卡限时
|
||||
|
||||
#### 9. [查询系统使用](query-system-usage.md)
|
||||
- 🔍 **基础查询** - 按组件查找实体
|
||||
- 🎯 **复杂查询** - 组合条件和排除条件
|
||||
- 📊 **性能监控** - 查询性能统计
|
||||
- 🚀 **优化技巧** - 提高查询效率
|
||||
- **基础查询** - 按组件查找实体
|
||||
- **复杂查询** - 组合条件和排除条件
|
||||
- **性能监控** - 查询性能统计
|
||||
- **优化技巧** - 提高查询效率
|
||||
|
||||
#### 10. [事件系统示例](event-system-example.md)
|
||||
- 📡 **事件基础** - 发送和监听事件
|
||||
- 🎮 **游戏事件** - 玩家输入、碰撞、分数等
|
||||
- **游戏事件** - 玩家输入、碰撞、分数等
|
||||
- 🔄 **系统解耦** - 用事件实现系统间通信
|
||||
- 📈 **事件统计** - 监控事件系统性能
|
||||
- **事件统计** - 监控事件系统性能
|
||||
|
||||
### 第四阶段:实战应用
|
||||
|
||||
#### 11. [实体管理器高级功能](entity-manager-example.md)
|
||||
- 🏭 **批量操作** - 高效创建和管理大量实体
|
||||
- 🔍 **高级查询** - EntityQueryBuilder的使用
|
||||
- 📊 **性能监控** - 实体管理性能统计
|
||||
- 🎯 **实际案例** - 弹幕游戏、RTS游戏的实体管理
|
||||
- **高级查询** - EntityQueryBuilder的使用
|
||||
- **性能监控** - 实体管理性能统计
|
||||
- **实际案例** - 弹幕游戏、RTS游戏的实体管理
|
||||
|
||||
#### 12. [应用案例集合](use-cases.md)
|
||||
- 🎮 **不同游戏类型** - 休闲游戏、动作游戏、策略游戏
|
||||
- 🎯 **具体实现** - 完整的代码示例
|
||||
- 📊 **性能分析** - 各种应用的性能特点
|
||||
- 💡 **设计思路** - 如何选择合适的架构
|
||||
- **不同游戏类型** - 休闲游戏、动作游戏、策略游戏
|
||||
- **具体实现** - 完整的代码示例
|
||||
- **性能分析** - 各种应用的性能特点
|
||||
- **设计思路** - 如何选择合适的架构
|
||||
|
||||
### 第五阶段:性能优化
|
||||
|
||||
#### 13. [性能基准测试](performance.md)
|
||||
- 📊 **基准数据** - 框架性能表现
|
||||
- 📈 **对比分析** - 与其他框架的比较
|
||||
- 🎯 **优化建议** - 针对不同规模的优化策略
|
||||
- **基准数据** - 框架性能表现
|
||||
- **对比分析** - 与其他框架的比较
|
||||
- **优化建议** - 针对不同规模的优化策略
|
||||
- 📋 **性能检查清单** - 确保最佳性能的要点
|
||||
|
||||
#### 14. [性能优化技术](performance-optimization.md)
|
||||
- 🚀 **核心优化** - 组件索引、Archetype、脏标记
|
||||
- **核心优化** - 组件索引、Archetype、脏标记
|
||||
- 💾 **内存优化** - 对象池、数据紧凑性
|
||||
- 🔄 **批量处理** - 减少单次操作开销
|
||||
- 📊 **监控工具** - 性能分析和调试
|
||||
- **监控工具** - 性能分析和调试
|
||||
|
||||
## 🎯 推荐学习顺序
|
||||
## 推荐学习顺序
|
||||
|
||||
### 适合完全新手(第一次接触ECS)
|
||||
|
||||
@@ -167,21 +167,21 @@ A:
|
||||
- [应用案例集合](use-cases.md) - 不同类型游戏的实现
|
||||
- [场景管理指南](scene-management-guide.md) - 复杂游戏的场景组织
|
||||
|
||||
## 💡 学习建议
|
||||
## 学习建议
|
||||
|
||||
### 实践为主
|
||||
- 📝 **边学边做** - 每学一个概念都尝试写代码实现
|
||||
- 🎮 **从小做起** - 先做简单的游戏,再逐步增加复杂度
|
||||
- 🔧 **多做实验** - 尝试不同的设计方案,体会优劣
|
||||
- **边学边做** - 每学一个概念都尝试写代码实现
|
||||
- **从小做起** - 先做简单的游戏,再逐步增加复杂度
|
||||
- **多做实验** - 尝试不同的设计方案,体会优劣
|
||||
|
||||
### 理解原理
|
||||
- 🤔 **思考为什么** - 不只学怎么做,更要理解为什么这样做
|
||||
- 📊 **关注性能** - 了解各种操作的性能影响
|
||||
- 🔍 **深入源码** - 有疑问时查看框架源码
|
||||
- **关注性能** - 了解各种操作的性能影响
|
||||
- **深入源码** - 有疑问时查看框架源码
|
||||
|
||||
### 循序渐进
|
||||
- 📚 **按顺序学习** - 先掌握基础,再学高级功能
|
||||
- 🎯 **专注重点** - 每次只专注一个主题,不要贪多
|
||||
- **专注重点** - 每次只专注一个主题,不要贪多
|
||||
- 🔄 **反复练习** - 重要概念要多练习才能熟练
|
||||
|
||||
开始你的ECS学习之旅吧!🚀
|
||||
开始你的ECS学习之旅吧!
|
||||
@@ -267,17 +267,29 @@ class InvincibleComponent extends Component {
|
||||
}
|
||||
|
||||
// 使用标记组件进行查询
|
||||
class GameSystem {
|
||||
updatePlayers() {
|
||||
// 只处理玩家实体
|
||||
const players = this.scene.findEntitiesWithComponent(PlayerComponent);
|
||||
// ...
|
||||
class PlayerSystem extends EntitySystem {
|
||||
constructor() {
|
||||
super(Matcher.all(PlayerComponent));
|
||||
}
|
||||
|
||||
updateEnemies() {
|
||||
// 只处理敌人实体
|
||||
const enemies = this.scene.findEntitiesWithComponent(EnemyComponent);
|
||||
// ...
|
||||
protected process(entities: Entity[]): void {
|
||||
// entities已经是玩家实体
|
||||
for (const entity of entities) {
|
||||
// 处理玩家逻辑
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class EnemySystem extends EntitySystem {
|
||||
constructor() {
|
||||
super(Matcher.all(EnemyComponent));
|
||||
}
|
||||
|
||||
protected process(entities: Entity[]): void {
|
||||
// entities已经是敌人实体
|
||||
for (const entity of entities) {
|
||||
// 处理敌人逻辑
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -146,7 +146,7 @@ componentIndex.setIndexType(TemporaryComponent, 'bitmap'); // 临时组件
|
||||
componentIndex.setIndexType(StateComponent, 'bitmap'); // 状态组件变化频繁
|
||||
```
|
||||
|
||||
#### 📊 选择决策表
|
||||
#### 选择决策表
|
||||
|
||||
| 考虑因素 | 哈希索引 (Hash) | 位图索引 (Bitmap) |
|
||||
|---------|----------------|-------------------|
|
||||
@@ -170,7 +170,7 @@ componentIndex.setIndexType(StateComponent, 'bitmap'); // 状态组件变
|
||||
- 需要频繁批量操作? → 选择 **位图索引**
|
||||
- 内存使用很重要? → 选择 **哈希索引**
|
||||
|
||||
#### 🎮 实际游戏中的选择示例
|
||||
#### 实际游戏中的选择示例
|
||||
|
||||
**射击游戏:**
|
||||
```typescript
|
||||
|
||||
@@ -71,7 +71,7 @@ ECS框架内置了强大的调试功能,支持运行时监控和远程调试
|
||||
|
||||
#### Cocos Creator专用调试插件
|
||||
|
||||
**🎯 对于Cocos Creator用户,我们提供了专门的可视化调试插件:**
|
||||
** 对于Cocos Creator用户,我们提供了专门的可视化调试插件:**
|
||||
|
||||
- **插件地址**:[cocos-ecs-framework 调试插件](https://store.cocos.com/app/detail/7823)
|
||||
- **插件版本**:v1.0.0
|
||||
@@ -163,7 +163,7 @@ const isDevelopment = process.env.NODE_ENV === 'development';
|
||||
Core.create(isDevelopment ? devConfig : prodConfig);
|
||||
```
|
||||
|
||||
**💡 调试功能说明:**
|
||||
** 调试功能说明:**
|
||||
- **Cocos Creator**:推荐使用[官方调试插件](https://store.cocos.com/app/detail/7823)获得最佳调试体验
|
||||
- **其他平台**:可以通过WebSocket连接自定义调试工具
|
||||
- **生产环境**:建议关闭调试功能以获得最佳性能
|
||||
@@ -221,16 +221,29 @@ class LayaECSGame extends LayaScene {
|
||||
// Laya渲染系统
|
||||
class LayaRenderSystem extends EntitySystem {
|
||||
private layaScene: LayaScene;
|
||||
private renderMatcher: Matcher;
|
||||
|
||||
constructor(layaScene: LayaScene) {
|
||||
super(Matcher.empty().all(PositionComponent, SpriteComponent));
|
||||
super();
|
||||
this.layaScene = layaScene;
|
||||
}
|
||||
|
||||
public initialize(): void {
|
||||
super.initialize();
|
||||
// 创建Matcher来查询需要渲染的实体
|
||||
if (this.scene) {
|
||||
this.renderMatcher = Matcher.create(this.scene.querySystem)
|
||||
.all(PositionComponent, SpriteComponent);
|
||||
}
|
||||
}
|
||||
|
||||
protected process(entities: Entity[]): void {
|
||||
entities.forEach(entity => {
|
||||
const pos = entity.getComponent(PositionComponent);
|
||||
const sprite = entity.getComponent(SpriteComponent);
|
||||
// 获取需要渲染的实体
|
||||
const renderableEntities = this.renderMatcher.query();
|
||||
|
||||
renderableEntities.forEach(entity => {
|
||||
const pos = entity.getComponent(PositionComponent)!;
|
||||
const sprite = entity.getComponent(SpriteComponent)!;
|
||||
|
||||
if (pos && sprite && sprite.layaSprite) {
|
||||
sprite.layaSprite.x = pos.x;
|
||||
@@ -294,18 +307,29 @@ export class ECSGameManager extends CocosComponent {
|
||||
// Cocos渲染系统
|
||||
class CocosRenderSystem extends EntitySystem {
|
||||
private rootNode: Node;
|
||||
private renderMatcher: Matcher;
|
||||
|
||||
constructor(rootNode: Node) {
|
||||
super(Matcher.empty().all(PositionComponent, SpriteComponent));
|
||||
super();
|
||||
this.rootNode = rootNode;
|
||||
}
|
||||
|
||||
public initialize(): void {
|
||||
super.initialize();
|
||||
if (this.scene) {
|
||||
this.renderMatcher = Matcher.create(this.scene.querySystem)
|
||||
.all(PositionComponent, SpriteComponent);
|
||||
}
|
||||
}
|
||||
|
||||
protected process(entities: Entity[]): void {
|
||||
entities.forEach(entity => {
|
||||
const pos = entity.getComponent(PositionComponent);
|
||||
const sprite = entity.getComponent(SpriteComponent);
|
||||
const renderableEntities = this.renderMatcher.query();
|
||||
|
||||
renderableEntities.forEach(entity => {
|
||||
const pos = entity.getComponent(PositionComponent)!;
|
||||
const sprite = entity.getComponent(SpriteComponent)!;
|
||||
|
||||
if (pos && sprite && sprite.cocosNode) {
|
||||
if (sprite.cocosNode) {
|
||||
sprite.cocosNode.setPosition(pos.x, pos.y);
|
||||
}
|
||||
});
|
||||
@@ -315,7 +339,7 @@ class CocosRenderSystem extends EntitySystem {
|
||||
// 将ECSGameManager脚本挂载到场景根节点
|
||||
```
|
||||
|
||||
**🔧 Cocos Creator调试提示:**
|
||||
** Cocos Creator调试提示:**
|
||||
为了获得最佳的ECS调试体验,建议安装我们的专用调试插件:
|
||||
- 插件地址:[https://store.cocos.com/app/detail/7823](https://store.cocos.com/app/detail/7823)
|
||||
- 支持Cocos Creator v3.0.0+
|
||||
@@ -549,13 +573,13 @@ import { EntitySystem, Entity, Matcher, Time } from '@esengine/ecs-framework';
|
||||
|
||||
class MovementSystem extends EntitySystem {
|
||||
constructor() {
|
||||
super(Matcher.empty().all(PositionComponent, VelocityComponent));
|
||||
super(Matcher.all(PositionComponent, VelocityComponent));
|
||||
}
|
||||
|
||||
protected process(entities: Entity[]): void {
|
||||
const movingEntities = this.scene.querySystem.queryAll(PositionComponent, VelocityComponent);
|
||||
const movingEntities = entities;
|
||||
|
||||
movingEntities.entities.forEach(entity => {
|
||||
movingEntities.forEach(entity => {
|
||||
const position = entity.getComponent(PositionComponent);
|
||||
const velocity = entity.getComponent(VelocityComponent);
|
||||
|
||||
@@ -568,14 +592,26 @@ class MovementSystem extends EntitySystem {
|
||||
}
|
||||
|
||||
class HealthSystem extends EntitySystem {
|
||||
private healthMatcher: Matcher;
|
||||
|
||||
constructor() {
|
||||
super(Matcher.empty().all(HealthComponent));
|
||||
super();
|
||||
}
|
||||
|
||||
public initialize(): void {
|
||||
super.initialize();
|
||||
if (this.scene) {
|
||||
this.healthMatcher = Matcher.create(this.scene.querySystem)
|
||||
.all(HealthComponent);
|
||||
}
|
||||
}
|
||||
|
||||
protected process(entities: Entity[]): void {
|
||||
const healthEntities = this.scene.querySystem.queryAll(HealthComponent);
|
||||
if (!this.healthMatcher) return;
|
||||
|
||||
healthEntities.entities.forEach(entity => {
|
||||
const healthEntities = this.healthMatcher.query();
|
||||
|
||||
healthEntities.forEach(entity => {
|
||||
const health = entity.getComponent(HealthComponent);
|
||||
if (health && health.currentHealth <= 0) {
|
||||
entity.destroy();
|
||||
|
||||
@@ -44,42 +44,50 @@ for (let i = 0; i < typedResult.entities.length; i++) {
|
||||
}
|
||||
|
||||
// 查询单个组件类型
|
||||
const healthEntities = querySystem.queryComponentTyped(HealthComponent);
|
||||
healthEntities.forEach(({ entity, component }) => {
|
||||
console.log(`实体 ${entity.name} 的生命值: ${component.value}`);
|
||||
});
|
||||
const healthResult = querySystem.queryAll(HealthComponent);
|
||||
for (const entity of healthResult.entities) {
|
||||
const health = entity.getComponent(HealthComponent);
|
||||
if (health) {
|
||||
console.log(`实体 ${entity.name} 的生命值: ${health.value}`);
|
||||
}
|
||||
}
|
||||
|
||||
// 查询两个组件类型
|
||||
const movableEntities = querySystem.queryTwoComponents(PositionComponent, VelocityComponent);
|
||||
movableEntities.forEach(({ entity, component1: position, component2: velocity }) => {
|
||||
// 更新位置
|
||||
position.x += velocity.x;
|
||||
position.y += velocity.y;
|
||||
});
|
||||
const movableResult = querySystem.queryAll(PositionComponent, VelocityComponent);
|
||||
for (const entity of movableResult.entities) {
|
||||
const position = entity.getComponent(PositionComponent);
|
||||
const velocity = entity.getComponent(VelocityComponent);
|
||||
if (position && velocity) {
|
||||
// 更新位置
|
||||
position.x += velocity.x;
|
||||
position.y += velocity.y;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 4. 使用查询构建器
|
||||
|
||||
```typescript
|
||||
// 创建复杂查询
|
||||
const query = querySystem.createQuery()
|
||||
.withAll(PositionComponent, RenderComponent)
|
||||
.without(HiddenComponent)
|
||||
.withTag(1) // 特定标签
|
||||
.orderByName()
|
||||
.limit(10);
|
||||
// QuerySystem不提供查询构建器,请使用Matcher进行复杂查询
|
||||
// 推荐使用Matcher配合EntitySystem
|
||||
|
||||
const result = query.execute();
|
||||
import { Matcher } from '@esengine/ecs-framework';
|
||||
|
||||
// 链式操作
|
||||
const visibleEnemies = querySystem.createQuery()
|
||||
.withAll(EnemyComponent, PositionComponent)
|
||||
.without(DeadComponent, HiddenComponent)
|
||||
// 创建复杂查询条件
|
||||
const visibleMatcher = Matcher.all(PositionComponent, RenderComponent)
|
||||
.none(HiddenComponent);
|
||||
|
||||
// 通过QuerySystem执行查询
|
||||
const visibleEntities = querySystem.query(visibleMatcher.getCondition());
|
||||
|
||||
// 过滤和排序需要手动处理
|
||||
const sortedEntities = visibleEntities.entities
|
||||
.filter(entity => entity.name.startsWith('Boss'))
|
||||
.orderBy((a, b) => a.id - b.id);
|
||||
.sort((a, b) => a.id - b.id)
|
||||
.slice(0, 10); // 限制数量
|
||||
|
||||
// 迭代结果
|
||||
visibleEnemies.forEach((entity, index) => {
|
||||
sortedEntities.forEach((entity, index) => {
|
||||
console.log(`敌人 ${index}: ${entity.name}`);
|
||||
});
|
||||
```
|
||||
@@ -87,31 +95,32 @@ visibleEnemies.forEach((entity, index) => {
|
||||
### 5. 高级查询功能
|
||||
|
||||
```typescript
|
||||
// 复合查询
|
||||
const complexResult = querySystem.queryComplex(
|
||||
{
|
||||
type: QueryConditionType.ALL,
|
||||
componentTypes: [PositionComponent, VelocityComponent],
|
||||
mask: /* 位掩码 */
|
||||
},
|
||||
{
|
||||
type: QueryConditionType.NONE,
|
||||
componentTypes: [DeadComponent],
|
||||
mask: /* 位掩码 */
|
||||
}
|
||||
// QuerySystem主要提供基础查询方法
|
||||
// 复杂查询推荐使用Matcher和EntitySystem
|
||||
|
||||
// 基本查询
|
||||
const positionResult = querySystem.queryAll(PositionComponent, VelocityComponent);
|
||||
const healthResult = querySystem.queryAll(HealthComponent);
|
||||
const manaResult = querySystem.queryAll(ManaComponent);
|
||||
|
||||
// 排除死亡实体的移动实体
|
||||
const aliveMovingEntities = positionResult.entities.filter(entity =>
|
||||
!entity.hasComponent(DeadComponent)
|
||||
);
|
||||
|
||||
// 批量查询
|
||||
const batchResults = querySystem.batchQuery([
|
||||
{ type: QueryConditionType.ALL, componentTypes: [HealthComponent], mask: /* 位掩码 */ },
|
||||
{ type: QueryConditionType.ALL, componentTypes: [ManaComponent], mask: /* 位掩码 */ }
|
||||
]);
|
||||
|
||||
// 并行查询
|
||||
const parallelResults = await querySystem.parallelQuery([
|
||||
{ type: QueryConditionType.ALL, componentTypes: [PositionComponent], mask: /* 位掩码 */ },
|
||||
{ type: QueryConditionType.ALL, componentTypes: [VelocityComponent], mask: /* 位掩码 */ }
|
||||
]);
|
||||
// 如果需要复杂查询,推荐在EntitySystem中使用Matcher
|
||||
class ComplexQuerySystem extends EntitySystem {
|
||||
constructor() {
|
||||
super(Matcher.all(PositionComponent, VelocityComponent).none(DeadComponent));
|
||||
}
|
||||
|
||||
protected process(entities: Entity[]): void {
|
||||
// entities已经是过滤后的结果
|
||||
for (const entity of entities) {
|
||||
// 处理逻辑
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 场景级别的实体查询
|
||||
@@ -147,12 +156,9 @@ querySystem.setCacheConfig(200, 2000); // 最大200个缓存项,2秒超时
|
||||
// 清空缓存
|
||||
querySystem.clearCache();
|
||||
|
||||
// 预热常用查询
|
||||
const commonQueries = [
|
||||
{ type: QueryConditionType.ALL, componentTypes: [PositionComponent], mask: /* 位掩码 */ },
|
||||
{ type: QueryConditionType.ALL, componentTypes: [VelocityComponent], mask: /* 位掩码 */ }
|
||||
];
|
||||
querySystem.warmUpCache(commonQueries);
|
||||
// 预热常用查询(使用基础查询方法)
|
||||
querySystem.queryAll(PositionComponent); // 预热Position查询
|
||||
querySystem.queryAll(VelocityComponent); // 预热Velocity查询
|
||||
```
|
||||
|
||||
### 2. 索引优化
|
||||
@@ -171,52 +177,101 @@ console.log(report);
|
||||
### 3. 查询监听和快照
|
||||
|
||||
```typescript
|
||||
// 监听查询结果变更
|
||||
const unwatch = querySystem.watchQuery(
|
||||
{ type: QueryConditionType.ALL, componentTypes: [EnemyComponent], mask: /* 位掩码 */ },
|
||||
(entities, changeType) => {
|
||||
console.log(`敌人实体${changeType}: ${entities.length}个`);
|
||||
// QuerySystem主要用于基础查询,高级功能请使用EntitySystem和事件系统
|
||||
|
||||
// 如需监听实体变化,使用事件系统
|
||||
scene.entityManager.eventBus.on('entity:added', (entity) => {
|
||||
if (entity.hasComponent(EnemyComponent)) {
|
||||
console.log('新增敌人实体');
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
// 取消监听
|
||||
unwatch();
|
||||
scene.entityManager.eventBus.on('entity:removed', (entity) => {
|
||||
if (entity.hasComponent(EnemyComponent)) {
|
||||
console.log('移除敌人实体');
|
||||
}
|
||||
});
|
||||
|
||||
// 创建查询快照
|
||||
const snapshot1 = querySystem.createSnapshot(
|
||||
{ type: QueryConditionType.ALL, componentTypes: [PlayerComponent], mask: /* 位掩码 */ }
|
||||
);
|
||||
|
||||
// 稍后创建另一个快照
|
||||
const snapshot2 = querySystem.createSnapshot(
|
||||
{ type: QueryConditionType.ALL, componentTypes: [PlayerComponent], mask: /* 位掩码 */ }
|
||||
);
|
||||
// 手动创建快照进行比较
|
||||
const snapshot1 = querySystem.queryAll(PlayerComponent).entities.map(e => e.id);
|
||||
// 稍后
|
||||
const snapshot2 = querySystem.queryAll(PlayerComponent).entities.map(e => e.id);
|
||||
|
||||
// 比较快照
|
||||
const diff = querySystem.compareSnapshots(snapshot1, snapshot2);
|
||||
console.log(`新增: ${diff.added.length}, 移除: ${diff.removed.length}`);
|
||||
const added = snapshot2.filter(id => !snapshot1.includes(id));
|
||||
const removed = snapshot1.filter(id => !snapshot2.includes(id));
|
||||
console.log(`新增: ${added.length}, 移除: ${removed.length}`);
|
||||
```
|
||||
|
||||
## 实际使用示例
|
||||
## 使用Matcher进行高级查询
|
||||
|
||||
### 移动系统示例
|
||||
Matcher是一个优雅的查询封装器,提供流畅的API和强大的缓存机制。
|
||||
|
||||
### 基本Matcher用法
|
||||
|
||||
```typescript
|
||||
import { Matcher } from '@esengine/ecs-framework';
|
||||
|
||||
// 创建Matcher查询条件
|
||||
const movingMatcher = Matcher.all(PositionComponent, VelocityComponent);
|
||||
// 在QuerySystem中需要使用基础查询方法
|
||||
const movingEntities = scene.querySystem.queryAll(PositionComponent, VelocityComponent).entities;
|
||||
|
||||
// 复合查询需要使用EntitySystem或手动过滤
|
||||
const aliveEnemiesMatcher = Matcher.all(EnemyComponent, HealthComponent)
|
||||
.any(WeaponComponent, MagicComponent)
|
||||
.none(DeadComponent, StunnedComponent);
|
||||
|
||||
// 在EntitySystem中使用
|
||||
class AliveEnemySystem extends EntitySystem {
|
||||
constructor() {
|
||||
super(aliveEnemiesMatcher);
|
||||
}
|
||||
|
||||
protected process(entities: Entity[]): void {
|
||||
// entities已经是匹配的实体
|
||||
for (const entity of entities) {
|
||||
// 处理存活的敌人
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 或者手动过滤
|
||||
const enemyResult = scene.querySystem.queryAll(EnemyComponent, HealthComponent);
|
||||
const aliveEnemies = enemyResult.entities.filter(entity => {
|
||||
const hasWeaponOrMagic = entity.hasComponent(WeaponComponent) || entity.hasComponent(MagicComponent);
|
||||
const isAlive = !entity.hasComponent(DeadComponent) && !entity.hasComponent(StunnedComponent);
|
||||
return hasWeaponOrMagic && isAlive;
|
||||
});
|
||||
|
||||
// 单个实体检查
|
||||
const playerResult = scene.querySystem.queryAll(PlayerComponent);
|
||||
if (playerResult.entities.includes(someEntity)) {
|
||||
console.log('这是玩家实体');
|
||||
}
|
||||
|
||||
// 统计信息
|
||||
console.log(`玩家数量: ${playerResult.count}`);
|
||||
console.log(`是否有玩家: ${playerResult.count > 0}`);
|
||||
```
|
||||
|
||||
### 系统中使用Matcher
|
||||
|
||||
```typescript
|
||||
import { EntitySystem, Entity, Matcher } from '@esengine/ecs-framework';
|
||||
|
||||
class MovementSystem extends EntitySystem {
|
||||
constructor() {
|
||||
super(Matcher.empty().all(PositionComponent, VelocityComponent));
|
||||
// 在构造函数中直接传入Matcher
|
||||
super(Matcher.all(PositionComponent, VelocityComponent));
|
||||
}
|
||||
|
||||
protected process(entities: Entity[]): void {
|
||||
// 查询所有可移动的实体
|
||||
const movableEntities = this.scene.querySystem.queryTwoComponents(
|
||||
PositionComponent,
|
||||
VelocityComponent
|
||||
);
|
||||
|
||||
movableEntities.forEach(({ entity, component1: position, component2: velocity }) => {
|
||||
// entities参数已经是系统自动过滤后的实体
|
||||
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;
|
||||
@@ -228,7 +283,7 @@ class MovementSystem extends EntitySystem {
|
||||
if (position.y < 0 || position.y > 600) {
|
||||
velocity.y = -velocity.y;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -238,15 +293,13 @@ class MovementSystem extends EntitySystem {
|
||||
```typescript
|
||||
class CollisionSystem extends EntitySystem {
|
||||
constructor() {
|
||||
super(Matcher.empty().all(PositionComponent, ColliderComponent));
|
||||
// 在构造函数中传入Matcher
|
||||
super(Matcher.all(PositionComponent, ColliderComponent));
|
||||
}
|
||||
|
||||
protected process(entities: Entity[]): void {
|
||||
// 获取所有具有碰撞器的实体
|
||||
const collidableEntities = this.scene.querySystem.queryTwoComponents(
|
||||
PositionComponent,
|
||||
ColliderComponent
|
||||
);
|
||||
// entities已经是匹配的实体
|
||||
const collidableEntities = entities;
|
||||
|
||||
// 检测碰撞
|
||||
for (let i = 0; i < collidableEntities.length; i++) {
|
||||
@@ -255,16 +308,15 @@ class CollisionSystem extends EntitySystem {
|
||||
const entityB = collidableEntities[j];
|
||||
|
||||
if (this.checkCollision(entityA, entityB)) {
|
||||
this.handleCollision(entityA.entity, entityB.entity);
|
||||
this.handleCollision(entityA, entityB);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private checkCollision(entityA: any, entityB: any): boolean {
|
||||
// 简单的距离检测
|
||||
const posA = entityA.component1;
|
||||
const posB = entityB.component1;
|
||||
private checkCollision(entityA: Entity, entityB: Entity): boolean {
|
||||
const posA = entityA.getComponent(PositionComponent)!;
|
||||
const posB = entityB.getComponent(PositionComponent)!;
|
||||
const distance = Math.sqrt(
|
||||
Math.pow(posA.x - posB.x, 2) + Math.pow(posA.y - posB.y, 2)
|
||||
);
|
||||
@@ -282,20 +334,23 @@ class CollisionSystem extends EntitySystem {
|
||||
```typescript
|
||||
class HealthSystem extends EntitySystem {
|
||||
constructor() {
|
||||
super(Matcher.empty().all(HealthComponent));
|
||||
// 在构造函数中传入Matcher
|
||||
super(Matcher.all(HealthComponent));
|
||||
}
|
||||
|
||||
protected process(entities: Entity[]): void {
|
||||
// 查询所有具有生命值的实体
|
||||
const healthEntities = this.scene.querySystem.queryComponentTyped(HealthComponent);
|
||||
// entities已经是匹配的实体
|
||||
const healthEntities = entities;
|
||||
const deadEntities: Entity[] = [];
|
||||
|
||||
healthEntities.forEach(({ entity, component: health }) => {
|
||||
for (const entity of healthEntities) {
|
||||
const health = entity.getComponent(HealthComponent)!;
|
||||
|
||||
// 检查死亡
|
||||
if (health.currentHealth <= 0) {
|
||||
deadEntities.push(entity);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 移除死亡实体
|
||||
deadEntities.forEach(entity => {
|
||||
@@ -306,28 +361,286 @@ class HealthSystem extends EntitySystem {
|
||||
}
|
||||
```
|
||||
|
||||
### Matcher完整API参考
|
||||
|
||||
#### 静态创建方法
|
||||
|
||||
```typescript
|
||||
// 基础静态方法
|
||||
const allMatcher = Matcher.all(PositionComponent, VelocityComponent); // 必须包含所有组件
|
||||
const anyMatcher = Matcher.any(WeaponComponent, MagicComponent); // 必须包含任意一个组件
|
||||
const noneMatcher = Matcher.none(DeadComponent, DisabledComponent); // 不能包含任何指定组件
|
||||
|
||||
// 特殊查询静态方法
|
||||
const tagMatcher = Matcher.byTag(1); // 按标签查询
|
||||
const nameMatcher = Matcher.byName("Player"); // 按名称查询
|
||||
const componentMatcher = Matcher.byComponent(HealthComponent); // 单组件查询
|
||||
|
||||
// 构建器方法
|
||||
const complexMatcher = Matcher.complex(); // 创建复杂查询构建器
|
||||
const emptyMatcher = Matcher.empty(); // 创建空匹配器
|
||||
```
|
||||
|
||||
#### 实例方法 - 条件构建
|
||||
|
||||
```typescript
|
||||
// 基础条件方法
|
||||
const matcher = Matcher.empty()
|
||||
.all(PositionComponent, VelocityComponent) // 必须包含所有组件
|
||||
.any(WeaponComponent, MagicComponent) // 必须包含任意一个组件
|
||||
.none(DeadComponent, StunnedComponent); // 不能包含任何指定组件
|
||||
|
||||
// 别名方法(提供更语义化的API)
|
||||
const semanticMatcher = Matcher.empty()
|
||||
.all(PositionComponent)
|
||||
.exclude(DeadComponent) // exclude() 等同于 none()
|
||||
.one(WeaponComponent, MagicComponent); // one() 等同于 any()
|
||||
|
||||
// 特殊条件方法
|
||||
const advancedMatcher = Matcher.empty()
|
||||
.all(EnemyComponent)
|
||||
.withTag(2) // 指定标签
|
||||
.withName("Boss") // 指定名称
|
||||
.withComponent(HealthComponent); // 单组件条件
|
||||
```
|
||||
|
||||
#### 条件移除方法
|
||||
|
||||
```typescript
|
||||
// 移除特殊条件
|
||||
const matcher = Matcher.byTag(1)
|
||||
.withName("Player")
|
||||
.withComponent(HealthComponent);
|
||||
|
||||
// 移除各种条件
|
||||
matcher.withoutTag(); // 移除标签条件
|
||||
matcher.withoutName(); // 移除名称条件
|
||||
matcher.withoutComponent(); // 移除单组件条件
|
||||
```
|
||||
|
||||
#### 实用工具方法
|
||||
|
||||
```typescript
|
||||
// 检查和调试
|
||||
const matcher = Matcher.all(PositionComponent, VelocityComponent)
|
||||
.none(DeadComponent);
|
||||
|
||||
// 检查是否为空条件
|
||||
if (matcher.isEmpty()) {
|
||||
console.log('匹配器没有设置任何条件');
|
||||
}
|
||||
|
||||
// 获取条件信息(只读)
|
||||
const condition = matcher.getCondition();
|
||||
console.log('必须组件:', condition.all.map(c => c.name));
|
||||
console.log('任选组件:', condition.any.map(c => c.name));
|
||||
console.log('排除组件:', condition.none.map(c => c.name));
|
||||
console.log('标签:', condition.tag);
|
||||
console.log('名称:', condition.name);
|
||||
console.log('单组件:', condition.component?.name);
|
||||
|
||||
// 调试输出
|
||||
console.log('匹配器描述:', matcher.toString());
|
||||
// 输出: "Matcher[all(PositionComponent, VelocityComponent) & none(DeadComponent)]"
|
||||
```
|
||||
|
||||
#### 克隆和重置
|
||||
|
||||
```typescript
|
||||
// 克隆匹配器
|
||||
const baseMatcher = Matcher.all(PositionComponent);
|
||||
const livingMatcher = baseMatcher.clone().all(HealthComponent).none(DeadComponent);
|
||||
const deadMatcher = baseMatcher.clone().all(DeadComponent);
|
||||
|
||||
// 重置匹配器
|
||||
const reusableMatcher = Matcher.all(PositionComponent);
|
||||
console.log(reusableMatcher.toString()); // "Matcher[all(PositionComponent)]"
|
||||
|
||||
reusableMatcher.reset(); // 清空所有条件
|
||||
console.log(reusableMatcher.toString()); // "Matcher[]"
|
||||
|
||||
reusableMatcher.all(PlayerComponent); // 重新设置条件
|
||||
console.log(reusableMatcher.toString()); // "Matcher[all(PlayerComponent)]"
|
||||
```
|
||||
|
||||
#### 链式调用示例
|
||||
|
||||
```typescript
|
||||
// 复杂的链式调用
|
||||
const complexMatcher = Matcher.empty()
|
||||
.all(PositionComponent, RenderComponent) // 必须有位置和渲染组件
|
||||
.any(PlayerComponent, NPCComponent) // 必须是玩家或NPC
|
||||
.none(DeadComponent, HiddenComponent) // 不能死亡或隐藏
|
||||
.withTag(1) // 标签为1
|
||||
.exclude(DisabledComponent); // 不能被禁用
|
||||
|
||||
// 在EntitySystem中使用
|
||||
class VisibleCharacterSystem extends EntitySystem {
|
||||
constructor() {
|
||||
super(complexMatcher);
|
||||
}
|
||||
|
||||
protected process(entities: Entity[]): void {
|
||||
// entities已经是符合所有条件的实体
|
||||
for (const entity of entities) {
|
||||
// 处理可见角色的逻辑
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 最佳实践
|
||||
|
||||
### 1. 查询优化
|
||||
### 1. Matcher使用建议
|
||||
|
||||
- 尽量使用类型安全的查询方法
|
||||
- 避免在每帧都创建新的查询
|
||||
- 合理使用缓存机制
|
||||
```typescript
|
||||
// 推荐的用法:
|
||||
const matcher = Matcher.all(Position, Velocity);
|
||||
|
||||
### 2. 性能监控
|
||||
// 在系统中使用
|
||||
class MySystem extends EntitySystem {
|
||||
constructor() {
|
||||
super(Matcher.all(RequiredComponent));
|
||||
}
|
||||
|
||||
protected process(entities: Entity[]): void {
|
||||
// entities已经是系统自动过滤的结果
|
||||
for (const entity of entities) {
|
||||
// 处理逻辑...
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- 定期检查查询性能报告
|
||||
// 避免在process方法中重复查询
|
||||
class InefficientSystem extends EntitySystem {
|
||||
protected process(entities: Entity[]): void {
|
||||
// 不必要的额外查询,性能差
|
||||
const condition = Matcher.all(RequiredComponent).getCondition();
|
||||
const result = this.scene.querySystem.query(condition);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Matcher API最佳实践
|
||||
|
||||
#### 选择合适的创建方式
|
||||
|
||||
```typescript
|
||||
// ✅ 推荐:单一条件使用静态方法
|
||||
const movingEntities = Matcher.all(PositionComponent, VelocityComponent);
|
||||
const playerEntities = Matcher.byTag(PLAYER_TAG);
|
||||
const specificEntity = Matcher.byName("Boss");
|
||||
|
||||
// ✅ 推荐:复杂条件使用链式调用
|
||||
const complexMatcher = Matcher.empty()
|
||||
.all(PositionComponent, HealthComponent)
|
||||
.any(WeaponComponent, MagicComponent)
|
||||
.none(DeadComponent);
|
||||
|
||||
// ❌ 不推荐:简单条件使用复杂语法
|
||||
const simpleButBad = Matcher.empty().all(PositionComponent);
|
||||
// 应该用: Matcher.all(PositionComponent)
|
||||
```
|
||||
|
||||
#### 合理使用别名方法
|
||||
|
||||
```typescript
|
||||
// 使用语义化的别名提高可读性
|
||||
const combatUnits = Matcher.all(PositionComponent, HealthComponent)
|
||||
.one(WeaponComponent, MagicComponent) // one() 比 any() 更语义化
|
||||
.exclude(DeadComponent, PacifistComponent); // exclude() 比 none() 更直观
|
||||
```
|
||||
|
||||
#### 合理的克隆和重用
|
||||
|
||||
```typescript
|
||||
// ✅ 推荐:基础匹配器重用
|
||||
const livingEntityMatcher = Matcher.all(HealthComponent).none(DeadComponent);
|
||||
const livingPlayerMatcher = livingEntityMatcher.clone().all(PlayerComponent);
|
||||
const livingEnemyMatcher = livingEntityMatcher.clone().all(EnemyComponent);
|
||||
|
||||
// ✅ 推荐:重置匹配器重用
|
||||
const reusableMatcher = Matcher.empty();
|
||||
|
||||
// 用于玩家系统
|
||||
reusableMatcher.reset().all(PlayerComponent);
|
||||
const playerSystem = new PlayerSystem(reusableMatcher.clone());
|
||||
|
||||
// 用于敌人系统
|
||||
reusableMatcher.reset().all(EnemyComponent);
|
||||
const enemySystem = new EnemySystem(reusableMatcher.clone());
|
||||
```
|
||||
|
||||
#### 调试和维护
|
||||
|
||||
```typescript
|
||||
// 在开发阶段添加调试信息
|
||||
const debugMatcher = Matcher.all(ComplexComponent)
|
||||
.any(VariantA, VariantB)
|
||||
.none(DisabledComponent);
|
||||
|
||||
if (DEBUG_MODE) {
|
||||
console.log('系统匹配条件:', debugMatcher.toString());
|
||||
const condition = debugMatcher.getCondition();
|
||||
console.log('预期匹配实体数:',
|
||||
scene.querySystem.queryAll(...condition.all).count);
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 查询优化
|
||||
|
||||
- **使用Matcher封装复杂查询**:提供更好的可读性和缓存
|
||||
- **避免频繁创建查询**:在系统初始化时创建,重复使用
|
||||
- **合理使用any()和none()条件**:减少不必要的实体遍历
|
||||
- **利用Matcher的缓存机制**:自动优化重复查询性能
|
||||
- **使用克隆方法复用基础条件**:避免重复定义相似的匹配条件
|
||||
- **选择合适的静态方法**:单一条件优先使用对应的静态方法
|
||||
|
||||
### 3. 性能监控
|
||||
|
||||
- 定期检查查询性能报告
|
||||
- 监控缓存命中率
|
||||
- 优化频繁使用的查询
|
||||
- 使用性能测试验证优化效果
|
||||
|
||||
### 3. 内存管理
|
||||
### 4. 内存管理
|
||||
|
||||
- 及时清理不需要的查询监听器
|
||||
- 合理设置缓存大小
|
||||
- 避免创建过多的查询快照
|
||||
- 适当使用Matcher的clone()和reset()方法
|
||||
|
||||
### 4. 代码组织
|
||||
### 5. 代码组织
|
||||
|
||||
- 将复杂查询封装到专门的方法中
|
||||
- 使用查询构建器创建可读性更好的查询
|
||||
- 在系统中合理组织查询逻辑
|
||||
- **系统级别的Matcher**:在系统中创建和管理Matcher
|
||||
- **查询逻辑封装**:将复杂查询封装到专门的方法中
|
||||
- **条件复用**:使用clone()方法复用基础查询条件
|
||||
- **清晰的命名**:给Matcher变量使用描述性的名称
|
||||
|
||||
### 6. 迁移指南
|
||||
|
||||
系统中Matcher的推荐用法:
|
||||
|
||||
```typescript
|
||||
// 在EntitySystem中使用Matcher
|
||||
class MySystem extends EntitySystem {
|
||||
constructor() {
|
||||
super(Matcher.all(ComponentA, ComponentB).none(ComponentC));
|
||||
}
|
||||
|
||||
protected process(entities: Entity[]): void {
|
||||
// entities已经是系统自动过滤的结果
|
||||
for (const entity of entities) {
|
||||
// 处理逻辑
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 使用最佳实践
|
||||
|
||||
- 在EntitySystem构造函数中传入Matcher
|
||||
- 使用`none()`来排除组件
|
||||
- 使用`any()`来匹配任意组件
|
||||
- 直接使用EntitySystem的entities参数,避免额外查询
|
||||
- 定期检查查询性能和缓存命中率
|
||||
@@ -7,11 +7,11 @@
|
||||
### 什么是场景?
|
||||
|
||||
场景是一个完整的游戏世界容器,它包含:
|
||||
- 🎮 **实体集合** - 所有游戏对象
|
||||
- **实体集合** - 所有游戏对象
|
||||
- ⚙️ **系统集合** - 处理游戏逻辑的系统
|
||||
- 📊 **事件系统** - 场景内的事件通信
|
||||
- 🔍 **查询系统** - 高效的实体查询
|
||||
- 📈 **性能监控** - 场景级别的性能统计
|
||||
- **事件系统** - 场景内的事件通信
|
||||
- **查询系统** - 高效的实体查询
|
||||
- **性能监控** - 场景级别的性能统计
|
||||
|
||||
```typescript
|
||||
import { Scene, Core } from '@esengine/ecs-framework';
|
||||
|
||||
@@ -7,10 +7,10 @@
|
||||
### 什么是系统?
|
||||
|
||||
系统是处理游戏逻辑的地方,它们:
|
||||
- 🎯 **专注单一职责** - 每个系统只处理一种类型的逻辑
|
||||
- 🔄 **自动执行** - 系统会在每帧自动被调用
|
||||
- 📊 **基于组件过滤** - 只处理包含特定组件的实体
|
||||
- ⚡ **高性能** - 利用ECS的数据局部性优势
|
||||
- **专注单一职责** - 每个系统只处理一种类型的逻辑
|
||||
- **自动执行** - 系统会在每帧自动被调用
|
||||
- **基于组件过滤** - 只处理包含特定组件的实体
|
||||
- **高性能** - 利用ECS的数据局部性优势
|
||||
|
||||
### 系统的工作原理
|
||||
|
||||
@@ -46,14 +46,17 @@ import { EntitySystem, Entity, Matcher } from '@esengine/ecs-framework';
|
||||
|
||||
class HealthSystem extends EntitySystem {
|
||||
constructor() {
|
||||
// 使用Matcher指定需要的组件
|
||||
super(Matcher.empty().all(HealthComponent));
|
||||
// 使用Matcher创建查询条件
|
||||
super(Matcher.all(HealthComponent));
|
||||
// 或者使用链式语法
|
||||
// super(Matcher.empty().all(HealthComponent));
|
||||
}
|
||||
|
||||
// 主要处理逻辑
|
||||
protected process(entities: Entity[]) {
|
||||
// 直接使用传入的entities参数,已经是匹配的实体
|
||||
for (const entity of entities) {
|
||||
const health = entity.getComponent(HealthComponent);
|
||||
const health = entity.getComponent(HealthComponent)!;
|
||||
|
||||
// 处理生命值逻辑
|
||||
if (health.currentHealth <= 0) {
|
||||
@@ -168,12 +171,21 @@ enum AIState {
|
||||
|
||||
class AISystem extends EntitySystem {
|
||||
constructor() {
|
||||
// 匹配所有有AI组件和位置组件的实体
|
||||
// 复杂匹配条件可以使用链式语法
|
||||
super(Matcher.empty().all(AIComponent, PositionComponent));
|
||||
// 或者使用简洁语法
|
||||
// super(Matcher.all(AIComponent, PositionComponent));
|
||||
}
|
||||
|
||||
// 处理每个匹配的实体
|
||||
public processEntity(entity: Entity) {
|
||||
// 处理所有匹配的实体
|
||||
protected process(entities: Entity[]) {
|
||||
for (const entity of entities) {
|
||||
this.processEntity(entity);
|
||||
}
|
||||
}
|
||||
|
||||
// 处理单个实体的逻辑(自定义方法)
|
||||
private processEntity(entity: Entity) {
|
||||
const ai = entity.getComponent(AIComponent);
|
||||
const position = entity.getComponent(PositionComponent);
|
||||
|
||||
@@ -251,8 +263,8 @@ class SpawnSystem extends IntervalSystem {
|
||||
|
||||
// 每2秒执行一次
|
||||
constructor() {
|
||||
// IntervalSystem需要指定Matcher和间隔时间
|
||||
super(Matcher.empty().all(SpawnerComponent), 2.0);
|
||||
// IntervalSystem需要指定间隔时间和Matcher
|
||||
super(2.0, Matcher.all(SpawnerComponent));
|
||||
}
|
||||
|
||||
// 间隔执行的逻辑(重写process方法)
|
||||
@@ -312,7 +324,7 @@ class SpawnSystem extends IntervalSystem {
|
||||
|
||||
### 4. PassiveSystem - 被动系统
|
||||
|
||||
不主动遍历实体,而是响应事件的系统。
|
||||
不处理实体的系统,主要用于事件监听和响应。
|
||||
|
||||
```typescript
|
||||
import { PassiveSystem, Matcher, Core } from '@esengine/ecs-framework';
|
||||
@@ -390,12 +402,11 @@ class ScoreSystem extends PassiveSystem {
|
||||
```typescript
|
||||
class ExampleSystem extends EntitySystem {
|
||||
/**
|
||||
* 系统初始化 - 系统被添加到场景时调用
|
||||
* 系统初始化回调 - 系统被添加到场景时调用
|
||||
* 用于设置事件监听器、初始化资源等
|
||||
* 注意:不要重写initialize()方法,而是重写onInitialize()
|
||||
*/
|
||||
initialize() {
|
||||
super.initialize();
|
||||
|
||||
protected onInitialize() {
|
||||
// 设置事件监听
|
||||
const eventBus = this.scene.entityManager.eventBus;
|
||||
eventBus.on('someEvent', this.handleEvent, { context: this });
|
||||
@@ -403,33 +414,10 @@ class ExampleSystem extends EntitySystem {
|
||||
console.log('系统已初始化');
|
||||
}
|
||||
|
||||
/**
|
||||
* 实体被添加到系统时调用
|
||||
* @param entity 被添加的实体
|
||||
*/
|
||||
protected onAdded(entity: Entity) {
|
||||
console.log(`实体 ${entity.name} 被添加到系统`);
|
||||
|
||||
// 可以在这里对新实体进行特殊处理
|
||||
const component = entity.getComponent(SomeComponent);
|
||||
component.initialize();
|
||||
}
|
||||
|
||||
/**
|
||||
* 实体从系统中移除时调用
|
||||
* @param entity 被移除的实体
|
||||
*/
|
||||
protected onRemoved(entity: Entity) {
|
||||
console.log(`实体 ${entity.name} 从系统中移除`);
|
||||
|
||||
// 清理与该实体相关的资源
|
||||
this.cleanupEntityResources(entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* 每帧处理开始前调用
|
||||
*/
|
||||
protected begin() {
|
||||
protected onBegin() {
|
||||
// 预处理逻辑,如重置计数器
|
||||
this.frameCounter++;
|
||||
}
|
||||
@@ -457,7 +445,7 @@ class ExampleSystem extends EntitySystem {
|
||||
/**
|
||||
* 每帧处理结束后调用
|
||||
*/
|
||||
protected end() {
|
||||
protected onEnd() {
|
||||
// 后处理逻辑,如统计数据更新
|
||||
this.updateStatistics();
|
||||
}
|
||||
@@ -468,14 +456,13 @@ class ExampleSystem extends EntitySystem {
|
||||
|
||||
系统的生命周期方法按以下顺序执行:
|
||||
|
||||
1. **initialize()** - 系统被添加到场景时执行一次
|
||||
2. **onAdded(entity)** - 当实体符合系统条件时执行
|
||||
3. **onRemoved(entity)** - 当实体不再符合系统条件时执行
|
||||
4. 每帧循环:
|
||||
- **begin()** - 帧开始前
|
||||
- **process(entities)** - 主要处理逻辑
|
||||
- **lateProcess(entities)** - 后期处理
|
||||
- **end()** - 帧结束后
|
||||
1. **initialize()** - 系统被添加到场景时执行一次(框架调用)
|
||||
- **onInitialize()** - 用户可重写的初始化回调
|
||||
2. 每帧循环:
|
||||
- **onBegin()** - 帧开始前(用户可重写)
|
||||
- **process(entities)** - 主要处理逻辑(用户必须实现)
|
||||
- **lateProcess(entities)** - 后期处理(用户可重写)
|
||||
- **onEnd()** - 帧结束后(用户可重写)
|
||||
|
||||
## 系统管理和注册
|
||||
|
||||
@@ -530,7 +517,7 @@ scene.removeEntityProcessor(gameLogicSystem);
|
||||
### 1. 单一职责原则
|
||||
|
||||
```typescript
|
||||
// ✅ 好的设计:每个系统只负责一件事
|
||||
// 好的设计:每个系统只负责一件事
|
||||
class MovementSystem extends EntitySystem {
|
||||
// 只负责移动
|
||||
}
|
||||
@@ -543,7 +530,7 @@ class RenderSystem extends EntitySystem {
|
||||
// 只负责渲染
|
||||
}
|
||||
|
||||
// ❌ 不好的设计:一个系统做太多事情
|
||||
// 不好的设计:一个系统做太多事情
|
||||
class GameplaySystem extends EntitySystem {
|
||||
// 既处理移动,又处理碰撞,还处理渲染...
|
||||
}
|
||||
@@ -650,7 +637,7 @@ A: 非常重要!合理的执行顺序可以避免逻辑错误:
|
||||
|
||||
A:
|
||||
- **EntitySystem** - 大部分游戏逻辑(移动、AI、碰撞等)
|
||||
- **ProcessingSystem** - 复杂的单实体处理(复杂AI、粒子系统)
|
||||
- **ProcessingSystem** - 不依赖特定实体的全局处理(游戏状态管理、全局逻辑)
|
||||
- **IntervalSystem** - 不需要每帧执行的逻辑(生成器、自动保存)
|
||||
- **PassiveSystem** - 事件响应系统(分数、音效、UI更新)
|
||||
|
||||
|
||||
@@ -10,15 +10,15 @@
|
||||
- ⏰ **延迟执行** - 在指定时间后执行某个操作
|
||||
- 🔄 **重复执行** - 定期重复执行某个操作
|
||||
- 🛑 **取消执行** - 在执行前取消定时器
|
||||
- 🎯 **精确控制** - 精确控制执行时机
|
||||
- **精确控制** - 精确控制执行时机
|
||||
|
||||
### 定时器的优势
|
||||
|
||||
相比直接在游戏循环中计时,定时器系统提供:
|
||||
- 🧹 **自动管理** - 自动处理定时器的生命周期
|
||||
- 🎮 **游戏时间控制** - 支持游戏暂停、时间缩放
|
||||
- **游戏时间控制** - 支持游戏暂停、时间缩放
|
||||
- 💾 **内存优化** - 自动回收完成的定时器
|
||||
- 🔧 **易于使用** - 简单的API调用
|
||||
- **易于使用** - 简单的API调用
|
||||
|
||||
## 基础定时器使用
|
||||
|
||||
@@ -499,7 +499,7 @@ class LevelTimer {
|
||||
this.updateTimer.stop();
|
||||
|
||||
const completionTime = this.timeLimit - this.timeRemaining;
|
||||
console.log(`🎉 关卡完成!用时:${completionTime} 秒`);
|
||||
console.log(` 关卡完成!用时:${completionTime} 秒`);
|
||||
|
||||
// 根据剩余时间给予奖励
|
||||
this.calculateTimeBonus(this.timeRemaining);
|
||||
|
||||
Reference in New Issue
Block a user