refactor(core): 改进事件系统类型安全并消除 ESLint 警告 (#208)
This commit is contained in:
@@ -89,10 +89,13 @@ export class EventBus implements IEventBus {
|
|||||||
const eventConfig: EventListenerConfig = {
|
const eventConfig: EventListenerConfig = {
|
||||||
once: config.once || false,
|
once: config.once || false,
|
||||||
priority: config.priority || EventPriority.NORMAL,
|
priority: config.priority || EventPriority.NORMAL,
|
||||||
async: config.async || false,
|
async: config.async || false
|
||||||
context: config.context
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (config.thisArg) {
|
||||||
|
eventConfig.thisArg = config.thisArg as object;
|
||||||
|
}
|
||||||
|
|
||||||
if (this.isDebugMode) {
|
if (this.isDebugMode) {
|
||||||
EventBus._logger.info(`添加监听器: ${eventType}`, eventConfig);
|
EventBus._logger.info(`添加监听器: ${eventType}`, eventConfig);
|
||||||
}
|
}
|
||||||
@@ -127,7 +130,7 @@ export class EventBus implements IEventBus {
|
|||||||
handler: (data: T) => Promise<void>,
|
handler: (data: T) => Promise<void>,
|
||||||
config: IEventListenerConfig = {}
|
config: IEventListenerConfig = {}
|
||||||
): string {
|
): string {
|
||||||
return this.on(eventType, handler as any, { ...config, async: true });
|
return this.on(eventType, handler, { ...config, async: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -466,4 +469,3 @@ export class GlobalEventBus {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -3,12 +3,12 @@ import { createLogger } from '../../Utils/Logger';
|
|||||||
/**
|
/**
|
||||||
* 事件处理器函数类型
|
* 事件处理器函数类型
|
||||||
*/
|
*/
|
||||||
export type EventHandler<T = any> = (event: T) => void;
|
export type EventHandler<T> = (event: T) => void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 异步事件处理器函数类型
|
* 异步事件处理器函数类型
|
||||||
*/
|
*/
|
||||||
export type AsyncEventHandler<T = any> = (event: T) => Promise<void>;
|
export type AsyncEventHandler<T> = (event: T) => Promise<void>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 事件监听器配置
|
* 事件监听器配置
|
||||||
@@ -20,15 +20,20 @@ export interface EventListenerConfig {
|
|||||||
priority?: number;
|
priority?: number;
|
||||||
/** 是否异步执行 */
|
/** 是否异步执行 */
|
||||||
async?: boolean;
|
async?: boolean;
|
||||||
/** 执行上下文 */
|
/** 事件处理函数的 this 绑定对象 */
|
||||||
context?: any;
|
thisArg?: object;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 内部事件监听器
|
* 内部事件监听器
|
||||||
|
*
|
||||||
|
* 注意:handler 使用 any 是必要的类型擦除
|
||||||
|
* 原因:需要在同一数组中存储处理不同事件类型(T)的监听器
|
||||||
|
* 类型安全保证:公共 API (on<T>/emit<T>) 在编译时保证类型匹配
|
||||||
*/
|
*/
|
||||||
interface InternalEventListener<T = any> {
|
interface InternalEventListener {
|
||||||
handler: EventHandler<T> | AsyncEventHandler<T>;
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
handler: EventHandler<any> | AsyncEventHandler<any>;
|
||||||
config: EventListenerConfig;
|
config: EventListenerConfig;
|
||||||
id: string;
|
id: string;
|
||||||
}
|
}
|
||||||
@@ -71,8 +76,9 @@ export class TypeSafeEventSystem {
|
|||||||
private static readonly _logger = createLogger('EventSystem');
|
private static readonly _logger = createLogger('EventSystem');
|
||||||
private listeners = new Map<string, InternalEventListener[]>();
|
private listeners = new Map<string, InternalEventListener[]>();
|
||||||
private stats = new Map<string, EventStats>();
|
private stats = new Map<string, EventStats>();
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
private batchQueue = new Map<string, any[]>();
|
private batchQueue = new Map<string, any[]>();
|
||||||
private batchTimers = new Map<string, number>();
|
private batchTimers = new Map<string, ReturnType<typeof setTimeout>>();
|
||||||
private batchConfigs = new Map<string, EventBatchConfig>();
|
private batchConfigs = new Map<string, EventBatchConfig>();
|
||||||
private nextListenerId = 0;
|
private nextListenerId = 0;
|
||||||
private isEnabled = true;
|
private isEnabled = true;
|
||||||
@@ -81,13 +87,13 @@ export class TypeSafeEventSystem {
|
|||||||
/**
|
/**
|
||||||
* 添加事件监听器
|
* 添加事件监听器
|
||||||
* @param eventType 事件类型
|
* @param eventType 事件类型
|
||||||
* @param handler 事件处理器
|
* @param handler 事件处理器(同步或异步,根据 config.async 决定)
|
||||||
* @param config 监听器配置
|
* @param config 监听器配置
|
||||||
* @returns 监听器ID(用于移除)
|
* @returns 监听器ID(用于移除)
|
||||||
*/
|
*/
|
||||||
public on<T>(
|
public on<T>(
|
||||||
eventType: string,
|
eventType: string,
|
||||||
handler: EventHandler<T>,
|
handler: EventHandler<T> | AsyncEventHandler<T>,
|
||||||
config: EventListenerConfig = {}
|
config: EventListenerConfig = {}
|
||||||
): string {
|
): string {
|
||||||
return this.addListener(eventType, handler, config);
|
return this.addListener(eventType, handler, config);
|
||||||
@@ -100,11 +106,7 @@ export class TypeSafeEventSystem {
|
|||||||
* @param config 监听器配置
|
* @param config 监听器配置
|
||||||
* @returns 监听器ID
|
* @returns 监听器ID
|
||||||
*/
|
*/
|
||||||
public once<T>(
|
public once<T>(eventType: string, handler: EventHandler<T>, config: EventListenerConfig = {}): string {
|
||||||
eventType: string,
|
|
||||||
handler: EventHandler<T>,
|
|
||||||
config: EventListenerConfig = {}
|
|
||||||
): string {
|
|
||||||
return this.addListener(eventType, handler, { ...config, once: true });
|
return this.addListener(eventType, handler, { ...config, once: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -115,11 +117,7 @@ export class TypeSafeEventSystem {
|
|||||||
* @param config 监听器配置
|
* @param config 监听器配置
|
||||||
* @returns 监听器ID
|
* @returns 监听器ID
|
||||||
*/
|
*/
|
||||||
public onAsync<T>(
|
public onAsync<T>(eventType: string, handler: AsyncEventHandler<T>, config: EventListenerConfig = {}): string {
|
||||||
eventType: string,
|
|
||||||
handler: AsyncEventHandler<T>,
|
|
||||||
config: EventListenerConfig = {}
|
|
||||||
): string {
|
|
||||||
return this.addListener(eventType, handler, { ...config, async: true });
|
return this.addListener(eventType, handler, { ...config, async: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -133,7 +131,7 @@ export class TypeSafeEventSystem {
|
|||||||
const listeners = this.listeners.get(eventType);
|
const listeners = this.listeners.get(eventType);
|
||||||
if (!listeners) return false;
|
if (!listeners) return false;
|
||||||
|
|
||||||
const index = listeners.findIndex(l => l.id === listenerId);
|
const index = listeners.findIndex((l) => l.id === listenerId);
|
||||||
if (index === -1) return false;
|
if (index === -1) return false;
|
||||||
|
|
||||||
listeners.splice(index, 1);
|
listeners.splice(index, 1);
|
||||||
@@ -197,8 +195,8 @@ export class TypeSafeEventSystem {
|
|||||||
if (listener.config.async) continue; // 跳过异步监听器
|
if (listener.config.async) continue; // 跳过异步监听器
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (listener.config.context) {
|
if (listener.config.thisArg) {
|
||||||
(listener.handler as EventHandler<T>).call(listener.config.context, event);
|
(listener.handler as EventHandler<T>).call(listener.config.thisArg, event);
|
||||||
} else {
|
} else {
|
||||||
(listener.handler as EventHandler<T>)(event);
|
(listener.handler as EventHandler<T>)(event);
|
||||||
}
|
}
|
||||||
@@ -344,7 +342,7 @@ export class TypeSafeEventSystem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const listenerId = `listener_${this.nextListenerId++}`;
|
const listenerId = `listener_${this.nextListenerId++}`;
|
||||||
const listener: InternalEventListener<T> = {
|
const listener: InternalEventListener = {
|
||||||
handler,
|
handler,
|
||||||
config: {
|
config: {
|
||||||
priority: 0,
|
priority: 0,
|
||||||
@@ -379,14 +377,14 @@ export class TypeSafeEventSystem {
|
|||||||
const sortedListeners = this.sortListenersByPriority(listeners);
|
const sortedListeners = this.sortListenersByPriority(listeners);
|
||||||
|
|
||||||
// 分离同步和异步监听器
|
// 分离同步和异步监听器
|
||||||
const syncListeners = sortedListeners.filter(l => !l.config.async);
|
const syncListeners = sortedListeners.filter((l) => !l.config.async);
|
||||||
const asyncListeners = sortedListeners.filter(l => l.config.async);
|
const asyncListeners = sortedListeners.filter((l) => l.config.async);
|
||||||
|
|
||||||
// 执行同步监听器
|
// 执行同步监听器
|
||||||
for (const listener of syncListeners) {
|
for (const listener of syncListeners) {
|
||||||
try {
|
try {
|
||||||
if (listener.config.context) {
|
if (listener.config.thisArg) {
|
||||||
(listener.handler as EventHandler<T>).call(listener.config.context, event);
|
(listener.handler as EventHandler<T>).call(listener.config.thisArg, event);
|
||||||
} else {
|
} else {
|
||||||
(listener.handler as EventHandler<T>)(event);
|
(listener.handler as EventHandler<T>)(event);
|
||||||
}
|
}
|
||||||
@@ -402,8 +400,8 @@ export class TypeSafeEventSystem {
|
|||||||
// 执行异步监听器
|
// 执行异步监听器
|
||||||
const asyncPromises = asyncListeners.map(async (listener) => {
|
const asyncPromises = asyncListeners.map(async (listener) => {
|
||||||
try {
|
try {
|
||||||
if (listener.config.context) {
|
if (listener.config.thisArg) {
|
||||||
await (listener.handler as AsyncEventHandler<T>).call(listener.config.context, event);
|
await (listener.handler as AsyncEventHandler<T>).call(listener.config.thisArg, event);
|
||||||
} else {
|
} else {
|
||||||
await (listener.handler as AsyncEventHandler<T>)(event);
|
await (listener.handler as AsyncEventHandler<T>)(event);
|
||||||
}
|
}
|
||||||
@@ -431,7 +429,7 @@ export class TypeSafeEventSystem {
|
|||||||
* @param listeners 监听器数组
|
* @param listeners 监听器数组
|
||||||
* @returns 排序后的监听器数组
|
* @returns 排序后的监听器数组
|
||||||
*/
|
*/
|
||||||
private sortListenersByPriority<T>(listeners: InternalEventListener<T>[]): InternalEventListener<T>[] {
|
private sortListenersByPriority(listeners: InternalEventListener[]): InternalEventListener[] {
|
||||||
return listeners.slice().sort((a, b) => (b.config.priority || 0) - (a.config.priority || 0));
|
return listeners.slice().sort((a, b) => (b.config.priority || 0) - (a.config.priority || 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -447,7 +445,7 @@ export class TypeSafeEventSystem {
|
|||||||
if (!listeners) return;
|
if (!listeners) return;
|
||||||
|
|
||||||
for (const id of listenerIds) {
|
for (const id of listenerIds) {
|
||||||
const index = listeners.findIndex(l => l.id === id);
|
const index = listeners.findIndex((l) => l.id === id);
|
||||||
if (index !== -1) {
|
if (index !== -1) {
|
||||||
listeners.splice(index, 1);
|
listeners.splice(index, 1);
|
||||||
}
|
}
|
||||||
@@ -488,7 +486,7 @@ export class TypeSafeEventSystem {
|
|||||||
this.flushBatch(eventType);
|
this.flushBatch(eventType);
|
||||||
}, config.delay);
|
}, config.delay);
|
||||||
|
|
||||||
this.batchTimers.set(eventType, timer as any);
|
this.batchTimers.set(eventType, timer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -577,5 +575,3 @@ export class TypeSafeEventSystem {
|
|||||||
* 全局事件系统实例
|
* 全局事件系统实例
|
||||||
*/
|
*/
|
||||||
export const GlobalEventSystem = new TypeSafeEventSystem();
|
export const GlobalEventSystem = new TypeSafeEventSystem();
|
||||||
|
|
||||||
|
|
||||||
166
packages/core/src/ECS/Systems/EntityCache.ts
Normal file
166
packages/core/src/ECS/Systems/EntityCache.ts
Normal file
@@ -0,0 +1,166 @@
|
|||||||
|
import { Entity } from '../Entity';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 实体缓存管理器
|
||||||
|
*
|
||||||
|
* 负责管理 EntitySystem 中的实体缓存,提供帧缓存和持久缓存两级缓存机制。
|
||||||
|
* 使用面向对象设计,将数据和行为封装在类中。
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* ```typescript
|
||||||
|
* const cache = new EntityCache();
|
||||||
|
* cache.setPersistent(entities);
|
||||||
|
* const cached = cache.getPersistent();
|
||||||
|
* cache.invalidate();
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
export class EntityCache {
|
||||||
|
/**
|
||||||
|
* 帧缓存
|
||||||
|
*
|
||||||
|
* 在update周期内使用,每帧结束后清理
|
||||||
|
*/
|
||||||
|
private _frameCache: readonly Entity[] | null = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 持久缓存
|
||||||
|
*
|
||||||
|
* 跨帧使用,直到被显式失效
|
||||||
|
*/
|
||||||
|
private _persistentCache: readonly Entity[] | null = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 被跟踪的实体集合
|
||||||
|
*
|
||||||
|
* 用于跟踪哪些实体正在被此系统处理
|
||||||
|
*/
|
||||||
|
private _trackedEntities: Set<Entity> = new Set();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取帧缓存
|
||||||
|
*/
|
||||||
|
public getFrame(): readonly Entity[] | null {
|
||||||
|
return this._frameCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置帧缓存
|
||||||
|
*
|
||||||
|
* @param entities 要缓存的实体列表
|
||||||
|
*/
|
||||||
|
public setFrame(entities: readonly Entity[]): void {
|
||||||
|
this._frameCache = entities;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取持久缓存
|
||||||
|
*/
|
||||||
|
public getPersistent(): readonly Entity[] | null {
|
||||||
|
return this._persistentCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置持久缓存
|
||||||
|
*
|
||||||
|
* @param entities 要缓存的实体列表
|
||||||
|
*/
|
||||||
|
public setPersistent(entities: readonly Entity[]): void {
|
||||||
|
this._persistentCache = entities;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取被跟踪的实体集合
|
||||||
|
*/
|
||||||
|
public getTracked(): ReadonlySet<Entity> {
|
||||||
|
return this._trackedEntities;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加被跟踪的实体
|
||||||
|
*
|
||||||
|
* @param entity 要跟踪的实体
|
||||||
|
*/
|
||||||
|
public addTracked(entity: Entity): void {
|
||||||
|
this._trackedEntities.add(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 移除被跟踪的实体
|
||||||
|
*
|
||||||
|
* @param entity 要移除的实体
|
||||||
|
*/
|
||||||
|
public removeTracked(entity: Entity): void {
|
||||||
|
this._trackedEntities.delete(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查实体是否被跟踪
|
||||||
|
*
|
||||||
|
* @param entity 要检查的实体
|
||||||
|
*/
|
||||||
|
public isTracked(entity: Entity): boolean {
|
||||||
|
return this._trackedEntities.has(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 使持久缓存失效
|
||||||
|
*
|
||||||
|
* 当实体变化时调用,强制下次查询时重新计算
|
||||||
|
*/
|
||||||
|
public invalidate(): void {
|
||||||
|
this._persistentCache = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清除帧缓存
|
||||||
|
*
|
||||||
|
* 在每帧结束时调用
|
||||||
|
*/
|
||||||
|
public clearFrame(): void {
|
||||||
|
this._frameCache = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清除所有缓存
|
||||||
|
*
|
||||||
|
* 在系统重置或销毁时调用
|
||||||
|
*/
|
||||||
|
public clearAll(): void {
|
||||||
|
this._frameCache = null;
|
||||||
|
this._persistentCache = null;
|
||||||
|
this._trackedEntities.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查是否有有效的持久缓存
|
||||||
|
*/
|
||||||
|
public hasPersistent(): boolean {
|
||||||
|
return this._persistentCache !== null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查是否有有效的帧缓存
|
||||||
|
*/
|
||||||
|
public hasFrame(): boolean {
|
||||||
|
return this._frameCache !== null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取缓存统计信息
|
||||||
|
*/
|
||||||
|
public getStats(): {
|
||||||
|
hasFrame: boolean;
|
||||||
|
hasPersistent: boolean;
|
||||||
|
trackedCount: number;
|
||||||
|
frameEntityCount: number;
|
||||||
|
persistentEntityCount: number;
|
||||||
|
} {
|
||||||
|
return {
|
||||||
|
hasFrame: this._frameCache !== null,
|
||||||
|
hasPersistent: this._persistentCache !== null,
|
||||||
|
trackedCount: this._trackedEntities.size,
|
||||||
|
frameEntityCount: this._frameCache?.length ?? 0,
|
||||||
|
persistentEntityCount: this._persistentCache?.length ?? 0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,14 +9,15 @@ import { createLogger } from '../../Utils/Logger';
|
|||||||
import type { EventListenerConfig, TypeSafeEventSystem, EventHandler } from '../Core/EventSystem';
|
import type { EventListenerConfig, TypeSafeEventSystem, EventHandler } from '../Core/EventSystem';
|
||||||
import type { ComponentConstructor, ComponentInstance } from '../../Types/TypeHelpers';
|
import type { ComponentConstructor, ComponentInstance } from '../../Types/TypeHelpers';
|
||||||
import type { IService } from '../../Core/ServiceContainer';
|
import type { IService } from '../../Core/ServiceContainer';
|
||||||
|
import { EntityCache } from './EntityCache';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 事件监听器记录
|
* 事件监听器记录
|
||||||
|
* 只存储引用信息,用于系统销毁时自动清理
|
||||||
*/
|
*/
|
||||||
interface EventListenerRecord {
|
interface EventListenerRecord {
|
||||||
eventSystem: TypeSafeEventSystem;
|
eventSystem: TypeSafeEventSystem;
|
||||||
eventType: string;
|
eventType: string;
|
||||||
handler: EventHandler;
|
|
||||||
listenerRef: string;
|
listenerRef: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,9 +64,7 @@ interface EventListenerRecord {
|
|||||||
* }
|
* }
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
export abstract class EntitySystem<_TComponents extends readonly ComponentConstructor[] = []>
|
export abstract class EntitySystem implements ISystemBase, IService {
|
||||||
implements ISystemBase, IService
|
|
||||||
{
|
|
||||||
private _updateOrder: number;
|
private _updateOrder: number;
|
||||||
private _enabled: boolean;
|
private _enabled: boolean;
|
||||||
private _performanceMonitor: PerformanceMonitor | null;
|
private _performanceMonitor: PerformanceMonitor | null;
|
||||||
@@ -85,30 +84,24 @@ export abstract class EntitySystem<_TComponents extends readonly ComponentConstr
|
|||||||
/**
|
/**
|
||||||
* 统一的实体缓存管理器
|
* 统一的实体缓存管理器
|
||||||
*/
|
*/
|
||||||
private _entityCache: {
|
private _entityCache: EntityCache;
|
||||||
frame: readonly Entity[] | null;
|
|
||||||
persistent: readonly Entity[] | null;
|
|
||||||
tracked: Set<Entity>;
|
|
||||||
invalidate(): void;
|
|
||||||
clearFrame(): void;
|
|
||||||
clearAll(): void;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取系统处理的实体列表
|
* 获取系统处理的实体列表
|
||||||
*/
|
*/
|
||||||
public get entities(): readonly Entity[] {
|
public get entities(): readonly Entity[] {
|
||||||
// 如果在update周期内,优先使用帧缓存
|
// 如果在update周期内,优先使用帧缓存
|
||||||
if (this._entityCache.frame !== null) {
|
const frameCache = this._entityCache.getFrame();
|
||||||
return this._entityCache.frame;
|
if (frameCache !== null) {
|
||||||
|
return frameCache;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 否则使用持久缓存
|
// 否则使用持久缓存
|
||||||
if (this._entityCache.persistent === null) {
|
if (!this._entityCache.hasPersistent()) {
|
||||||
this._entityCache.persistent = this.queryEntities();
|
this._entityCache.setPersistent(this.queryEntities());
|
||||||
}
|
}
|
||||||
|
|
||||||
return this._entityCache.persistent;
|
return this._entityCache.getPersistent()!;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -159,22 +152,7 @@ export abstract class EntitySystem<_TComponents extends readonly ComponentConstr
|
|||||||
// 初始化logger
|
// 初始化logger
|
||||||
this.logger = createLogger(this.getLoggerName());
|
this.logger = createLogger(this.getLoggerName());
|
||||||
|
|
||||||
this._entityCache = {
|
this._entityCache = new EntityCache();
|
||||||
frame: null,
|
|
||||||
persistent: null,
|
|
||||||
tracked: new Set<Entity>(),
|
|
||||||
invalidate() {
|
|
||||||
this.persistent = null;
|
|
||||||
},
|
|
||||||
clearFrame() {
|
|
||||||
this.frame = null;
|
|
||||||
},
|
|
||||||
clearAll() {
|
|
||||||
this.frame = null;
|
|
||||||
this.persistent = null;
|
|
||||||
this.tracked.clear();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -521,7 +499,7 @@ export abstract class EntitySystem<_TComponents extends readonly ComponentConstr
|
|||||||
const entityMap = this.getEntityIdMap(allEntities);
|
const entityMap = this.getEntityIdMap(allEntities);
|
||||||
|
|
||||||
const size = idSet.size;
|
const size = idSet.size;
|
||||||
const result = new Array(size);
|
const result = new Array<Entity>(size);
|
||||||
let index = 0;
|
let index = 0;
|
||||||
|
|
||||||
for (const id of idSet) {
|
for (const id of idSet) {
|
||||||
@@ -564,10 +542,11 @@ export abstract class EntitySystem<_TComponents extends readonly ComponentConstr
|
|||||||
this.onBegin();
|
this.onBegin();
|
||||||
// 查询实体并存储到帧缓存中
|
// 查询实体并存储到帧缓存中
|
||||||
// 响应式查询会自动维护最新的实体列表,updateEntityTracking会在检测到变化时invalidate
|
// 响应式查询会自动维护最新的实体列表,updateEntityTracking会在检测到变化时invalidate
|
||||||
this._entityCache.frame = this.queryEntities();
|
const queriedEntities = this.queryEntities();
|
||||||
entityCount = this._entityCache.frame.length;
|
this._entityCache.setFrame(queriedEntities);
|
||||||
|
entityCount = queriedEntities.length;
|
||||||
|
|
||||||
this.process(this._entityCache.frame);
|
this.process(queriedEntities);
|
||||||
} finally {
|
} finally {
|
||||||
monitor.endMonitoring(this._systemName, startTime, entityCount);
|
monitor.endMonitoring(this._systemName, startTime, entityCount);
|
||||||
}
|
}
|
||||||
@@ -587,7 +566,7 @@ export abstract class EntitySystem<_TComponents extends readonly ComponentConstr
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
// 使用缓存的实体列表,避免重复查询
|
// 使用缓存的实体列表,避免重复查询
|
||||||
const entities = this._entityCache.frame || [];
|
const entities = this._entityCache.getFrame() || [];
|
||||||
entityCount = entities.length;
|
entityCount = entities.length;
|
||||||
this.lateProcess(entities);
|
this.lateProcess(entities);
|
||||||
this.onEnd();
|
this.onEnd();
|
||||||
@@ -697,17 +676,17 @@ export abstract class EntitySystem<_TComponents extends readonly ComponentConstr
|
|||||||
|
|
||||||
// 检查新增的实体
|
// 检查新增的实体
|
||||||
for (const entity of currentEntities) {
|
for (const entity of currentEntities) {
|
||||||
if (!this._entityCache.tracked.has(entity)) {
|
if (!this._entityCache.isTracked(entity)) {
|
||||||
this._entityCache.tracked.add(entity);
|
this._entityCache.addTracked(entity);
|
||||||
this.onAdded(entity);
|
this.onAdded(entity);
|
||||||
hasChanged = true;
|
hasChanged = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查移除的实体
|
// 检查移除的实体
|
||||||
for (const entity of this._entityCache.tracked) {
|
for (const entity of this._entityCache.getTracked()) {
|
||||||
if (!currentSet.has(entity)) {
|
if (!currentSet.has(entity)) {
|
||||||
this._entityCache.tracked.delete(entity);
|
this._entityCache.removeTracked(entity);
|
||||||
this.onRemoved(entity);
|
this.onRemoved(entity);
|
||||||
hasChanged = true;
|
hasChanged = true;
|
||||||
}
|
}
|
||||||
@@ -778,15 +757,16 @@ export abstract class EntitySystem<_TComponents extends readonly ComponentConstr
|
|||||||
* @param eventType 事件类型
|
* @param eventType 事件类型
|
||||||
* @param handler 事件处理函数
|
* @param handler 事件处理函数
|
||||||
* @param config 监听器配置
|
* @param config 监听器配置
|
||||||
|
* @returns 监听器引用ID,可用于手动移除监听器
|
||||||
*/
|
*/
|
||||||
protected addEventListener<T = any>(
|
protected addEventListener<T>(
|
||||||
eventType: string,
|
eventType: string,
|
||||||
handler: EventHandler<T>,
|
handler: EventHandler<T>,
|
||||||
config?: EventListenerConfig
|
config?: EventListenerConfig
|
||||||
): void {
|
): string | null {
|
||||||
if (!this.scene?.eventSystem) {
|
if (!this.scene?.eventSystem) {
|
||||||
this.logger.warn(`${this.systemName}: 无法添加事件监听器,scene.eventSystem 不可用`);
|
this.logger.warn(`${this.systemName}: 无法添加事件监听器,scene.eventSystem 不可用`);
|
||||||
return;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const listenerRef = this.scene.eventSystem.on(eventType, handler, config);
|
const listenerRef = this.scene.eventSystem.on(eventType, handler, config);
|
||||||
@@ -796,21 +776,22 @@ export abstract class EntitySystem<_TComponents extends readonly ComponentConstr
|
|||||||
this._eventListeners.push({
|
this._eventListeners.push({
|
||||||
eventSystem: this.scene.eventSystem,
|
eventSystem: this.scene.eventSystem,
|
||||||
eventType,
|
eventType,
|
||||||
handler,
|
|
||||||
listenerRef
|
listenerRef
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return listenerRef;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 移除特定的事件监听器
|
* 移除特定的事件监听器
|
||||||
*
|
*
|
||||||
* @param eventType 事件类型
|
* @param eventType 事件类型
|
||||||
* @param handler 事件处理函数
|
* @param listenerRef 监听器引用ID(由 addEventListener 返回)
|
||||||
*/
|
*/
|
||||||
protected removeEventListener<T = any>(eventType: string, handler: EventHandler<T>): void {
|
protected removeEventListener(eventType: string, listenerRef: string): void {
|
||||||
const listenerIndex = this._eventListeners.findIndex(
|
const listenerIndex = this._eventListeners.findIndex(
|
||||||
(listener) => listener.eventType === eventType && listener.handler === handler
|
(listener) => listener.eventType === eventType && listener.listenerRef === listenerRef
|
||||||
);
|
);
|
||||||
|
|
||||||
if (listenerIndex >= 0) {
|
if (listenerIndex >= 0) {
|
||||||
@@ -895,7 +876,7 @@ export abstract class EntitySystem<_TComponents extends readonly ComponentConstr
|
|||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
protected requireComponent<T extends ComponentConstructor>(entity: Entity, componentType: T): ComponentInstance<T> {
|
protected requireComponent<T extends ComponentConstructor>(entity: Entity, componentType: T): ComponentInstance<T> {
|
||||||
const component = entity.getComponent(componentType as any);
|
const component = entity.getComponent(componentType);
|
||||||
if (!component) {
|
if (!component) {
|
||||||
throw new Error(`Component ${componentType.name} not found on entity ${entity.name} in ${this.systemName}`);
|
throw new Error(`Component ${componentType.name} not found on entity ${entity.name} in ${this.systemName}`);
|
||||||
}
|
}
|
||||||
@@ -929,7 +910,9 @@ export abstract class EntitySystem<_TComponents extends readonly ComponentConstr
|
|||||||
entity: Entity,
|
entity: Entity,
|
||||||
...components: T
|
...components: T
|
||||||
): { [K in keyof T]: ComponentInstance<T[K]> } {
|
): { [K in keyof T]: ComponentInstance<T[K]> } {
|
||||||
return components.map((type) => this.requireComponent(entity, type)) as any;
|
return components.map((type) => this.requireComponent(entity, type)) as {
|
||||||
|
[K in keyof T]: ComponentInstance<T[K]>;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -142,8 +142,8 @@ export interface IEventListenerConfig {
|
|||||||
priority?: number;
|
priority?: number;
|
||||||
/** 是否异步执行 */
|
/** 是否异步执行 */
|
||||||
async?: boolean;
|
async?: boolean;
|
||||||
/** 执行上下文 */
|
/** 事件处理函数的 this 绑定对象 */
|
||||||
context?: unknown;
|
thisArg?: object;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -50,17 +50,19 @@ class ConcreteEntitySystem extends EntitySystem {
|
|||||||
const handler = (event: any) => {
|
const handler = (event: any) => {
|
||||||
this.eventHandlerCallCount++;
|
this.eventHandlerCallCount++;
|
||||||
};
|
};
|
||||||
this.addEventListener('manual_event', handler);
|
const listenerRef = this.addEventListener('manual_event', handler);
|
||||||
this.removeEventListener('manual_event', handler);
|
if (listenerRef) {
|
||||||
|
this.removeEventListener('manual_event', listenerRef);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 公开测试方法
|
// 公开测试方法
|
||||||
public testAddEventListener(eventType: string, handler: (event: any) => void): void {
|
public testAddEventListener(eventType: string, handler: (event: any) => void): string | null {
|
||||||
this.addEventListener(eventType, handler);
|
return this.addEventListener(eventType, handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
public testRemoveEventListener(eventType: string, handler: (event: any) => void): void {
|
public testRemoveEventListener(eventType: string, listenerRef: string): void {
|
||||||
this.removeEventListener(eventType, handler);
|
this.removeEventListener(eventType, listenerRef);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -118,14 +120,16 @@ describe('EntitySystem', () => {
|
|||||||
const handler = jest.fn();
|
const handler = jest.fn();
|
||||||
|
|
||||||
// 添加监听器
|
// 添加监听器
|
||||||
system.testAddEventListener('manual_remove_event', handler);
|
const listenerRef = system.testAddEventListener('manual_remove_event', handler);
|
||||||
|
|
||||||
// 发射事件验证监听器工作
|
// 发射事件验证监听器工作
|
||||||
scene.eventSystem.emitSync('manual_remove_event', {});
|
scene.eventSystem.emitSync('manual_remove_event', {});
|
||||||
expect(handler).toHaveBeenCalledTimes(1);
|
expect(handler).toHaveBeenCalledTimes(1);
|
||||||
|
|
||||||
// 移除监听器
|
// 移除监听器
|
||||||
system.testRemoveEventListener('manual_remove_event', handler);
|
if (listenerRef) {
|
||||||
|
system.testRemoveEventListener('manual_remove_event', listenerRef);
|
||||||
|
}
|
||||||
|
|
||||||
// 再次发射事件
|
// 再次发射事件
|
||||||
scene.eventSystem.emitSync('manual_remove_event', {});
|
scene.eventSystem.emitSync('manual_remove_event', {});
|
||||||
@@ -205,11 +209,11 @@ describe('EntitySystem', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('当移除不存在的监听器时,应该安全处理', () => {
|
it('当移除不存在的监听器时,应该安全处理', () => {
|
||||||
const nonExistentHandler = () => {};
|
const nonExistentListenerRef = 'non_existent_listener_ref';
|
||||||
|
|
||||||
// 应该不会抛出错误
|
// 应该不会抛出错误
|
||||||
expect(() => {
|
expect(() => {
|
||||||
system.testRemoveEventListener('non_existent_event', nonExistentHandler);
|
system.testRemoveEventListener('non_existent_event', nonExistentListenerRef);
|
||||||
}).not.toThrow();
|
}).not.toThrow();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user