优化ECS

This commit is contained in:
dgflash
2026-01-16 21:40:03 +08:00
parent d28abbcf46
commit b51ec298f7
7 changed files with 261 additions and 171 deletions

View File

@@ -128,31 +128,37 @@ export namespace ecs {
}
}
*/
export function register<T>(name: string, canNew = true) {
return function (ctor: any) {
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 (ctor.s) {
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);
system.add(new (ctor as new () => ComblockSystem)());
}
// 注册实体
else if (ctor.tid == undefined) {
ECSModel.entityCtors.set(ctor as EntityCtor<T>, name);
else if (ctorAny.tid === undefined) {
ECSModel.entityCtors.set(ctor as EntityCtor<ECSEntity>, name);
}
// 注册组件
else {
if (ctor.tid === -1) {
ctor.tid = ECSModel.compTid++;
ctor.compName = name;
ECSModel.compCtors.push(ctor); // 注册不同类型的组件
if (ctorAny.tid === -1) {
ctorAny.tid = ECSModel.compTid++;
ctorAny.compName = name;
ECSModel.compCtors.push(ctor as CompCtor<IComp>);
if (canNew) {
ECSModel.compPools.set(ctor.tid, []);
ECSModel.compPools.set(ctorAny.tid, []);
}
ECSModel.compAddOrRemove.set(ctor.tid, []);
ECSModel.compAddOrRemove.set(ctorAny.tid, []);
}
else {
throw new Error(`ECS 组件重复注册: ${name}.`);
@@ -167,29 +173,32 @@ export namespace ecs {
*/
export function getEntity<T extends Entity>(ctor: EntityCtor<T>): T {
// 获取实体对象名
const entityName = ECSModel.entityCtors.get(ctor);
if (entityName == undefined)
const entityName = ECSModel.entityCtors.get(ctor as EntityCtor<ECSEntity>);
if (entityName === undefined) {
console.error(`${ctor.name} 实体没有注册`);
throw new Error(`${ctor.name} 实体没有注册`);
}
// 获取实体对象池
const entitys = ECSModel.entityPool.get(entityName!) || [];
let entity: any = entitys.pop();
const entitys = ECSModel.entityPool.get(entityName) || [];
let entity: T | undefined = entitys.pop() as T | undefined;
// 缓存中没有同类实体,则创建一个新的
if (!entity) {
entity = new ctor();
entity.eid = ECSModel.eid++; // 实体唯一编号
entity.name = entityName;
(entity as ECSEntity).eid = ECSModel.eid++; // 实体唯一编号
(entity as ECSEntity).name = entityName;
}
// 触发实体初始化逻辑
if (entity.init) {
entity.isValid = true;
entity.init();
const entityWithInit = entity as ECSEntity & { init?: () => void };
if (entityWithInit.init) {
entityWithInit.isValid = true;
entityWithInit.init();
}
ECSModel.eid2Entity.set(entity.eid, entity);
return entity as T;
ECSModel.eid2Entity.set((entity as ECSEntity).eid, entity as ECSEntity);
return entity;
}
/**
@@ -208,7 +217,7 @@ export namespace ecs {
}
/** 清理所有的实体 */
export function clear() {
export function clear(): void {
ECSModel.eid2Entity.forEach((entity) => {
entity.destroy();
});
@@ -226,7 +235,7 @@ export namespace ecs {
* 清理所有对象池 - 用于释放不再使用的缓存内存
* 注意:此操作会清空所有实体池、组件池和 Mask 池,请在确保不再需要这些缓存时调用
*/
export function clearPools() {
export function clearPools(): void {
// 清理实体池
ECSModel.entityPool.forEach((pool) => {
pool.length = 0;
@@ -246,20 +255,20 @@ export namespace ecs {
* 通过实体唯一编号获得实体对象
* @param eid 实体唯一编号
*/
export function getEntityByEid<E extends Entity = Entity>(eid: number): E {
return ECSModel.eid2Entity.get(eid) as E;
export function getEntityByEid<E extends Entity = Entity>(eid: number): E | undefined {
return ECSModel.eid2Entity.get(eid) as E | undefined;
}
/** 当前活动中的实体数量 */
export function activeEntityCount() {
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.eid, entity);
(entity as ECSEntity).eid = ECSModel.eid++; // 实体id也是有限的资源
ECSModel.eid2Entity.set((entity as ECSEntity).eid, entity as ECSEntity);
return entity as E;
}
@@ -269,7 +278,7 @@ export namespace ecs {
*/
function createEntityWithComp<T extends IComp>(ctor: CompCtor<T>): T {
const entity = createEntity();
return entity.add(ctor);
return (entity as ECSEntity).add(ctor);
}
//#region 过滤器
@@ -280,7 +289,7 @@ export namespace ecs {
* @example
* ecs.allOf(AComponent, BComponent, CComponent);
*/
export function allOf(...args: CompType<IComp>[]) {
export function allOf(...args: CompType<IComp>[]): IMatcher {
return new ECSMatcher().allOf(...args);
}
@@ -290,7 +299,7 @@ export namespace ecs {
* @example
* ecs.anyOf(AComponent, BComponent);
*/
export function anyOf(...args: CompType<IComp>[]) {
export function anyOf(...args: CompType<IComp>[]): IMatcher {
return new ECSMatcher().anyOf(...args);
}
@@ -305,7 +314,7 @@ export namespace ecs {
// 不同时包含CComponent和DComponent
ecs.allOf(AComponent, BComponent).excludeOf(CComponent).excludeOf(DComponent);
*/
export function onlyOf(...args: CompType<IComp>[]) {
export function onlyOf(...args: CompType<IComp>[]): IMatcher {
return new ECSMatcher().onlyOf(...args);
}
@@ -316,7 +325,7 @@ export namespace ecs {
// 表示不包含组件A或者组件B
ecs.excludeOf(A, B);
*/
export function excludeOf(...args: CompType<IComp>[]) {
export function excludeOf(...args: CompType<IComp>[]): IMatcher {
return new ECSMatcher().excludeOf(...args);
}
//#endregion
@@ -326,9 +335,9 @@ export namespace ecs {
* 获取单例组件
* @param ctor 组件类
*/
export function getSingleton<T extends IComp>(ctor: CompCtor<T>) {
export function getSingleton<T extends IComp>(ctor: CompCtor<T>): T {
if (!ECSModel.tid2comp.has(ctor.tid)) {
const comp = createEntityWithComp(ctor) as T;
const comp = createEntityWithComp(ctor);
ECSModel.tid2comp.set(ctor.tid, comp);
}
return ECSModel.tid2comp.get(ctor.tid) as T;
@@ -338,7 +347,7 @@ export namespace ecs {
* 注册单例组件 - 主要用于那些不能手动创建对象的组件
* @param obj
*/
export function addSingleton(obj: IComp) {
export function addSingleton(obj: IComp): void {
const tid = (obj.constructor as CompCtor<IComp>).tid;
if (!ECSModel.tid2comp.has(tid)) {
ECSModel.tid2comp.set(tid, obj);

View File

@@ -10,10 +10,12 @@ import { ECSModel } from './ECSModel';
* @param entity 实体对象
* @param componentTypeId 组件类型id
*/
function broadcastCompAddOrRemove(entity: ECSEntity, componentTypeId: number) {
function broadcastCompAddOrRemove(entity: ECSEntity, componentTypeId: number): void {
const events = ECSModel.compAddOrRemove.get(componentTypeId);
for (let i = events!.length - 1; i >= 0; i--) {
events![i](entity);
if (events) {
for (let i = events.length - 1; i >= 0; i--) {
events[i](entity);
}
}
// 判断是不是删了单例组件
if (ECSModel.tid2comp.has(componentTypeId)) {
@@ -21,6 +23,27 @@ function broadcastCompAddOrRemove(entity: ECSEntity, componentTypeId: number) {
}
}
/**
* 设置实体上的组件属性(类型安全的动态属性访问)
*/
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
@@ -30,8 +53,11 @@ function createComp<T extends ecs.IComp>(ctor: CompCtor<T>): T {
if (!cct) {
throw Error(`没有找到该组件的构造函数,检查${ctor.compName}是否为不可构造的组件`);
}
const comps = ECSModel.compPools.get(ctor.tid)!;
const component = comps.pop() || new (cct as CompCtor<T>);
const comps = ECSModel.compPools.get(ctor.tid);
if (!comps) {
throw Error(`组件${ctor.compName}的对象池不存在`);
}
const component = comps.pop() || new (cct as CompCtor<T>)();
return component as T;
}
@@ -41,10 +67,10 @@ function createComp<T extends ecs.IComp>(ctor: CompCtor<T>): T {
* 缓存销毁的实体,下次新建实体时会优先从缓存中拿
* @param entity
*/
function destroyEntity(entity: ECSEntity) {
function destroyEntity(entity: ECSEntity): void {
if (ECSModel.eid2Entity.has(entity.eid)) {
let entitys = ECSModel.entityPool.get(entity.name);
if (entitys == null) {
if (entitys === undefined) {
entitys = [];
ECSModel.entityPool.set(entity.name, entitys);
}
@@ -70,7 +96,7 @@ export class ECSEntity {
/** 实体是否有效 */
isValid = true;
/** 组件过滤数据 */
private mask = new ECSMask();
private mask: ECSMask = new ECSMask();
/** 当前实体身上附加的组件构造函数 */
private compTid2Ctor: Map<number, CompType<ecs.IComp>> = new Map();
/** 配合 entity.remove(Comp, false) 记录组件实例上的缓存数据,在添加时恢复原数据 */
@@ -78,6 +104,11 @@ export class ECSEntity {
/** 组件缓存容量限制,防止内存泄漏 */
private static readonly MAX_CACHE_COMP = 10;
/** 获取 mask 对象(内部使用) */
getMask(): ECSMask {
return this.mask;
}
private _parent: ECSEntity | null = null;
/** 父实体 */
get parent(): ECSEntity | null {
@@ -88,11 +119,11 @@ export class ECSEntity {
}
/** 子实体 */
private childs: Map<number, ECSEntity> = null!;
private childs: Map<number, ECSEntity> | null = null;
/** 获取子实体 */
getChild<T>(eid: number) {
return this.childs.get(eid) as T;
getChild<T extends ECSEntity>(eid: number): T | undefined {
return this.childs?.get(eid) as T | undefined;
}
/**
@@ -101,7 +132,9 @@ export class ECSEntity {
* @returns 子实体的唯一编号, -1表示添加失败
*/
addChild(entity: ECSEntity): number {
if (this.childs == null) this.childs = new Map<number, ECSEntity>();
if (this.childs === null) {
this.childs = new Map<number, ECSEntity>();
}
if (this.childs.has(entity.eid)) {
console.warn(`子实体${entity.name}已存在`);
@@ -119,8 +152,8 @@ export class ECSEntity {
* @param isDestroy 被移除的实体是否释放,默认为释放
* @returns
*/
removeChild(entity: ECSEntity, isDestroy = true) {
if (this.childs == null) return;
removeChild(entity: ECSEntity, isDestroy = true): void {
if (this.childs === null) return;
entity.parent = null;
this.childs.delete(entity.eid);
@@ -148,8 +181,10 @@ export class ECSEntity {
}
else {
console.log(`${this.name}】实体【${ctor.compName}】组件已存在`);
// @ts-ignore
return this[ctor.compName] as T;
const existingComp = getEntityComp<T>(this, ctor.compName);
if (existingComp) {
return existingComp;
}
}
}
this.mask.set(compTid);
@@ -161,12 +196,11 @@ export class ECSEntity {
}
else {
// 创建组件对象
comp = createComp(ctor) as T;
comp = createComp(ctor);
}
// 将组件对象直接附加到实体对象身上,方便直接获取
// @ts-ignore
this[ctor.compName] = comp;
setEntityComp(this, ctor.compName, comp);
this.compTid2Ctor.set(compTid, ctor);
comp.tid = compTid;
comp.ent = this;
@@ -176,23 +210,19 @@ export class ECSEntity {
return comp;
}
else {
const tmpCtor = (ctor.constructor as CompCtor<T>);
// 此时 ctor 是组件实例
const compInstance = ctor as T;
const tmpCtor = (compInstance.constructor as CompCtor<T>);
const compTid = tmpCtor.tid;
// console.assert(compTid !== -1 || !compTid, '组件未注册!');
// console.assert(this.compTid2Ctor.has(compTid), '已存在该组件!');
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);
//@ts-ignore
this[tmpCtor.compName] = ctor;
setEntityComp(this, tmpCtor.compName, compInstance);
this.compTid2Ctor.set(compTid, tmpCtor);
//@ts-ignore
ctor.tid = compTid;
//@ts-ignore
ctor.canRecycle = false;
//@ts-ignore
ctor.ent = this;
compInstance.tid = compTid;
compInstance.canRecycle = false;
compInstance.ent = this;
broadcastCompAddOrRemove(this, compTid);
return this;
@@ -204,9 +234,10 @@ export class ECSEntity {
* @param ctors 组件类
* @returns
*/
addComponents<T extends ecs.IComp>(...ctors: CompType<T>[]) {
for (const ctor of ctors) {
this.add(ctor);
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;
}
@@ -215,11 +246,8 @@ export class ECSEntity {
* 获取一个组件实例
* @param ctor 组件类
*/
get(ctor: number): number;
get<T extends ecs.IComp>(ctor: CompCtor<T>): T;
get<T extends ecs.IComp>(ctor: CompCtor<T> | number): T {
// @ts-ignore
return this[ctor.compName];
get<T extends ecs.IComp>(ctor: CompCtor<T>): T | undefined {
return getEntityComp<T>(this, ctor.compName);
}
/**
@@ -227,12 +255,8 @@ export class ECSEntity {
* @param ctor 组件类
*/
has(ctor: CompType<ecs.IComp>): boolean {
if (typeof ctor === 'number') {
return this.mask.has(ctor);
}
else {
return this.compTid2Ctor.has(ctor.tid);
}
const tid = typeof ctor === 'number' ? ctor : ctor.tid;
return this.compTid2Ctor.has(tid);
}
/**
@@ -242,59 +266,58 @@ export class ECSEntity {
* 设置该参数为false这样该组件对象会缓存在实体身上下次重新添加组件时会将该组件对象添加回来不会重新从组件缓存
* 池中拿一个组件来用。
*/
remove(ctor: CompType<ecs.IComp>, isRecycle = true) {
let hasComp = false;
//@ts-ignore
const componentTypeId = ctor.tid;
//@ts-ignore
const compName = ctor.compName;
if (this.mask.has(componentTypeId)) {
hasComp = true;
//@ts-ignore
const comp = this[ctor.compName];
//@ts-ignore
comp.ent = null;
if (isRecycle) {
comp.reset();
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;
}
// 回收组件到指定缓存池中,限制池大小
if (comp.canRecycle) {
const compPoolsType = ECSModel.compPools.get(componentTypeId)!;
if (compPoolsType.length < ECSModel.MAX_COMP_POOL_SIZE) {
compPoolsType.push(comp);
}
const comp = getEntityComp<ecs.IComp>(this, compName);
if (!comp) {
return;
}
comp.ent = null!;
if (isRecycle) {
comp.reset();
// 回收组件到指定缓存池中,限制池大小
if (comp.canRecycle) {
const compPoolsType = ECSModel.compPools.get(componentTypeId);
if (compPoolsType && compPoolsType.length < ECSModel.MAX_COMP_POOL_SIZE) {
compPoolsType.push(comp);
}
}
else {
// 限制缓存组件数量,防止内存泄漏
if (this.compTid2Obj.size < ECSEntity.MAX_CACHE_COMP) {
this.compTid2Obj.set(componentTypeId, comp); // 用于缓存显示对象组件
} else {
// 超过限制,强制回收
console.warn(`实体 ${this.name} 缓存组件数量超过限制,强制回收组件 ${compName}`);
comp.reset();
if (comp.canRecycle) {
const compPoolsType = ECSModel.compPools.get(componentTypeId);
if (compPoolsType) {
compPoolsType.push(comp);
}
}
else {
// 限制缓存组件数量,防止内存泄漏
if (this.compTid2Obj.size < ECSEntity.MAX_CACHE_COMP) {
this.compTid2Obj.set(componentTypeId, comp); // 用于缓存显示对象组件
} else {
// 超过限制,强制回收
console.warn(`实体 ${this.name} 缓存组件数量超过限制,强制回收组件 ${compName}`);
comp.reset();
if (comp.canRecycle) {
const compPoolsType = ECSModel.compPools.get(componentTypeId);
if (compPoolsType && compPoolsType.length < ECSModel.MAX_COMP_POOL_SIZE) {
compPoolsType.push(comp);
}
}
}
}
// 删除实体上的组件逻辑
if (hasComp) {
//@ts-ignore
this[compName] = null;
this.mask.delete(componentTypeId);
this.compTid2Ctor.delete(componentTypeId);
broadcastCompAddOrRemove(this, componentTypeId);
}
deleteEntityComp(this, compName);
this.mask.delete(componentTypeId);
this.compTid2Ctor.delete(componentTypeId);
broadcastCompAddOrRemove(this, componentTypeId);
}
/** 销毁实体,实体会被回收到实体缓存池中 */
destroy() {
destroy(): void {
this.isValid = false;
// 如果有父模块,则移除父模块上记录的子模块
@@ -308,7 +331,8 @@ export class ECSEntity {
this.childs.forEach((e) => {
this.removeChild(e);
});
this.childs = null!;
this.childs.clear();
this.childs = null;
}
// 移除实体上所有组件
@@ -322,7 +346,7 @@ export class ECSEntity {
this.mask.destroy();
}
private _remove(comp: CompType<ecs.IComp>) {
private _remove(comp: CompType<ecs.IComp>): void {
this.remove(comp, true);
}
}

View File

@@ -9,9 +9,9 @@ import type { ECSEntity } from './ECSEntity';
export class ECSGroup<E extends ECSEntity = ECSEntity> {
/** 实体筛选规则 */
private matcher: ecs.IMatcher;
private readonly matcher: ecs.IMatcher;
private _matchEntities: Map<number, E> = new Map();
private readonly _matchEntities: Map<number, E> = new Map();
private _entitiesCache: E[] = [];
private _cacheValid = false;
@@ -19,7 +19,7 @@ export class ECSGroup<E extends ECSEntity = ECSEntity> {
/**
* 符合规则的实体
*/
get matchEntities() {
get matchEntities(): E[] {
if (!this._cacheValid) {
// 复用数组,减少 GC 压力
const cache = this._entitiesCache;
@@ -47,7 +47,7 @@ export class ECSGroup<E extends ECSEntity = ECSEntity> {
count = 0;
/** 获取matchEntities中第一个实体 */
get entity(): E {
get entity(): E | undefined {
return this.matchEntities[0];
}
@@ -58,7 +58,7 @@ export class ECSGroup<E extends ECSEntity = ECSEntity> {
this.matcher = matcher;
}
onComponentAddOrRemove(entity: E) {
onComponentAddOrRemove(entity: E): void {
if (this.matcher.isMatch(entity)) { // Group只关心指定组件在实体身上的添加和删除动作。
if (!this._matchEntities.has(entity.eid)) {
this._matchEntities.set(entity.eid, entity);
@@ -83,12 +83,12 @@ export class ECSGroup<E extends ECSEntity = ECSEntity> {
}
}
watchEntityEnterAndRemove(enteredEntities: Map<number, E>, removedEntities: Map<number, E>) {
watchEntityEnterAndRemove(enteredEntities: Map<number, E>, removedEntities: Map<number, E>): void {
this._enteredEntities = enteredEntities;
this._removedEntities = removedEntities;
}
clear() {
clear(): void {
this._matchEntities.clear();
this._entitiesCache.length = 0;
this._cacheValid = false;

View File

@@ -28,20 +28,27 @@ class MaskPool {
return new Uint32Array(length);
}
static recycle(mask: Uint32Array) {
// 只回收到池中,不超过最大容量
static recycle(mask: Uint32Array | null): void {
// 验证参数有效性
if (!mask) {
return;
}
// 只回收到池中,不超过最大容量,防止内存无限增长
if (this.pool.length < this.MAX_POOL_SIZE) {
// 清空数组,避免泄漏引用
mask.fill(0);
this.pool.push(mask);
}
}
static clear() {
static clear(): void {
// 清空对象池,释放内存
this.pool.length = 0;
}
}
export class ECSMask {
private mask!: Uint32Array;
private mask: Uint32Array | null = null;
private size = 0;
constructor() {
@@ -50,7 +57,11 @@ export class ECSMask {
this.size = length;
}
set(num: number) {
set(num: number): void {
if (!this.mask) {
console.error('ECSMask: 尝试在已销毁的 mask 上设置位');
return;
}
// https://stackoverflow.com/questions/34896909/is-it-correct-to-set-bit-31-in-javascript
// this.mask[((num / 32) >>> 0)] |= ((1 << (num % 32)) >>> 0);
const index = (num / 31) >>> 0;
@@ -58,19 +69,31 @@ export class ECSMask {
this.mask[index] |= (1 << bit);
}
delete(num: number) {
delete(num: number): void {
if (!this.mask) {
console.error('ECSMask: 尝试在已销毁的 mask 上删除位');
return;
}
const index = (num / 31) >>> 0;
const bit = num % 31;
this.mask[index] &= ~(1 << bit);
}
has(num: number): boolean {
if (!this.mask) {
console.error('ECSMask: 尝试在已销毁的 mask 上检查位');
return false;
}
const index = (num / 31) >>> 0;
const bit = num % 31;
return !!(this.mask[index] & (1 << bit));
}
or(other: ECSMask): boolean {
if (!this.mask || !other.mask) {
console.error('ECSMask: 尝试在已销毁的 mask 上执行 or 操作');
return false;
}
const size = this.size;
const thisMask = this.mask;
const otherMask = other.mask;
@@ -85,6 +108,10 @@ export class ECSMask {
}
and(other: ECSMask): boolean {
if (!this.mask || !other.mask) {
console.error('ECSMask: 尝试在已销毁的 mask 上执行 and 操作');
return false;
}
const size = this.size;
const thisMask = this.mask;
const otherMask = other.mask;
@@ -97,7 +124,10 @@ export class ECSMask {
return true;
}
clear() {
clear(): void {
if (!this.mask) {
return;
}
// 使用 fill 方法更高效
this.mask.fill(0);
}
@@ -105,10 +135,10 @@ export class ECSMask {
/**
* 销毁并回收到对象池
*/
destroy() {
destroy(): void {
if (this.mask) {
MaskPool.recycle(this.mask);
this.mask = null!;
this.mask = null;
this.size = 0;
}
}

View File

@@ -137,7 +137,7 @@ export class ECSMatcher implements ecs.IMatcher {
abstract class BaseOf {
indices: number[] = [];
protected mask = new ECSMask();
protected mask: ECSMask = new ECSMask();
private _keyCache: string | null = null; // 缓存 key避免重复生成
constructor(...args: CompType<ecs.IComp>[]) {
@@ -163,6 +163,13 @@ abstract class BaseOf {
// 从 Set 转为排序数组
this.indices = Array.from(uniqueIds).sort((a, b) => a - b);
}
/** 清理资源,防止内存泄漏 */
destroy(): void {
this.mask.destroy();
this.indices.length = 0;
this._keyCache = null;
}
toString(): string {
// 使用缓存避免重复生成字符串
@@ -182,8 +189,7 @@ abstract class BaseOf {
*/
class AnyOf extends BaseOf {
isMatch(entity: ECSEntity): boolean {
// @ts-ignore
return this.mask.or(entity.mask);
return this.mask.or((entity as ECSEntityInternal).getMask());
}
getKey(): string {
@@ -192,12 +198,11 @@ class AnyOf extends BaseOf {
}
/**
* 用于描述包含了这些组件的实体,这个实体除了包含这些组件还可以包含其他组件
* 用于描述包含了"这些"组件的实体,这个实体除了包含这些组件还可以包含其他组件
*/
class AllOf extends BaseOf {
isMatch(entity: ECSEntity): boolean {
// @ts-ignore
return this.mask.and(entity.mask);
return this.mask.and((entity as ECSEntityInternal).getMask());
}
getKey(): string {
@@ -214,7 +219,11 @@ class ExcludeOf extends BaseOf {
}
isMatch(entity: ECSEntity): boolean {
// @ts-ignore
return !this.mask.or(entity.mask);
return !this.mask.or((entity as ECSEntityInternal).getMask());
}
}
/** 内部接口,用于访问 ECSEntity 的私有成员 */
interface ECSEntityInternal {
getMask(): ECSMask;
}

View File

@@ -31,8 +31,8 @@ export interface CompCtor<T> {
export class ECSModel {
/** 实体自增id */
static eid = 1;
/** 实体造函数 */
static entityCtors: Map<EntityCtor<any>, string> = new Map();
/** 实体造函数 */
static entityCtors: Map<EntityCtor<ECSEntity>, string> = new Map();
/** 实体对象缓存池 */
static entityPool: Map<string, ECSEntity[]> = new Map();
/** 通过实体id查找实体对象 */
@@ -43,7 +43,7 @@ export class ECSModel {
/** 组件缓存池 */
static compPools: Map<number, ecs.IComp[]> = new Map();
/** 组件构造函数用于ecs.register注册时记录不同类型的组件 */
static compCtors: (CompCtor<any> | number)[] = [];
static compCtors: CompCtor<ecs.IComp>[] = [];
/**
* 每个组件的添加和删除的动作都要派送到"关心"它们的group上。goup对当前拥有或者之前删除前拥有该组件的实体进行组件规则判断。判断该实体是否满足group
* 所期望的组件组合。

View File

@@ -7,11 +7,11 @@ import { ECSModel } from './ECSModel';
export abstract class ECSComblockSystem<E extends ECSEntity = ECSEntity> {
static s = true;
protected group: ECSGroup<E>;
protected group!: ECSGroup<E>;
protected dt = 0;
private enteredEntities: Map<number, E> = null!;
private removedEntities: Map<number, E> = null!;
private enteredEntities: Map<number, E> | null = null;
private removedEntities: Map<number, E> | null = null;
private hasEntityEnter = false;
private hasEntityRemove = false;
@@ -51,6 +51,13 @@ export abstract class ECSComblockSystem<E extends ECSEntity = ECSEntity> {
this.execute = this.updateOnce;
}
}
/** 清理系统资源 */
protected cleanup(): void {
// 清理实体映射,防止内存泄漏
this.enteredEntities?.clear();
this.removedEntities?.clear();
}
/** 系统实始化 */
init(): void {
@@ -72,7 +79,7 @@ export abstract class ECSComblockSystem<E extends ECSEntity = ECSEntity> {
* @param dt
* @returns
*/
private updateOnce(dt: number) {
private updateOnce(dt: number): void {
if (this.group.count === 0) {
return;
}
@@ -80,8 +87,8 @@ export abstract class ECSComblockSystem<E extends ECSEntity = ECSEntity> {
this.dt = dt;
// 处理刚进来的实体 - 使用标准 for 循环优化性能
if (this.enteredEntities.size > 0) {
const entityEnterFn = (this as unknown as ecs.IEntityEnterSystem).entityEnter;
if (this.enteredEntities && this.enteredEntities.size > 0) {
const entityEnterFn = (this as unknown as ecs.IEntityEnterSystem<E>).entityEnter;
const iterator = this.enteredEntities.values();
let result = iterator.next();
while (!result.done) {
@@ -92,7 +99,7 @@ export abstract class ECSComblockSystem<E extends ECSEntity = ECSEntity> {
}
// 只执行firstUpdate - 使用标准 for 循环
const firstUpdateFn = (this as unknown as ecs.ISystemFirstUpdate).firstUpdate;
const firstUpdateFn = (this as unknown as ecs.ISystemFirstUpdate<E>).firstUpdate;
const entities = this.group.matchEntities;
const len = entities.length;
for (let i = 0; i < len; i++) {
@@ -116,7 +123,7 @@ export abstract class ECSComblockSystem<E extends ECSEntity = ECSEntity> {
// 执行update - 使用标准 for 循环提升性能
if (this.hasUpdate) {
const updateFn = (this as unknown as ecs.ISystemUpdate).update;
const updateFn = (this as unknown as ecs.ISystemUpdate<E>).update;
const entities = this.group.matchEntities;
const len = entities.length;
for (let i = 0; i < len; i++) {
@@ -132,9 +139,9 @@ export abstract class ECSComblockSystem<E extends ECSEntity = ECSEntity> {
*/
private execute1(dt: number): void {
// 处理移除的实体 - 使用标准循环优化
if (this.removedEntities.size > 0) {
if (this.removedEntities && this.removedEntities.size > 0) {
if (this.hasEntityRemove) {
const entityRemoveFn = (this as unknown as ecs.IEntityRemoveSystem).entityRemove;
const entityRemoveFn = (this as unknown as ecs.IEntityRemoveSystem<E>).entityRemove;
const iterator = this.removedEntities.values();
let result = iterator.next();
while (!result.done) {
@@ -150,22 +157,22 @@ export abstract class ECSComblockSystem<E extends ECSEntity = ECSEntity> {
this.dt = dt;
// 处理刚进来的实体 - 使用标准循环优化
if (this.enteredEntities!.size > 0) {
if (this.enteredEntities && this.enteredEntities.size > 0) {
if (this.hasEntityEnter) {
const entityEnterFn = (this as unknown as ecs.IEntityEnterSystem).entityEnter;
const iterator = this.enteredEntities!.values();
const entityEnterFn = (this as unknown as ecs.IEntityEnterSystem<E>).entityEnter;
const iterator = this.enteredEntities.values();
let result = iterator.next();
while (!result.done) {
entityEnterFn.call(this, result.value);
result = iterator.next();
}
}
this.enteredEntities!.clear();
this.enteredEntities.clear();
}
// 执行update - 使用标准 for 循环提升性能
if (this.hasUpdate) {
const updateFn = (this as unknown as ecs.ISystemUpdate).update;
const updateFn = (this as unknown as ecs.ISystemUpdate<E>).update;
const entities = this.group.matchEntities;
const len = entities.length;
for (let i = 0; i < len; i++) {
@@ -214,19 +221,25 @@ export class ECSRootSystem {
}
}
clear() {
this.executeSystemFlows.forEach((sys) => sys.onDestroy());
clear(): void {
const len = this.executeSystemFlows.length;
for (let i = 0; i < len; i++) {
this.executeSystemFlows[i].onDestroy();
}
this.executeSystemFlows.length = 0;
this.systemCnt = 0;
}
}
/** 系统组合器,用于将多个相同功能模块的系统逻辑上放在一起,系统也可以嵌套系统 */
export class ECSSystem {
private _comblockSystems: ECSComblockSystem[] = [];
get comblockSystems() {
get comblockSystems(): ECSComblockSystem[] {
return this._comblockSystems;
}
add(system: ECSSystem | ECSComblockSystem) {
add(system: ECSSystem | ECSComblockSystem): this {
if (system instanceof ECSSystem) {
Array.prototype.push.apply(this._comblockSystems, system._comblockSystems);
system._comblockSystems.length = 0;
@@ -236,4 +249,9 @@ export class ECSSystem {
}
return this;
}
/** 清理系统资源 */
clear(): void {
this._comblockSystems.length = 0;
}
}