!30 修复LayerPopUp关闭遮罩时把mask节点的uiSprite关闭了,但是打开遮罩时没有启用,导致只有第一个PopUp能显示mask

Merge pull request !30 from dgflash/develop
This commit is contained in:
dgflash
2026-05-17 11:05:37 +00:00
committed by Gitee
20 changed files with 1429 additions and 1172 deletions

View File

@@ -103,7 +103,10 @@ export class LayerPopUp extends LayerUI {
this.black.enabled = false;
}
if (config.mask) this.mask.parent = this;
if (config.mask) {
this.mask.parent = this;
this.mask.uiSprite.enabled = true;
}
}
/** 关闭触摸非窗口区域关闭 */

View File

@@ -1,362 +1,348 @@
import { ECSComp } from './ECSComp';
import { ECSEntity } from './ECSEntity';
import { ECSMask } from './ECSMask';
import { ECSMatcher } from './ECSMatcher';
import type { CompCtor, CompType, EntityCtor } from './ECSModel';
import { ECSModel } from './ECSModel';
import { ECSComblockSystem, ECSRootSystem, ECSSystem } from './ECSSystem';
import { ecsPoolCoordinator } from './ECSPoolManager';
/**
* ECSEntity对象在destroy后会回收到ECSModel.entityPool实体对象池中
* ECSComp对象从ECSEntity.remove后数据组件会回收到ECSModel.compPools组件对象池中
*/
/**
* Entity-Component-System实体-组件-系统)框架
* 文档https://gitee.com/dgflash/oops-framework/wikis/pages?sort_id=12033388&doc_id=2873565
*/
export namespace ecs {
/** 实体 - 一个概念上的定义,指的是游戏世界中的一个独特物体,是一系列组件的集合 */
export type Entity = ECSEntity;
/** 组件 - 一堆数据的集合,即不存在任何的行为,只用来存储状态 */
export type Comp = ECSComp;
/** 系统 - 关注实体上组件数据变化,处理游戏逻辑 */
export type System = ECSSystem;
/** 根系统 - 驱动游戏中所有系统工作 */
export type RootSystem = ECSRootSystem;
/** 处理游戏逻辑系统对象 - 继承此对象实现自定义业务逻辑 */
export type ComblockSystem = ECSComblockSystem;
/** 实体 - 一个概念上的定义,指的是游戏世界中的一个独特物体,是一系列组件的集合 */
export const Entity = ECSEntity;
/** 组件 - 一堆数据的集合,即不存在任何的行为,只用来存储状态 */
export const Comp = ECSComp;
/** 系统 - 关注实体上组件数据变化,处理游戏逻辑 */
export const System = ECSSystem;
/** 根系统 - 驱动游戏中所有系统工作 */
export const RootSystem = ECSRootSystem;
/** 处理游戏逻辑系统对象 - 继承此对象实现自定义业务逻辑 */
export const ComblockSystem = ECSComblockSystem;
//#region 接口
/** 组件接口 */
export interface IComp {
canRecycle: boolean;
ent: Entity;
tid: number;
reset(): void;
}
/** 实体匹配器接口 */
export interface IMatcher {
mid: number;
indices: number[];
key: string;
isMatch(entity: Entity): boolean;
}
/**
* 监听组件首次添加到实体上时在ComblockSystem上实现这个接口
* 1. entityEnter会在update方法之前执行实体进入后不会再次进入entityEnter方法中
* 2. 当实体从当前System移除下次再次符合条件进入System也会执行上述流程
* @example
export class RoleUpgradeSystem extends ecs.ComblockSystem implements ecs.IEntityEnterSystem {
filter(): ecs.IMatcher {
return ecs.allOf(RoleUpgradeComp, RoleModelLevelComp);
}
entityEnter(e: Role): void {
e.remove(RoleUpgradeComp);
}
}
*/
export interface IEntityEnterSystem<E extends Entity = Entity> {
entityEnter(entity: E): void;
}
/** 监听组件从实体上移除时在ComblockSystem上实现这个接口 */
export interface IEntityRemoveSystem<E extends Entity = Entity> {
entityRemove(entity: E): void;
}
/** 监听系统第一次执行update处理实体时在ComblockSystem上实现这个接口 */
export interface ISystemFirstUpdate<E extends Entity = Entity> {
firstUpdate(entity: E): void;
}
/** 监听系统执行update处理实体时在ComblockSystem上实现这个接口 */
export interface ISystemUpdate<E extends Entity = Entity> {
update(entity: E): void;
}
//#endregion
/**
* 注册组件到ecs系统中
* @param name 由于js打包会改变类名所以这里必须手动传入组件的名称
* @param canNew 标识是否可以new对象。想继承自Cocos Creator的组件就不能去new需要写成@ecs.register('name', false)
* @example
// 注册实体
@ecs.register('Role')
export class Role extends ecs.Entity {
}
// 注册数据组件
@ecs.register('RoleModel')
export class RoleModelComp extends ecs.Comp {
id: number = -1;
reset() {
this.id = -1;
}
}
// 注册系统组件
@ecs.register('Initialize')
export class InitResSystem extends ecs.ComblockSystem implements ecs.IEntityEnterSystem {
}
// 注册显示对象组件
@ccclass('RoleViewComp')
@ecs.register('RoleView', false)
export class RoleViewComp extends CCComp {
onLoad(){
}
}
*/
export function register<T extends Entity | IComp | ComblockSystem>(name: string, canNew = true) {
return function (ctor: EntityCtor<T> | CompCtor<T> | (new () => T)) {
const ctorAny = ctor as unknown as {
s?: boolean;
tid?: number;
compName?: string;
};
// 注册系统
if (ctorAny.s) {
let system = ECSModel.systems.get(name);
if (system == null) {
system = new ecs.System();
ECSModel.systems.set(name, system);
}
system.add(new (ctor as new () => ComblockSystem)());
}
// 注册实体
else if (ctorAny.tid === undefined) {
ECSModel.entityCtors.set(ctor as EntityCtor<ECSEntity>, name);
}
// 注册组件
else {
if (ctorAny.tid === -1) {
ctorAny.tid = ECSModel.compTid++;
ctorAny.compName = name;
ECSModel.compCtors.push(ctor as CompCtor<IComp>);
if (canNew) {
ECSModel.compPools.set(ctorAny.tid, []);
}
ECSModel.compAddOrRemove.set(ctorAny.tid, []);
}
else {
throw new Error(`ECS 组件重复注册: ${name}.`);
}
}
};
}
/**
* 创建一个新的实体对象或从缓存中获取一个实体对象(使用动态池)
* @param ctor 实体类
*/
export function getEntity<T extends Entity>(ctor: EntityCtor<T>): T {
// 获取实体对象名
const entityName = ECSModel.entityCtors.get(ctor as EntityCtor<ECSEntity>);
if (entityName === undefined) {
console.error(`${ctor.name} 实体没有注册`);
throw new Error(`${ctor.name} 实体没有注册`);
}
// 使用动态池管理器
const pool = ecsPoolCoordinator.getPool(
entityName,
() => {
const entity = new ctor();
entity.eid = ECSModel.eid++; // 实体唯一编号
entity.name = entityName;
return entity;
}
);
const entity = pool.get();
// 触发实体初始化逻辑
const entityWithInit = entity as ECSEntity & { init?: () => void };
entity.isValid = true; // 无论新建还是复用,都标记为有效
if (entityWithInit.init) entityWithInit.init();
ECSModel.eid2Entity.set((entity as ECSEntity).eid, entity as ECSEntity);
return entity;
}
/**
* 动态查询实体
* @param matcher 匹配器
* @example
* ecs.query(ecs.allOf(Comp1, Comp2));
*/
export function query<E extends Entity = Entity>(matcher: IMatcher): E[] {
let group = ECSModel.groups.get(matcher.mid);
if (!group) {
group = ECSModel.createGroup(matcher);
ECSModel.eid2Entity.forEach(group.onComponentAddOrRemove, group);
}
return group.matchEntities as E[];
}
/** 清理所有的实体 */
export function clear(): void {
ECSModel.eid2Entity.forEach((entity) => {
entity.destroy();
});
ECSModel.groups.forEach((group) => {
group.clear();
});
ECSModel.compAddOrRemove.forEach((callbackLst) => {
callbackLst.length = 0;
});
ECSModel.eid2Entity.clear();
ECSModel.groups.clear();
}
/**
* 清理所有对象池 - 用于释放不再使用的缓存内存
* 注意:此操作会清空所有实体池、组件池和 Mask 池,请在确保不再需要这些缓存时调用
*/
export function clearPools(): void {
// 清理旧的实体池
ECSModel.entityPool.forEach((pool) => {
pool.length = 0;
});
ECSModel.entityPool.clear();
// 清理旧的组件池
ECSModel.compPools.forEach((pool) => {
pool.length = 0;
});
// 清理 Mask 对象池
ECSMask.clearPool();
// 清理动态池管理器
ecsPoolCoordinator.clearAll();
}
/**
* 通过实体唯一编号获得实体对象
* @param eid 实体唯一编号
*/
export function getEntityByEid<E extends Entity = Entity>(eid: number): E | undefined {
return ECSModel.eid2Entity.get(eid) as E | undefined;
}
/** 当前活动中的实体数量 */
export function activeEntityCount(): number {
return ECSModel.eid2Entity.size;
}
/** 创建实体 */
function createEntity<E extends Entity = Entity>(): E {
const entity = new Entity();
entity.eid = ECSModel.eid++; // 实体id也是有限的资源
ECSModel.eid2Entity.set((entity as ECSEntity).eid, entity as ECSEntity);
return entity as E;
}
/**
* 指定一个组件创建实体,返回组件对象。
* @param ctor
*/
function createEntityWithComp<T extends IComp>(ctor: CompCtor<T>): T {
const entity = createEntity();
return entity.add(ctor);
}
//#region 过滤器
/**
* 表示只关心这些组件的添加和删除动作。虽然实体可能有这些组件之外的组件,但是它们的添加和删除没有被关注,所以不会存在对关注之外的组件
* 进行添加操作引发Group重复添加实体。
* @param args
* @example
* ecs.allOf(AComponent, BComponent, CComponent);
*/
export function allOf(...args: CompType<IComp>[]): IMatcher {
return new ECSMatcher().allOf(...args);
}
/**
* 组件间是或的关系,表示关注拥有任意一个这些组件的实体
* @param args 组件类
* @example
* ecs.anyOf(AComponent, BComponent);
*/
export function anyOf(...args: CompType<IComp>[]): IMatcher {
return new ECSMatcher().anyOf(...args);
}
/**
* 表示关注只拥有这些组件的实体
* 不是特殊情况不建议使用onlyOf。因为onlyOf会监听所有组件的添加和删除事
* @param args 组件类
* @example
// 不包含CComponent或者DComponent
ecs.allOf(AComponent, BComponent).excludeOf(CComponent, DComponent);
// 不同时包含CComponent和DComponent
ecs.allOf(AComponent, BComponent).excludeOf(CComponent).excludeOf(DComponent);
*/
export function onlyOf(...args: CompType<IComp>[]): IMatcher {
return new ECSMatcher().onlyOf(...args);
}
/**
* 不包含指定的任意一个组件
* @param args 组件类
* @example
// 表示不包含组件A或者组件B
ecs.excludeOf(A, B);
*/
export function excludeOf(...args: CompType<IComp>[]): IMatcher {
return new ECSMatcher().excludeOf(...args);
}
//#endregion
//#region 单例组件
/**
* 获取单例组件
* @param ctor 组件类
*/
export function getSingleton<T extends IComp>(ctor: CompCtor<T>): T {
if (!ECSModel.tid2comp.has(ctor.tid)) {
const comp = createEntityWithComp(ctor);
ECSModel.tid2comp.set(ctor.tid, comp);
}
return ECSModel.tid2comp.get(ctor.tid) as T;
}
/**
* 注册单例组件 - 主要用于那些不能手动创建对象的组件
* @param obj
*/
export function addSingleton(obj: IComp): void {
const tid = (obj.constructor as CompCtor<IComp>).tid;
if (!ECSModel.tid2comp.has(tid)) {
ECSModel.tid2comp.set(tid, obj);
}
}
//#endregion
}
import { ECSComp } from './ECSComp';
import { ECSEntity } from './ECSEntity';
import { ECSMask } from './ECSMask';
import { ECSMatcher } from './ECSMatcher';
import type { CompCtor, CompType, EntityCtor } from './ECSModel';
import { ECSModel } from './ECSModel';
import { ECSComblockSystem, ECSRootSystem, ECSSystem } from './ECSSystem';
import { ecsPoolCoordinator } from './pool';
/**
* ECSEntity对象在destroy后会回收到ECSPoolManager动态对象池中
* ECSComp对象从ECSEntity.remove后数据组件会回收到ECSPoolManager动态对象池中
*/
/**
* Entity-Component-System实体-组件-系统)框架
* 文档https://gitee.com/dgflash/oops-framework/wikis/pages?sort_id=12033388&doc_id=2873565
*/
export namespace ecs {
/** 实体 - 一个概念上的定义,指的是游戏世界中的一个独特物体,是一系列组件的集合 */
export type Entity = ECSEntity;
/** 组件 - 一堆数据的集合,即不存在任何的行为,只用来存储状态 */
export type Comp = ECSComp;
/** 系统 - 关注实体上组件数据变化,处理游戏逻辑 */
export type System = ECSSystem;
/** 根系统 - 驱动游戏中所有系统工作 */
export type RootSystem = ECSRootSystem;
/** 处理游戏逻辑系统对象 - 继承此对象实现自定义业务逻辑 */
export type ComblockSystem = ECSComblockSystem;
/** 实体 - 一个概念上的定义,指的是游戏世界中的一个独特物体,是一系列组件的集合 */
export const Entity = ECSEntity;
/** 组件 - 一堆数据的集合,即不存在任何的行为,只用来存储状态 */
export const Comp = ECSComp;
/** 系统 - 关注实体上组件数据变化,处理游戏逻辑 */
export const System = ECSSystem;
/** 根系统 - 驱动游戏中所有系统工作 */
export const RootSystem = ECSRootSystem;
/** 处理游戏逻辑系统对象 - 继承此对象实现自定义业务逻辑 */
export const ComblockSystem = ECSComblockSystem;
//#region 接口
/** 组件接口 */
export interface IComp {
canRecycle: boolean;
ent: Entity;
tid: number;
reset(): void;
}
/** 实体匹配器接口 */
export interface IMatcher {
mid: number;
indices: number[];
key: string;
isMatch(entity: Entity): boolean;
}
/**
* 监听组件首次添加到实体上时在ComblockSystem上实现这个接口
* 1. entityEnter会在update方法之前执行实体进入后不会再次进入entityEnter方法中
* 2. 当实体从当前System移除下次再次符合条件进入System也会执行上述流程
* @example
export class RoleUpgradeSystem extends ecs.ComblockSystem implements ecs.IEntityEnterSystem {
filter(): ecs.IMatcher {
return ecs.allOf(RoleUpgradeComp, RoleModelLevelComp);
}
entityEnter(e: Role): void {
e.remove(RoleUpgradeComp);
}
}
*/
export interface IEntityEnterSystem<E extends Entity = Entity> {
entityEnter(entity: E): void;
}
/** 监听组件从实体上移除时在ComblockSystem上实现这个接口 */
export interface IEntityRemoveSystem<E extends Entity = Entity> {
entityRemove(entity: E): void;
}
/** 监听系统第一次执行update处理实体时在ComblockSystem上实现这个接口 */
export interface ISystemFirstUpdate<E extends Entity = Entity> {
firstUpdate(entity: E): void;
}
/** 监听系统执行update处理实体时在ComblockSystem上实现这个接口 */
export interface ISystemUpdate<E extends Entity = Entity> {
update(entity: E): void;
}
//#endregion
/**
* 注册组件到ecs系统中
* @param name 由于js打包会改变类名所以这里必须手动传入组件的名称
* @param canNew 标识是否可以new对象。想继承自Cocos Creator的组件就不能去new需要写成@ecs.register('name', false)
* @example
// 注册实体
@ecs.register('Role')
export class Role extends ecs.Entity {
}
// 注册数据组件
@ecs.register('RoleModel')
export class RoleModelComp extends ecs.Comp {
id: number = -1;
reset() {
this.id = -1;
}
}
// 注册系统组件
@ecs.register('Initialize')
export class InitResSystem extends ecs.ComblockSystem implements ecs.IEntityEnterSystem {
}
// 注册显示对象组件
@ccclass('RoleViewComp')
@ecs.register('RoleView', false)
export class RoleViewComp extends CCComp {
onLoad(){
}
}
*/
export function register<T extends Entity | IComp | ComblockSystem>(name: string, canNew = true) {
return function (ctor: EntityCtor<T> | CompCtor<T> | (new () => T)) {
const ctorAny = ctor as unknown as {
s?: boolean;
tid?: number;
compName?: string;
};
// 注册系统
if (ctorAny.s) {
let system = ECSModel.systems.get(name);
if (system == null) {
system = new ecs.System();
ECSModel.systems.set(name, system);
}
system.add(new (ctor as new () => ComblockSystem)());
}
// 注册实体
else if (ctorAny.tid === undefined) {
ECSModel.entityCtors.set(ctor as EntityCtor<ECSEntity>, name);
}
// 注册组件
else {
if (ctorAny.tid === -1) {
ctorAny.tid = ECSModel.compTid++;
ctorAny.compName = name;
ECSModel.compCtors.push(ctor as CompCtor<IComp>);
ECSModel.compAddOrRemove.set(ctorAny.tid, []);
}
else {
throw new Error(`ECS 组件重复注册: ${name}.`);
}
}
};
}
/**
* 创建一个新的实体对象或从缓存中获取一个实体对象(使用动态池)
* @param ctor 实体类
*/
export function getEntity<T extends Entity>(ctor: EntityCtor<T>): T {
// 获取实体对象名
const entityName = ECSModel.entityCtors.get(ctor as EntityCtor<ECSEntity>);
if (entityName === undefined) {
console.error(`${ctor.name} 实体没有注册`);
throw new Error(`${ctor.name} 实体没有注册`);
}
// 使用动态池管理器
const pool = ecsPoolCoordinator.getPool(
entityName,
() => {
const entity = new ctor();
entity.eid = ECSModel.eid++; // 实体唯一编号
entity.name = entityName;
return entity;
}
);
const entity = pool.get();
// 触发实体初始化逻辑
const entityWithInit = entity as ECSEntity & { init?: () => void };
entity.isValid = true; // 无论新建还是复用,都标记为有效
if (entityWithInit.init) entityWithInit.init();
ECSModel.eid2Entity.set((entity as ECSEntity).eid, entity as ECSEntity);
return entity;
}
/**
* 动态查询实体
* @param matcher 匹配器
* @example
* ecs.query(ecs.allOf(Comp1, Comp2));
*/
export function query<E extends Entity = Entity>(matcher: IMatcher): E[] {
let group = ECSModel.groups.get(matcher.mid);
if (!group) {
group = ECSModel.createGroup(matcher);
ECSModel.eid2Entity.forEach(group.onComponentAddOrRemove, group);
}
return group.matchEntities as E[];
}
/** 清理所有的实体 */
export function clear(): void {
ECSModel.eid2Entity.forEach((entity) => {
entity.destroy();
});
ECSModel.groups.forEach((group) => {
group.clear();
});
ECSModel.compAddOrRemove.forEach((callbackLst) => {
callbackLst.length = 0;
});
ECSModel.eid2Entity.clear();
ECSModel.groups.clear();
}
/**
* 清理所有对象池 - 用于释放不再使用的缓存内存
* 注意:此操作会清空所有实体池、组件池和 Mask 池,请在确保不再需要这些缓存时调用
*/
export function clearPools(): void {
// 清理 Mask 对象池
ECSMask.clearPool();
// 清理动态池管理器
ecsPoolCoordinator.clearAll();
}
/**
* 通过实体唯一编号获得实体对象
* @param eid 实体唯一编号
*/
export function getEntityByEid<E extends Entity = Entity>(eid: number): E | undefined {
return ECSModel.eid2Entity.get(eid) as E | undefined;
}
/** 当前活动中的实体数量 */
export function activeEntityCount(): number {
return ECSModel.eid2Entity.size;
}
/** 创建实体 */
function createEntity<E extends Entity = Entity>(): E {
const entity = new Entity();
entity.eid = ECSModel.eid++; // 实体id也是有限的资源
ECSModel.eid2Entity.set((entity as ECSEntity).eid, entity as ECSEntity);
return entity as E;
}
/**
* 指定一个组件创建实体,返回组件对象。
* @param ctor
*/
function createEntityWithComp<T extends IComp>(ctor: CompCtor<T>): T {
const entity = createEntity();
return entity.add(ctor);
}
//#region 过滤器
/**
* 表示只关心这些组件的添加和删除动作。虽然实体可能有这些组件之外的组件,但是它们的添加和删除没有被关注,所以不会存在对关注之外的组件
* 进行添加操作引发Group重复添加实体。
* @param args
* @example
* ecs.allOf(AComponent, BComponent, CComponent);
*/
export function allOf(...args: CompType<IComp>[]): IMatcher {
return new ECSMatcher().allOf(...args);
}
/**
* 组件间是或的关系,表示关注拥有任意一个这些组件的实体
* @param args 组件类
* @example
* ecs.anyOf(AComponent, BComponent);
*/
export function anyOf(...args: CompType<IComp>[]): IMatcher {
return new ECSMatcher().anyOf(...args);
}
/**
* 表示关注只拥有这些组件的实体
* 注不是特殊情况不建议使用onlyOf。因为onlyOf会监听所有组件的添加和删除事件
* @param args 组件类
* @example
// 不包含CComponent或者DComponent
ecs.allOf(AComponent, BComponent).excludeOf(CComponent, DComponent);
// 不同时包含CComponent和DComponent
ecs.allOf(AComponent, BComponent).excludeOf(CComponent).excludeOf(DComponent);
*/
export function onlyOf(...args: CompType<IComp>[]): IMatcher {
return new ECSMatcher().onlyOf(...args);
}
/**
* 不包含指定的任意一个组
* @param args 组件类
* @example
// 表示不包含组件A或者组件B
ecs.excludeOf(A, B);
*/
export function excludeOf(...args: CompType<IComp>[]): IMatcher {
return new ECSMatcher().excludeOf(...args);
}
//#endregion
//#region 单例组件
/**
* 获取单例组件
* @param ctor 组件
*/
export function getSingleton<T extends IComp>(ctor: CompCtor<T>): T {
if (!ECSModel.tid2comp.has(ctor.tid)) {
const comp = createEntityWithComp(ctor);
ECSModel.tid2comp.set(ctor.tid, comp);
}
return ECSModel.tid2comp.get(ctor.tid) as T;
}
/**
* 注册单例组件 - 主要用于那些不能手动创建对象的组件
* @param obj
*/
export function addSingleton(obj: IComp): void {
const tid = (obj.constructor as CompCtor<IComp>).tid;
if (!ECSModel.tid2comp.has(tid)) {
ECSModel.tid2comp.set(tid, obj);
}
}
//#endregion
}

View File

@@ -32,4 +32,4 @@ export abstract class ECSComp implements ecs.IComp {
* 注:不要偷懒,除非你能确定并保证组件在复用时,里面的数据是先赋值然后再使用
*/
abstract reset(): void;
}
}

View File

@@ -1,470 +1,470 @@
import type { ecs } from './ECS';
import { ECSMask } from './ECSMask';
import type { CompCtor, CompType } from './ECSModel';
import { ECSModel } from './ECSModel';
import { ecsPoolCoordinator } from './ECSPoolManager';
//#region 辅助方法
/**
* 实体身上组件有增删操作,广播通知对应的观察者
* @param entity 实体对象
* @param componentTypeId 组件类型id
*/
function broadcastCompAddOrRemove(entity: ECSEntity, componentTypeId: number): void {
const events = ECSModel.compAddOrRemove.get(componentTypeId);
if (events) {
for (let i = events.length - 1; i >= 0; i--) {
events[i](entity);
}
}
// 判断是不是删了单例组件
if (ECSModel.tid2comp.has(componentTypeId)) {
ECSModel.tid2comp.delete(componentTypeId);
}
}
/**
* 设置实体上的组件属性(类型安全的动态属性访问)
*/
function setEntityComp<T extends ecs.IComp>(entity: ECSEntity, compName: string, comp: T): void {
Reflect.set(entity, compName, comp);
}
/**
* 获取实体上的组件属性(类型安全的动态属性访问)
*/
function getEntityComp<T extends ecs.IComp>(entity: ECSEntity, compName: string): T | undefined {
return Reflect.get(entity, compName) as T | undefined;
}
/**
* 删除实体上的组件属性(类型安全的动态属性访问)
*/
function deleteEntityComp(entity: ECSEntity, compName: string): void {
Reflect.set(entity, compName, null);
}
/**
* 创建组件对象(使用动态池管理)
* @param ctor
*/
function createComp<T extends ecs.IComp>(ctor: CompCtor<T>): T {
const cct = ECSModel.compCtors[ctor.tid];
if (!cct) {
throw Error(`没有找到该组件的构造函数,检查${ctor.compName}是否为不可构造的组件`);
}
// 使用动态池管理器
const pool = ecsPoolCoordinator.getPool(
ctor.compName,
() => new (cct as CompCtor<T>)()
);
const component = pool.get();
return component as T;
}
/**
* 销毁实体(使用动态池管理)
*
* 缓存销毁的实体,下次新建实体时会优先从缓存中拿
* @param entity
*/
function destroyEntity(entity: ECSEntity): void {
if (ECSModel.eid2Entity.has(entity.eid)) {
// 使用动态池管理器
const pool = ecsPoolCoordinator.getPool(
entity.name,
() => new ECSEntity()
);
// 清空mask并回收
entity.getMask().clear();
pool.recycle(entity);
ECSModel.eid2Entity.delete(entity.eid);
}
else {
console.warn('试图销毁不存在的实体');
}
}
//#endregion
/** ECS实体对象 */
export class ECSEntity {
/** 实体唯一标识,不要手动修改 */
eid = -1;
/** 实体对象名 */
name = '';
/** 实体是否有效 */
isValid = true;
/** 组件过滤数据 */
private mask: ECSMask = new ECSMask();
/** 当前实体身上附加的组件构造函数 */
private compTid2Ctor: Map<number, CompType<ecs.IComp>> = new Map();
/**
* 配合 entity.remove(Comp, false) 记录组件实例上的缓存数据,在添加时恢复原数据
*
* ⚠️ 核心机制:
* - isRecycle=true调用reset()根据canRecycle决定是否回收到全局池
* - isRecycle=false不调用reset(),缓存在实体上
*
* ⚠️ 使用场景说明:
* 1. UI显示/隐藏 - 保留UI状态数据
* 2. 技能冷却 - 保留冷却时间
* 3. 临时禁用功能 - 保留配置数据
*
* 💡 管理建议:
* - 无任何限制,完全由用户自行管理
* - 使用 ECSMemoryMonitor 监控缓存使用情况
* - 定期调用 clearComponentCache() 或 clearAllComponentCache() 清理
* - 在场景切换、内存警告时主动清理缓存
*/
private compTid2Obj: Map<number, ecs.IComp> = new Map();
/** 获取 mask 对象(内部使用) */
getMask(): ECSMask {
return this.mask;
}
private _parent: ECSEntity | null = null;
/** 父实体 */
get parent(): ECSEntity | null {
return this._parent;
}
set parent(value: ECSEntity | null) {
this._parent = value;
}
/** 子实体 */
private childs: Map<number, ECSEntity> | null = null;
/** 获取子实体 */
getChild<T extends ECSEntity>(eid: number): T | undefined {
return this.childs?.get(eid) as T | undefined;
}
/**
* 添加子实体(带循环引用检测)
* @param entity 被添加的实体对象
* @returns 子实体的唯一编号, -1表示添加失败
*/
addChild(entity: ECSEntity): number {
if (this.childs === null) {
this.childs = new Map<number, ECSEntity>();
}
if (this.childs.has(entity.eid)) {
console.warn(`子实体${entity.name}已存在`);
return -1;
}
// 检测循环引用
if (this.hasAncestor(entity)) {
console.error(`检测到循环引用: ${this.name} -> ${entity.name}`);
return -1;
}
entity._parent = this;
this.childs.set(entity.eid, entity);
return entity.eid;
}
/**
* 检测是否存在祖先关系(防止循环引用)
*/
private hasAncestor(entity: ECSEntity): boolean {
let current: ECSEntity | null = this;
while (current) {
if (current === entity) return true;
current = current.parent;
}
return false;
}
/**
* 移除子实体
* @param entity 被移除的实体对象
* @param isDestroy 被移除的实体是否释放,默认为释放
* @returns
*/
removeChild(entity: ECSEntity, isDestroy = true): void {
if (this.childs === null) return;
entity.parent = null;
this.childs.delete(entity.eid);
if (isDestroy) entity.destroy();
}
/**
* 根据组件类动态创建组件,并通知关心的系统。如果实体存在了这个组件,那么会先删除之前的组件然后添加新的
*
* 注意不要直接new Componentnew来的Component不会从Component的缓存池拿缓存的数据
* @param componentTypeId 组件类
* @param isReAdd true-表示用户指定这个实体可能已经存在了该组件那么再次add组件的时候会先移除该组件然后再添加一遍。false-表示不重复添加组件
*/
add<T extends ecs.IComp>(obj: T): ECSEntity;
add<T extends ecs.IComp>(ctor: CompType<T>, isReAdd?: boolean): T;
add<T extends ecs.IComp>(ctor: CompType<T> | T, isReAdd = false): T | ECSEntity {
if (typeof ctor === 'function') {
const compTid = ctor.tid;
if (ctor.tid === -1) {
throw Error(`${this.name}】实体【${ctor.compName}】组件未注册`);
}
if (this.compTid2Ctor.has(compTid)) { // 判断是否有该组件,如果有则先移除
if (isReAdd) {
this.remove(ctor);
}
else {
console.log(`${this.name}】实体【${ctor.compName}】组件已存在`);
const existingComp = getEntityComp<T>(this, ctor.compName);
if (existingComp) {
return existingComp;
}
}
}
this.mask.set(compTid);
let comp: T;
if (this.compTid2Obj.has(compTid)) {
comp = this.compTid2Obj.get(compTid) as T;
this.compTid2Obj.delete(compTid);
}
else {
// 创建组件对象
comp = createComp(ctor);
}
// 将组件对象直接附加到实体对象身上,方便直接获取
setEntityComp(this, ctor.compName, comp);
this.compTid2Ctor.set(compTid, ctor);
comp.tid = compTid;
comp.ent = this;
// 广播实体添加组件的消息
broadcastCompAddOrRemove(this, compTid);
return comp;
}
else {
// 此时 ctor 是组件实例
const compInstance = ctor as T;
const tmpCtor = (compInstance.constructor as CompCtor<T>);
const compTid = tmpCtor.tid;
if (compTid === -1 || compTid == null) throw Error(`${this.name}】实体【${tmpCtor.name}】组件未注册`);
if (this.compTid2Ctor.has(compTid)) throw Error(`${this.name}】实体【${tmpCtor.name}】组件已经存在`);
this.mask.set(compTid);
setEntityComp(this, tmpCtor.compName, compInstance);
this.compTid2Ctor.set(compTid, tmpCtor);
compInstance.tid = compTid;
compInstance.canRecycle = false;
compInstance.ent = this;
broadcastCompAddOrRemove(this, compTid);
return this;
}
}
/**
* 批量添加组件
* @param ctors 组件类
* @returns
*/
addComponents<T extends ecs.IComp>(...ctors: CompType<T>[]): this {
const len = ctors.length;
for (let i = 0; i < len; i++) {
this.add(ctors[i]);
}
return this;
}
/**
* 获取一个组件实例
* @param ctor 组件类
*/
get<T extends ecs.IComp>(ctor: CompCtor<T>): T | undefined {
return getEntityComp<T>(this, ctor.compName);
}
/**
* 组件是否在实体存在内
* @param ctor 组件类
*/
has(ctor: CompType<ecs.IComp>): boolean {
const tid = typeof ctor === 'number' ? ctor : ctor.tid;
return this.compTid2Ctor.has(tid);
}
/**
* 从实体上删除指定组件
* @param ctor 组件构造函数或者组件Tag
* @param isRecycle 是否回收该组件对象
*
* **isRecycle=true默认**
* - 调用组件的 reset() 方法清理数据
* - 如果 canRecycle=true回收到全局对象池供复用
* - 如果 canRecycle=false直接销毁
* - 适用于大部分场景
*
* **isRecycle=false**
* - 不调用 reset(),数据完整保留
* - 组件缓存在当前实体上,下次 add() 时恢复
* - 无任何限制和检查,完全由用户自行管理
* - 适用于需要保留状态的场景UI切换、技能冷却等
*
* ⚠️ isRecycle=false 使用场景:
* - UI显示/隐藏保留UI状态如滚动位置、选中项
* - 技能冷却:保留冷却剩余时间
* - 临时禁用:保留配置数据,稍后恢复
*
* 💡 管理建议:
* - 使用 ECSMemoryMonitor 监控缓存使用情况
* - 定期调用 clearComponentCache() 或 clearAllComponentCache() 清理
* - 在场景切换、内存警告时主动清理缓存
*/
remove(ctor: CompType<ecs.IComp>, isRecycle = true): void {
const componentTypeId = typeof ctor === 'number' ? ctor : ctor.tid;
const compName = typeof ctor === 'number' ? '' : ctor.compName;
if (!this.mask.has(componentTypeId)) {
return;
}
const comp = getEntityComp<ecs.IComp>(this, compName);
if (!comp) {
return;
}
comp.ent = null!;
if (isRecycle) {
comp.reset();
// 回收到全局池
if (comp.canRecycle) {
const ctor = ECSModel.compCtors[componentTypeId];
if (ctor) {
const pool = ecsPoolCoordinator.getPool(
ctor.compName,
() => new (ctor as any)()
);
pool.recycle(comp);
}
}
}
else {
// isRecycle=false用户明确要求缓存完全尊重用户意图
// 不做任何检查和限制,由用户通过 ECSMemoryMonitor 自行管理
this.compTid2Obj.set(componentTypeId, comp);
}
// 删除实体上的组件逻辑
deleteEntityComp(this, compName);
this.mask.delete(componentTypeId);
this.compTid2Ctor.delete(componentTypeId);
broadcastCompAddOrRemove(this, componentTypeId);
}
/**
* 清理指定组件的缓存
* @param ctor 组件构造函数
*/
clearComponentCache(ctor: CompType<ecs.IComp>): void {
const componentTypeId = typeof ctor === 'number' ? ctor : ctor.tid;
const comp = this.compTid2Obj.get(componentTypeId);
if (comp) {
this.compTid2Obj.delete(componentTypeId);
const ctorObj = ECSModel.compCtors[componentTypeId];
const compName = ctorObj?.compName || `tid:${componentTypeId}`;
comp.reset();
if (comp.canRecycle) {
if (ctorObj) {
const pool = ecsPoolCoordinator.getPool(
ctorObj.compName,
() => new (ctorObj as any)()
);
pool.recycle(comp);
}
}
console.log(`[ECS] 实体 ${this.name} 清理组件缓存: ${compName}`);
}
}
/**
* 清理所有组件缓存
*/
clearAllComponentCache(): void {
if (this.compTid2Obj.size === 0) return;
const count = this.compTid2Obj.size;
this.compTid2Obj.forEach((comp, tid) => {
comp.reset();
if (comp.canRecycle) {
const ctor = ECSModel.compCtors[tid];
if (ctor) {
const pool = ecsPoolCoordinator.getPool(
ctor.compName,
() => new (ctor as any)()
);
pool.recycle(comp);
}
}
});
this.compTid2Obj.clear();
console.log(`[ECS] 实体 ${this.name} 清理所有缓存,共 ${count} 个组件`);
}
/**
* 获取缓存的组件数量
*/
getCachedComponentCount(): number {
return this.compTid2Obj.size;
}
/**
* 获取缓存的组件列表(用于监控)
*/
getCachedComponents(): Map<number, ecs.IComp> {
return new Map(this.compTid2Obj);
}
/** 销毁实体,实体会被回收到实体缓存池中 */
destroy(): void {
this.isValid = false;
// 如果有父模块,则移除父模块上记录的子模块
if (this._parent) {
this._parent.removeChild(this, false);
this._parent = null;
}
// 移除模块上所有子模块
if (this.childs) {
this.childs.forEach((e) => {
this.removeChild(e);
});
this.childs.clear();
this.childs = null;
}
// 移除实体上所有组件
this.compTid2Ctor.forEach(this._remove, this);
destroyEntity(this);
// 清理缓存的组件对象,防止内存泄漏
this.clearAllComponentCache();
}
private _remove(comp: CompType<ecs.IComp>): void {
this.remove(comp, true);
}
}
import type { ecs } from './ECS';
import { ECSMask } from './ECSMask';
import type { CompCtor, CompType } from './ECSModel';
import { ECSModel } from './ECSModel';
import { ecsPoolCoordinator } from './pool';
//#region 辅助方法
/**
* 实体身上组件有增删操作,广播通知对应的观察者
* @param entity 实体对象
* @param componentTypeId 组件类型id
*/
function broadcastCompAddOrRemove(entity: ECSEntity, componentTypeId: number): void {
const events = ECSModel.compAddOrRemove.get(componentTypeId);
if (events) {
for (let i = events.length - 1; i >= 0; i--) {
events[i](entity);
}
}
// 判断是不是删了单例组件
if (ECSModel.tid2comp.has(componentTypeId)) {
ECSModel.tid2comp.delete(componentTypeId);
}
}
/**
* 设置实体上的组件属性(类型安全的动态属性访问)
*/
function setEntityComp<T extends ecs.IComp>(entity: ECSEntity, compName: string, comp: T): void {
Reflect.set(entity, compName, comp);
}
/**
* 获取实体上的组件属性(类型安全的动态属性访问)
*/
function getEntityComp<T extends ecs.IComp>(entity: ECSEntity, compName: string): T | undefined {
return Reflect.get(entity, compName) as T | undefined;
}
/**
* 删除实体上的组件属性(类型安全的动态属性访问)
*/
function deleteEntityComp(entity: ECSEntity, compName: string): void {
Reflect.set(entity, compName, null);
}
/**
* 创建组件对象(使用动态池管理)
* @param ctor
*/
function createComp<T extends ecs.IComp>(ctor: CompCtor<T>): T {
const cct = ECSModel.compCtors[ctor.tid];
if (!cct) {
throw Error(`没有找到该组件的构造函数,检查${ctor.compName}是否为不可构造的组件`);
}
// 使用动态池管理器
const pool = ecsPoolCoordinator.getPool(
ctor.compName,
() => new (cct as CompCtor<T>)()
);
const component = pool.get();
return component as T;
}
/**
* 销毁实体(使用动态池管理)
*
* 缓存销毁的实体,下次新建实体时会优先从缓存中拿
* @param entity
*/
function destroyEntity(entity: ECSEntity): void {
if (ECSModel.eid2Entity.has(entity.eid)) {
// 使用动态池管理器
const pool = ecsPoolCoordinator.getPool(
entity.name,
() => new ECSEntity()
);
// 清空mask并回收
entity.getMask().clear();
pool.recycle(entity);
ECSModel.eid2Entity.delete(entity.eid);
}
else {
console.warn('试图销毁不存在的实体');
}
}
//#endregion
/** ECS实体对象 */
export class ECSEntity {
/** 实体唯一标识,不要手动修改 */
eid = -1;
/** 实体对象名 */
name = '';
/** 实体是否有效 */
isValid = true;
/** 组件过滤数据 */
private mask: ECSMask = new ECSMask();
/** 当前实体身上附加的组件构造函数 */
private compTid2Ctor: Map<number, CompType<ecs.IComp>> = new Map();
/**
* 配合 entity.remove(Comp, false) 记录组件实例上的缓存数据,在添加时恢复原数据
*
* ⚠️ 核心机制:
* - isRecycle=true调用reset()根据canRecycle决定是否回收到全局池
* - isRecycle=false不调用reset(),缓存在实体上
*
* ⚠️ 使用场景说明:
* 1. UI显示/隐藏 - 保留UI状态数据
* 2. 技能冷却 - 保留冷却时间
* 3. 临时禁用功能 - 保留配置数据
*
* 💡 管理建议:
* - 无任何限制,完全由用户自行管理
* - 使用 ECSMemoryMonitor 监控缓存使用情况
* - 定期调用 clearComponentCache() 或 clearAllComponentCache() 清理
* - 在场景切换、内存警告时主动清理缓存
*/
private compTid2Obj: Map<number, ecs.IComp> = new Map();
/** 获取 mask 对象(内部使用) */
getMask(): ECSMask {
return this.mask;
}
private _parent: ECSEntity | null = null;
/** 父实体 */
get parent(): ECSEntity | null {
return this._parent;
}
set parent(value: ECSEntity | null) {
this._parent = value;
}
/** 子实体 */
private childs: Map<number, ECSEntity> | null = null;
/** 获取子实体 */
getChild<T extends ECSEntity>(eid: number): T | undefined {
return this.childs?.get(eid) as T | undefined;
}
/**
* 添加子实体(带循环引用检测)
* @param entity 被添加的实体对象
* @returns 子实体的唯一编号, -1表示添加失败
*/
addChild(entity: ECSEntity): number {
if (this.childs === null) {
this.childs = new Map<number, ECSEntity>();
}
if (this.childs.has(entity.eid)) {
console.warn(`子实体${entity.name}已存在`);
return -1;
}
// 检测循环引用
if (this.hasAncestor(entity)) {
console.error(`检测到循环引用: ${this.name} -> ${entity.name}`);
return -1;
}
entity._parent = this;
this.childs.set(entity.eid, entity);
return entity.eid;
}
/**
* 检测是否存在祖先关系(防止循环引用)
*/
private hasAncestor(entity: ECSEntity): boolean {
let current: ECSEntity | null = this;
while (current) {
if (current === entity) return true;
current = current.parent;
}
return false;
}
/**
* 移除子实体
* @param entity 被移除的实体对象
* @param isDestroy 被移除的实体是否释放,默认为释放
* @returns
*/
removeChild(entity: ECSEntity, isDestroy = true): void {
if (this.childs === null) return;
entity.parent = null;
this.childs.delete(entity.eid);
if (isDestroy) entity.destroy();
}
/**
* 根据组件类动态创建组件,并通知关心的系统。如果实体存在了这个组件,那么会先删除之前的组件然后添加新的
*
* 注意不要直接new Componentnew来的Component不会从Component的缓存池拿缓存的数据
* @param componentTypeId 组件类
* @param isReAdd true-表示用户指定这个实体可能已经存在了该组件那么再次add组件的时候会先移除该组件然后再添加一遍。false-表示不重复添加组件
*/
add<T extends ecs.IComp>(obj: T): ECSEntity;
add<T extends ecs.IComp>(ctor: CompType<T>, isReAdd?: boolean): T;
add<T extends ecs.IComp>(ctor: CompType<T> | T, isReAdd = false): T | ECSEntity {
if (typeof ctor === 'function') {
const compTid = ctor.tid;
if (ctor.tid === -1) {
throw Error(`${this.name}】实体【${ctor.compName}】组件未注册`);
}
if (this.compTid2Ctor.has(compTid)) { // 判断是否有该组件,如果有则先移除
if (isReAdd) {
this.remove(ctor);
}
else {
console.log(`${this.name}】实体【${ctor.compName}】组件已存在`);
const existingComp = getEntityComp<T>(this, ctor.compName);
if (existingComp) {
return existingComp;
}
}
}
this.mask.set(compTid);
let comp: T;
if (this.compTid2Obj.has(compTid)) {
comp = this.compTid2Obj.get(compTid) as T;
this.compTid2Obj.delete(compTid);
}
else {
// 创建组件对象
comp = createComp(ctor);
}
// 将组件对象直接附加到实体对象身上,方便直接获取
setEntityComp(this, ctor.compName, comp);
this.compTid2Ctor.set(compTid, ctor);
comp.tid = compTid;
comp.ent = this;
// 广播实体添加组件的消息
broadcastCompAddOrRemove(this, compTid);
return comp;
}
else {
// 此时 ctor 是组件实例
const compInstance = ctor as T;
const tmpCtor = (compInstance.constructor as CompCtor<T>);
const compTid = tmpCtor.tid;
if (compTid === -1 || compTid == null) throw Error(`${this.name}】实体【${tmpCtor.name}】组件未注册`);
if (this.compTid2Ctor.has(compTid)) throw Error(`${this.name}】实体【${tmpCtor.name}】组件已经存在`);
this.mask.set(compTid);
setEntityComp(this, tmpCtor.compName, compInstance);
this.compTid2Ctor.set(compTid, tmpCtor);
compInstance.tid = compTid;
compInstance.canRecycle = false;
compInstance.ent = this;
broadcastCompAddOrRemove(this, compTid);
return this;
}
}
/**
* 批量添加组件
* @param ctors 组件类
* @returns
*/
addComponents<T extends ecs.IComp>(...ctors: CompType<T>[]): this {
const len = ctors.length;
for (let i = 0; i < len; i++) {
this.add(ctors[i]);
}
return this;
}
/**
* 获取一个组件实例
* @param ctor 组件类
*/
get<T extends ecs.IComp>(ctor: CompCtor<T>): T | undefined {
return getEntityComp<T>(this, ctor.compName);
}
/**
* 组件是否在实体存在内
* @param ctor 组件类
*/
has(ctor: CompType<ecs.IComp>): boolean {
const tid = typeof ctor === 'number' ? ctor : ctor.tid;
return this.compTid2Ctor.has(tid);
}
/**
* 从实体上删除指定组件
* @param ctor 组件构造函数或者组件Tag
* @param isRecycle 是否回收该组件对象
*
* **isRecycle=true默认**
* - 调用组件的 reset() 方法清理数据
* - 如果 canRecycle=true回收到全局对象池供复用
* - 如果 canRecycle=false直接销毁
* - 适用于大部分场景
*
* **isRecycle=false**
* - 不调用 reset(),数据完整保留
* - 组件缓存在当前实体上,下次 add() 时恢复
* - 无任何限制和检查,完全由用户自行管理
* - 适用于需要保留状态的场景UI切换、技能冷却等
*
* ⚠️ isRecycle=false 使用场景:
* - UI显示/隐藏保留UI状态如滚动位置、选中项
* - 技能冷却:保留冷却剩余时间
* - 临时禁用:保留配置数据,稍后恢复
*
* 💡 管理建议:
* - 使用 ECSMemoryMonitor 监控缓存使用情况
* - 定期调用 clearComponentCache() 或 clearAllComponentCache() 清理
* - 在场景切换、内存警告时主动清理缓存
*/
remove(ctor: CompType<ecs.IComp>, isRecycle = true): void {
const componentTypeId = typeof ctor === 'number' ? ctor : ctor.tid;
const compName = typeof ctor === 'number' ? '' : ctor.compName;
if (!this.mask.has(componentTypeId)) {
return;
}
const comp = getEntityComp<ecs.IComp>(this, compName);
if (!comp) {
return;
}
comp.ent = null!;
if (isRecycle) {
comp.reset();
// 回收到全局池
if (comp.canRecycle) {
const ctor = ECSModel.compCtors[componentTypeId];
if (ctor) {
const pool = ecsPoolCoordinator.getPool(
ctor.compName,
() => new (ctor as any)()
);
pool.recycle(comp);
}
}
}
else {
// isRecycle=false用户明确要求缓存完全尊重用户意图
// 不做任何检查和限制,由用户通过 ECSMemoryMonitor 自行管理
this.compTid2Obj.set(componentTypeId, comp);
}
// 删除实体上的组件逻辑
deleteEntityComp(this, compName);
this.mask.delete(componentTypeId);
this.compTid2Ctor.delete(componentTypeId);
broadcastCompAddOrRemove(this, componentTypeId);
}
/**
* 清理指定组件的缓存
* @param ctor 组件构造函数
*/
clearComponentCache(ctor: CompType<ecs.IComp>): void {
const componentTypeId = typeof ctor === 'number' ? ctor : ctor.tid;
const comp = this.compTid2Obj.get(componentTypeId);
if (comp) {
this.compTid2Obj.delete(componentTypeId);
const ctorObj = ECSModel.compCtors[componentTypeId];
const compName = ctorObj?.compName || `tid:${componentTypeId}`;
comp.reset();
if (comp.canRecycle) {
if (ctorObj) {
const pool = ecsPoolCoordinator.getPool(
ctorObj.compName,
() => new (ctorObj as any)()
);
pool.recycle(comp);
}
}
console.log(`[ECS] 实体 ${this.name} 清理组件缓存: ${compName}`);
}
}
/**
* 清理所有组件缓存
*/
clearAllComponentCache(): void {
if (this.compTid2Obj.size === 0) return;
const count = this.compTid2Obj.size;
this.compTid2Obj.forEach((comp, tid) => {
comp.reset();
if (comp.canRecycle) {
const ctor = ECSModel.compCtors[tid];
if (ctor) {
const pool = ecsPoolCoordinator.getPool(
ctor.compName,
() => new (ctor as any)()
);
pool.recycle(comp);
}
}
});
this.compTid2Obj.clear();
console.log(`[ECS] 实体 ${this.name} 清理所有缓存,共 ${count} 个组件`);
}
/**
* 获取缓存的组件数量
*/
getCachedComponentCount(): number {
return this.compTid2Obj.size;
}
/**
* 获取缓存的组件列表(用于监控)
*/
getCachedComponents(): Map<number, ecs.IComp> {
return new Map(this.compTid2Obj);
}
/** 销毁实体,实体会被回收到实体缓存池中 */
destroy(): void {
this.isValid = false;
// 如果有父模块,则移除父模块上记录的子模块
if (this._parent) {
this._parent.removeChild(this, false);
this._parent = null;
}
// 移除模块上所有子模块
if (this.childs) {
this.childs.forEach((e) => {
this.removeChild(e);
});
this.childs.clear();
this.childs = null;
}
// 移除实体上所有组件
this.compTid2Ctor.forEach(this._remove, this);
destroyEntity(this);
// 清理缓存的组件对象,防止内存泄漏
this.clearAllComponentCache();
}
private _remove(comp: CompType<ecs.IComp>): void {
this.remove(comp, true);
}
}

View File

@@ -23,14 +23,15 @@ export class ECSGroup<E extends ECSEntity = ECSEntity> {
if (!this._cacheValid) {
const cache = this._entitiesCache;
const targetSize = this._matchEntities.size;
// 如果缓存数组过大,重新创建以释放内存
if (cache.length > targetSize * 2 && targetSize < 100) {
this._entitiesCache = [];
} else {
}
else {
cache.length = 0;
}
// 直接遍历 Map values 比 Array.from 更高效
const iterator = this._matchEntities.values();
let result = iterator.next();
@@ -38,7 +39,7 @@ export class ECSGroup<E extends ECSEntity = ECSEntity> {
this._entitiesCache.push(result.value);
result = iterator.next();
}
this._cacheValid = true;
}
return this._entitiesCache;

View File

@@ -145,7 +145,7 @@ abstract class BaseOf {
const len = args.length;
// 使用 Set 去重,性能更好
const uniqueIds = new Set<number>();
for (let i = 0; i < len; i++) {
if (typeof (args[i]) === 'number') {
componentTypeId = args[i] as number;
@@ -159,11 +159,11 @@ abstract class BaseOf {
this.mask.set(componentTypeId);
uniqueIds.add(componentTypeId);
}
// 从 Set 转为排序数组
this.indices = Array.from(uniqueIds).sort((a, b) => a - b);
}
/** 清理资源,防止内存泄漏 */
destroy(): void {
this.mask.destroy();

View File

@@ -33,15 +33,11 @@ export class ECSModel {
static eid = 1;
/** 实体构造函数 */
static entityCtors: Map<EntityCtor<ECSEntity>, string> = new Map();
/** 实体对象缓存池 */
static entityPool: Map<string, ECSEntity[]> = new Map();
/** 通过实体id查找实体对象 */
static eid2Entity: Map<number, ECSEntity> = new Map();
/** 组件类型id */
static compTid = 0;
/** 组件缓存池 */
static compPools: Map<number, ecs.IComp[]> = new Map();
/** 组件构造函数用于ecs.register注册时记录不同类型的组件 */
static compCtors: CompCtor<ecs.IComp>[] = [];
/**
@@ -62,7 +58,7 @@ export class ECSModel {
/** 对象池配置 */
static readonly MAX_ENTITY_POOL_SIZE = 200; // 每种实体类型最多缓存数量
static readonly MAX_COMP_POOL_SIZE = 500; // 每种组件类型最多缓存数量
static readonly MAX_COMP_POOL_SIZE = 500; // 每种组件类型最多缓存数量
/**
* 创建group每个group只关心对应组件的添加和删除

View File

@@ -0,0 +1,287 @@
/**
* ECS 对象数量监控日志模块
* 独立模块,零入侵现有 ECS 代码
* 通过控制台命令触发打印 ECS 对象统计表格
*/
import { ECSModel } from './ECSModel';
import { ecsPoolCoordinator } from './pool';
/** 监控数据项 */
interface MonitorItem {
/** 对象类型 */
type: string;
/** 活跃数 */
active: number;
/** 缓存数 */
cached: number;
/** 总计 */
total: number;
}
/** 池监控数据项 */
interface PoolMonitorItem {
/** 类型名 */
typeName: string;
/** 活跃数 */
active: number;
/** 缓存命中 */
hitCount: number;
/** 缓存未中 */
missCount: number;
/** 当前缓存 */
currentCache: number;
/** 总创建 */
totalCreated: number;
}
/** 实体缓存监控项 */
interface EntityCacheItem {
/** 实体名 */
entityName: string;
/** 缓存组件数 */
cachedCompCount: number;
}
/** ECS 监控日志 */
export class ECSMonitorLogger {
/**
* 打印 ECS 总体统计表格
*/
printSummary(): void {
const data: MonitorItem[] = [];
// 实体统计
const activeEntities = ECSModel.eid2Entity.size;
let entityCache = 0;
const poolMetrics = ecsPoolCoordinator.getAllMetrics();
const entityNames = new Set(ECSModel.entityCtors.values());
poolMetrics.forEach((metrics, name) => {
// 实体池的名称是通过 entityCtors 注册的名称
if (entityNames.has(name)) {
entityCache += metrics.currentSize;
}
});
data.push({
type: 'ECSEntity',
active: activeEntities,
cached: entityCache,
total: activeEntities + entityCache,
});
// 组件统计
let activeComps = 0;
ECSModel.eid2Entity.forEach((entity) => {
activeComps += entity.getMask().toString().split('1').length - 1;
});
// 只统计动态池中的组件缓存
let compCache = 0;
poolMetrics.forEach((metrics, name) => {
if (ECSModel.compCtors.some((ctor) => ctor.compName === name)) {
compCache += metrics.currentSize;
}
});
data.push({
type: 'ECSComp',
active: activeComps,
cached: compCache,
total: activeComps + compCache,
});
// Group 统计
data.push({
type: 'ECSGroup',
active: ECSModel.groups.size,
cached: 0,
total: ECSModel.groups.size,
});
// System 统计
let systemCount = 0;
ECSModel.systems.forEach((system) => {
systemCount += system.comblockSystems.length;
});
data.push({
type: 'ECSSystem',
active: systemCount,
cached: 0,
total: systemCount,
});
console.log('%c[ECS Monitor] 总体统计', 'color:#fff;background:#3a5fcd;padding:2px 8px;border-radius:4px;font-weight:bold;');
console.table(data);
}
/**
* 打印实体池明细表格
*/
printEntityPools(): void {
const data: PoolMonitorItem[] = [];
const poolMetrics = ecsPoolCoordinator.getAllMetrics();
const entityNames = new Set(ECSModel.entityCtors.values());
poolMetrics.forEach((metrics, name) => {
if (entityNames.has(name)) {
// 计算活跃数:通过 eid2Entity 中 name 匹配的实体
let activeCount = 0;
ECSModel.eid2Entity.forEach((entity) => {
if (entity.name === name) activeCount++;
});
data.push({
typeName: name,
active: activeCount,
hitCount: metrics.hitCount,
missCount: metrics.missCount,
currentCache: metrics.currentSize,
totalCreated: metrics.createCount,
});
}
});
if (data.length === 0) {
console.log('%c[ECS Monitor] 暂无实体池数据', 'color:#ee7700;');
return;
}
console.log('%c[ECS Monitor] 实体池明细', 'color:#fff;background:#3a5fcd;padding:2px 8px;border-radius:4px;font-weight:bold;');
console.table(data);
}
/**
* 获取组件分类排序权重
* @param compName 组件名
* @returns 排序权重(越小越靠前)
*/
private getCompSortWeight(compName: string): number {
// M_ 开头 - Model 数据层 (权重 1)
if (compName.startsWith('M_')) return 1;
// B_ 开头 - Business 业务层 (权重 2)
if (compName.startsWith('B_')) return 2;
// V_ 开头 - View 视图层 (权重 3)
if (compName.startsWith('V_')) return 3;
// VC_ 开头 - ViewController 视图控制层 (权重 4)
if (compName.startsWith('VC_')) return 4;
// 其他 (权重 5)
return 5;
}
/**
* 打印组件池明细表格(按 M/B/V/VC/其他 分类排序)
*/
printComponentPools(): void {
const data: PoolMonitorItem[] = [];
const poolMetrics = ecsPoolCoordinator.getAllMetrics();
// 收集所有组件数据
const compDataList: { item: PoolMonitorItem; weight: number }[] = [];
ECSModel.compCtors.forEach((ctor) => {
const metrics = poolMetrics.get(ctor.compName);
// 计算活跃组件数
let activeCount = 0;
ECSModel.eid2Entity.forEach((entity) => {
if (entity.has(ctor.tid)) activeCount++;
});
const item: PoolMonitorItem = metrics
? {
typeName: ctor.compName,
active: activeCount,
hitCount: metrics.hitCount,
missCount: metrics.missCount,
currentCache: metrics.currentSize,
totalCreated: metrics.createCount,
}
: {
typeName: ctor.compName,
active: activeCount,
hitCount: 0,
missCount: 0,
currentCache: 0,
totalCreated: 0,
};
compDataList.push({
item,
weight: this.getCompSortWeight(ctor.compName),
});
});
// 按权重排序,同权重按名称排序
compDataList.sort((a, b) => {
if (a.weight !== b.weight) {
return a.weight - b.weight;
}
return a.item.typeName.localeCompare(b.item.typeName);
});
// 提取排序后的数据
compDataList.forEach(({ item }) => data.push(item));
console.log('%c[ECS Monitor] 组件池明细', 'color:#fff;background:#3a5fcd;padding:2px 8px;border-radius:4px;font-weight:bold;');
console.table(data);
}
/**
* 打印实体组件缓存明细isRecycle=false 缓存的组件)
*/
printEntityCaches(): void {
const data: EntityCacheItem[] = [];
ECSModel.eid2Entity.forEach((entity) => {
const count = entity.getCachedComponentCount();
if (count > 0) {
data.push({
entityName: `${entity.name}(eid:${entity.eid})`,
cachedCompCount: count,
});
}
});
if (data.length === 0) {
console.log('%c[ECS Monitor] 暂无实体组件缓存', 'color:#00aa00;');
return;
}
console.log('%c[ECS Monitor] 实体组件缓存明细 (isRecycle=false)', 'color:#fff;background:#ee7700;padding:2px 8px;border-radius:4px;font-weight:bold;');
console.table(data);
}
/**
* 打印所有监控表格
*/
printAll(): void {
console.log('%c══════════════ ECS 对象监控 ══════════════', 'color:#3a5fcd;font-weight:bold;font-size:14px;');
this.printSummary();
this.printEntityPools();
this.printComponentPools();
this.printEntityCaches();
console.log('%c══════════════════════════════════════════', 'color:#3a5fcd;font-weight:bold;font-size:14px;');
}
}
/** ECS 监控日志全局实例 */
export const ecsMonitor = new ECSMonitorLogger();
// 注册全局控制台命令
if (typeof window !== 'undefined') {
Object.assign(window, {
ecsMonitor,
ecsLog: () => ecsMonitor.printAll(),
ecsSummary: () => ecsMonitor.printSummary(),
ecsEntityPools: () => ecsMonitor.printEntityPools(),
ecsCompPools: () => ecsMonitor.printComponentPools(),
ecsEntityCaches: () => ecsMonitor.printEntityCaches(),
ecsHelp: () => {
console.log('%c[ECS Monitor] 可用命令:', 'color:#fff;background:#3a5fcd;padding:2px 8px;border-radius:4px;font-weight:bold;');
console.table([
{ command: 'ecsLog()', description: '打印所有监控表格' },
{ command: 'ecsSummary()', description: '打印总体统计(实体/组件/分组/系统数量)' },
{ command: 'ecsEntityPools()', description: '打印实体池明细(命中/未命中/缓存统计)' },
{ command: 'ecsCompPools()', description: '打印组件池明细(命中/未命中/缓存统计)' },
{ command: 'ecsEntityCaches()', description: '打印实体组件缓存明细isRecycle=false 缓存的组件)' },
{ command: 'ecsHelp()', description: '显示此帮助信息' },
]);
},
});
}

View File

@@ -0,0 +1,9 @@
{
"ver": "4.0.24",
"importer": "typescript",
"imported": true,
"uuid": "4a744dda-576a-4194-bec4-1ad35c91a823",
"files": [],
"subMetas": {},
"userData": {}
}

View File

@@ -1,319 +0,0 @@
/*
* 对象池管理系统
*/
//#region 类型定义
/** 池统计指标 */
interface PoolMetrics {
createCount: number; // 创建次数
recycleCount: number; // 回收次数
hitCount: number; // 命中次数(从池中获取)
missCount: number; // 未命中次数(需要新建)
currentSize: number; // 当前池大小
}
//#endregion
//#region 动态对象池
/**
* 动态对象池
*/
class DynamicPool<T> {
private pool: T[] = [];
private metrics: PoolMetrics;
/**
* 构造函数
* @param typeName 池类型名称
* @param factory 对象工厂函数
*/
constructor(
private typeName: string,
private factory: () => T
) {
this.metrics = {
createCount: 0,
recycleCount: 0,
hitCount: 0,
missCount: 0,
currentSize: 0
};
}
/**
* 从池中获取对象
* @returns 池中的对象或新创建的对象
*/
get(): T {
if (this.pool.length > 0) {
this.metrics.hitCount++;
const item = this.pool.pop()!;
this.metrics.currentSize = this.pool.length;
return item;
}
this.metrics.missCount++;
this.metrics.createCount++;
return this.factory();
}
/**
* 回收对象到池中
* @param item 要回收的对象
*/
recycle(item: T): void {
this.pool.push(item);
this.metrics.currentSize = this.pool.length;
}
/**
* 预热池,提前创建指定数量的对象
* @param count 要预热的对象数量
*/
preWarm(count: number): void {
while (this.pool.length < count) {
this.pool.push(this.factory());
this.metrics.createCount++;
}
this.metrics.currentSize = this.pool.length;
}
/**
* 获取池的统计信息
* @returns 只读的统计指标对象
*/
getMetrics(): Readonly<PoolMetrics> {
return { ...this.metrics };
}
/**
* 清空池中的所有对象
*/
clear(): void {
this.pool.length = 0;
this.metrics.currentSize = 0;
}
/**
* 手动缩减池大小到指定容量
* @param targetSize 目标池大小
* @returns 移除的对象数量
*/
shrinkTo(targetSize: number): number {
let removed = 0;
while (this.pool.length > targetSize) {
this.pool.pop();
removed++;
}
if (removed > 0) {
this.metrics.currentSize = this.pool.length;
}
return removed;
}
}
//#endregion
//#region 池管理器
/**
* 池管理器
*/
class PoolManager {
private pools: Map<string, DynamicPool<any>> = new Map();
/**
* 获取或创建池
* @param typeName 池类型名称
* @param factory 对象工厂函数
* @returns 动态对象池实例
*/
getPool<T>(typeName: string, factory: () => T): DynamicPool<T> {
if (!this.pools.has(typeName)) {
const pool = new DynamicPool(typeName, factory);
this.pools.set(typeName, pool);
}
return this.pools.get(typeName)!;
}
/**
* 清空所有池
*/
clearAll(): void {
this.pools.forEach(pool => pool.clear());
this.pools.clear();
}
/**
* 场景切换时预热池
* @param sceneType 场景类型标识
* @param hints 类型名称到预热数量的映射
*/
onSceneChange(sceneType: string, hints: Map<string, number>): void {
hints.forEach((count, typeName) => {
const pool = this.pools.get(typeName);
if (pool) {
pool.preWarm(count);
}
});
}
/**
* 获取所有池的统计信息
* @returns 类型名称到统计指标的映射
*/
getAllMetrics(): Map<string, PoolMetrics> {
const result = new Map<string, PoolMetrics>();
this.pools.forEach((pool, typeName) => {
result.set(typeName, pool.getMetrics());
});
return result;
}
/**
* 手动缩减所有池到指定百分比
* @param percent 目标百分比0-1之间
* @returns 总共移除的对象数量
*/
shrinkAllTo(percent: number): number {
let totalRemoved = 0;
this.pools.forEach(pool => {
const metrics = pool.getMetrics();
const targetSize = Math.floor(metrics.currentSize * percent);
totalRemoved += pool.shrinkTo(targetSize);
});
return totalRemoved;
}
/**
* 获取指定类型的池
* @param typeName 池类型名称
* @returns 池实例如果不存在则返回undefined
*/
getPoolByName(typeName: string): DynamicPool<any> | undefined {
return this.pools.get(typeName);
}
}
//#endregion
//#region 全局协调器
/**
* 全局池协调器 - 提供手动管理API
*/
class GlobalPoolCoordinator {
private poolManager: PoolManager;
constructor() {
this.poolManager = new PoolManager();
}
/**
* 获取或创建池
* @param typeName 池类型名称
* @param factory 对象工厂函数
* @returns 动态对象池实例
*/
getPool<T>(typeName: string, factory: () => T): DynamicPool<T> {
return this.poolManager.getPool(typeName, factory);
}
/**
* 获取池管理器实例
* @returns 池管理器
*/
getPoolManager(): PoolManager {
return this.poolManager;
}
/**
* 场景切换时预热池
* @param sceneType 场景类型标识
* @param hints 类型名称到预热数量的映射
*/
onSceneChange(sceneType: string, hints: Map<string, number>): void {
this.poolManager.onSceneChange(sceneType, hints);
}
/**
* 手动缩减所有池到指定百分比
* @param percent 目标百分比0-1之间
* @returns 总共移除的对象数量
*/
shrinkAll(percent: number): number {
return this.poolManager.shrinkAllTo(percent);
}
/**
* 清空所有池中的对象
*/
clearAll(): void {
this.poolManager.clearAll();
}
/**
* 清空指定池中的对象
* @param typeName 池类型名称
*/
clearPool(typeName: string): void {
const pool = this.poolManager.getPoolByName(typeName);
if (pool) {
pool.clear();
}
}
/**
* 手动缩减指定池到目标大小
* @param typeName 池类型名称
* @param targetSize 目标池大小
* @returns 移除的对象数量
*/
shrinkPool(typeName: string, targetSize: number): number {
const pool = this.poolManager.getPoolByName(typeName);
return pool ? pool.shrinkTo(targetSize) : 0;
}
/**
* 获取所有池的统计信息
* @returns 类型名称到统计指标的映射
*/
getAllMetrics(): Map<string, PoolMetrics> {
return this.poolManager.getAllMetrics();
}
/**
* 获取指定池的统计信息
* @param typeName 池类型名称
* @returns 统计指标对象如果池不存在则返回undefined
*/
getPoolMetrics(typeName: string): PoolMetrics | undefined {
const pool = this.poolManager.getPoolByName(typeName);
return pool ? pool.getMetrics() : undefined;
}
}
//#endregion
/**
* 全局池协调器实例
*
* 用于管理 ECS 框架中的对象池,主要包括:
* 1. ECS 实体对象 (ECSEntity) - 实体销毁后回收复用
* 2. ECS 组件对象 (IComp) - 组件移除后回收复用
* 3. 其他自定义对象 - 支持任意类型的对象池化
*
* 核心功能:
* - 对象复用:减少频繁创建销毁带来的性能开销
* - 统计监控:跟踪命中率、创建次数等指标
* - 池管理:支持预热、缩减、清空等操作
* - 场景优化:场景切换时自动预热相关对象池
*/
export const ecsPoolCoordinator = new GlobalPoolCoordinator();

View File

@@ -51,7 +51,7 @@ export abstract class ECSComblockSystem<E extends ECSEntity = ECSEntity> {
this.execute = this.updateOnce;
}
}
/** 清理系统资源 */
protected cleanup(): void {
// 清理实体映射,防止内存泄漏
@@ -234,7 +234,7 @@ export class ECSRootSystem {
/** 系统组合器,用于将多个相同功能模块的系统逻辑上放在一起,系统也可以嵌套系统 */
export class ECSSystem {
private _comblockSystems: ECSComblockSystem[] = [];
get comblockSystems(): ECSComblockSystem[] {
return this._comblockSystems;
}
@@ -249,7 +249,7 @@ export class ECSSystem {
}
return this;
}
/** 清理系统资源 */
clear(): void {
this._comblockSystems.length = 0;

View File

@@ -0,0 +1,9 @@
{
"ver": "1.2.0",
"importer": "directory",
"imported": true,
"uuid": "84525bc8-4095-4145-b04b-621c673eee5d",
"files": [],
"subMetas": {},
"userData": {}
}

View File

@@ -0,0 +1,76 @@
/**
* 动态对象池
*/
import { IECSPoolMetrics } from './IECSPoolMetrics';
/**
* 动态对象池
* @template T 池中对象的类型
*/
export class ECSDynamicPool<T> {
private pool: T[] = [];
private metrics: IECSPoolMetrics;
/**
* 构造函数
* @param typeName 池类型名称
* @param factory 对象工厂函数
*/
constructor(
private typeName: string,
private factory: () => T
) {
this.metrics = {
createCount: 0,
recycleCount: 0,
hitCount: 0,
missCount: 0,
currentSize: 0
};
}
/**
* 从池中获取对象
* @returns 池中的对象或新创建的对象
*/
get(): T {
if (this.pool.length > 0) {
this.metrics.hitCount++;
const item = this.pool.pop()!;
this.metrics.currentSize = this.pool.length;
return item;
}
this.metrics.missCount++;
this.metrics.createCount++;
return this.factory();
}
/**
* 回收对象到池中
* @param item 要回收的对象
*/
recycle(item: T): void {
this.pool.push(item);
this.metrics.recycleCount++;
this.metrics.currentSize = this.pool.length;
}
/**
* 获取池的统计信息
* @returns 只读的统计指标对象
*/
getMetrics(): Readonly<IECSPoolMetrics> {
return { ...this.metrics };
}
/**
* 清空池中的所有对象
*/
clear(): void {
this.pool.length = 0;
this.metrics.currentSize = 0;
}
}

View File

@@ -0,0 +1,9 @@
{
"ver": "4.0.24",
"importer": "typescript",
"imported": true,
"uuid": "e18efb47-61df-4094-8457-d330b7847318",
"files": [],
"subMetas": {},
"userData": {}
}

View File

@@ -0,0 +1,142 @@
/**
* 池管理器
*/
import { ECSDynamicPool } from './ECSDynamicPool';
import type { IECSPoolMetrics } from './IECSPoolMetrics';
import type { ECSEntity } from '../ECSEntity';
import type { ecs } from '../ECS';
import { ECSModel, type CompCtor, type EntityCtor } from '../ECSModel';
/** 池对象类型 - 实体或组件 */
type ECSPoolObject = ECSEntity | ecs.IComp;
/** 池类型名称 - 实体名或组件名 */
type ECSPoolTypeName = string;
/**
* 池管理器 - 统一管理所有对象池
*/
export class ECSPoolManager {
/** 所有对象池的映射 */
private pools: Map<ECSPoolTypeName, ECSDynamicPool<ECSPoolObject>> = new Map();
/**
* 从构造函数获取池类型名称
* @param ctor 实体或组件的构造函数
* @returns 池类型名称
*/
private getTypeNameFromCtor<T extends ECSEntity>(ctor: EntityCtor<T>): string {
// 检查是否是组件构造函数(有 compName 属性)
const ctorAny = ctor as unknown as { compName?: string };
if (typeof ctorAny.compName === 'string') {
return ctorAny.compName;
}
// 检查是否是实体构造函数(从 ECSModel.entityCtors 查找)
const entityName = ECSModel.entityCtors.get(ctor as EntityCtor<ECSEntity>);
if (entityName) {
return entityName;
}
// 如果都找不到,使用构造函数名
return ctor.name;
}
/**
* 获取或创建池
* @param typeName 池类型名称(实体名如 "Account"、"RedDot",或组件名如 "M_Equip_Model"
* @param factory 对象工厂函数
* @returns 动态对象池实例
*/
getPool<T extends ECSPoolObject>(typeName: ECSPoolTypeName, factory: () => T): ECSDynamicPool<T> {
if (!this.pools.has(typeName)) {
const pool = new ECSDynamicPool<ECSPoolObject>(typeName, factory);
this.pools.set(typeName, pool);
}
return this.pools.get(typeName)! as ECSDynamicPool<T>;
}
/**
* 删除指定池(清空对象并从管理中移除)
* @param typeNameOrCtor 池类型名称(实体名或组件名)或构造函数
* @returns 是否成功删除
*/
removePool(typeNameOrCtor: ECSPoolTypeName | EntityCtor<ECSEntity> | CompCtor<ecs.IComp>): boolean {
let typeName: string;
if (typeof typeNameOrCtor === 'string') {
typeName = typeNameOrCtor;
}
else {
typeName = this.getTypeNameFromCtor(typeNameOrCtor as EntityCtor<ECSEntity>);
}
const pool = this.pools.get(typeName);
if (pool) {
pool.clear();
this.pools.delete(typeName);
return true;
}
return false;
}
/**
* 清空所有池
*/
clearAll(): void {
this.pools.forEach(pool => pool.clear());
this.pools.clear();
}
/**
* 获取所有池的统计信息
* @returns 类型名称到统计指标的映射
*/
getAllMetrics(): Map<ECSPoolTypeName, IECSPoolMetrics> {
const result = new Map<ECSPoolTypeName, IECSPoolMetrics>();
this.pools.forEach((pool, typeName) => {
result.set(typeName, pool.getMetrics());
});
return result;
}
/**
* 获取指定类型的池
* @param typeName 池类型名称(实体名或组件名)
* @returns 池实例如果不存在则返回undefined
*/
getPoolByName<T extends ECSPoolObject>(typeName: ECSPoolTypeName): ECSDynamicPool<T> | undefined {
return this.pools.get(typeName) as ECSDynamicPool<T> | undefined;
}
/**
* 获取所有池的名称
* @returns 池名称数组(实体名或组件名数组)
*/
getPoolNames(): ECSPoolTypeName[] {
return Array.from(this.pools.keys());
}
/**
* 清空指定池中的对象
* @param typeName 池类型名称(实体名或组件名)
*/
clearPool(typeName: ECSPoolTypeName): void {
const pool = this.pools.get(typeName);
if (pool) {
pool.clear();
}
}
/**
* 获取指定池的统计信息
* @param typeName 池类型名称(实体名或组件名)
* @returns 统计指标对象如果池不存在则返回undefined
*/
getPoolMetrics(typeName: ECSPoolTypeName): IECSPoolMetrics | undefined {
const pool = this.pools.get(typeName);
return pool ? pool.getMetrics() : undefined;
}
}

View File

@@ -2,10 +2,8 @@
"ver": "4.0.24",
"importer": "typescript",
"imported": true,
"uuid": "88db857b-ee25-442e-aef3-64a3b1e474de",
"uuid": "7ee37e6e-4033-4bb0-99c6-ba2cde97e5bf",
"files": [],
"subMetas": {},
"userData": {
"simulateGlobals": []
}
"userData": {}
}

View File

@@ -0,0 +1,17 @@
/**
* 池统计指标
*/
/** 池统计指标 */
export interface IECSPoolMetrics {
/** 创建次数 */
createCount: number;
/** 回收次数 */
recycleCount: number;
/** 命中次数(从池中获取) */
hitCount: number;
/** 未命中次数(需要新建) */
missCount: number;
/** 当前池大小 */
currentSize: number;
}

View File

@@ -0,0 +1,9 @@
{
"ver": "4.0.24",
"importer": "typescript",
"imported": true,
"uuid": "575be89a-5bfa-4f9e-80f1-88f43edaa1bc",
"files": [],
"subMetas": {},
"userData": {}
}

View File

@@ -0,0 +1,25 @@
/**
* 对象池管理系统
*
* 用于管理 ECS 框架中的对象池,主要包括:
* 1. ECS 实体对象 (ECSEntity) - 实体销毁后回收复用
* 2. ECS 组件对象 (IComp) - 组件移除后回收复用
* 3. 其他自定义对象 - 支持任意类型的对象池化
*
* 核心功能:
* - 对象复用:减少频繁创建销毁带来的性能开销
* - 统计监控:跟踪命中率、创建次数等指标
* - 池管理:支持预热、缩减、清空等操作
* - 场景优化:场景切换时自动预热相关对象池
*/
export { IECSPoolMetrics } from './IECSPoolMetrics';
export { ECSDynamicPool } from './ECSDynamicPool';
export { ECSPoolManager } from './ECSPoolManager';
import { ECSPoolManager } from './ECSPoolManager';
/**
* 全局池协调器实例
*/
export const ecsPoolCoordinator = new ECSPoolManager();

View File

@@ -0,0 +1,9 @@
{
"ver": "4.0.24",
"importer": "typescript",
"imported": true,
"uuid": "72c10359-0587-429d-957f-743e7f97673d",
"files": [],
"subMetas": {},
"userData": {}
}