修复了QuerySystem在销毁实体时的内存泄漏问题
实现了完整的onAdded/onRemoved回调系统 修复了override修饰符和类型兼容性问题
This commit is contained in:
@@ -1,121 +0,0 @@
|
||||
/**
|
||||
* 简化后的Matcher使用示例
|
||||
* 展示框架自动处理QuerySystem的优雅设计
|
||||
*/
|
||||
|
||||
import { Scene } from '../src/ECS/Scene';
|
||||
import { Component } from '../src/ECS/Component';
|
||||
import { Matcher } from '../src/ECS/Utils/Matcher';
|
||||
|
||||
// 示例组件
|
||||
class Position extends Component {
|
||||
constructor(public x: number = 0, public y: number = 0) {
|
||||
super();
|
||||
}
|
||||
}
|
||||
|
||||
class Velocity extends Component {
|
||||
constructor(public vx: number = 0, public vy: number = 0) {
|
||||
super();
|
||||
}
|
||||
}
|
||||
|
||||
class Health extends Component {
|
||||
constructor(public hp: number = 100) {
|
||||
super();
|
||||
}
|
||||
}
|
||||
|
||||
class Dead extends Component {}
|
||||
|
||||
function demonstrateSimplifiedMatcher() {
|
||||
console.log('=== 简化的Matcher API示例 ===\n');
|
||||
|
||||
// 创建场景 - QuerySystem自动创建
|
||||
const scene = new Scene();
|
||||
scene.begin();
|
||||
|
||||
// 创建测试实体
|
||||
const player = scene.createEntity('Player');
|
||||
player.addComponent(new Position(100, 200));
|
||||
player.addComponent(new Velocity(5, 0));
|
||||
player.addComponent(new Health(100));
|
||||
|
||||
const enemy = scene.createEntity('Enemy');
|
||||
enemy.addComponent(new Position(300, 200));
|
||||
enemy.addComponent(new Health(50));
|
||||
|
||||
const corpse = scene.createEntity('Corpse');
|
||||
corpse.addComponent(new Position(150, 150));
|
||||
corpse.addComponent(new Dead());
|
||||
|
||||
// ===== 推荐的新API =====
|
||||
|
||||
// 1. 直接使用scene.querySystem创建Matcher
|
||||
const movingEntities = Matcher.create(scene.querySystem)
|
||||
.all(Position, Velocity)
|
||||
.query();
|
||||
|
||||
console.log('移动实体:', movingEntities.map(e => e.name));
|
||||
|
||||
// 2. 复合查询 - 活着的实体
|
||||
const aliveEntities = Matcher.create(scene.querySystem)
|
||||
.all(Health)
|
||||
.none(Dead)
|
||||
.query();
|
||||
|
||||
console.log('活着的实体:', aliveEntities.map(e => e.name));
|
||||
|
||||
// 3. 实用方法
|
||||
const healthMatcher = Matcher.create(scene.querySystem).all(Health);
|
||||
console.log(`有血量的实体数量: ${healthMatcher.count()}`);
|
||||
console.log(`玩家是否有血量: ${healthMatcher.matches(player)}`);
|
||||
|
||||
// 4. 系统中使用Matcher的典型模式
|
||||
class MovementSystem {
|
||||
private movementMatcher: Matcher;
|
||||
|
||||
constructor(scene: Scene) {
|
||||
// 在系统初始化时创建Matcher
|
||||
this.movementMatcher = Matcher.create(scene.querySystem)
|
||||
.all(Position, Velocity);
|
||||
}
|
||||
|
||||
update() {
|
||||
// 高效的批量查询
|
||||
const movableEntities = this.movementMatcher.query();
|
||||
|
||||
for (const entity of movableEntities) {
|
||||
const pos = entity.getComponent(Position)!;
|
||||
const vel = entity.getComponent(Velocity)!;
|
||||
|
||||
pos.x += vel.vx;
|
||||
pos.y += vel.vy;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 5. 创建并使用系统
|
||||
const movementSystem = new MovementSystem(scene);
|
||||
movementSystem.update();
|
||||
|
||||
console.log('玩家更新后位置:', player.getComponent(Position));
|
||||
|
||||
scene.end();
|
||||
}
|
||||
|
||||
// ===== 展示设计哲学 =====
|
||||
function designPhilosophy() {
|
||||
console.log('\n=== 设计哲学 ===');
|
||||
console.log('✅ QuerySystem是框架核心,总是存在');
|
||||
console.log('✅ Matcher强制要求QuerySystem,避免回退复杂性');
|
||||
console.log('✅ 清晰的错误提示,引导正确使用');
|
||||
console.log('✅ 旧API保持兼容,但明确标记deprecated');
|
||||
console.log('✅ 新API简洁明了,符合现代设计原则');
|
||||
}
|
||||
|
||||
// 运行示例
|
||||
if (require.main === module) {
|
||||
demonstrateSimplifiedMatcher();
|
||||
designPhilosophy();
|
||||
}
|
||||
@@ -933,10 +933,18 @@ export class Entity {
|
||||
this.removeAllComponents();
|
||||
|
||||
// 从场景中移除
|
||||
if (this.scene && this.scene.entities) {
|
||||
if (this.scene) {
|
||||
// 从查询系统中移除
|
||||
if (this.scene.querySystem) {
|
||||
this.scene.querySystem.removeEntity(this);
|
||||
}
|
||||
|
||||
// 从实体列表中移除
|
||||
if (this.scene.entities) {
|
||||
this.scene.entities.remove(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 比较实体
|
||||
|
||||
@@ -5,7 +5,7 @@ import { IdentifierPool } from './Utils/IdentifierPool';
|
||||
import { EntitySystem } from './Systems/EntitySystem';
|
||||
import { ComponentStorageManager } from './Core/ComponentStorage';
|
||||
import { QuerySystem } from './Core/QuerySystem';
|
||||
import { TypeSafeEventSystem, GlobalEventSystem } from './Core/EventSystem';
|
||||
import { TypeSafeEventSystem } from './Core/EventSystem';
|
||||
import { EventBus } from './Core/EventBus';
|
||||
|
||||
/**
|
||||
@@ -164,6 +164,9 @@ export class Scene {
|
||||
// 移除所有实体
|
||||
this.entities.removeAllEntities();
|
||||
|
||||
// 清理查询系统中的实体引用和缓存
|
||||
this.querySystem.setEntities([]);
|
||||
|
||||
// 清空组件存储
|
||||
this.componentStorageManager.clear();
|
||||
|
||||
@@ -281,6 +284,9 @@ export class Scene {
|
||||
*/
|
||||
public destroyAllEntities() {
|
||||
this.entities.removeAllEntities();
|
||||
|
||||
// 清理查询系统中的实体引用和缓存
|
||||
this.querySystem.setEntities([]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -35,6 +35,8 @@ export abstract class EntitySystem implements ISystemBase {
|
||||
private _systemName: string;
|
||||
private _initialized: boolean = false;
|
||||
private _matcher: Matcher;
|
||||
private _trackedEntities: Set<Entity> = new Set();
|
||||
private _lastQueryResult: Entity[] = [];
|
||||
|
||||
/**
|
||||
* 获取系统处理的实体列表(动态查询)
|
||||
@@ -134,6 +136,10 @@ export abstract class EntitySystem implements ISystemBase {
|
||||
* 子类可以重写此方法进行初始化操作。
|
||||
*/
|
||||
protected onInitialize(): void {
|
||||
// 初始化时触发一次实体查询,以便正确跟踪现有实体
|
||||
if (this.scene) {
|
||||
this.queryEntities();
|
||||
}
|
||||
// 子类可以重写此方法进行初始化
|
||||
}
|
||||
|
||||
@@ -144,6 +150,8 @@ export abstract class EntitySystem implements ISystemBase {
|
||||
*/
|
||||
public reset(): void {
|
||||
this._initialized = false;
|
||||
this._trackedEntities.clear();
|
||||
this._lastQueryResult = [];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -151,24 +159,30 @@ export abstract class EntitySystem implements ISystemBase {
|
||||
*/
|
||||
private queryEntities(): Entity[] {
|
||||
if (!this.scene?.querySystem || !this._matcher) {
|
||||
this._lastQueryResult = [];
|
||||
return [];
|
||||
}
|
||||
|
||||
const condition = this._matcher.getCondition();
|
||||
const querySystem = this.scene.querySystem;
|
||||
let currentEntities: Entity[] = [];
|
||||
|
||||
// 空条件返回所有实体
|
||||
if (this._matcher.isEmpty()) {
|
||||
return querySystem.getAllEntities();
|
||||
}
|
||||
|
||||
currentEntities = querySystem.getAllEntities();
|
||||
} else if (this.isSingleCondition(condition)) {
|
||||
// 单一条件优化查询
|
||||
if (this.isSingleCondition(condition)) {
|
||||
return this.executeSingleConditionQuery(condition, querySystem);
|
||||
currentEntities = this.executeSingleConditionQuery(condition, querySystem);
|
||||
} else {
|
||||
// 复合查询
|
||||
currentEntities = this.executeComplexQuery(condition, querySystem);
|
||||
}
|
||||
|
||||
// 复合查询
|
||||
return this.executeComplexQuery(condition, querySystem);
|
||||
// 检查实体变化并触发回调
|
||||
this.updateEntityTracking(currentEntities);
|
||||
|
||||
this._lastQueryResult = currentEntities;
|
||||
return currentEntities;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -436,4 +450,49 @@ export abstract class EntitySystem implements ISystemBase {
|
||||
|
||||
return `${this._systemName}[${entityCount} entities]${perfInfo}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新实体跟踪,检查新增和移除的实体
|
||||
*/
|
||||
private updateEntityTracking(currentEntities: Entity[]): void {
|
||||
const currentSet = new Set(currentEntities);
|
||||
|
||||
// 检查新增的实体
|
||||
for (const entity of currentEntities) {
|
||||
if (!this._trackedEntities.has(entity)) {
|
||||
this._trackedEntities.add(entity);
|
||||
this.onAdded(entity);
|
||||
}
|
||||
}
|
||||
|
||||
// 检查移除的实体
|
||||
for (const entity of this._trackedEntities) {
|
||||
if (!currentSet.has(entity)) {
|
||||
this._trackedEntities.delete(entity);
|
||||
this.onRemoved(entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 当实体被添加到系统时调用
|
||||
*
|
||||
* 子类可以重写此方法来处理实体添加事件。
|
||||
*
|
||||
* @param entity 被添加的实体
|
||||
*/
|
||||
protected onAdded(_entity: Entity): void {
|
||||
// 子类可以重写此方法
|
||||
}
|
||||
|
||||
/**
|
||||
* 当实体从系统中移除时调用
|
||||
*
|
||||
* 子类可以重写此方法来处理实体移除事件。
|
||||
*
|
||||
* @param entity 被移除的实体
|
||||
*/
|
||||
protected onRemoved(_entity: Entity): void {
|
||||
// 子类可以重写此方法
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,7 @@ export class TypeUtils {
|
||||
* @param obj 对象
|
||||
* @returns 对象的构造函数
|
||||
*/
|
||||
public static getType(obj: Record<string, unknown> & { constructor: Function }) {
|
||||
public static getType(obj: any) {
|
||||
return obj.constructor;
|
||||
}
|
||||
}
|
||||
@@ -9,8 +9,9 @@ class TestPositionComponent extends Component {
|
||||
public y: number = 0;
|
||||
public z: number = 0;
|
||||
|
||||
constructor(x = 0, y = 0, z = 0) {
|
||||
constructor(...args: unknown[]) {
|
||||
super();
|
||||
const [x = 0, y = 0, z = 0] = args as [number?, number?, number?];
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
@@ -24,8 +25,9 @@ class TestVelocityComponent extends Component {
|
||||
public vz: number = 0;
|
||||
public maxSpeed: number = 100;
|
||||
|
||||
constructor(vx = 0, vy = 0, vz = 0) {
|
||||
constructor(...args: unknown[]) {
|
||||
super();
|
||||
const [vx = 0, vy = 0, vz = 0] = args as [number?, number?, number?];
|
||||
this.vx = vx;
|
||||
this.vy = vy;
|
||||
this.vz = vz;
|
||||
@@ -38,8 +40,9 @@ class TestHealthComponent extends Component {
|
||||
public max: number = 100;
|
||||
public regeneration: number = 1;
|
||||
|
||||
constructor(current = 100, max = 100) {
|
||||
constructor(...args: unknown[]) {
|
||||
super();
|
||||
const [current = 100, max = 100] = args as [number?, number?];
|
||||
this.current = current;
|
||||
this.max = max;
|
||||
}
|
||||
@@ -51,8 +54,9 @@ class OriginalPositionComponent extends Component {
|
||||
public y: number = 0;
|
||||
public z: number = 0;
|
||||
|
||||
constructor(x = 0, y = 0, z = 0) {
|
||||
constructor(...args: unknown[]) {
|
||||
super();
|
||||
const [x = 0, y = 0, z = 0] = args as [number?, number?, number?];
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
@@ -65,8 +69,9 @@ class OriginalVelocityComponent extends Component {
|
||||
public vz: number = 0;
|
||||
public maxSpeed: number = 100;
|
||||
|
||||
constructor(vx = 0, vy = 0, vz = 0) {
|
||||
constructor(...args: unknown[]) {
|
||||
super();
|
||||
const [vx = 0, vy = 0, vz = 0] = args as [number?, number?, number?];
|
||||
this.vx = vx;
|
||||
this.vy = vy;
|
||||
this.vz = vz;
|
||||
@@ -78,8 +83,9 @@ class OriginalHealthComponent extends Component {
|
||||
public max: number = 100;
|
||||
public regeneration: number = 1;
|
||||
|
||||
constructor(current = 100, max = 100) {
|
||||
constructor(...args: unknown[]) {
|
||||
super();
|
||||
const [current = 100, max = 100] = args as [number?, number?];
|
||||
this.current = current;
|
||||
this.max = max;
|
||||
}
|
||||
|
||||
@@ -24,9 +24,16 @@ class TrackingSystem extends EntitySystem {
|
||||
const wasInitialized = (this as any)._initialized;
|
||||
super.initialize();
|
||||
|
||||
// 只有在真正执行初始化时才增加计数
|
||||
// 只有在真正执行初始化时才增加计数和处理实体
|
||||
if (!wasInitialized) {
|
||||
this.initializeCallCount++;
|
||||
|
||||
// 处理所有现有实体
|
||||
if (this.scene) {
|
||||
for (const entity of this.scene.entities.buffer) {
|
||||
this.onChanged(entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user