优化matcher内部实现改为querysystem

完善type类型
更新文档
This commit is contained in:
YHH
2025-07-31 11:56:04 +08:00
parent b7d17fb16d
commit 6ea366cfed
39 changed files with 2054 additions and 1898 deletions

View File

@@ -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参数避免额外查询
- 定期检查查询性能和缓存命中率