优化ECS

This commit is contained in:
dgflash
2026-05-10 21:25:36 +08:00
parent 1d8f4988a4
commit c2f787e964
13 changed files with 1134 additions and 1147 deletions

View File

@@ -1,348 +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后会回收到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
}
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

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

@@ -5,7 +5,7 @@
*/
import { ECSModel } from './ECSModel';
import { ecsPoolCoordinator } from './ECSPoolManager';
import { ecsPoolCoordinator } from './pool';
/** 监控数据项 */
interface MonitorItem {

View File

@@ -1,324 +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

@@ -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,108 @@
/**
* 动态对象池
*/
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;
}
/**
* 预热池,提前创建指定数量的对象
* @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<IECSPoolMetrics> {
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;
}
}

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,127 @@
/**
* 池管理器
*/
import { ECSDynamicPool } from './ECSDynamicPool';
import type { IECSPoolMetrics } from './IECSPoolMetrics';
/**
* 池管理器 - 统一管理所有对象池
*/
export class ECSPoolManager {
/** 所有对象池的映射 */
private pools: Map<string, ECSDynamicPool<unknown>> = new Map();
/**
* 获取或创建池
* @param typeName 池类型名称
* @param factory 对象工厂函数
* @returns 动态对象池实例
*/
getPool<T>(typeName: string, factory: () => T): ECSDynamicPool<T> {
if (!this.pools.has(typeName)) {
const pool = new ECSDynamicPool<unknown>(typeName, factory);
this.pools.set(typeName, pool);
}
return this.pools.get(typeName)! as ECSDynamicPool<T>;
}
/**
* 清空所有池
*/
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, IECSPoolMetrics> {
const result = new Map<string, IECSPoolMetrics>();
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<T>(typeName: string): ECSDynamicPool<T> | undefined {
return this.pools.get(typeName) as ECSDynamicPool<T> | undefined;
}
/**
* 获取所有池的名称
* @returns 池名称数组
*/
getPoolNames(): string[] {
return Array.from(this.pools.keys());
}
/**
* 清空指定池中的对象
* @param typeName 池类型名称
*/
clearPool(typeName: string): void {
const pool = this.pools.get(typeName);
if (pool) {
pool.clear();
}
}
/**
* 手动缩减指定池到目标大小
* @param typeName 池类型名称
* @param targetSize 目标池大小
* @returns 移除的对象数量
*/
shrinkPool(typeName: string, targetSize: number): number {
const pool = this.pools.get(typeName);
return pool ? pool.shrinkTo(targetSize) : 0;
}
/**
* 获取指定池的统计信息
* @param typeName 池类型名称
* @returns 统计指标对象如果池不存在则返回undefined
*/
getPoolMetrics(typeName: string): 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": {}
}