diff --git a/assets/main.scene b/assets/main.scene index 29e9380..787aa20 100644 --- a/assets/main.scene +++ b/assets/main.scene @@ -227,7 +227,7 @@ "_priority": 1073741824, "_fov": 45, "_fovAxis": 0, - "_orthoHeight": 375, + "_orthoHeight": 427.33542319749216, "_near": 1, "_far": 2000, "_color": { diff --git a/assets/resources/gui/prefab/role_info.prefab b/assets/resources/gui/prefab/role_info.prefab index da98b18..5bb2606 100644 --- a/assets/resources/gui/prefab/role_info.prefab +++ b/assets/resources/gui/prefab/role_info.prefab @@ -8,8 +8,8 @@ "__id__": 1 }, "optimizationPolicy": 0, - "asyncLoadAssets": false, - "persistent": false + "persistent": false, + "asyncLoadAssets": false }, { "__type__": "cc.Node", @@ -197,6 +197,7 @@ "_enableWrapText": true, "_font": null, "_isSystemFontUsed": true, + "_spacingX": 0, "_isItalic": true, "_isBold": true, "_isUnderline": false, @@ -382,6 +383,7 @@ "_enableWrapText": true, "_font": null, "_isSystemFontUsed": true, + "_spacingX": 0, "_isItalic": false, "_isBold": false, "_isUnderline": false, @@ -533,6 +535,7 @@ "_enableWrapText": true, "_font": null, "_isSystemFontUsed": true, + "_spacingX": 0, "_isItalic": false, "_isBold": false, "_isUnderline": false, @@ -811,6 +814,7 @@ "_enableWrapText": true, "_font": null, "_isSystemFontUsed": true, + "_spacingX": 0, "_isItalic": false, "_isBold": false, "_isUnderline": false, @@ -962,6 +966,7 @@ "_enableWrapText": true, "_font": null, "_isSystemFontUsed": true, + "_spacingX": 0, "_isItalic": false, "_isBold": false, "_isUnderline": false, @@ -1240,6 +1245,7 @@ "_enableWrapText": true, "_font": null, "_isSystemFontUsed": true, + "_spacingX": 0, "_isItalic": false, "_isBold": false, "_isUnderline": false, @@ -1389,6 +1395,7 @@ "_enableWrapText": true, "_font": null, "_isSystemFontUsed": true, + "_spacingX": 0, "_isItalic": false, "_isBold": false, "_isUnderline": false, @@ -1671,6 +1678,7 @@ "_enableWrapText": true, "_font": null, "_isSystemFontUsed": true, + "_spacingX": 0, "_isItalic": false, "_isBold": false, "_isUnderline": false, @@ -1821,6 +1829,7 @@ "_enableWrapText": true, "_font": null, "_isSystemFontUsed": true, + "_spacingX": 0, "_isItalic": false, "_isBold": false, "_isUnderline": false, @@ -2101,6 +2110,7 @@ "_enableWrapText": true, "_font": null, "_isSystemFontUsed": true, + "_spacingX": 0, "_isItalic": false, "_isBold": false, "_isUnderline": false, @@ -2250,6 +2260,7 @@ "_enableWrapText": true, "_font": null, "_isSystemFontUsed": true, + "_spacingX": 0, "_isItalic": false, "_isBold": false, "_isUnderline": false, @@ -2530,6 +2541,7 @@ "_enableWrapText": true, "_font": null, "_isSystemFontUsed": true, + "_spacingX": 0, "_isItalic": false, "_isBold": false, "_isUnderline": false, @@ -2679,6 +2691,7 @@ "_enableWrapText": true, "_font": null, "_isSystemFontUsed": true, + "_spacingX": 0, "_isItalic": false, "_isBold": false, "_isUnderline": false, @@ -3011,6 +3024,7 @@ "_enableWrapText": false, "_font": null, "_isSystemFontUsed": true, + "_spacingX": 0, "_isItalic": false, "_isBold": false, "_isUnderline": false, @@ -3114,7 +3128,7 @@ "__id__": 153 }, "once": false, - "interval": 500, + "interval": 0, "disabledEffect": false, "_id": "" }, @@ -3293,6 +3307,7 @@ "_enableWrapText": false, "_font": null, "_isSystemFontUsed": true, + "_spacingX": 0, "_isItalic": false, "_isBold": false, "_isUnderline": false, diff --git a/assets/script/Main.ts b/assets/script/Main.ts index bf95b6d..c4ae963 100644 --- a/assets/script/Main.ts +++ b/assets/script/Main.ts @@ -6,7 +6,7 @@ */ import { setDisplayStats, _decorator } from 'cc'; import { DEBUG, JSB } from 'cc/env'; -import { ecs } from './core/libs/ECS'; +import { ecs } from './core/libs/ecs/ECS'; import { oops } from './core/Oops'; import { CommonEnter } from './game/common/ecs/CommonEnter'; import { smc } from './game/common/ecs/SingletonModuleComp'; diff --git a/assets/script/core/libs/ECS.ts b/assets/script/core/libs/ECS.ts deleted file mode 100644 index b6b47d4..0000000 --- a/assets/script/core/libs/ECS.ts +++ /dev/null @@ -1,1172 +0,0 @@ -// 重构原则:如无必要,勿增实体。 -export module ecs { - export interface IComp { - canRecycle: boolean; - ent: Entity; - - reset(): void; - } - - export interface CompCtor { - new(): T; - tid: number; - compName: string; - } - - export interface EntityCtor { - new(): T; - } - - /** - * 组件里面只放数据可能在实际写代码的时候比较麻烦。如果是单纯对组件内的数据操作可以在组件里面写方法。 - */ - export abstract class Comp implements IComp { - /** - * 组件的类型id,-1表示未给该组件分配id - */ - static tid: number = -1; - static compName: string; - /** - * 拥有该组件的实体 - */ - ent!: Entity; - - /** - * 是否可回收组件对象,默认情况下都是可回收的。 - * 如果该组件对象是由ecs系统外部创建的,则不可回收,需要用户自己手动进行回收。 - */ - canRecycle: boolean = true; - - /** - * 组件被回收时会调用这个接口。可以在这里重置数据,或者解除引用。 - * - * **不要偷懒,除非你能确定并保证组件在复用时,里面的数据是先赋值然后再使用。** - */ - abstract reset(): void; - } - - //#region 类型声明 - - type CompAddOrRemove = (entity: Entity) => void; - - export type CompType = CompCtor | number; - //#endregion - - //#region 注册组件 - - /** - * 组件缓存池 - */ - let compPools: Map = new Map(); - - /** - * 组件类型id - */ - let compTid = 0; - - /** - * 组件构造函数 - */ - let compCtors: (CompCtor | number)[] = []; - - /** - * 每个组件的添加和删除的动作都要派送到“关心”它们的group上。goup对当前拥有或者之前(删除前)拥有该组件的实体进行组件规则判断。判断该实体是否满足group - * 所期望的组件组合。 - */ - let compAddOrRemove: Map = new Map(); - - let tags: Map = new Map(); - - /** - * 注册组件到ecs系统中 - * @param compName 由于js打包会改变类名,所以这里必须手动传入组件的名称。 - * @param canNew 标识是否可以new对象。想继承自Cocos Creator的组件就不能去new,需要写成@ecs.register('name', false) - */ - export function register(compName: string, canNew: boolean = true) { - return function (ctor: CompCtor) { - if (ctor.tid === -1) { - ctor.tid = compTid++; - ctor.compName = compName; - if (canNew) { - compCtors.push(ctor); - compPools.set(ctor.tid, []); - } - else { - compCtors.push(null!); - } - compAddOrRemove.set(ctor.tid, []); - } - else { - throw new Error(`重复注册组件: ${compName}.`); - } - } - } - - /** - * 添加tag - * - * eg. - * @registerTag() - * class Tag { - * static A: number; - * static B: number - * } - * @returns - */ - export function registerTag() { - return function (_class: any) { - let tid = compTid; - for (let k in _class) { - tid = compTid++; - _class[k] = tid; - compCtors.push(tid); - compPools.set(tid, []); - compAddOrRemove.set(tid, []); - tags.set(tid, k); - } - } - } - //#endregion - - //#region context - - /** - * 实体对象缓存池 - */ - let entityPool: Map = new Map(); - - /** - * 通过实体id查找实体对象 - */ - let eid2Entity: Map = new Map(); - - /** - * 缓存的group - * - * key是组件的筛选规则,一个筛选规则对应一个group - */ - let groups: Map = new Map(); - - /** - * 实体自增id - */ - let eid = 1; - - /** 扩展:获取带 eid 自增量的实体(继承Entity方式的编码风格,可减少一定代码量) */ - export function getEntity(ctor: EntityCtor): T { - var entitys = entityPool.get(ctor.name) || []; - let entity: any = entitys.pop(); - if (!entity) { - entity = new ctor(); - entity.eid = eid++; // 实体id也是有限的资源 - } - - if (entity.init) - entity.init(); - else - console.error(`${ctor.name} 实体缺少 init 方法初始化默认组件`); - - eid2Entity.set(entity.eid, entity); - return entity as T; - } - - /** - * 创建实体 - */ - function createEntity(): E { - let entity = new Entity(); - entity.eid = eid++; // 实体id也是有限的资源 - eid2Entity.set(entity.eid, entity); - return entity as E; - } - - /** - * 创建组件对象 - * @param ctor - */ - function createComp(ctor: CompCtor): T { - var cct = compCtors[ctor.tid]; - if (!cct) { - throw Error(`没有找到该组件的构造函数,检查${ctor.compName}是否为不可构造的组件`); - } - let comps = compPools.get(ctor.tid)!; - let component = comps.pop() || new (cct as CompCtor); - return component as T; - } - - /** - * 指定一个组件创建实体,返回组件对象。 - * @param ctor - */ - function createEntityWithComp(ctor: CompCtor): T { - let entity = createEntity(); - return entity.add(ctor); - } - - // /** - // * 指定多个组件创建实体,返回实体对象。 - // * @param ctors - // */ - // function createEntityWithComps(...ctors: CompType[]): E { - // let entity = createEntity(); - // entity.addComponents(...ctors); - // return entity as E; - // } - - /** - * 销毁实体。 - * - * 缓存销毁的实体,下次新建实体时会优先从缓存中拿。 - * @param entity - */ - function destroyEntity(entity: Entity) { - if (eid2Entity.has(entity.eid)) { - var entitys = entityPool.get(entity.constructor.name); - if (entitys == null) { - entitys = []; - entityPool.set(entity.constructor.name, entitys); - } - entitys.push(entity); - eid2Entity.delete(entity.eid); - } - else { - console.warn('试图销毁不存在的实体!'); - } - } - - /** - * 创建group,每个group只关心对应组件的添加和删除 - * @param matcher 实体筛选器 - */ - export function createGroup(matcher: IMatcher): Group { - let group = groups.get(matcher.mid); - if (!group) { - group = new Group(matcher); - groups.set(matcher.mid, group); - let careComponentTypeIds = matcher.indices; - for (let i = 0; i < careComponentTypeIds.length; i++) { - compAddOrRemove.get(careComponentTypeIds[i])!.push(group.onComponentAddOrRemove.bind(group)); - } - } - return group as unknown as Group; - } - - /** - * 动态查询实体 - * @param matcher - * @returns - */ - export function query(matcher: IMatcher): E[] { - let group = groups.get(matcher.mid); - if (!group) { - group = createGroup(matcher); - eid2Entity.forEach(group.onComponentAddOrRemove, group); - } - return group.matchEntities as E[]; - } - - /** - * 清理所有的实体 - */ - export function clear() { - eid2Entity.forEach((entity) => { - entity.destroy(); - }); - groups.forEach((group) => { - group.clear(); - }); - compAddOrRemove.forEach(callbackLst => { - callbackLst.length = 0; - }); - eid2Entity.clear(); - groups.clear(); - } - - /** - * 实体身上组件有增删操作,广播通知对应的观察者。 - * @param entity 实体对象 - * @param componentTypeId 组件类型id - */ - function broadcastCompAddOrRemove(entity: Entity, componentTypeId: number) { - let events = compAddOrRemove.get(componentTypeId); - for (let i = events!.length - 1; i >= 0; i--) { - events![i](entity); - } - // 判断是不是删了单例组件 - if (tid2comp.has(componentTypeId)) { - tid2comp.delete(componentTypeId); - } - } - - /** - * 根据实体id获得实体对象 - * @param eid - */ - export function getEntityByEid(eid: number): E { - return eid2Entity.get(eid) as E; - } - - /** - * 当前活动中的实体数量 - */ - export function activeEntityCount() { - return eid2Entity.size; - } - //#endregion - - - /** - * 表示只关心这些组件的添加和删除动作。虽然实体可能有这些组件之外的组件,但是它们的添加和删除没有被关注,所以不会存在对关注之外的组件 - * 进行添加操作引发Group重复添加实体。 - * @param args - */ - export function allOf(...args: CompType[]) { - return new Matcher().allOf(...args); - } - - /** - * 组件间是或的关系,表示关注拥有任意一个这些组件的实体。 - * @param args 组件索引 - */ - export function anyOf(...args: CompType[]) { - return new Matcher().anyOf(...args); - } - - /** - * 表示关注只拥有这些组件的实体 - * - * 注意: - * 不是特殊情况不建议使用onlyOf。因为onlyOf会监听所有组件的添加和删除事件。 - * @param args 组件索引 - */ - export function onlyOf(...args: CompType[]) { - return new Matcher().onlyOf(...args); - } - - /** - * 不包含指定的任意一个组件 - * - * eg. - * ecs.excludeOf(A, B);表示不包含组件A或者组件B - * @param args - */ - export function excludeOf(...args: CompType[]) { - return new Matcher().excludeOf(...args); - } - - //#region 单例组件 - let tid2comp: Map = new Map(); - /** - * 获取单例组件 - * @param ctor 组件类 - */ - export function getSingleton(ctor: CompCtor) { - if (!tid2comp.has(ctor.tid)) { - let comp = createEntityWithComp(ctor) as T; - tid2comp.set(ctor.tid, comp); - } - return tid2comp.get(ctor.tid) as T; - } - - /** - * 注册单例。主要用于那些不能手动创建对象的组件 - * @param obj - */ - export function addSingleton(obj: IComp) { - let tid = (obj.constructor as CompCtor).tid; - if (!tid2comp.has(tid)) { - tid2comp.set(tid, obj); - } - } - //#endregion - - class Mask { - private mask: Uint32Array; - private size: number = 0; - - constructor() { - let length = Math.ceil(compTid / 31); - this.mask = new Uint32Array(length); - this.size = length; - } - - set(num: number) { - // https://stackoverflow.com/questions/34896909/is-it-correct-to-set-bit-31-in-javascript - // this.mask[((num / 32) >>> 0)] |= ((1 << (num % 32)) >>> 0); - this.mask[((num / 31) >>> 0)] |= (1 << (num % 31)); - } - - delete(num: number) { - this.mask[((num / 31) >>> 0)] &= ~(1 << (num % 31)); - } - - has(num: number) { - return !!(this.mask[((num / 31) >>> 0)] & (1 << (num % 31))); - } - - or(other: Mask) { - for (let i = 0; i < this.size; i++) { - // &操作符最大也只能对2^30进行操作,如果对2^31&2^31会得到负数。当然可以(2^31&2^31) >>> 0,这样多了一步右移操作。 - if (this.mask[i] & other.mask[i]) { - return true; - } - } - return false; - } - - and(other: Mask) { - for (let i = 0; i < this.size; i++) { - if ((this.mask[i] & other.mask[i]) != this.mask[i]) { - return false; - } - } - return true; - } - - clear() { - for (let i = 0; i < this.size; i++) { - this.mask[i] = 0; - } - } - } - - export class Entity { - /** - * 实体唯一标识,不要手动修改。 - */ - public eid: number = -1; - - private mask = new Mask(); - - /** - * 当前实体身上附加的组件构造函数 - */ - private compTid2Ctor: Map> = new Map(); - /** - * 配合 entity.remove(Comp, false), 记录组件实例上的缓存数据,在添加时恢复原数据 - */ - private compTid2Obj: Map = new Map(); - - constructor() { } - - /** - * 根据组件id动态创建组件,并通知关心的系统。 - * - * 如果实体存在了这个组件,那么会先删除之前的组件然后添加新的。 - * - * 注意:不要直接new Component,new来的Component不会从Component的缓存池拿缓存的数据。 - * @param componentTypeId 组件id - * @param isReAdd true-表示用户指定这个实体可能已经存在了该组件,那么再次add组件的时候会先移除该组件然后再添加一遍。false-表示不重复添加组件。 - */ - add(obj: T): Entity; - add(ctor: number, isReAdd?: boolean): Entity; - add(ctor: CompCtor, isReAdd?: boolean): T; - add(ctor: CompType, isReAdd?: boolean): T; - add(ctor: CompType | T, isReAdd: boolean = false): T | Entity { - // console.log('typeof: ', typeof ctor); - if (typeof ctor === 'function') { - let compTid = ctor.tid; - if (ctor.tid === -1) { - throw Error('组件未注册!'); - } - if (this.compTid2Ctor.has(compTid)) {// 判断是否有该组件,如果有则先移除 - if (isReAdd) { - this.remove(ctor); - } - else { - console.log(`已经存在组件:${ctor.compName}`); - // @ts-ignore - return this[ctor.compName] as T; - } - } - 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) as T; - } - // 将组件对象直接附加到实体对象身上,方便直接获取。 - // @ts-ignore - this[ctor.compName] = comp; - this.compTid2Ctor.set(compTid, ctor); - comp.ent = this; - // 广播实体添加组件的消息 - broadcastCompAddOrRemove(this, compTid); - - return comp; - } - else if (typeof ctor === 'number') { - if (tags.has(ctor)) { - this.mask.set(ctor); - this.compTid2Ctor.set(ctor, ctor); - let tagName = tags.get(ctor)!; - // @ts-ignore - this[tagName] = ctor; - broadcastCompAddOrRemove(this, ctor); - } - else { - throw Error('不存在的tag!'); - } - return this; - } - else { - let tmpCtor = (ctor.constructor as CompCtor); - let compTid = tmpCtor.tid; - // console.assert(compTid !== -1 || !compTid, '组件未注册!'); - // console.assert(this.compTid2Ctor.has(compTid), '已存在该组件!'); - if (compTid === -1 || compTid == null) { - throw Error('组件未注册!'); - } - if (this.compTid2Ctor.has(compTid)) { - throw Error('已经存在该组件!'); - } - - this.mask.set(compTid); - //@ts-ignore - this[tmpCtor.compName] = ctor; - this.compTid2Ctor.set(compTid, tmpCtor); - ctor.ent = this; - ctor.canRecycle = false; - broadcastCompAddOrRemove(this, compTid); - return this; - } - } - - addComponents(...ctors: CompType[]) { - for (let ctor of ctors) { - this.add(ctor); - } - return this; - } - - get(ctor: number): number; - get(ctor: CompCtor): T; - get(ctor: CompCtor | number): T { - let compName: string; - if (typeof (ctor) === 'number') { - compName = tags.get(ctor)!; - } - else { - compName = ctor.compName; - } - // @ts-ignore - return this[compName]; - } - - has(ctor: CompType): boolean { - if (typeof ctor == "number") { - return this.mask.has(ctor); - } - else { - return this.compTid2Ctor.has(ctor.tid); - } - } - - /** - * - * @param ctor 组件构造函数或者组件Tag - * @param isRecycle 是否回收该组件对象。对于有些组件上有大量数据,当要描述移除组件但是不想清除组件上的数据是可以 - * 设置该参数为false,这样该组件对象会缓存在实体身上,下次重新添加组件时会将该组件对象添加回来,不会重新从组件缓存 - * 池中拿一个组件来用。 - */ - remove(ctor: CompType, isRecycle: boolean = true) { - let componentTypeId = -1; - let compName = ''; - let hasComp = false; - if (typeof ctor === "number") { - componentTypeId = ctor; - if (this.mask.has(ctor)) { - hasComp = true; - compName = tags.get(ctor)!; - } - } - else { - componentTypeId = ctor.tid; - compName = ctor.compName; - if (this.mask.has(componentTypeId)) { - hasComp = true; - //@ts-ignore - let comp = this[ctor.compName] as IComp; - //@ts-ignore - comp.ent = null; - if (isRecycle) { - comp.reset(); - if (comp.canRecycle) { - compPools.get(componentTypeId)!.push(comp); - } - } - else { - this.compTid2Obj.set(componentTypeId, comp); - } - } - } - - if (hasComp) { - //@ts-ignore - this[compName] = null; - this.mask.delete(componentTypeId); - this.compTid2Ctor.delete(componentTypeId); - broadcastCompAddOrRemove(this, componentTypeId); - } - } - - private _remove(comp: CompType) { - this.remove(comp, false); - } - - /** - * 销毁实体,实体会被回收到实体缓存池中。 - */ - destroy() { - this.compTid2Ctor.forEach(this._remove, this); - destroyEntity(this); - this.compTid2Obj.clear(); - } - } - - export class Group { - /** - * 实体筛选规则 - */ - private matcher: IMatcher; - - private _matchEntities: Map = new Map(); - - private _entitiesCache: E[] | null = null; - - /** - * 符合规则的实体 - */ - public get matchEntities() { - if (this._entitiesCache === null) { - this._entitiesCache = Array.from(this._matchEntities.values()); - } - return this._entitiesCache; - } - - /** - * 当前group中实体的数量。 - * - * 不要手动修改这个属性值。 - */ - public count = 0; // 其实可以通过this._matchEntities.size获得实体数量,但是需要封装get方法。为了减少一次方法的调用所以才直接创建一个count属性 - - /** - * 获取matchEntities中第一个实体 - */ - get entity(): E { - return this.matchEntities[0]; - } - - private _enteredEntities: Map | null = null; - private _removedEntities: Map | null = null; - - constructor(matcher: IMatcher) { - this.matcher = matcher; - } - - public onComponentAddOrRemove(entity: E) { - if (this.matcher.isMatch(entity)) { // Group只关心指定组件在实体身上的添加和删除动作。 - this._matchEntities.set(entity.eid, entity); - this._entitiesCache = null; - this.count++; - - if (this._enteredEntities) { - this._enteredEntities.set(entity.eid, entity); - this._removedEntities!.delete(entity.eid); - } - } - else if (this._matchEntities.has(entity.eid)) { // 如果Group中有这个实体,但是这个实体已经不满足匹配规则,则从Group中移除该实体 - this._matchEntities.delete(entity.eid); - this._entitiesCache = null; - this.count--; - - if (this._enteredEntities) { - this._enteredEntities.delete(entity.eid); - this._removedEntities!.set(entity.eid, entity); - } - } - } - - public watchEntityEnterAndRemove(enteredEntities: Map, removedEntities: Map) { - this._enteredEntities = enteredEntities; - this._removedEntities = removedEntities; - } - - clear() { - this._matchEntities.clear(); - this._entitiesCache = null; - this.count = 0; - this._enteredEntities?.clear(); - this._removedEntities?.clear(); - } - } - - //#region Matcher - - abstract class BaseOf { - protected mask = new Mask(); - public indices: number[] = []; - constructor(...args: CompType[]) { - let componentTypeId = -1; - let len = args.length; - for (let i = 0; i < len; i++) { - if (typeof (args[i]) === "number") { - componentTypeId = args[i] as number; - } - else { - componentTypeId = (args[i] as CompCtor).tid; - } - if (componentTypeId == -1) { - throw Error('存在没有注册的组件!'); - } - this.mask.set(componentTypeId); - - if (this.indices.indexOf(componentTypeId) < 0) { // 去重 - this.indices.push(componentTypeId); - } - } - if (len > 1) { - this.indices.sort((a, b) => { return a - b; }); // 对组件类型id进行排序,这样关注相同组件的系统就能共用同一个group - } - } - - public toString(): string { - return this.indices.join('-'); // 生成group的key - } - - public abstract getKey(): string; - - public abstract isMatch(entity: Entity): boolean; - } - - /** - * 用于描述包含任意一个这些组件的实体 - */ - class AnyOf extends BaseOf { - public isMatch(entity: Entity): boolean { - // @ts-ignore - return this.mask.or(entity.mask); - } - - getKey(): string { - return 'anyOf:' + this.toString(); - } - } - - /** - * 用于描述包含了“这些”组件的实体,这个实体除了包含这些组件还可以包含其他组件 - */ - class AllOf extends BaseOf { - public isMatch(entity: Entity): boolean { - // @ts-ignore - return this.mask.and(entity.mask); - } - - getKey(): string { - return 'allOf:' + this.toString(); - } - } - - /** - * 不包含指定的任意一个组件 - */ - class ExcludeOf extends BaseOf { - public getKey(): string { - return 'excludeOf:' + this.toString(); - } - - public isMatch(entity: Entity): boolean { - // @ts-ignore - return !this.mask.or(entity.mask); - } - } - - export interface IMatcher { - mid: number; - indices: number[]; - key: string; - isMatch(entity: Entity): boolean; - } - - let macherId: number = 1; - - /** - * 筛选规则间是“与”的关系 - * 比如:ecs.Macher.allOf(...).excludeOf(...)表达的是allOf && excludeOf,即实体有“这些组件” 并且 “没有这些组件” - */ - class Matcher implements IMatcher { - protected rules: BaseOf[] = []; - protected _indices: number[] | null = null; - public isMatch!: (entity: Entity) => boolean; - public mid: number = -1; - - private _key: string | null = null; - public get key(): string { - if (!this._key) { - let s = ''; - for (let i = 0; i < this.rules.length; i++) { - s += this.rules[i].getKey() - if (i < this.rules.length - 1) { - s += ' && ' - } - } - this._key = s; - } - return this._key; - } - - constructor() { - this.mid = macherId++; - } - - /** - * 匹配器关注的组件索引。在创建Group时,Context根据组件id去给Group关联组件的添加和移除事件。 - */ - public get indices() { - if (this._indices === null) { - this._indices = []; - this.rules.forEach((rule) => { - Array.prototype.push.apply(this._indices, rule.indices); - }); - } - return this._indices; - } - - /** - * 组件间是或的关系,表示关注拥有任意一个这些组件的实体。 - * @param args 组件索引 - */ - public anyOf(...args: CompType[]): Matcher { - this.rules.push(new AnyOf(...args)); - this.bindMatchMethod(); - return this; - } - - /** - * 组件间是与的关系,表示关注拥有所有这些组件的实体。 - * @param args 组件索引 - */ - public allOf(...args: CompType[]): Matcher { - this.rules.push(new AllOf(...args)); - this.bindMatchMethod(); - return this; - } - - /** - * 表示关注只拥有这些组件的实体 - * - * 注意: - * 不是特殊情况不建议使用onlyOf。因为onlyOf会监听所有组件的添加和删除事件。 - * @param args 组件索引 - */ - public onlyOf(...args: CompType[]): Matcher { - this.rules.push(new AllOf(...args)); - let otherTids: CompType[] = []; - for (let ctor of compCtors) { - if (args.indexOf(ctor) < 0) { - otherTids.push(ctor); - } - } - this.rules.push(new ExcludeOf(...otherTids)); - this.bindMatchMethod(); - return this; - } - - /** - * 不包含指定的任意一个组件 - * @param args - */ - public excludeOf(...args: CompType[]) { - this.rules.push(new ExcludeOf(...args)); - this.bindMatchMethod(); - return this; - } - - private bindMatchMethod() { - if (this.rules.length === 1) { - this.isMatch = this.isMatch1; - } - else if (this.rules.length === 2) { - this.isMatch = this.isMatch2; - } - else { - this.isMatch = this.isMatchMore; - } - } - - private isMatch1(entity: Entity): boolean { - return this.rules[0].isMatch(entity); - } - - private isMatch2(entity: Entity): boolean { - return this.rules[0].isMatch(entity) && this.rules[1].isMatch(entity); - } - - private isMatchMore(entity: Entity): boolean { - for (let rule of this.rules) { - if (!rule.isMatch(entity)) { - return false; - } - } - return true; - } - - public clone(): Matcher { - let newMatcher = new Matcher(); - newMatcher.mid = macherId++; - this.rules.forEach(rule => newMatcher.rules.push(rule)); - return newMatcher; - } - } - //#endregion - - //#region System - /** - * 如果需要监听实体首次进入System的情况,实现这个接口。 - * - * entityEnter会在update方法之前执行,实体进入后,不会再次进入entityEnter方法中。 - * 当实体从当前System移除,下次再次符合条件进入System也会执行上述流程。 - */ - export interface IEntityEnterSystem { - entityEnter(entity: E): void; - } - - /** - * 如果需要监听实体从当前System移除,需要实现这个接口。 - */ - export interface IEntityRemoveSystem { - entityRemove(entity: E): void; - } - - /** - * 第一次执行update - */ - export interface ISystemFirstUpdate { - firstUpdate(entity: E): void; - } - - /** - * 执行update - */ - export interface ISystemUpdate { - update(entity: E): void; - } - - export abstract class ComblockSystem { - protected group: Group; - protected dt: number = 0; - - private enteredEntities: Map = null!; - private removedEntities: Map = null!; - - private hasEntityEnter: boolean = false; - private hasEntityRemove: boolean = false; - private hasUpdate: boolean = false; - - private tmpExecute: ((dt: number) => void) | null = null; - private execute!: (dt: number) => void; - - constructor() { - let hasOwnProperty = Object.hasOwnProperty; - let prototype = Object.getPrototypeOf(this); - let hasEntityEnter = hasOwnProperty.call(prototype, 'entityEnter'); - let hasEntityRemove = hasOwnProperty.call(prototype, 'entityRemove'); - let hasFirstUpdate = hasOwnProperty.call(prototype, 'firstUpdate'); - let hasUpdate = hasOwnProperty.call(prototype, 'update'); - - this.hasEntityEnter = hasEntityEnter; - this.hasEntityRemove = hasEntityRemove; - this.hasUpdate = hasUpdate; - - if (hasEntityEnter || hasEntityRemove) { - this.enteredEntities = new Map(); - this.removedEntities = new Map(); - - this.execute = this.execute1; - this.group = createGroup(this.filter()); - this.group.watchEntityEnterAndRemove(this.enteredEntities, this.removedEntities); - } - else { - this.execute = this.execute0; - this.group = createGroup(this.filter()); - } - - if (hasFirstUpdate) { - this.tmpExecute = this.execute; - this.execute = this.updateOnce; - } - } - - init(): void { - - } - - onDestroy(): void { - - } - - hasEntity(): boolean { - return this.group.count > 0; - } - - /** - * 先执行entityEnter,最后执行firstUpdate - * @param dt - * @returns - */ - private updateOnce(dt: number) { - if (this.group.count === 0) { - return; - } - - this.dt = dt; - - // 处理刚进来的实体 - if (this.enteredEntities.size > 0) { - var entities = this.enteredEntities.values(); - for (let entity of entities) { - (this as unknown as IEntityEnterSystem).entityEnter(entity); - } - this.enteredEntities.clear(); - } - - // 只执行firstUpdate - for (let entity of this.group.matchEntities) { - (this as unknown as ISystemFirstUpdate).firstUpdate(entity); - } - - this.execute = this.tmpExecute!; - this.execute(dt); - this.tmpExecute = null; - } - - /** - * 只执行update - * @param dt - * @returns - */ - private execute0(dt: number): void { - if (this.group.count === 0) return; - - this.dt = dt; - - // 执行update - if (this.hasUpdate) { - for (let entity of this.group.matchEntities) { - (this as unknown as ISystemUpdate).update(entity); - } - } - } - - /** - * 先执行entityRemove,再执行entityEnter,最后执行update - * @param dt - * @returns - */ - private execute1(dt: number): void { - if (this.removedEntities.size > 0) { - if (this.hasEntityRemove) { - var entities = this.removedEntities.values(); - for (let entity of entities) { - (this as unknown as IEntityRemoveSystem).entityRemove(entity); - } - } - this.removedEntities.clear(); - } - - if (this.group.count === 0) return; - - this.dt = dt; - - // 处理刚进来的实体 - if (this.enteredEntities!.size > 0) { - if (this.hasEntityEnter) { - var entities = this.enteredEntities!.values(); - for (let entity of entities) { - (this as unknown as IEntityEnterSystem).entityEnter(entity); - } - } - this.enteredEntities!.clear(); - } - - // 执行update - if (this.hasUpdate) { - for (let entity of this.group.matchEntities) { - (this as unknown as ISystemUpdate).update(entity); - } - } - } - - /** - * 实体过滤规则 - * - * 根据提供的组件过滤实体。 - */ - abstract filter(): IMatcher; - } - - /** - * System的root,对游戏中的System遍历从这里开始。 - * - * 一个System组合中只能有一个RootSystem,可以有多个并行的RootSystem。 - */ - export class RootSystem { - private executeSystemFlows: ComblockSystem[] = []; - private systemCnt: number = 0; - - add(system: System | ComblockSystem) { - if (system instanceof System) { - // 将嵌套的System都“摊平”,放在根System中进行遍历,减少execute的频繁进入退出。 - Array.prototype.push.apply(this.executeSystemFlows, system.comblockSystems); - } - else { - this.executeSystemFlows.push(system as ComblockSystem); - } - this.systemCnt = this.executeSystemFlows.length; - return this; - } - - init() { - this.executeSystemFlows.forEach(sys => sys.init()); - } - - execute(dt: number) { - for (let i = 0; i < this.systemCnt; i++) { - // @ts-ignore - this.executeSystemFlows[i].execute(dt); - } - } - - clear() { - this.executeSystemFlows.forEach(sys => sys.onDestroy()); - } - } - - /** - * 系统组合器,用于将多个相同功能模块的系统逻辑上放在一起。System也可以嵌套System。 - */ - export class System { - private _comblockSystems: ComblockSystem[] = []; - get comblockSystems() { - return this._comblockSystems; - } - - add(system: System | ComblockSystem) { - if (system instanceof System) { - Array.prototype.push.apply(this._comblockSystems, system._comblockSystems); - system._comblockSystems.length = 0; - } - else { - this._comblockSystems.push(system as ComblockSystem); - } - return this; - } - } - //#endregion -} \ No newline at end of file diff --git a/assets/script/core/libs/ecs.meta b/assets/script/core/libs/ecs.meta new file mode 100644 index 0000000..ba08855 --- /dev/null +++ b/assets/script/core/libs/ecs.meta @@ -0,0 +1,12 @@ +{ + "ver": "1.1.0", + "importer": "directory", + "imported": true, + "uuid": "1a67473d-eb8f-4021-9e62-a4fe85df465e", + "files": [], + "subMetas": {}, + "userData": { + "compressionType": {}, + "isRemoteBundle": {} + } +} diff --git a/assets/script/core/libs/ecs/ECS.ts b/assets/script/core/libs/ecs/ECS.ts new file mode 100644 index 0000000..d33be71 --- /dev/null +++ b/assets/script/core/libs/ecs/ECS.ts @@ -0,0 +1,245 @@ +import { ECSComp } from "./ECSComp"; +import { ECSEntity } from "./ECSEntity"; +import { ECSMatcher } from "./ECSMatcher"; +import { ECSModel } from "./ECSModel"; +import { createGroup, ECSComblockSystem, ECSRootSystem, ECSSystem } from "./ECSSystem"; + +export module ecs { + export type Entity = ECSEntity; + export type Comp = ECSComp; + export type RootSystem = ECSRootSystem; + export type ComblockSystem = ECSComblockSystem; + export type System = ECSSystem; + + export const Entity = ECSEntity; + export const Comp = ECSComp; + export const RootSystem = ECSRootSystem; + export const System = ECSSystem; + export const ComblockSystem = ECSComblockSystem; + + /** 注:不要尝试修改此对象数据,非对外使用 */ + export const model = new ECSModel(); + + export type CompAddOrRemove = (entity: Entity) => void; + export type CompType = CompCtor | number; + + //#region 接口 + export interface EntityCtor { + new(): T; + } + + export interface IComp { + canRecycle: boolean; + ent: Entity; + + reset(): void; + } + + export interface CompCtor { + new(): T; + tid: number; + compName: string; + } + + export interface IMatcher { + mid: number; + indices: number[]; + key: string; + isMatch(entity: Entity): boolean; + } + + /** + * 如果需要监听实体首次进入System的情况,实现这个接口。 + * + * entityEnter会在update方法之前执行,实体进入后,不会再次进入entityEnter方法中。 + * 当实体从当前System移除,下次再次符合条件进入System也会执行上述流程。 + */ + export interface IEntityEnterSystem { + entityEnter(entity: E): void; + } + + /** 如果需要监听实体从当前System移除,需要实现这个接口。*/ + export interface IEntityRemoveSystem { + entityRemove(entity: E): void; + } + + /** 第一次执行update */ + export interface ISystemFirstUpdate { + firstUpdate(entity: E): void; + } + + /** 执行update */ + export interface ISystemUpdate { + update(entity: E): void; + } + //#endregion + + /** + * 注册组件到ecs系统中 + * @param compName 由于js打包会改变类名,所以这里必须手动传入组件的名称。 + * @param canNew 标识是否可以new对象。想继承自Cocos Creator的组件就不能去new,需要写成@ecs.register('name', false) + */ + export function register(compName: string, canNew: boolean = true) { + return function (ctor: CompCtor) { + if (ctor.tid === -1) { + ctor.tid = model.compTid++; + ctor.compName = compName; + if (canNew) { + model.compCtors.push(ctor); + model.compPools.set(ctor.tid, []); + } + else { + model.compCtors.push(null!); + } + model.compAddOrRemove.set(ctor.tid, []); + } + else { + throw new Error(`重复注册组件: ${compName}.`); + } + } + } + + /** 扩展:获取带 eid 自增量的实体(继承Entity方式的编码风格,可减少一定代码量) */ + export function getEntity(ctor: EntityCtor): T { + var entitys = model.entityPool.get(ctor.name) || []; + let entity: any = entitys.pop(); + if (!entity) { + entity = new ctor(); + entity.eid = model.eid++; // 实体id也是有限的资源 + } + + if (entity.init) + entity.init(); + else + console.error(`${ctor.name} 实体缺少 init 方法初始化默认组件`); + + model.eid2Entity.set(entity.eid, entity); + return entity as T; + } + + /** + * 动态查询实体 + * @param matcher + * @returns + */ + export function query(matcher: IMatcher): E[] { + let group = model.groups.get(matcher.mid); + if (!group) { + group = createGroup(matcher); + model.eid2Entity.forEach(group.onComponentAddOrRemove, group); + } + return group.matchEntities as E[]; + } + + /** 清理所有的实体 */ + export function clear() { + model.eid2Entity.forEach((entity) => { + entity.destroy(); + }); + model.groups.forEach((group) => { + group.clear(); + }); + model.compAddOrRemove.forEach(callbackLst => { + callbackLst.length = 0; + }); + model.eid2Entity.clear(); + model.groups.clear(); + } + + /** + * 根据实体id获得实体对象 + * @param eid + */ + export function getEntityByEid(eid: number): E { + return model.eid2Entity.get(eid) as E; + } + + /** 当前活动中的实体数量 */ + export function activeEntityCount() { + return model.eid2Entity.size; + } + + /** 创建实体 */ + function createEntity(): E { + let entity = new Entity(); + entity.eid = model.eid++; // 实体id也是有限的资源 + model.eid2Entity.set(entity.eid, entity); + return entity as E; + } + + /** + * 指定一个组件创建实体,返回组件对象。 + * @param ctor + */ + function createEntityWithComp(ctor: CompCtor): T { + let entity = createEntity(); + return entity.add(ctor); + } + + //#region 过滤器 + /** + * 表示只关心这些组件的添加和删除动作。虽然实体可能有这些组件之外的组件,但是它们的添加和删除没有被关注,所以不会存在对关注之外的组件 + * 进行添加操作引发Group重复添加实体。 + * @param args + */ + export function allOf(...args: CompType[]) { + return new ECSMatcher().allOf(...args); + } + + /** + * 组件间是或的关系,表示关注拥有任意一个这些组件的实体。 + * @param args 组件索引 + */ + export function anyOf(...args: CompType[]) { + return new ECSMatcher().anyOf(...args); + } + + /** + * 表示关注只拥有这些组件的实体 + * + * 注意: + * 不是特殊情况不建议使用onlyOf。因为onlyOf会监听所有组件的添加和删除事件。 + * @param args 组件索引 + */ + export function onlyOf(...args: CompType[]) { + return new ECSMatcher().onlyOf(...args); + } + + /** + * 不包含指定的任意一个组件 + * + * eg. + * ecs.excludeOf(A, B);表示不包含组件A或者组件B + * @param args + */ + export function excludeOf(...args: CompType[]) { + return new ECSMatcher().excludeOf(...args); + } + //#endregion + + //#region 单例组件 + /** + * 获取单例组件 + * @param ctor 组件类 + */ + export function getSingleton(ctor: CompCtor) { + if (!model.tid2comp.has(ctor.tid)) { + let comp = createEntityWithComp(ctor) as T; + model.tid2comp.set(ctor.tid, comp); + } + return model.tid2comp.get(ctor.tid) as T; + } + + /** + * 注册单例。主要用于那些不能手动创建对象的组件 + * @param obj + */ + export function addSingleton(obj: IComp) { + let tid = (obj.constructor as CompCtor).tid; + if (!model.tid2comp.has(tid)) { + model.tid2comp.set(tid, obj); + } + } + + //#endregion +} \ No newline at end of file diff --git a/assets/script/core/libs/ECS.ts.meta b/assets/script/core/libs/ecs/ECS.ts.meta similarity index 52% rename from assets/script/core/libs/ECS.ts.meta rename to assets/script/core/libs/ecs/ECS.ts.meta index 05e38f9..0ced2ed 100644 --- a/assets/script/core/libs/ECS.ts.meta +++ b/assets/script/core/libs/ecs/ECS.ts.meta @@ -2,10 +2,8 @@ "ver": "4.0.23", "importer": "typescript", "imported": true, - "uuid": "cdf35542-2aa6-4319-b138-da7226504f07", + "uuid": "82e3b858-f25a-4985-8dfd-db86fcd8369e", "files": [], "subMetas": {}, - "userData": { - "simulateGlobals": [] - } + "userData": {} } diff --git a/assets/script/core/libs/ecs/ECSComp.ts b/assets/script/core/libs/ecs/ECSComp.ts new file mode 100644 index 0000000..17869d2 --- /dev/null +++ b/assets/script/core/libs/ecs/ECSComp.ts @@ -0,0 +1,28 @@ +import { ecs } from "./ECS"; +import { ECSEntity } from "./ECSEntity"; + +/** 组件里面只放数据可能在实际写代码的时候比较麻烦。如果是单纯对组件内的数据操作可以在组件里面写方法 */ +export abstract class ECSComp implements ecs.IComp { + /** + * 组件的类型id,-1表示未给该组件分配id + */ + static tid: number = -1; + static compName: string; + /** + * 拥有该组件的实体 + */ + ent!: ECSEntity; + + /** + * 是否可回收组件对象,默认情况下都是可回收的。 + * 如果该组件对象是由ecs系统外部创建的,则不可回收,需要用户自己手动进行回收。 + */ + canRecycle: boolean = true; + + /** + * 组件被回收时会调用这个接口。可以在这里重置数据,或者解除引用。 + * + * **不要偷懒,除非你能确定并保证组件在复用时,里面的数据是先赋值然后再使用。** + */ + abstract reset(): void; +} \ No newline at end of file diff --git a/assets/script/core/libs/ecs/ECSComp.ts.meta b/assets/script/core/libs/ecs/ECSComp.ts.meta new file mode 100644 index 0000000..492f734 --- /dev/null +++ b/assets/script/core/libs/ecs/ECSComp.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "4.0.23", + "importer": "typescript", + "imported": true, + "uuid": "3d017661-0194-47e1-b3df-a4baf9fbef05", + "files": [], + "subMetas": {}, + "userData": {} +} diff --git a/assets/script/core/libs/ecs/ECSEntity.ts b/assets/script/core/libs/ecs/ECSEntity.ts new file mode 100644 index 0000000..534851c --- /dev/null +++ b/assets/script/core/libs/ecs/ECSEntity.ts @@ -0,0 +1,227 @@ +import { ecs } from "./ECS"; +import { ECSMask } from "./ECSMask"; + +//#region 辅助方法 + +/** + * 实体身上组件有增删操作,广播通知对应的观察者 + * @param entity 实体对象 + * @param componentTypeId 组件类型id + */ +function broadcastCompAddOrRemove(entity: ECSEntity, componentTypeId: number) { + let events = ecs.model.compAddOrRemove.get(componentTypeId); + for (let i = events!.length - 1; i >= 0; i--) { + events![i](entity); + } + // 判断是不是删了单例组件 + if (ecs.model.tid2comp.has(componentTypeId)) { + ecs.model.tid2comp.delete(componentTypeId); + } +} + +/** + * 创建组件对象 + * @param ctor + */ +function createComp(ctor: ecs.CompCtor): T { + var cct = ecs.model.compCtors[ctor.tid]; + if (!cct) { + throw Error(`没有找到该组件的构造函数,检查${ctor.compName}是否为不可构造的组件`); + } + let comps = ecs.model.compPools.get(ctor.tid)!; + let component = comps.pop() || new (cct as ecs.CompCtor); + return component as T; +} + +/** + * 销毁实体。 + * + * 缓存销毁的实体,下次新建实体时会优先从缓存中拿。 + * @param entity + */ +function destroyEntity(entity: ECSEntity) { + if (ecs.model.eid2Entity.has(entity.eid)) { + var entitys = ecs.model.entityPool.get(entity.constructor.name); + if (entitys == null) { + entitys = []; + ecs.model.entityPool.set(entity.constructor.name, entitys); + } + entitys.push(entity); + ecs.model.eid2Entity.delete(entity.eid); + } + else { + console.warn('试图销毁不存在的实体'); + } +} + +//#endregion + +export class ECSEntity { + /** + * 实体唯一标识,不要手动修改。 + */ + eid: number = -1; + + private mask = new ECSMask(); + + /** + * 当前实体身上附加的组件构造函数 + */ + private compTid2Ctor: Map> = new Map(); + /** + * 配合 entity.remove(Comp, false), 记录组件实例上的缓存数据,在添加时恢复原数据 + */ + private compTid2Obj: Map = new Map(); + + /** + * 根据组件id动态创建组件,并通知关心的系统。 + * + * 如果实体存在了这个组件,那么会先删除之前的组件然后添加新的。 + * + * 注意:不要直接new Component,new来的Component不会从Component的缓存池拿缓存的数据。 + * @param componentTypeId 组件id + * @param isReAdd true-表示用户指定这个实体可能已经存在了该组件,那么再次add组件的时候会先移除该组件然后再添加一遍。false-表示不重复添加组件。 + */ + add(obj: T): ECSEntity; + add(ctor: number, isReAdd?: boolean): ECSEntity; + add(ctor: ecs.CompCtor, isReAdd?: boolean): T; + add(ctor: ecs.CompType, isReAdd?: boolean): T; + add(ctor: ecs.CompType | T, isReAdd: boolean = false): T | ECSEntity { + // console.log('typeof: ', typeof ctor); + if (typeof ctor === 'function') { + let compTid = ctor.tid; + if (ctor.tid === -1) { + throw Error('组件未注册!'); + } + if (this.compTid2Ctor.has(compTid)) { // 判断是否有该组件,如果有则先移除 + if (isReAdd) { + this.remove(ctor); + } + else { + console.log(`已经存在组件:${ctor.compName}`); + // @ts-ignore + return this[ctor.compName] as T; + } + } + 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) as T; + } + + // 将组件对象直接附加到实体对象身上,方便直接获取 + // @ts-ignore + this[ctor.compName] = comp; + this.compTid2Ctor.set(compTid, ctor); + comp.ent = this; + // 广播实体添加组件的消息 + broadcastCompAddOrRemove(this, compTid); + + return comp; + } + else { + let tmpCtor = (ctor.constructor as ecs.CompCtor); + let compTid = tmpCtor.tid; + // console.assert(compTid !== -1 || !compTid, '组件未注册!'); + // console.assert(this.compTid2Ctor.has(compTid), '已存在该组件!'); + if (compTid === -1 || compTid == null) { + throw Error('组件未注册'); + } + if (this.compTid2Ctor.has(compTid)) { + throw Error('已经存在该组件'); + } + + this.mask.set(compTid); + //@ts-ignore + this[tmpCtor.compName] = ctor; + this.compTid2Ctor.set(compTid, tmpCtor); + //@ts-ignore + ctor.ent = this; + //@ts-ignore + ctor.canRecycle = false; + broadcastCompAddOrRemove(this, compTid); + return this; + } + } + + addComponents(...ctors: ecs.CompType[]) { + for (let ctor of ctors) { + this.add(ctor); + } + return this; + } + + get(ctor: number): number; + get(ctor: ecs.CompCtor): T; + get(ctor: ecs.CompCtor | number): T { + // @ts-ignore + return this[ctor.compName]; + } + + has(ctor: ecs.CompType): boolean { + if (typeof ctor == "number") { + return this.mask.has(ctor); + } + else { + return this.compTid2Ctor.has(ctor.tid); + } + } + + /** + * + * @param ctor 组件构造函数或者组件Tag + * @param isRecycle 是否回收该组件对象。对于有些组件上有大量数据,当要描述移除组件但是不想清除组件上的数据是可以 + * 设置该参数为false,这样该组件对象会缓存在实体身上,下次重新添加组件时会将该组件对象添加回来,不会重新从组件缓存 + * 池中拿一个组件来用。 + */ + remove(ctor: ecs.CompType, isRecycle: boolean = true) { + let hasComp = false; + //@ts-ignore + let componentTypeId = ctor.tid; + //@ts-ignore + let compName = ctor.compName; + if (this.mask.has(componentTypeId)) { + hasComp = true; + //@ts-ignore + let comp = this[ctor.compName] as IECSComp; + //@ts-ignore + comp.ent = null; + if (isRecycle) { + comp.reset(); + if (comp.canRecycle) { + ecs.model.compPools.get(componentTypeId)!.push(comp); + } + } + else { + this.compTid2Obj.set(componentTypeId, comp); + } + } + + if (hasComp) { + //@ts-ignore + this[compName] = null; + this.mask.delete(componentTypeId); + this.compTid2Ctor.delete(componentTypeId); + broadcastCompAddOrRemove(this, componentTypeId); + } + } + + private _remove(comp: ecs.CompType) { + this.remove(comp, false); + } + + /** + * 销毁实体,实体会被回收到实体缓存池中。 + */ + destroy() { + this.compTid2Ctor.forEach(this._remove, this); + destroyEntity(this); + this.compTid2Obj.clear(); + } +} \ No newline at end of file diff --git a/assets/script/core/libs/ecs/ECSEntity.ts.meta b/assets/script/core/libs/ecs/ECSEntity.ts.meta new file mode 100644 index 0000000..4bdbf7d --- /dev/null +++ b/assets/script/core/libs/ecs/ECSEntity.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "4.0.23", + "importer": "typescript", + "imported": true, + "uuid": "1fb62582-dcf6-4fbc-b863-a1941fad1109", + "files": [], + "subMetas": {}, + "userData": {} +} diff --git a/assets/script/core/libs/ecs/ECSGroup.ts b/assets/script/core/libs/ecs/ECSGroup.ts new file mode 100644 index 0000000..3c3bd9d --- /dev/null +++ b/assets/script/core/libs/ecs/ECSGroup.ts @@ -0,0 +1,77 @@ +import { ecs } from "./ECS"; +import { ECSEntity } from "./ECSEntity"; + +export class ECSGroup { + /** 实体筛选规则 */ + private matcher: ecs.IMatcher; + + private _matchEntities: Map = new Map(); + + private _entitiesCache: E[] | null = null; + + /** + * 符合规则的实体 + */ + get matchEntities() { + if (this._entitiesCache === null) { + this._entitiesCache = Array.from(this._matchEntities.values()); + } + return this._entitiesCache; + } + + /** + * 当前group中实体的数量。 + * + * 注:不要手动修改这个属性值。 + * 注:其实可以通过this._matchEntities.size获得实体数量,但是需要封装get方法。为了减少一次方法的调用所以才直接创建一个count属性 + */ + count = 0; + + /** 获取matchEntities中第一个实体 */ + get entity(): E { + return this.matchEntities[0]; + } + + private _enteredEntities: Map | null = null; + private _removedEntities: Map | null = null; + + constructor(matcher: ecs.IMatcher) { + this.matcher = matcher; + } + + onComponentAddOrRemove(entity: E) { + if (this.matcher.isMatch(entity)) { // Group只关心指定组件在实体身上的添加和删除动作。 + this._matchEntities.set(entity.eid, entity); + this._entitiesCache = null; + this.count++; + + if (this._enteredEntities) { + this._enteredEntities.set(entity.eid, entity); + this._removedEntities!.delete(entity.eid); + } + } + else if (this._matchEntities.has(entity.eid)) { // 如果Group中有这个实体,但是这个实体已经不满足匹配规则,则从Group中移除该实体 + this._matchEntities.delete(entity.eid); + this._entitiesCache = null; + this.count--; + + if (this._enteredEntities) { + this._enteredEntities.delete(entity.eid); + this._removedEntities!.set(entity.eid, entity); + } + } + } + + watchEntityEnterAndRemove(enteredEntities: Map, removedEntities: Map) { + this._enteredEntities = enteredEntities; + this._removedEntities = removedEntities; + } + + clear() { + this._matchEntities.clear(); + this._entitiesCache = null; + this.count = 0; + this._enteredEntities?.clear(); + this._removedEntities?.clear(); + } +} \ No newline at end of file diff --git a/assets/script/core/libs/ecs/ECSGroup.ts.meta b/assets/script/core/libs/ecs/ECSGroup.ts.meta new file mode 100644 index 0000000..f898922 --- /dev/null +++ b/assets/script/core/libs/ecs/ECSGroup.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "4.0.23", + "importer": "typescript", + "imported": true, + "uuid": "c21a2de8-f4fe-4534-96a6-70c9a86167ec", + "files": [], + "subMetas": {}, + "userData": {} +} diff --git a/assets/script/core/libs/ecs/ECSMask.ts b/assets/script/core/libs/ecs/ECSMask.ts new file mode 100644 index 0000000..4446101 --- /dev/null +++ b/assets/script/core/libs/ecs/ECSMask.ts @@ -0,0 +1,51 @@ +import { ecs } from "./ECS"; + +export class ECSMask { + private mask: Uint32Array; + private size: number = 0; + + constructor() { + let length = Math.ceil(ecs.model.compTid / 31); + this.mask = new Uint32Array(length); + this.size = length; + } + + set(num: number) { + // https://stackoverflow.com/questions/34896909/is-it-correct-to-set-bit-31-in-javascript + // this.mask[((num / 32) >>> 0)] |= ((1 << (num % 32)) >>> 0); + this.mask[((num / 31) >>> 0)] |= (1 << (num % 31)); + } + + delete(num: number) { + this.mask[((num / 31) >>> 0)] &= ~(1 << (num % 31)); + } + + has(num: number) { + return !!(this.mask[((num / 31) >>> 0)] & (1 << (num % 31))); + } + + or(other: ECSMask) { + for (let i = 0; i < this.size; i++) { + // &操作符最大也只能对2^30进行操作,如果对2^31&2^31会得到负数。当然可以(2^31&2^31) >>> 0,这样多了一步右移操作。 + if (this.mask[i] & other.mask[i]) { + return true; + } + } + return false; + } + + and(other: ECSMask) { + for (let i = 0; i < this.size; i++) { + if ((this.mask[i] & other.mask[i]) != this.mask[i]) { + return false; + } + } + return true; + } + + clear() { + for (let i = 0; i < this.size; i++) { + this.mask[i] = 0; + } + } +} \ No newline at end of file diff --git a/assets/script/core/libs/ecs/ECSMask.ts.meta b/assets/script/core/libs/ecs/ECSMask.ts.meta new file mode 100644 index 0000000..a89e84e --- /dev/null +++ b/assets/script/core/libs/ecs/ECSMask.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "4.0.23", + "importer": "typescript", + "imported": true, + "uuid": "d1869e0f-3dbb-46b3-9229-828fef689057", + "files": [], + "subMetas": {}, + "userData": {} +} diff --git a/assets/script/core/libs/ecs/ECSMatcher.ts b/assets/script/core/libs/ecs/ECSMatcher.ts new file mode 100644 index 0000000..78d99e9 --- /dev/null +++ b/assets/script/core/libs/ecs/ECSMatcher.ts @@ -0,0 +1,214 @@ +import { ecs } from "./ECS"; +import { ECSEntity } from "./ECSEntity"; +import { ECSMask } from "./ECSMask"; + +let macherId: number = 1; + +/** + * 筛选规则间是“与”的关系 + * 比如:ecs.Macher.allOf(...).excludeOf(...)表达的是allOf && excludeOf,即实体有“这些组件” 并且 “没有这些组件” + */ +export class ECSMatcher implements ecs.IMatcher { + protected rules: BaseOf[] = []; + protected _indices: number[] | null = null; + public isMatch!: (entity: ECSEntity) => boolean; + public mid: number = -1; + + private _key: string | null = null; + public get key(): string { + if (!this._key) { + let s = ''; + for (let i = 0; i < this.rules.length; i++) { + s += this.rules[i].getKey() + if (i < this.rules.length - 1) { + s += ' && ' + } + } + this._key = s; + } + return this._key; + } + + constructor() { + this.mid = macherId++; + } + + /** + * 匹配器关注的组件索引。在创建Group时,Context根据组件id去给Group关联组件的添加和移除事件。 + */ + get indices() { + if (this._indices === null) { + this._indices = []; + this.rules.forEach((rule) => { + Array.prototype.push.apply(this._indices, rule.indices); + }); + } + return this._indices; + } + + /** + * 组件间是或的关系,表示关注拥有任意一个这些组件的实体。 + * @param args 组件索引 + */ + anyOf(...args: ecs.CompType[]): ECSMatcher { + this.rules.push(new AnyOf(...args)); + this.bindMatchMethod(); + return this; + } + + /** + * 组件间是与的关系,表示关注拥有所有这些组件的实体。 + * @param args 组件索引 + */ + allOf(...args: ecs.CompType[]): ECSMatcher { + this.rules.push(new AllOf(...args)); + this.bindMatchMethod(); + return this; + } + + /** + * 表示关注只拥有这些组件的实体 + * + * 注意: + * 不是特殊情况不建议使用onlyOf。因为onlyOf会监听所有组件的添加和删除事件。 + * @param args 组件索引 + */ + onlyOf(...args: ecs.CompType[]): ECSMatcher { + this.rules.push(new AllOf(...args)); + let otherTids: ecs.CompType[] = []; + for (let ctor of ecs.model.compCtors) { + if (args.indexOf(ctor) < 0) { + otherTids.push(ctor); + } + } + this.rules.push(new ExcludeOf(...otherTids)); + this.bindMatchMethod(); + return this; + } + + /** + * 不包含指定的任意一个组件 + * @param args + */ + excludeOf(...args: ecs.CompType[]) { + this.rules.push(new ExcludeOf(...args)); + this.bindMatchMethod(); + return this; + } + + private bindMatchMethod() { + if (this.rules.length === 1) { + this.isMatch = this.isMatch1; + } + else if (this.rules.length === 2) { + this.isMatch = this.isMatch2; + } + else { + this.isMatch = this.isMatchMore; + } + } + + private isMatch1(entity: ECSEntity): boolean { + return this.rules[0].isMatch(entity); + } + + private isMatch2(entity: ECSEntity): boolean { + return this.rules[0].isMatch(entity) && this.rules[1].isMatch(entity); + } + + private isMatchMore(entity: ECSEntity): boolean { + for (let rule of this.rules) { + if (!rule.isMatch(entity)) { + return false; + } + } + return true; + } + + clone(): ECSMatcher { + let newMatcher = new ECSMatcher(); + newMatcher.mid = macherId++; + this.rules.forEach(rule => newMatcher.rules.push(rule)); + return newMatcher; + } +} + +abstract class BaseOf { + indices: number[] = []; + + protected mask = new ECSMask(); + + constructor(...args: ecs.CompType[]) { + let componentTypeId = -1; + let len = args.length; + for (let i = 0; i < len; i++) { + if (typeof (args[i]) === "number") { + componentTypeId = args[i] as number; + } + else { + componentTypeId = (args[i] as ecs.CompCtor).tid; + } + if (componentTypeId == -1) { + throw Error('存在没有注册的组件!'); + } + this.mask.set(componentTypeId); + + if (this.indices.indexOf(componentTypeId) < 0) { // 去重 + this.indices.push(componentTypeId); + } + } + if (len > 1) { + this.indices.sort((a, b) => { return a - b; }); // 对组件类型id进行排序,这样关注相同组件的系统就能共用同一个group + } + } + + toString(): string { + return this.indices.join('-'); // 生成group的key + } + + abstract getKey(): string; + + abstract isMatch(entity: ECSEntity): boolean; +} + +/** + * 用于描述包含任意一个这些组件的实体 + */ +class AnyOf extends BaseOf { + public isMatch(entity: ECSEntity): boolean { + // @ts-ignore + return this.mask.or(entity.mask); + } + + getKey(): string { + return 'anyOf:' + this.toString(); + } +} + +/** + * 用于描述包含了“这些”组件的实体,这个实体除了包含这些组件还可以包含其他组件 + */ +class AllOf extends BaseOf { + public isMatch(entity: ECSEntity): boolean { + // @ts-ignore + return this.mask.and(entity.mask); + } + + getKey(): string { + return 'allOf:' + this.toString(); + } +} + +/** + * 不包含指定的任意一个组件 + */ +class ExcludeOf extends BaseOf { + public getKey(): string { + return 'excludeOf:' + this.toString(); + } + + public isMatch(entity: ECSEntity): boolean { + // @ts-ignore + return !this.mask.or(entity.mask); + } +} \ No newline at end of file diff --git a/assets/script/core/libs/ecs/ECSMatcher.ts.meta b/assets/script/core/libs/ecs/ECSMatcher.ts.meta new file mode 100644 index 0000000..db09b0e --- /dev/null +++ b/assets/script/core/libs/ecs/ECSMatcher.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "4.0.23", + "importer": "typescript", + "imported": true, + "uuid": "37e8aae5-a8f9-4ded-a999-6321c5bc1229", + "files": [], + "subMetas": {}, + "userData": {} +} diff --git a/assets/script/core/libs/ecs/ECSModel.ts b/assets/script/core/libs/ecs/ECSModel.ts new file mode 100644 index 0000000..3c61c79 --- /dev/null +++ b/assets/script/core/libs/ecs/ECSModel.ts @@ -0,0 +1,43 @@ +import { ecs } from "./ECS"; +import { ECSEntity } from "./ECSEntity"; +import { ECSGroup } from "./ECSGroup"; + +export class ECSModel { + /** 实体自增id */ + eid = 1; + + /** 组件类型id */ + compTid = 0; + + /** 组件缓存池 */ + compPools: Map = new Map(); + + /** 组件构造函数 */ + compCtors: (ecs.CompCtor | number)[] = []; + + /** + * 每个组件的添加和删除的动作都要派送到“关心”它们的group上。goup对当前拥有或者之前(删除前)拥有该组件的实体进行组件规则判断。判断该实体是否满足group + * 所期望的组件组合。 + */ + compAddOrRemove: Map = new Map(); + + /** 编号获取组件 */ + tid2comp: Map = new Map(); + + /** + * 实体对象缓存池 + */ + entityPool: Map = new Map(); + + /** + * 通过实体id查找实体对象 + */ + eid2Entity: Map = new Map(); + + /** + * 缓存的group + * + * key是组件的筛选规则,一个筛选规则对应一个group + */ + groups: Map = new Map(); +} \ No newline at end of file diff --git a/assets/script/core/libs/ecs/ECSModel.ts.meta b/assets/script/core/libs/ecs/ECSModel.ts.meta new file mode 100644 index 0000000..923c8b6 --- /dev/null +++ b/assets/script/core/libs/ecs/ECSModel.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "4.0.23", + "importer": "typescript", + "imported": true, + "uuid": "1d60e80c-eabf-46b5-ae0e-a7754005218c", + "files": [], + "subMetas": {}, + "userData": {} +} diff --git a/assets/script/core/libs/ecs/ECSSystem.ts b/assets/script/core/libs/ecs/ECSSystem.ts new file mode 100644 index 0000000..ebc3d4b --- /dev/null +++ b/assets/script/core/libs/ecs/ECSSystem.ts @@ -0,0 +1,231 @@ +import { ecs } from "./ECS"; +import { ECSEntity } from "./ECSEntity"; +import { ECSGroup } from "./ECSGroup"; + +/** + * 创建group,每个group只关心对应组件的添加和删除 + * @param matcher 实体筛选器 + */ + export function createGroup(matcher: ecs.IMatcher): ECSGroup { + let group = ecs.model.groups.get(matcher.mid); + if (!group) { + group = new ECSGroup(matcher); + ecs.model.groups.set(matcher.mid, group); + let careComponentTypeIds = matcher.indices; + for (let i = 0; i < careComponentTypeIds.length; i++) { + ecs.model.compAddOrRemove.get(careComponentTypeIds[i])!.push(group.onComponentAddOrRemove.bind(group)); + } + } + return group as unknown as ECSGroup; +} + +export abstract class ECSComblockSystem { + protected group: ECSGroup; + protected dt: number = 0; + + private enteredEntities: Map = null!; + private removedEntities: Map = null!; + + private hasEntityEnter: boolean = false; + private hasEntityRemove: boolean = false; + private hasUpdate: boolean = false; + + private tmpExecute: ((dt: number) => void) | null = null; + private execute!: (dt: number) => void; + + constructor() { + let hasOwnProperty = Object.hasOwnProperty; + let prototype = Object.getPrototypeOf(this); + let hasEntityEnter = hasOwnProperty.call(prototype, 'entityEnter'); + let hasEntityRemove = hasOwnProperty.call(prototype, 'entityRemove'); + let hasFirstUpdate = hasOwnProperty.call(prototype, 'firstUpdate'); + let hasUpdate = hasOwnProperty.call(prototype, 'update'); + + this.hasEntityEnter = hasEntityEnter; + this.hasEntityRemove = hasEntityRemove; + this.hasUpdate = hasUpdate; + + if (hasEntityEnter || hasEntityRemove) { + this.enteredEntities = new Map(); + this.removedEntities = new Map(); + + this.execute = this.execute1; + this.group = createGroup(this.filter()); + this.group.watchEntityEnterAndRemove(this.enteredEntities, this.removedEntities); + } + else { + this.execute = this.execute0; + this.group = createGroup(this.filter()); + } + + if (hasFirstUpdate) { + this.tmpExecute = this.execute; + this.execute = this.updateOnce; + } + } + + init(): void { + + } + + onDestroy(): void { + + } + + hasEntity(): boolean { + return this.group.count > 0; + } + + /** + * 先执行entityEnter,最后执行firstUpdate + * @param dt + * @returns + */ + private updateOnce(dt: number) { + if (this.group.count === 0) { + return; + } + + this.dt = dt; + + // 处理刚进来的实体 + if (this.enteredEntities.size > 0) { + var entities = this.enteredEntities.values(); + for (let entity of entities) { + (this as unknown as ecs.IEntityEnterSystem).entityEnter(entity); + } + this.enteredEntities.clear(); + } + + // 只执行firstUpdate + for (let entity of this.group.matchEntities) { + (this as unknown as ecs.ISystemFirstUpdate).firstUpdate(entity); + } + + this.execute = this.tmpExecute!; + this.execute(dt); + this.tmpExecute = null; + } + + /** + * 只执行update + * @param dt + * @returns + */ + private execute0(dt: number): void { + if (this.group.count === 0) return; + + this.dt = dt; + + // 执行update + if (this.hasUpdate) { + for (let entity of this.group.matchEntities) { + (this as unknown as ecs.ISystemUpdate).update(entity); + } + } + } + + /** + * 先执行entityRemove,再执行entityEnter,最后执行update + * @param dt + * @returns + */ + private execute1(dt: number): void { + if (this.removedEntities.size > 0) { + if (this.hasEntityRemove) { + var entities = this.removedEntities.values(); + for (let entity of entities) { + (this as unknown as ecs.IEntityRemoveSystem).entityRemove(entity); + } + } + this.removedEntities.clear(); + } + + if (this.group.count === 0) return; + + this.dt = dt; + + // 处理刚进来的实体 + if (this.enteredEntities!.size > 0) { + if (this.hasEntityEnter) { + var entities = this.enteredEntities!.values(); + for (let entity of entities) { + (this as unknown as ecs.IEntityEnterSystem).entityEnter(entity); + } + } + this.enteredEntities!.clear(); + } + + // 执行update + if (this.hasUpdate) { + for (let entity of this.group.matchEntities) { + (this as unknown as ecs.ISystemUpdate).update(entity); + } + } + } + + /** + * 实体过滤规则 + * + * 根据提供的组件过滤实体。 + */ + abstract filter(): ecs.IMatcher; +} + +/** + * System的root,对游戏中的System遍历从这里开始。 + * + * 一个System组合中只能有一个RootSystem,可以有多个并行的RootSystem。 + */ +export class ECSRootSystem { + private executeSystemFlows: ECSComblockSystem[] = []; + private systemCnt: number = 0; + + add(system: ECSSystem | ECSComblockSystem) { + if (system instanceof ECSSystem) { + // 将嵌套的System都“摊平”,放在根System中进行遍历,减少execute的频繁进入退出。 + Array.prototype.push.apply(this.executeSystemFlows, system.comblockSystems); + } + else { + this.executeSystemFlows.push(system as ECSComblockSystem); + } + this.systemCnt = this.executeSystemFlows.length; + return this; + } + + init() { + this.executeSystemFlows.forEach(sys => sys.init()); + } + + execute(dt: number) { + for (let i = 0; i < this.systemCnt; i++) { + // @ts-ignore + this.executeSystemFlows[i].execute(dt); + } + } + + clear() { + this.executeSystemFlows.forEach(sys => sys.onDestroy()); + } +} + +/** + * 系统组合器,用于将多个相同功能模块的系统逻辑上放在一起。System也可以嵌套System。 + */ +export class ECSSystem { + private _comblockSystems: ECSComblockSystem[] = []; + get comblockSystems() { + return this._comblockSystems; + } + + add(system: ECSSystem | ECSComblockSystem) { + if (system instanceof ECSSystem) { + Array.prototype.push.apply(this._comblockSystems, system._comblockSystems); + system._comblockSystems.length = 0; + } + else { + this._comblockSystems.push(system as ECSComblockSystem); + } + return this; + } +} \ No newline at end of file diff --git a/assets/script/core/libs/ecs/ECSSystem.ts.meta b/assets/script/core/libs/ecs/ECSSystem.ts.meta new file mode 100644 index 0000000..1e1683b --- /dev/null +++ b/assets/script/core/libs/ecs/ECSSystem.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "4.0.23", + "importer": "typescript", + "imported": true, + "uuid": "9261f456-8364-4163-9931-6c526c635402", + "files": [], + "subMetas": {}, + "userData": {} +} diff --git a/assets/script/core/libs/ecs/IECS.ts.meta b/assets/script/core/libs/ecs/IECS.ts.meta new file mode 100644 index 0000000..74160fb --- /dev/null +++ b/assets/script/core/libs/ecs/IECS.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "4.0.23", + "importer": "typescript", + "imported": true, + "uuid": "70bdbf80-64ba-465d-baca-d19506dee907", + "files": [], + "subMetas": {}, + "userData": {} +} diff --git a/assets/script/game/account/Account.ts b/assets/script/game/account/Account.ts index 473ae47..f4de036 100644 --- a/assets/script/game/account/Account.ts +++ b/assets/script/game/account/Account.ts @@ -6,7 +6,7 @@ */ import { Message } from "../../core/common/event/MessageManager"; -import { ecs } from "../../core/libs/ECS"; +import { ecs } from "../../core/libs/ecs/ECS"; import { GameEvent } from "../common/config/GameEvent"; import { AccountNetDataComp, AccountNetDataSystem } from "./bll/AccountNetData"; import { AccountModelComp } from "./model/AccountModelComp"; diff --git a/assets/script/game/account/bll/AccountNetData.ts b/assets/script/game/account/bll/AccountNetData.ts index 56ddc7b..080c3cf 100644 --- a/assets/script/game/account/bll/AccountNetData.ts +++ b/assets/script/game/account/bll/AccountNetData.ts @@ -8,7 +8,7 @@ import { v3 } from "cc"; import { Message } from "../../../core/common/event/MessageManager"; import { storage } from "../../../core/common/storage/StorageManager"; -import { ecs } from "../../../core/libs/ECS"; +import { ecs } from "../../../core/libs/ecs/ECS"; import { oops } from "../../../core/Oops"; import { GameEvent } from "../../common/config/GameEvent"; import { netConfig } from "../../common/net/NetConfig"; diff --git a/assets/script/game/account/model/AccountModelComp.ts b/assets/script/game/account/model/AccountModelComp.ts index d1a7117..36b5217 100644 --- a/assets/script/game/account/model/AccountModelComp.ts +++ b/assets/script/game/account/model/AccountModelComp.ts @@ -7,7 +7,7 @@ * @LastEditTime: 2022-01-27 11:07:05 */ -import { ecs } from "../../../core/libs/ECS"; +import { ecs } from "../../../core/libs/ecs/ECS"; import { Role } from "../../role/Role"; /** diff --git a/assets/script/game/common/ecs/CCComp.ts b/assets/script/game/common/ecs/CCComp.ts index 2f644a8..2461270 100644 --- a/assets/script/game/common/ecs/CCComp.ts +++ b/assets/script/game/common/ecs/CCComp.ts @@ -7,7 +7,7 @@ import { Component, Node, _decorator } from 'cc'; import { EventDispatcher } from '../../../core/common/event/EventDispatcher'; -import { ecs } from '../../../core/libs/ECS'; +import { ecs } from '../../../core/libs/ecs/ECS'; import { ViewUtil } from '../../../core/utils/ViewUtil'; const { ccclass, property } = _decorator; diff --git a/assets/script/game/common/ecs/CCVMParentComp.ts b/assets/script/game/common/ecs/CCVMParentComp.ts index 4f77ede..b973153 100644 --- a/assets/script/game/common/ecs/CCVMParentComp.ts +++ b/assets/script/game/common/ecs/CCVMParentComp.ts @@ -7,7 +7,7 @@ import { Node, _decorator } from 'cc'; import { EventDispatcher } from "../../../core/common/event/EventDispatcher"; -import { ecs } from "../../../core/libs/ECS"; +import { ecs } from '../../../core/libs/ecs/ECS'; import VMParent from "../../../core/libs/model-view/VMParent"; import { ViewUtil } from "../../../core/utils/ViewUtil"; diff --git a/assets/script/game/common/ecs/CommonSystem.ts b/assets/script/game/common/ecs/CommonSystem.ts index de74a92..0529e53 100644 --- a/assets/script/game/common/ecs/CommonSystem.ts +++ b/assets/script/game/common/ecs/CommonSystem.ts @@ -4,7 +4,7 @@ * @LastEditors: dgflash * @LastEditTime: 2022-02-25 09:49:22 */ -import { ecs } from "../../../core/libs/ECS"; +import { ecs } from "../../../core/libs/ecs/ECS"; import { EcsAccountSystem } from "../../account/Account"; import { EcsRoleSystem } from "../../role/Role"; import { EcsPositionSystem } from "./position/EcsPositionSystem"; diff --git a/assets/script/game/common/ecs/SingletonModuleComp.ts b/assets/script/game/common/ecs/SingletonModuleComp.ts index 43a5ecb..226d57d 100644 --- a/assets/script/game/common/ecs/SingletonModuleComp.ts +++ b/assets/script/game/common/ecs/SingletonModuleComp.ts @@ -5,7 +5,7 @@ * @LastEditTime: 2022-03-15 10:36:13 */ -import { ecs } from "../../../core/libs/ECS"; +import { ecs } from "../../../core/libs/ecs/ECS"; import { Account } from "../../account/Account"; import { Initialize } from "../../initialize/Initialize"; diff --git a/assets/script/game/common/ecs/position/EcsPositionSystem.ts b/assets/script/game/common/ecs/position/EcsPositionSystem.ts index db973a5..097d27a 100644 --- a/assets/script/game/common/ecs/position/EcsPositionSystem.ts +++ b/assets/script/game/common/ecs/position/EcsPositionSystem.ts @@ -4,7 +4,7 @@ * @LastEditors: dgflash * @LastEditTime: 2022-01-27 11:10:44 */ -import { ecs } from "../../../../core/libs/ECS"; +import { ecs } from "../../../../core/libs/ecs/ECS"; import { MoveToSystem } from "./MoveTo"; export class EcsPositionSystem extends ecs.System { diff --git a/assets/script/game/common/ecs/position/MoveTo.ts b/assets/script/game/common/ecs/position/MoveTo.ts index 3b1af43..6bb0aab 100644 --- a/assets/script/game/common/ecs/position/MoveTo.ts +++ b/assets/script/game/common/ecs/position/MoveTo.ts @@ -6,7 +6,7 @@ */ import { Node, Vec3 } from "cc"; import { Timer } from "../../../../core/common/manager/TimerManager"; -import { ecs } from "../../../../core/libs/ECS"; +import { ecs } from "../../../../core/libs/ecs/ECS"; import { Vec3Util } from "../../../../core/utils/Vec3Util"; /** 向目标移动,移动过程中目标位置变化会自动修正移动目标点,直到未修正前移动到目标点停止 */ diff --git a/assets/script/game/demo/Demo.ts b/assets/script/game/demo/Demo.ts index 3f3e8eb..fb1f661 100644 --- a/assets/script/game/demo/Demo.ts +++ b/assets/script/game/demo/Demo.ts @@ -5,8 +5,8 @@ * @LastEditTime: 2022-04-14 18:16:20 */ import { Component, EventTouch, _decorator } from "cc"; +import { ecs } from "../../core/libs/ecs/ECS"; import { tips } from "../../core/gui/prompt/TipsManager"; -import { ecs } from "../../core/libs/ECS"; import { oops } from "../../core/Oops"; import { UIID } from "../common/config/GameUIConfig"; import { SingletonModuleComp } from "../common/ecs/SingletonModuleComp"; diff --git a/assets/script/game/initialize/Initialize.ts b/assets/script/game/initialize/Initialize.ts index f873b46..d60f762 100644 --- a/assets/script/game/initialize/Initialize.ts +++ b/assets/script/game/initialize/Initialize.ts @@ -7,8 +7,8 @@ import { Node } from "cc"; import { resLoader } from "../../core/common/loader/ResLoader"; import { AsyncQueue, NextFunction } from "../../core/common/queue/AsyncQueue"; +import { ecs } from "../../core/libs/ecs/ECS"; import { UICallbacks } from "../../core/gui/layer/Defines"; -import { ecs } from "../../core/libs/ECS"; import { oops } from "../../core/Oops"; import { config } from "../common/config/Config"; import { UIID } from "../common/config/GameUIConfig"; diff --git a/assets/script/game/initialize/view/LoadingViewComp.ts b/assets/script/game/initialize/view/LoadingViewComp.ts index 7a56d73..6a931a1 100644 --- a/assets/script/game/initialize/view/LoadingViewComp.ts +++ b/assets/script/game/initialize/view/LoadingViewComp.ts @@ -6,7 +6,7 @@ */ import { sys, _decorator } from "cc"; import { resLoader } from "../../../core/common/loader/ResLoader"; -import { ecs } from "../../../core/libs/ECS"; +import { ecs } from "../../../core/libs/ecs/ECS"; import { oops } from "../../../core/Oops"; import { JsonUtil } from "../../../core/utils/JsonUtil"; import { Account } from "../../account/Account"; diff --git a/assets/script/game/role/Role.ts b/assets/script/game/role/Role.ts index 78a5dd6..b4f56f4 100644 --- a/assets/script/game/role/Role.ts +++ b/assets/script/game/role/Role.ts @@ -6,7 +6,7 @@ * @LastEditTime: 2022-04-25 12:02:10 */ import { Node, Vec3 } from "cc"; -import { ecs } from "../../core/libs/ECS"; +import { ecs } from "../../core/libs/ecs/ECS"; import { ViewUtil } from "../../core/utils/ViewUtil"; import { MoveToComp } from "../common/ecs/position/MoveTo"; import { RoleChangeJobComp, RoleChangeJobSystem } from "./bll/RoleChangeJob"; diff --git a/assets/script/game/role/bll/RoleChangeJob.ts b/assets/script/game/role/bll/RoleChangeJob.ts index 4aac585..b6c6ea1 100644 --- a/assets/script/game/role/bll/RoleChangeJob.ts +++ b/assets/script/game/role/bll/RoleChangeJob.ts @@ -6,7 +6,7 @@ */ import { Message } from "../../../core/common/event/MessageManager"; -import { ecs } from "../../../core/libs/ECS"; +import { ecs } from "../../../core/libs/ecs/ECS"; import { RoleModelJobComp } from "../model/RoleModelJobComp"; import { Role } from "../Role"; import { RoleEvent } from "../RoleEvent"; diff --git a/assets/script/game/role/bll/RoleUpgrade.ts b/assets/script/game/role/bll/RoleUpgrade.ts index f6fd48b..b433771 100644 --- a/assets/script/game/role/bll/RoleUpgrade.ts +++ b/assets/script/game/role/bll/RoleUpgrade.ts @@ -1,4 +1,4 @@ -import { ecs } from "../../../core/libs/ECS"; +import { ecs } from "../../../core/libs/ecs/ECS"; import { RoleAttributeType } from "../model/RoleEnum"; import { RoleModelLevelComp } from "../model/RoleModelLevelComp"; import { Role } from "../Role"; diff --git a/assets/script/game/role/model/RoleModelBaseComp.ts b/assets/script/game/role/model/RoleModelBaseComp.ts index b35e7a6..bf02d53 100644 --- a/assets/script/game/role/model/RoleModelBaseComp.ts +++ b/assets/script/game/role/model/RoleModelBaseComp.ts @@ -5,7 +5,7 @@ * @LastEditTime: 2022-03-10 10:23:41 */ -import { ecs } from "../../../core/libs/ECS"; +import { ecs } from "../../../core/libs/ecs/ECS"; import { VM } from "../../../core/libs/model-view/ViewModel"; import { RoleAttributeType } from "./RoleEnum"; import { RoleModelComp } from "./RoleModelComp"; diff --git a/assets/script/game/role/model/RoleModelComp.ts b/assets/script/game/role/model/RoleModelComp.ts index 367aabc..1c57b53 100644 --- a/assets/script/game/role/model/RoleModelComp.ts +++ b/assets/script/game/role/model/RoleModelComp.ts @@ -5,7 +5,7 @@ * @LastEditTime: 2022-03-10 10:25:15 */ -import { ecs } from "../../../core/libs/ECS"; +import { ecs } from "../../../core/libs/ecs/ECS"; import { VM } from "../../../core/libs/model-view/ViewModel"; import { RoleNumeric } from "./attribute/RoleNumeric"; import { RoleNumericMap } from "./attribute/RoleNumericMap"; diff --git a/assets/script/game/role/model/RoleModelJobComp.ts b/assets/script/game/role/model/RoleModelJobComp.ts index 64917d3..116ee8d 100644 --- a/assets/script/game/role/model/RoleModelJobComp.ts +++ b/assets/script/game/role/model/RoleModelJobComp.ts @@ -4,7 +4,7 @@ * @LastEditors: dgflash * @LastEditTime: 2022-01-29 10:56:57 */ -import { ecs } from "../../../core/libs/ECS"; +import { ecs } from "../../../core/libs/ecs/ECS"; import { TableRoleJob } from "../../common/table/TableRoleJob"; import { RoleAttributeType } from "./RoleEnum"; import { RoleModelComp } from "./RoleModelComp"; diff --git a/assets/script/game/role/model/RoleModelLevelComp.ts b/assets/script/game/role/model/RoleModelLevelComp.ts index 7b37056..8a16fd5 100644 --- a/assets/script/game/role/model/RoleModelLevelComp.ts +++ b/assets/script/game/role/model/RoleModelLevelComp.ts @@ -5,7 +5,7 @@ * @LastEditTime: 2022-03-10 11:38:31 */ -import { ecs } from "../../../core/libs/ECS"; +import { ecs } from "../../../core/libs/ecs/ECS"; import { VM } from "../../../core/libs/model-view/ViewModel"; import { TableRoleLevelUp } from "../../common/table/TableRoleLevelUp"; diff --git a/assets/script/game/role/view/RoleViewComp.ts b/assets/script/game/role/view/RoleViewComp.ts index d3fb322..d693a83 100644 --- a/assets/script/game/role/view/RoleViewComp.ts +++ b/assets/script/game/role/view/RoleViewComp.ts @@ -6,7 +6,7 @@ */ import { sp, _decorator } from "cc"; -import { ecs } from "../../../core/libs/ECS"; +import { ecs } from "../../../core/libs/ecs/ECS"; import { CCComp } from "../../common/ecs/CCComp"; import { Role } from "../Role"; import { RoleEvent } from "../RoleEvent"; diff --git a/assets/script/game/role/view/RoleViewInfoComp.ts b/assets/script/game/role/view/RoleViewInfoComp.ts index bd1d5fa..cff0372 100644 --- a/assets/script/game/role/view/RoleViewInfoComp.ts +++ b/assets/script/game/role/view/RoleViewInfoComp.ts @@ -1,6 +1,6 @@ import { EventTouch, Node, _decorator } from "cc"; +import { ecs } from "../../../core/libs/ecs/ECS"; import { oops } from "../../../core/Oops"; -import { ecs } from "../../../core/libs/ECS"; import { UIID } from "../../common/config/GameUIConfig"; import { CCComp } from "../../common/ecs/CCComp"; import { SingletonModuleComp } from "../../common/ecs/SingletonModuleComp";