273 lines
12 KiB
TypeScript
273 lines
12 KiB
TypeScript
import { Matcher } from '../../../src/ECS/Utils/Matcher';
|
|
import { Entity } from '../../../src/ECS/Entity';
|
|
import { Component } from '../../../src/ECS/Component';
|
|
import { ComponentTypeManager } from '../../../src/ECS/Utils/ComponentTypeManager';
|
|
|
|
// 简单测试组件
|
|
class SimplePositionComponent extends Component {
|
|
constructor(public x: number = 0, public y: number = 0) {
|
|
super();
|
|
}
|
|
}
|
|
|
|
class SimpleVelocityComponent extends Component {
|
|
constructor(public vx: number = 0, public vy: number = 0) {
|
|
super();
|
|
}
|
|
}
|
|
|
|
class SimpleHealthComponent extends Component {
|
|
constructor(public health: number = 100) {
|
|
super();
|
|
}
|
|
}
|
|
|
|
describe('Matcher集成测试', () => {
|
|
beforeEach(() => {
|
|
// 重置组件类型管理器
|
|
ComponentTypeManager.instance.reset();
|
|
// 重置Entity的静态eventBus以避免副作用
|
|
Entity.eventBus = null;
|
|
});
|
|
|
|
describe('基础实体匹配测试', () => {
|
|
test('空匹配器匹配所有实体', () => {
|
|
const matcher = Matcher.empty();
|
|
|
|
const entity1 = new Entity('Entity1', 1);
|
|
const entity2 = new Entity('Entity2', 2);
|
|
|
|
expect(matcher.isInterestedEntity(entity1)).toBe(true);
|
|
expect(matcher.isInterestedEntity(entity2)).toBe(true);
|
|
});
|
|
|
|
test('单一组件匹配测试', () => {
|
|
const matcher = Matcher.empty().all(SimplePositionComponent);
|
|
|
|
const entityWithoutComponents = new Entity('Empty', 1);
|
|
expect(matcher.isInterestedEntity(entityWithoutComponents)).toBe(false);
|
|
|
|
// 小心添加组件,这里可能是问题所在
|
|
const entityWithPosition = new Entity('WithPosition', 2);
|
|
try {
|
|
entityWithPosition.addComponent(new SimplePositionComponent(10, 20));
|
|
expect(matcher.isInterestedEntity(entityWithPosition)).toBe(true);
|
|
} catch (error) {
|
|
console.error('Error adding component:', error);
|
|
throw error;
|
|
}
|
|
});
|
|
|
|
test('多组件匹配测试', () => {
|
|
const matcher = Matcher.empty().all(SimplePositionComponent, SimpleVelocityComponent);
|
|
|
|
const completeEntity = new Entity('Complete', 1);
|
|
completeEntity.addComponent(new SimplePositionComponent(10, 20));
|
|
completeEntity.addComponent(new SimpleVelocityComponent(1, 1));
|
|
|
|
const partialEntity = new Entity('Partial', 2);
|
|
partialEntity.addComponent(new SimplePositionComponent(0, 0));
|
|
|
|
expect(matcher.isInterestedEntity(completeEntity)).toBe(true);
|
|
expect(matcher.isInterestedEntity(partialEntity)).toBe(false);
|
|
});
|
|
|
|
test('排除组件匹配测试', () => {
|
|
const matcher = Matcher.empty()
|
|
.all(SimplePositionComponent)
|
|
.exclude(SimpleHealthComponent);
|
|
|
|
const normalEntity = new Entity('Normal', 1);
|
|
normalEntity.addComponent(new SimplePositionComponent(10, 20));
|
|
|
|
const excludedEntity = new Entity('Excluded', 2);
|
|
excludedEntity.addComponent(new SimplePositionComponent(50, 60));
|
|
excludedEntity.addComponent(new SimpleHealthComponent(100));
|
|
|
|
expect(matcher.isInterestedEntity(normalEntity)).toBe(true);
|
|
expect(matcher.isInterestedEntity(excludedEntity)).toBe(false);
|
|
});
|
|
|
|
test('任一组件匹配测试', () => {
|
|
const matcher = Matcher.empty().one(SimpleVelocityComponent, SimpleHealthComponent);
|
|
|
|
const velocityEntity = new Entity('Velocity', 1);
|
|
velocityEntity.addComponent(new SimpleVelocityComponent(1, 1));
|
|
|
|
const healthEntity = new Entity('Health', 2);
|
|
healthEntity.addComponent(new SimpleHealthComponent(100));
|
|
|
|
const neitherEntity = new Entity('Neither', 3);
|
|
neitherEntity.addComponent(new SimplePositionComponent(0, 0));
|
|
|
|
expect(matcher.isInterestedEntity(velocityEntity)).toBe(true);
|
|
expect(matcher.isInterestedEntity(healthEntity)).toBe(true);
|
|
expect(matcher.isInterestedEntity(neitherEntity)).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe('复杂匹配组合测试', () => {
|
|
test('all + exclude组合', () => {
|
|
const matcher = Matcher.empty()
|
|
.all(SimplePositionComponent, SimpleVelocityComponent)
|
|
.exclude(SimpleHealthComponent);
|
|
|
|
const validEntity = new Entity('Valid', 1);
|
|
validEntity.addComponent(new SimplePositionComponent(10, 20));
|
|
validEntity.addComponent(new SimpleVelocityComponent(1, 1));
|
|
|
|
const excludedEntity = new Entity('Excluded', 2);
|
|
excludedEntity.addComponent(new SimplePositionComponent(30, 40));
|
|
excludedEntity.addComponent(new SimpleVelocityComponent(2, 2));
|
|
excludedEntity.addComponent(new SimpleHealthComponent(100));
|
|
|
|
const incompleteEntity = new Entity('Incomplete', 3);
|
|
incompleteEntity.addComponent(new SimplePositionComponent(50, 60));
|
|
|
|
expect(matcher.isInterestedEntity(validEntity)).toBe(true);
|
|
expect(matcher.isInterestedEntity(excludedEntity)).toBe(false);
|
|
expect(matcher.isInterestedEntity(incompleteEntity)).toBe(false);
|
|
});
|
|
|
|
test('all + one组合', () => {
|
|
const matcher = Matcher.empty()
|
|
.all(SimplePositionComponent)
|
|
.one(SimpleVelocityComponent, SimpleHealthComponent);
|
|
|
|
const velocityEntity = new Entity('Velocity', 1);
|
|
velocityEntity.addComponent(new SimplePositionComponent(10, 20));
|
|
velocityEntity.addComponent(new SimpleVelocityComponent(1, 1));
|
|
|
|
const healthEntity = new Entity('Health', 2);
|
|
healthEntity.addComponent(new SimplePositionComponent(30, 40));
|
|
healthEntity.addComponent(new SimpleHealthComponent(100));
|
|
|
|
const bothEntity = new Entity('Both', 3);
|
|
bothEntity.addComponent(new SimplePositionComponent(50, 60));
|
|
bothEntity.addComponent(new SimpleVelocityComponent(2, 2));
|
|
bothEntity.addComponent(new SimpleHealthComponent(80));
|
|
|
|
const invalidEntity = new Entity('Invalid', 4);
|
|
invalidEntity.addComponent(new SimplePositionComponent(70, 80));
|
|
|
|
expect(matcher.isInterestedEntity(velocityEntity)).toBe(true);
|
|
expect(matcher.isInterestedEntity(healthEntity)).toBe(true);
|
|
expect(matcher.isInterestedEntity(bothEntity)).toBe(true);
|
|
expect(matcher.isInterestedEntity(invalidEntity)).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe('动态组件变化测试', () => {
|
|
test('实体组件动态添加', () => {
|
|
const matcher = Matcher.empty().all(SimplePositionComponent, SimpleVelocityComponent);
|
|
|
|
const entity = new Entity('Dynamic', 1);
|
|
|
|
// 初始状态:不匹配
|
|
expect(matcher.isInterestedEntity(entity)).toBe(false);
|
|
|
|
// 添加位置组件:仍不匹配
|
|
entity.addComponent(new SimplePositionComponent(10, 20));
|
|
expect(matcher.isInterestedEntity(entity)).toBe(false);
|
|
|
|
// 添加速度组件:现在匹配
|
|
entity.addComponent(new SimpleVelocityComponent(1, 1));
|
|
expect(matcher.isInterestedEntity(entity)).toBe(true);
|
|
});
|
|
|
|
test('匹配器配置修改', () => {
|
|
const matcher = Matcher.empty().all(SimplePositionComponent);
|
|
|
|
const entity = new Entity('Test', 1);
|
|
entity.addComponent(new SimplePositionComponent(10, 20));
|
|
entity.addComponent(new SimpleVelocityComponent(1, 1));
|
|
|
|
// 初始匹配
|
|
expect(matcher.isInterestedEntity(entity)).toBe(true);
|
|
|
|
// 修改匹配器,添加更多要求
|
|
matcher.all(SimpleHealthComponent);
|
|
|
|
// 现在应该不匹配(缺少健康组件)
|
|
expect(matcher.isInterestedEntity(entity)).toBe(false);
|
|
|
|
// 添加健康组件后应该匹配
|
|
entity.addComponent(new SimpleHealthComponent(100));
|
|
expect(matcher.isInterestedEntity(entity)).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe('边界情况测试', () => {
|
|
test('空实体测试', () => {
|
|
const allMatcher = Matcher.empty().all(SimplePositionComponent);
|
|
const excludeMatcher = Matcher.empty().exclude(SimplePositionComponent);
|
|
const oneMatcher = Matcher.empty().one(SimplePositionComponent, SimpleVelocityComponent);
|
|
|
|
const emptyEntity = new Entity('Empty', 1);
|
|
|
|
expect(allMatcher.isInterestedEntity(emptyEntity)).toBe(false);
|
|
expect(excludeMatcher.isInterestedEntity(emptyEntity)).toBe(true);
|
|
expect(oneMatcher.isInterestedEntity(emptyEntity)).toBe(false);
|
|
});
|
|
|
|
test('重复组件类型配置', () => {
|
|
const matcher = Matcher.empty()
|
|
.all(SimplePositionComponent)
|
|
.all(SimplePositionComponent); // 重复添加
|
|
|
|
const entity = new Entity('Test', 1);
|
|
entity.addComponent(new SimplePositionComponent(10, 20));
|
|
|
|
// 应该仍然正常工作
|
|
expect(matcher.isInterestedEntity(entity)).toBe(true);
|
|
|
|
// 内部应该有重复的组件类型
|
|
expect(matcher.getAllSet().length).toBe(2);
|
|
});
|
|
});
|
|
|
|
describe('匹配器工具方法测试', () => {
|
|
test('toString方法', () => {
|
|
const emptyMatcher = Matcher.empty();
|
|
expect(emptyMatcher.toString()).toBe('Matcher()');
|
|
|
|
const simpleMatcher = Matcher.empty().all(SimplePositionComponent);
|
|
const str = simpleMatcher.toString();
|
|
expect(str).toContain('all: [SimplePositionComponent]');
|
|
|
|
const complexMatcher = Matcher.empty()
|
|
.all(SimplePositionComponent, SimpleVelocityComponent)
|
|
.exclude(SimpleHealthComponent);
|
|
|
|
const complexStr = complexMatcher.toString();
|
|
expect(complexStr).toContain('all: [SimplePositionComponent, SimpleVelocityComponent]');
|
|
expect(complexStr).toContain('exclude: [SimpleHealthComponent]');
|
|
});
|
|
|
|
test('配置获取方法', () => {
|
|
const matcher = Matcher.empty()
|
|
.all(SimplePositionComponent, SimpleVelocityComponent)
|
|
.exclude(SimpleHealthComponent);
|
|
|
|
expect(matcher.getAllSet()).toEqual([SimplePositionComponent, SimpleVelocityComponent]);
|
|
expect(matcher.getExclusionSet()).toEqual([SimpleHealthComponent]);
|
|
expect(matcher.getOneSet()).toEqual([]);
|
|
});
|
|
|
|
test('链式调用', () => {
|
|
const matcher = Matcher.empty();
|
|
const result = matcher
|
|
.all(SimplePositionComponent)
|
|
.exclude(SimpleHealthComponent)
|
|
.one(SimpleVelocityComponent);
|
|
|
|
expect(result).toBe(matcher);
|
|
});
|
|
});
|
|
|
|
afterEach(() => {
|
|
// 清理
|
|
ComponentTypeManager.instance.reset();
|
|
Entity.eventBus = null;
|
|
});
|
|
}); |