mirror of
https://gitee.com/dgflash/oops-plugin-framework.git
synced 2026-05-08 03:16:49 +08:00
优化ECS
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
* 所期望的组件组合。
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user