修复了QuerySystem在销毁实体时的内存泄漏问题

实现了完整的onAdded/onRemoved回调系统
修复了override修饰符和类型兼容性问题
This commit is contained in:
YHH
2025-08-06 09:39:08 +08:00
parent 69655f1936
commit ccbfa78070
7 changed files with 105 additions and 140 deletions

View File

@@ -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();
}

View File

@@ -933,8 +933,16 @@ export class Entity {
this.removeAllComponents();
// 从场景中移除
if (this.scene && this.scene.entities) {
this.scene.entities.remove(this);
if (this.scene) {
// 从查询系统中移除
if (this.scene.querySystem) {
this.scene.querySystem.removeEntity(this);
}
// 从实体列表中移除
if (this.scene.entities) {
this.scene.entities.remove(this);
}
}
}

View File

@@ -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([]);
}
/**

View File

@@ -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)) {
// 单一条件优化查询
currentEntities = this.executeSingleConditionQuery(condition, querySystem);
} else {
// 复合查询
currentEntities = this.executeComplexQuery(condition, querySystem);
}
// 单一条件优化查询
if (this.isSingleCondition(condition)) {
return this.executeSingleConditionQuery(condition, querySystem);
}
// 检查实体变化并触发回调
this.updateEntityTracking(currentEntities);
// 复合查询
return this.executeComplexQuery(condition, querySystem);
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 {
// 子类可以重写此方法
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}

View File

@@ -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);
}
}
}
}