fix: 修复系统 onAdded 回调受注册顺序影响的问题 (#270)
This commit is contained in:
@@ -321,11 +321,19 @@ export class Entity {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 通知Scene中的QuerySystem实体组件发生变动
|
* 通知Scene中的QuerySystem实体组件发生变动
|
||||||
|
*
|
||||||
|
* Notify the QuerySystem in Scene that entity components have changed
|
||||||
|
*
|
||||||
|
* @param changedComponentType 变化的组件类型(可选,用于优化通知) | Changed component type (optional, for optimized notification)
|
||||||
*/
|
*/
|
||||||
private notifyQuerySystems(): void {
|
private notifyQuerySystems(changedComponentType?: ComponentType): void {
|
||||||
if (this.scene && this.scene.querySystem) {
|
if (this.scene && this.scene.querySystem) {
|
||||||
this.scene.querySystem.updateEntity(this);
|
this.scene.querySystem.updateEntity(this);
|
||||||
this.scene.clearSystemEntityCaches();
|
this.scene.clearSystemEntityCaches();
|
||||||
|
// 事件驱动:立即通知关心该组件的系统 | Event-driven: notify systems that care about this component
|
||||||
|
if (this.scene.notifyEntityComponentChanged) {
|
||||||
|
this.scene.notifyEntityComponentChanged(this, changedComponentType);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -381,7 +389,7 @@ export class Entity {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
this.notifyQuerySystems();
|
this.notifyQuerySystems(componentType);
|
||||||
|
|
||||||
return component;
|
return component;
|
||||||
}
|
}
|
||||||
@@ -514,7 +522,7 @@ export class Entity {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
this.notifyQuerySystems();
|
this.notifyQuerySystems(componentType);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { Entity } from './Entity';
|
|||||||
import { EntityList } from './Utils/EntityList';
|
import { EntityList } from './Utils/EntityList';
|
||||||
import { IdentifierPool } from './Utils/IdentifierPool';
|
import { IdentifierPool } from './Utils/IdentifierPool';
|
||||||
import { EntitySystem } from './Systems/EntitySystem';
|
import { EntitySystem } from './Systems/EntitySystem';
|
||||||
import { ComponentStorageManager } from './Core/ComponentStorage';
|
import { ComponentStorageManager, ComponentType } from './Core/ComponentStorage';
|
||||||
import { QuerySystem } from './Core/QuerySystem';
|
import { QuerySystem } from './Core/QuerySystem';
|
||||||
import { TypeSafeEventSystem } from './Core/EventSystem';
|
import { TypeSafeEventSystem } from './Core/EventSystem';
|
||||||
import type { ReferenceTracker } from './Core/ReferenceTracker';
|
import type { ReferenceTracker } from './Core/ReferenceTracker';
|
||||||
@@ -120,9 +120,26 @@ export interface IScene {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 清除所有EntitySystem的实体缓存
|
* 清除所有EntitySystem的实体缓存
|
||||||
|
* Clear all EntitySystem entity caches
|
||||||
*/
|
*/
|
||||||
clearSystemEntityCaches(): void;
|
clearSystemEntityCaches(): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通知相关系统实体的组件发生了变化
|
||||||
|
*
|
||||||
|
* 当组件被添加或移除时调用,立即通知相关系统检查该实体是否匹配,
|
||||||
|
* 并触发 onAdded/onRemoved 回调。通过组件ID索引优化,只通知关心该组件的系统。
|
||||||
|
*
|
||||||
|
* Notify relevant systems that an entity's components have changed.
|
||||||
|
* Called when a component is added or removed, immediately notifying
|
||||||
|
* relevant systems to check if the entity matches and trigger onAdded/onRemoved callbacks.
|
||||||
|
* Optimized via component ID indexing to only notify systems that care about the changed component.
|
||||||
|
*
|
||||||
|
* @param entity 组件发生变化的实体 | The entity whose components changed
|
||||||
|
* @param changedComponentType 变化的组件类型(可选) | The changed component type (optional)
|
||||||
|
*/
|
||||||
|
notifyEntityComponentChanged(entity: Entity, changedComponentType?: ComponentType): void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 添加实体
|
* 添加实体
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -151,6 +151,30 @@ export class Scene implements IScene {
|
|||||||
*/
|
*/
|
||||||
private _systemAddCounter: number = 0;
|
private _systemAddCounter: number = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 组件ID到系统的索引映射
|
||||||
|
*
|
||||||
|
* 用于快速查找关心特定组件的系统,避免遍历所有系统。
|
||||||
|
* 使用组件ID(数字)而非ComponentType作为key,避免类引用问题。
|
||||||
|
*
|
||||||
|
* Component ID to systems index map.
|
||||||
|
* Used for fast lookup of systems that care about specific components.
|
||||||
|
* Uses component ID (number) instead of ComponentType as key to avoid class reference issues.
|
||||||
|
*/
|
||||||
|
private _componentIdToSystems: Map<number, Set<EntitySystem>> = new Map();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 需要接收所有组件变化通知的系统集合
|
||||||
|
*
|
||||||
|
* 包括使用 none 条件、tag/name 查询、或空匹配器的系统。
|
||||||
|
* 这些系统无法通过组件ID索引优化,需要在每次组件变化时都检查。
|
||||||
|
*
|
||||||
|
* Systems that need to receive all component change notifications.
|
||||||
|
* Includes systems using none conditions, tag/name queries, or empty matchers.
|
||||||
|
* These systems cannot be optimized via component ID indexing.
|
||||||
|
*/
|
||||||
|
private _globalNotifySystems: Set<EntitySystem> = new Set();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取场景中所有已注册的EntitySystem
|
* 获取场景中所有已注册的EntitySystem
|
||||||
*
|
*
|
||||||
@@ -344,6 +368,10 @@ export class Scene implements IScene {
|
|||||||
// 清空系统缓存
|
// 清空系统缓存
|
||||||
this._cachedSystems = null;
|
this._cachedSystems = null;
|
||||||
this._systemsOrderDirty = true;
|
this._systemsOrderDirty = true;
|
||||||
|
|
||||||
|
// 清空组件索引 | Clear component indices
|
||||||
|
this._componentIdToSystems.clear();
|
||||||
|
this._globalNotifySystems.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -453,6 +481,146 @@ export class Scene implements IScene {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通知相关系统实体的组件发生了变化
|
||||||
|
*
|
||||||
|
* 这是事件驱动设计的核心:当组件被添加或移除时,立即通知相关系统检查该实体是否匹配,
|
||||||
|
* 并触发 onAdded/onRemoved 回调。通过组件ID索引优化,只通知关心该组件的系统。
|
||||||
|
*
|
||||||
|
* This is the core of event-driven design: when a component is added or removed,
|
||||||
|
* immediately notify relevant systems to check if the entity matches and trigger
|
||||||
|
* onAdded/onRemoved callbacks. Optimized via component ID indexing to only notify
|
||||||
|
* systems that care about the changed component.
|
||||||
|
*
|
||||||
|
* @param entity 组件发生变化的实体 | The entity whose components changed
|
||||||
|
* @param changedComponentType 变化的组件类型(可选) | The changed component type (optional)
|
||||||
|
*/
|
||||||
|
public notifyEntityComponentChanged(entity: Entity, changedComponentType?: ComponentType): void {
|
||||||
|
// 已通知的系统集合,避免重复通知 | Set of notified systems to avoid duplicates
|
||||||
|
const notifiedSystems = new Set<EntitySystem>();
|
||||||
|
|
||||||
|
// 如果提供了组件类型,使用索引优化 | If component type provided, use index optimization
|
||||||
|
if (changedComponentType && ComponentRegistry.isRegistered(changedComponentType)) {
|
||||||
|
const componentId = ComponentRegistry.getBitIndex(changedComponentType);
|
||||||
|
const interestedSystems = this._componentIdToSystems.get(componentId);
|
||||||
|
|
||||||
|
if (interestedSystems) {
|
||||||
|
for (const system of interestedSystems) {
|
||||||
|
system.handleEntityComponentChanged(entity);
|
||||||
|
notifiedSystems.add(system);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 通知全局监听系统(none条件、tag/name查询等) | Notify global listener systems
|
||||||
|
for (const system of this._globalNotifySystems) {
|
||||||
|
if (!notifiedSystems.has(system)) {
|
||||||
|
system.handleEntityComponentChanged(entity);
|
||||||
|
notifiedSystems.add(system);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果没有提供组件类型,回退到遍历所有系统 | Fallback to all systems if no component type
|
||||||
|
if (!changedComponentType) {
|
||||||
|
for (const system of this.systems) {
|
||||||
|
if (!notifiedSystems.has(system)) {
|
||||||
|
system.handleEntityComponentChanged(entity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将系统添加到组件索引
|
||||||
|
*
|
||||||
|
* 根据系统的 Matcher 条件,将系统注册到相应的组件ID索引中。
|
||||||
|
*
|
||||||
|
* Index a system by its interested component types.
|
||||||
|
* Registers the system to component ID indices based on its Matcher conditions.
|
||||||
|
*
|
||||||
|
* @param system 要索引的系统 | The system to index
|
||||||
|
*/
|
||||||
|
private indexSystemByComponents(system: EntitySystem): void {
|
||||||
|
const matcher = system.matcher;
|
||||||
|
if (!matcher) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// nothing 匹配器不需要索引 | Nothing matcher doesn't need indexing
|
||||||
|
if (matcher.isNothing()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const condition = matcher.getCondition();
|
||||||
|
|
||||||
|
// 有 none/tag/name 条件的系统加入全局通知 | Systems with none/tag/name go to global
|
||||||
|
if (condition.none.length > 0 || condition.tag !== undefined || condition.name !== undefined) {
|
||||||
|
this._globalNotifySystems.add(system);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 空匹配器(匹配所有实体)加入全局通知 | Empty matcher (matches all) goes to global
|
||||||
|
if (matcher.isEmpty()) {
|
||||||
|
this._globalNotifySystems.add(system);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 索引 all 条件中的组件 | Index components in all condition
|
||||||
|
for (const componentType of condition.all) {
|
||||||
|
this.addSystemToComponentIndex(componentType, system);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 索引 any 条件中的组件 | Index components in any condition
|
||||||
|
for (const componentType of condition.any) {
|
||||||
|
this.addSystemToComponentIndex(componentType, system);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 索引单组件查询 | Index single component query
|
||||||
|
if (condition.component) {
|
||||||
|
this.addSystemToComponentIndex(condition.component, system);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将系统添加到指定组件的索引
|
||||||
|
*
|
||||||
|
* Add system to the index for a specific component type.
|
||||||
|
*
|
||||||
|
* @param componentType 组件类型 | Component type
|
||||||
|
* @param system 系统 | System
|
||||||
|
*/
|
||||||
|
private addSystemToComponentIndex(componentType: ComponentType, system: EntitySystem): void {
|
||||||
|
if (!ComponentRegistry.isRegistered(componentType)) {
|
||||||
|
ComponentRegistry.register(componentType);
|
||||||
|
}
|
||||||
|
|
||||||
|
const componentId = ComponentRegistry.getBitIndex(componentType);
|
||||||
|
let systems = this._componentIdToSystems.get(componentId);
|
||||||
|
|
||||||
|
if (!systems) {
|
||||||
|
systems = new Set();
|
||||||
|
this._componentIdToSystems.set(componentId, systems);
|
||||||
|
}
|
||||||
|
|
||||||
|
systems.add(system);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从组件索引中移除系统
|
||||||
|
*
|
||||||
|
* Remove a system from all component indices.
|
||||||
|
*
|
||||||
|
* @param system 要移除的系统 | The system to remove
|
||||||
|
*/
|
||||||
|
private removeSystemFromIndex(system: EntitySystem): void {
|
||||||
|
// 从全局通知列表移除 | Remove from global notify list
|
||||||
|
this._globalNotifySystems.delete(system);
|
||||||
|
|
||||||
|
// 从所有组件索引中移除 | Remove from all component indices
|
||||||
|
for (const systems of this._componentIdToSystems.values()) {
|
||||||
|
systems.delete(system);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 在场景的实体列表中添加一个实体
|
* 在场景的实体列表中添加一个实体
|
||||||
* @param entity 要添加的实体
|
* @param entity 要添加的实体
|
||||||
@@ -738,6 +906,9 @@ export class Scene implements IScene {
|
|||||||
// 标记系统列表已变化
|
// 标记系统列表已变化
|
||||||
this.markSystemsOrderDirty();
|
this.markSystemsOrderDirty();
|
||||||
|
|
||||||
|
// 建立组件类型到系统的索引 | Build component type to system index
|
||||||
|
this.indexSystemByComponents(system);
|
||||||
|
|
||||||
injectProperties(system, this._services);
|
injectProperties(system, this._services);
|
||||||
|
|
||||||
// 调试模式下自动包装系统方法以收集性能数据(ProfilerSDK 启用时表示调试模式)
|
// 调试模式下自动包装系统方法以收集性能数据(ProfilerSDK 启用时表示调试模式)
|
||||||
@@ -822,6 +993,9 @@ export class Scene implements IScene {
|
|||||||
// 标记系统列表已变化
|
// 标记系统列表已变化
|
||||||
this.markSystemsOrderDirty();
|
this.markSystemsOrderDirty();
|
||||||
|
|
||||||
|
// 从组件类型索引中移除 | Remove from component type index
|
||||||
|
this.removeSystemFromIndex(processor);
|
||||||
|
|
||||||
// 重置System状态
|
// 重置System状态
|
||||||
processor.reset();
|
processor.reset();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -235,7 +235,7 @@ export abstract class EntitySystem implements ISystemBase, IService {
|
|||||||
* 在系统创建时调用。框架内部使用,用户不应直接调用。
|
* 在系统创建时调用。框架内部使用,用户不应直接调用。
|
||||||
*/
|
*/
|
||||||
public initialize(): void {
|
public initialize(): void {
|
||||||
// 防止重复初始化
|
// 防止重复初始化 | Prevent re-initialization
|
||||||
if (this._initialized) {
|
if (this._initialized) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -243,13 +243,20 @@ export abstract class EntitySystem implements ISystemBase, IService {
|
|||||||
this._initialized = true;
|
this._initialized = true;
|
||||||
|
|
||||||
// 框架内部初始化:触发一次实体查询,以便正确跟踪现有实体
|
// 框架内部初始化:触发一次实体查询,以便正确跟踪现有实体
|
||||||
|
// Framework initialization: query entities once to track existing entities
|
||||||
if (this.scene) {
|
if (this.scene) {
|
||||||
// 清理缓存确保初始化时重新查询
|
// 清理缓存确保初始化时重新查询 | Clear cache to ensure fresh query
|
||||||
this._entityCache.invalidate();
|
this._entityCache.invalidate();
|
||||||
this.queryEntities();
|
const entities = this.queryEntities();
|
||||||
|
|
||||||
|
// 初始化时对已存在的匹配实体触发 onAdded
|
||||||
|
// Trigger onAdded for existing matching entities during initialization
|
||||||
|
for (const entity of entities) {
|
||||||
|
this.onAdded(entity);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 调用用户可重写的初始化方法
|
// 调用用户可重写的初始化方法 | Call user-overridable initialization method
|
||||||
this.onInitialize();
|
this.onInitialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -718,32 +725,151 @@ export abstract class EntitySystem implements ISystemBase, IService {
|
|||||||
return `${this._systemName}[${entityCount} entities]${perfInfo}`;
|
return `${this._systemName}[${entityCount} entities]${perfInfo}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查实体是否匹配当前系统的查询条件
|
||||||
|
* Check if an entity matches this system's query condition
|
||||||
|
*
|
||||||
|
* @param entity 要检查的实体 / The entity to check
|
||||||
|
* @returns 是否匹配 / Whether the entity matches
|
||||||
|
*/
|
||||||
|
public matchesEntity(entity: Entity): boolean {
|
||||||
|
if (!this._matcher) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// nothing 匹配器不匹配任何实体
|
||||||
|
if (this._matcher.isNothing()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 空匹配器匹配所有实体
|
||||||
|
if (this._matcher.isEmpty()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const condition = this._matcher.getCondition();
|
||||||
|
|
||||||
|
// 检查 all 条件
|
||||||
|
for (const componentType of condition.all) {
|
||||||
|
if (!entity.hasComponent(componentType)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查 any 条件
|
||||||
|
if (condition.any.length > 0) {
|
||||||
|
let hasAny = false;
|
||||||
|
for (const componentType of condition.any) {
|
||||||
|
if (entity.hasComponent(componentType)) {
|
||||||
|
hasAny = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!hasAny) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查 none 条件
|
||||||
|
for (const componentType of condition.none) {
|
||||||
|
if (entity.hasComponent(componentType)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查 tag 条件
|
||||||
|
if (condition.tag !== undefined && entity.tag !== condition.tag) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查 name 条件
|
||||||
|
if (condition.name !== undefined && entity.name !== condition.name) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查单组件条件
|
||||||
|
if (condition.component !== undefined && !entity.hasComponent(condition.component)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查实体是否正在被此系统跟踪
|
||||||
|
* Check if an entity is being tracked by this system
|
||||||
|
*
|
||||||
|
* @param entity 要检查的实体 / The entity to check
|
||||||
|
* @returns 是否正在跟踪 / Whether the entity is being tracked
|
||||||
|
*/
|
||||||
|
public isTracking(entity: Entity): boolean {
|
||||||
|
return this._entityCache.isTracked(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 当实体的组件发生变化时由 Scene 调用
|
||||||
|
*
|
||||||
|
* 立即检查实体是否匹配并触发 onAdded/onRemoved 回调。
|
||||||
|
* 这是事件驱动设计的核心:组件变化时立即通知相关系统。
|
||||||
|
*
|
||||||
|
* Called by Scene when an entity's components change.
|
||||||
|
* Immediately checks if the entity matches and triggers onAdded/onRemoved callbacks.
|
||||||
|
* This is the core of event-driven design: notify relevant systems immediately when components change.
|
||||||
|
*
|
||||||
|
* @param entity 组件发生变化的实体 / The entity whose components changed
|
||||||
|
* @internal 由 Scene.notifyEntityComponentChanged 调用 / Called by Scene.notifyEntityComponentChanged
|
||||||
|
*/
|
||||||
|
public handleEntityComponentChanged(entity: Entity): void {
|
||||||
|
if (!this._matcher || !this._enabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const wasTracked = this._entityCache.isTracked(entity);
|
||||||
|
const nowMatches = this.matchesEntity(entity);
|
||||||
|
|
||||||
|
if (!wasTracked && nowMatches) {
|
||||||
|
// 新匹配:添加跟踪并触发 onAdded | New match: add tracking and trigger onAdded
|
||||||
|
this._entityCache.addTracked(entity);
|
||||||
|
this._entityCache.invalidate();
|
||||||
|
this.onAdded(entity);
|
||||||
|
} else if (wasTracked && !nowMatches) {
|
||||||
|
// 不再匹配:移除跟踪并触发 onRemoved | No longer matches: remove tracking and trigger onRemoved
|
||||||
|
this._entityCache.removeTracked(entity);
|
||||||
|
this._entityCache.invalidate();
|
||||||
|
this.onRemoved(entity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 更新实体跟踪,检查新增和移除的实体
|
* 更新实体跟踪,检查新增和移除的实体
|
||||||
|
*
|
||||||
|
* 由于采用了事件驱动设计,运行时的 onAdded/onRemoved 已在 handleEntityComponentChanged 中
|
||||||
|
* 立即触发。此方法不再触发回调,只同步跟踪状态。
|
||||||
|
*
|
||||||
|
* With event-driven design, runtime onAdded/onRemoved are triggered immediately in
|
||||||
|
* handleEntityComponentChanged. This method no longer triggers callbacks, only syncs tracking state.
|
||||||
*/
|
*/
|
||||||
private updateEntityTracking(currentEntities: readonly Entity[]): void {
|
private updateEntityTracking(currentEntities: readonly Entity[]): void {
|
||||||
const currentSet = new Set(currentEntities);
|
const currentSet = new Set(currentEntities);
|
||||||
let hasChanged = false;
|
let hasChanged = false;
|
||||||
|
|
||||||
// 检查新增的实体
|
// 检查新增的实体 | Check for newly added entities
|
||||||
for (const entity of currentEntities) {
|
for (const entity of currentEntities) {
|
||||||
if (!this._entityCache.isTracked(entity)) {
|
if (!this._entityCache.isTracked(entity)) {
|
||||||
this._entityCache.addTracked(entity);
|
this._entityCache.addTracked(entity);
|
||||||
this.onAdded(entity);
|
|
||||||
hasChanged = true;
|
hasChanged = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查移除的实体
|
// 检查移除的实体 | Check for removed entities
|
||||||
for (const entity of this._entityCache.getTracked()) {
|
for (const entity of this._entityCache.getTracked()) {
|
||||||
if (!currentSet.has(entity)) {
|
if (!currentSet.has(entity)) {
|
||||||
this._entityCache.removeTracked(entity);
|
this._entityCache.removeTracked(entity);
|
||||||
this.onRemoved(entity);
|
|
||||||
hasChanged = true;
|
hasChanged = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 如果实体发生了变化,使缓存失效
|
// 如果实体发生了变化,使缓存失效 | If entities changed, invalidate cache
|
||||||
if (hasChanged) {
|
if (hasChanged) {
|
||||||
this._entityCache.invalidate();
|
this._entityCache.invalidate();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -439,6 +439,215 @@ describe('EntitySystem', () => {
|
|||||||
|
|
||||||
scene.removeSystem(trackingSystem);
|
scene.removeSystem(trackingSystem);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('在系统 process 中添加组件时应立即触发其他系统的 onAdded', () => {
|
||||||
|
// 使用独立的场景,避免 beforeEach 创建的实体干扰
|
||||||
|
// Use independent scene to avoid interference from beforeEach entities
|
||||||
|
const testScene = new Scene();
|
||||||
|
|
||||||
|
// 组件定义
|
||||||
|
class TagComponent extends TestComponent {}
|
||||||
|
|
||||||
|
// SystemA: 匹配 TestComponent + TagComponent
|
||||||
|
class SystemA extends EntitySystem {
|
||||||
|
public onAddedEntities: Entity[] = [];
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super(Matcher.all(TestComponent, TagComponent));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override onAdded(entity: Entity): void {
|
||||||
|
this.onAddedEntities.push(entity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TriggerSystem: 在 process 中添加 TagComponent
|
||||||
|
class TriggerSystem extends EntitySystem {
|
||||||
|
constructor() {
|
||||||
|
super(Matcher.all(TestComponent));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override process(entities: readonly Entity[]): void {
|
||||||
|
for (const entity of entities) {
|
||||||
|
if (!entity.hasComponent(TagComponent)) {
|
||||||
|
entity.addComponent(new TagComponent());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const systemA = new SystemA();
|
||||||
|
const triggerSystem = new TriggerSystem();
|
||||||
|
|
||||||
|
// 注意:SystemA 先注册,TriggerSystem 后注册
|
||||||
|
// 事件驱动设计确保即使 SystemA 已执行完毕,也能收到 onAdded 通知
|
||||||
|
testScene.addSystem(systemA);
|
||||||
|
testScene.addSystem(triggerSystem);
|
||||||
|
|
||||||
|
// 创建实体(已有 TestComponent)
|
||||||
|
const testEntity = testScene.createEntity('test');
|
||||||
|
testEntity.addComponent(new TestComponent());
|
||||||
|
|
||||||
|
// 执行一帧:TriggerSystem 会添加 TagComponent,SystemA 应立即收到 onAdded
|
||||||
|
testScene.update();
|
||||||
|
|
||||||
|
expect(systemA.onAddedEntities.length).toBe(1);
|
||||||
|
expect(systemA.onAddedEntities[0]).toBe(testEntity);
|
||||||
|
|
||||||
|
testScene.removeSystem(systemA);
|
||||||
|
testScene.removeSystem(triggerSystem);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('同一帧内添加后移除组件,onAdded 和 onRemoved 都应触发', () => {
|
||||||
|
// 使用独立的场景,避免 beforeEach 创建的实体干扰
|
||||||
|
// Use independent scene to avoid interference from beforeEach entities
|
||||||
|
const testScene = new Scene();
|
||||||
|
|
||||||
|
class TagComponent extends TestComponent {}
|
||||||
|
|
||||||
|
class TrackingSystemWithTag extends EntitySystem {
|
||||||
|
public onAddedEntities: Entity[] = [];
|
||||||
|
public onRemovedEntities: Entity[] = [];
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super(Matcher.all(TestComponent, TagComponent));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override onAdded(entity: Entity): void {
|
||||||
|
this.onAddedEntities.push(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override onRemoved(entity: Entity): void {
|
||||||
|
this.onRemovedEntities.push(entity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddSystem: 在 process 中添加 TagComponent
|
||||||
|
class AddSystem extends EntitySystem {
|
||||||
|
constructor() {
|
||||||
|
super(Matcher.all(TestComponent));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override process(entities: readonly Entity[]): void {
|
||||||
|
for (const entity of entities) {
|
||||||
|
if (!entity.hasComponent(TagComponent)) {
|
||||||
|
entity.addComponent(new TagComponent());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveSystem: 在 lateProcess 中移除 TagComponent
|
||||||
|
class RemoveSystem extends EntitySystem {
|
||||||
|
constructor() {
|
||||||
|
super(Matcher.all(TagComponent));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override lateProcess(entities: readonly Entity[]): void {
|
||||||
|
for (const entity of entities) {
|
||||||
|
const tag = entity.getComponent(TagComponent);
|
||||||
|
if (tag) {
|
||||||
|
entity.removeComponent(tag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const trackingSystem = new TrackingSystemWithTag();
|
||||||
|
const addSystem = new AddSystem();
|
||||||
|
const removeSystem = new RemoveSystem();
|
||||||
|
|
||||||
|
testScene.addSystem(trackingSystem);
|
||||||
|
testScene.addSystem(addSystem);
|
||||||
|
testScene.addSystem(removeSystem);
|
||||||
|
|
||||||
|
const testEntity = testScene.createEntity('test');
|
||||||
|
testEntity.addComponent(new TestComponent());
|
||||||
|
|
||||||
|
// 执行一帧
|
||||||
|
testScene.update();
|
||||||
|
|
||||||
|
// AddSystem 添加了 TagComponent,RemoveSystem 在 lateProcess 中移除
|
||||||
|
expect(testEntity.hasComponent(TagComponent)).toBe(false);
|
||||||
|
|
||||||
|
// 事件驱动:onAdded 应该在组件添加时立即触发
|
||||||
|
expect(trackingSystem.onAddedEntities.length).toBe(1);
|
||||||
|
// onRemoved 应该在组件移除时立即触发
|
||||||
|
expect(trackingSystem.onRemovedEntities.length).toBe(1);
|
||||||
|
|
||||||
|
testScene.removeSystem(trackingSystem);
|
||||||
|
testScene.removeSystem(addSystem);
|
||||||
|
testScene.removeSystem(removeSystem);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('多个系统监听同一组件变化时都应收到 onAdded 通知', () => {
|
||||||
|
// 使用独立的场景,避免 beforeEach 创建的实体干扰
|
||||||
|
// Use independent scene to avoid interference from beforeEach entities
|
||||||
|
const testScene = new Scene();
|
||||||
|
|
||||||
|
// 使用独立的组件类,避免继承带来的问题
|
||||||
|
// Use independent component class to avoid inheritance issues
|
||||||
|
class TagComponent2 extends Component {}
|
||||||
|
|
||||||
|
class SystemA extends EntitySystem {
|
||||||
|
public onAddedEntities: Entity[] = [];
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super(Matcher.all(TestComponent, TagComponent2));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override onAdded(entity: Entity): void {
|
||||||
|
this.onAddedEntities.push(entity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SystemB extends EntitySystem {
|
||||||
|
public onAddedEntities: Entity[] = [];
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super(Matcher.all(TestComponent, TagComponent2));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override onAdded(entity: Entity): void {
|
||||||
|
this.onAddedEntities.push(entity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class TriggerSystem extends EntitySystem {
|
||||||
|
constructor() {
|
||||||
|
super(Matcher.all(TestComponent));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override process(entities: readonly Entity[]): void {
|
||||||
|
for (const entity of entities) {
|
||||||
|
if (!entity.hasComponent(TagComponent2)) {
|
||||||
|
entity.addComponent(new TagComponent2());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const systemA = new SystemA();
|
||||||
|
const systemB = new SystemB();
|
||||||
|
const triggerSystem = new TriggerSystem();
|
||||||
|
|
||||||
|
testScene.addSystem(systemA);
|
||||||
|
testScene.addSystem(systemB);
|
||||||
|
testScene.addSystem(triggerSystem);
|
||||||
|
|
||||||
|
const testEntity = testScene.createEntity('test');
|
||||||
|
testEntity.addComponent(new TestComponent());
|
||||||
|
|
||||||
|
testScene.update();
|
||||||
|
|
||||||
|
// 两个系统都应收到 onAdded 通知
|
||||||
|
expect(systemA.onAddedEntities.length).toBe(1);
|
||||||
|
expect(systemB.onAddedEntities.length).toBe(1);
|
||||||
|
|
||||||
|
testScene.removeSystem(systemA);
|
||||||
|
testScene.removeSystem(systemB);
|
||||||
|
testScene.removeSystem(triggerSystem);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('reset 方法', () => {
|
describe('reset 方法', () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user