mirror of
https://gitee.com/dgflash/oops-framework.git
synced 2026-05-11 02:21:14 +08:00
重构ECS框架实现代码分离方便后续扩展新特性
This commit is contained in:
@@ -227,7 +227,7 @@
|
||||
"_priority": 1073741824,
|
||||
"_fov": 45,
|
||||
"_fovAxis": 0,
|
||||
"_orthoHeight": 375,
|
||||
"_orthoHeight": 427.33542319749216,
|
||||
"_near": 1,
|
||||
"_far": 2000,
|
||||
"_color": {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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';
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
12
assets/script/core/libs/ecs.meta
Normal file
12
assets/script/core/libs/ecs.meta
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"ver": "1.1.0",
|
||||
"importer": "directory",
|
||||
"imported": true,
|
||||
"uuid": "1a67473d-eb8f-4021-9e62-a4fe85df465e",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {
|
||||
"compressionType": {},
|
||||
"isRemoteBundle": {}
|
||||
}
|
||||
}
|
||||
245
assets/script/core/libs/ecs/ECS.ts
Normal file
245
assets/script/core/libs/ecs/ECS.ts
Normal file
@@ -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<T> = CompCtor<T> | number;
|
||||
|
||||
//#region 接口
|
||||
export interface EntityCtor<T> {
|
||||
new(): T;
|
||||
}
|
||||
|
||||
export interface IComp {
|
||||
canRecycle: boolean;
|
||||
ent: Entity;
|
||||
|
||||
reset(): void;
|
||||
}
|
||||
|
||||
export interface CompCtor<T> {
|
||||
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<E extends Entity = Entity> {
|
||||
entityEnter(entity: E): void;
|
||||
}
|
||||
|
||||
/** 如果需要监听实体从当前System移除,需要实现这个接口。*/
|
||||
export interface IEntityRemoveSystem<E extends Entity = Entity> {
|
||||
entityRemove(entity: E): void;
|
||||
}
|
||||
|
||||
/** 第一次执行update */
|
||||
export interface ISystemFirstUpdate<E extends Entity = Entity> {
|
||||
firstUpdate(entity: E): void;
|
||||
}
|
||||
|
||||
/** 执行update */
|
||||
export interface ISystemUpdate<E extends Entity = Entity> {
|
||||
update(entity: E): void;
|
||||
}
|
||||
//#endregion
|
||||
|
||||
/**
|
||||
* 注册组件到ecs系统中
|
||||
* @param compName 由于js打包会改变类名,所以这里必须手动传入组件的名称。
|
||||
* @param canNew 标识是否可以new对象。想继承自Cocos Creator的组件就不能去new,需要写成@ecs.register('name', false)
|
||||
*/
|
||||
export function register<T>(compName: string, canNew: boolean = true) {
|
||||
return function (ctor: CompCtor<T>) {
|
||||
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<T extends Entity>(ctor: EntityCtor<T>): 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<E extends Entity = Entity>(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<E extends Entity = Entity>(eid: number): E {
|
||||
return model.eid2Entity.get(eid) as E;
|
||||
}
|
||||
|
||||
/** 当前活动中的实体数量 */
|
||||
export function activeEntityCount() {
|
||||
return model.eid2Entity.size;
|
||||
}
|
||||
|
||||
/** 创建实体 */
|
||||
function createEntity<E extends Entity = Entity>(): E {
|
||||
let entity = new Entity();
|
||||
entity.eid = model.eid++; // 实体id也是有限的资源
|
||||
model.eid2Entity.set(entity.eid, entity);
|
||||
return entity as E;
|
||||
}
|
||||
|
||||
/**
|
||||
* 指定一个组件创建实体,返回组件对象。
|
||||
* @param ctor
|
||||
*/
|
||||
function createEntityWithComp<T extends IComp>(ctor: CompCtor<T>): T {
|
||||
let entity = createEntity();
|
||||
return entity.add(ctor);
|
||||
}
|
||||
|
||||
//#region 过滤器
|
||||
/**
|
||||
* 表示只关心这些组件的添加和删除动作。虽然实体可能有这些组件之外的组件,但是它们的添加和删除没有被关注,所以不会存在对关注之外的组件
|
||||
* 进行添加操作引发Group重复添加实体。
|
||||
* @param args
|
||||
*/
|
||||
export function allOf(...args: CompType<IComp>[]) {
|
||||
return new ECSMatcher().allOf(...args);
|
||||
}
|
||||
|
||||
/**
|
||||
* 组件间是或的关系,表示关注拥有任意一个这些组件的实体。
|
||||
* @param args 组件索引
|
||||
*/
|
||||
export function anyOf(...args: CompType<IComp>[]) {
|
||||
return new ECSMatcher().anyOf(...args);
|
||||
}
|
||||
|
||||
/**
|
||||
* 表示关注只拥有这些组件的实体
|
||||
*
|
||||
* 注意:
|
||||
* 不是特殊情况不建议使用onlyOf。因为onlyOf会监听所有组件的添加和删除事件。
|
||||
* @param args 组件索引
|
||||
*/
|
||||
export function onlyOf(...args: CompType<IComp>[]) {
|
||||
return new ECSMatcher().onlyOf(...args);
|
||||
}
|
||||
|
||||
/**
|
||||
* 不包含指定的任意一个组件
|
||||
*
|
||||
* eg.
|
||||
* ecs.excludeOf(A, B);表示不包含组件A或者组件B
|
||||
* @param args
|
||||
*/
|
||||
export function excludeOf(...args: CompType<IComp>[]) {
|
||||
return new ECSMatcher().excludeOf(...args);
|
||||
}
|
||||
//#endregion
|
||||
|
||||
//#region 单例组件
|
||||
/**
|
||||
* 获取单例组件
|
||||
* @param ctor 组件类
|
||||
*/
|
||||
export function getSingleton<T extends IComp>(ctor: CompCtor<T>) {
|
||||
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<IComp>).tid;
|
||||
if (!model.tid2comp.has(tid)) {
|
||||
model.tid2comp.set(tid, obj);
|
||||
}
|
||||
}
|
||||
|
||||
//#endregion
|
||||
}
|
||||
@@ -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": {}
|
||||
}
|
||||
28
assets/script/core/libs/ecs/ECSComp.ts
Normal file
28
assets/script/core/libs/ecs/ECSComp.ts
Normal file
@@ -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;
|
||||
}
|
||||
9
assets/script/core/libs/ecs/ECSComp.ts.meta
Normal file
9
assets/script/core/libs/ecs/ECSComp.ts.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.23",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "3d017661-0194-47e1-b3df-a4baf9fbef05",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
227
assets/script/core/libs/ecs/ECSEntity.ts
Normal file
227
assets/script/core/libs/ecs/ECSEntity.ts
Normal file
@@ -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;
|
||||
}
|
||||
// 判断是不是删了单例组件
|
||||
if (ecs.model.tid2comp.has(componentTypeId)) {
|
||||
ecs.model.tid2comp.delete(componentTypeId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建组件对象
|
||||
* @param ctor
|
||||
*/
|
||||
function createComp<T extends ecs.IComp>(ctor: ecs.CompCtor<T>): 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<T>);
|
||||
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<number, ecs.CompType<ecs.IComp>> = new Map();
|
||||
/**
|
||||
* 配合 entity.remove(Comp, false), 记录组件实例上的缓存数据,在添加时恢复原数据
|
||||
*/
|
||||
private compTid2Obj: Map<number, ecs.IComp> = new Map();
|
||||
|
||||
/**
|
||||
* 根据组件id动态创建组件,并通知关心的系统。
|
||||
*
|
||||
* 如果实体存在了这个组件,那么会先删除之前的组件然后添加新的。
|
||||
*
|
||||
* 注意:不要直接new Component,new来的Component不会从Component的缓存池拿缓存的数据。
|
||||
* @param componentTypeId 组件id
|
||||
* @param isReAdd true-表示用户指定这个实体可能已经存在了该组件,那么再次add组件的时候会先移除该组件然后再添加一遍。false-表示不重复添加组件。
|
||||
*/
|
||||
add<T extends ecs.IComp>(obj: T): ECSEntity;
|
||||
add(ctor: number, isReAdd?: boolean): ECSEntity;
|
||||
add<T extends ecs.IComp>(ctor: ecs.CompCtor<T>, isReAdd?: boolean): T;
|
||||
add<T extends ecs.IComp>(ctor: ecs.CompType<T>, isReAdd?: boolean): T;
|
||||
add<T extends ecs.IComp>(ctor: ecs.CompType<T> | 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<T>);
|
||||
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<T extends ecs.IComp>(...ctors: ecs.CompType<T>[]) {
|
||||
for (let ctor of ctors) {
|
||||
this.add(ctor);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
get(ctor: number): number;
|
||||
get<T extends ecs.IComp>(ctor: ecs.CompCtor<T>): T;
|
||||
get<T extends ecs.IComp>(ctor: ecs.CompCtor<T> | number): T {
|
||||
// @ts-ignore
|
||||
return this[ctor.compName];
|
||||
}
|
||||
|
||||
has(ctor: ecs.CompType<ecs.IComp>): 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<ecs.IComp>, 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<ecs.IComp>) {
|
||||
this.remove(comp, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 销毁实体,实体会被回收到实体缓存池中。
|
||||
*/
|
||||
destroy() {
|
||||
this.compTid2Ctor.forEach(this._remove, this);
|
||||
destroyEntity(this);
|
||||
this.compTid2Obj.clear();
|
||||
}
|
||||
}
|
||||
9
assets/script/core/libs/ecs/ECSEntity.ts.meta
Normal file
9
assets/script/core/libs/ecs/ECSEntity.ts.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.23",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "1fb62582-dcf6-4fbc-b863-a1941fad1109",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
77
assets/script/core/libs/ecs/ECSGroup.ts
Normal file
77
assets/script/core/libs/ecs/ECSGroup.ts
Normal file
@@ -0,0 +1,77 @@
|
||||
import { ecs } from "./ECS";
|
||||
import { ECSEntity } from "./ECSEntity";
|
||||
|
||||
export class ECSGroup<E extends ECSEntity = ECSEntity> {
|
||||
/** 实体筛选规则 */
|
||||
private matcher: ecs.IMatcher;
|
||||
|
||||
private _matchEntities: Map<number, E> = 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<number, E> | null = null;
|
||||
private _removedEntities: Map<number, E> | 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<number, E>, removedEntities: Map<number, E>) {
|
||||
this._enteredEntities = enteredEntities;
|
||||
this._removedEntities = removedEntities;
|
||||
}
|
||||
|
||||
clear() {
|
||||
this._matchEntities.clear();
|
||||
this._entitiesCache = null;
|
||||
this.count = 0;
|
||||
this._enteredEntities?.clear();
|
||||
this._removedEntities?.clear();
|
||||
}
|
||||
}
|
||||
9
assets/script/core/libs/ecs/ECSGroup.ts.meta
Normal file
9
assets/script/core/libs/ecs/ECSGroup.ts.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.23",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "c21a2de8-f4fe-4534-96a6-70c9a86167ec",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
51
assets/script/core/libs/ecs/ECSMask.ts
Normal file
51
assets/script/core/libs/ecs/ECSMask.ts
Normal file
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
9
assets/script/core/libs/ecs/ECSMask.ts.meta
Normal file
9
assets/script/core/libs/ecs/ECSMask.ts.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.23",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "d1869e0f-3dbb-46b3-9229-828fef689057",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
214
assets/script/core/libs/ecs/ECSMatcher.ts
Normal file
214
assets/script/core/libs/ecs/ECSMatcher.ts
Normal file
@@ -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<ecs.IComp>[]): ECSMatcher {
|
||||
this.rules.push(new AnyOf(...args));
|
||||
this.bindMatchMethod();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 组件间是与的关系,表示关注拥有所有这些组件的实体。
|
||||
* @param args 组件索引
|
||||
*/
|
||||
allOf(...args: ecs.CompType<ecs.IComp>[]): ECSMatcher {
|
||||
this.rules.push(new AllOf(...args));
|
||||
this.bindMatchMethod();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 表示关注只拥有这些组件的实体
|
||||
*
|
||||
* 注意:
|
||||
* 不是特殊情况不建议使用onlyOf。因为onlyOf会监听所有组件的添加和删除事件。
|
||||
* @param args 组件索引
|
||||
*/
|
||||
onlyOf(...args: ecs.CompType<ecs.IComp>[]): ECSMatcher {
|
||||
this.rules.push(new AllOf(...args));
|
||||
let otherTids: ecs.CompType<ecs.IComp>[] = [];
|
||||
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<ecs.IComp>[]) {
|
||||
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<ecs.IComp>[]) {
|
||||
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<ecs.IComp>).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);
|
||||
}
|
||||
}
|
||||
9
assets/script/core/libs/ecs/ECSMatcher.ts.meta
Normal file
9
assets/script/core/libs/ecs/ECSMatcher.ts.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.23",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "37e8aae5-a8f9-4ded-a999-6321c5bc1229",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
43
assets/script/core/libs/ecs/ECSModel.ts
Normal file
43
assets/script/core/libs/ecs/ECSModel.ts
Normal file
@@ -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<number, ecs.IComp[]> = new Map();
|
||||
|
||||
/** 组件构造函数 */
|
||||
compCtors: (ecs.CompCtor<any> | number)[] = [];
|
||||
|
||||
/**
|
||||
* 每个组件的添加和删除的动作都要派送到“关心”它们的group上。goup对当前拥有或者之前(删除前)拥有该组件的实体进行组件规则判断。判断该实体是否满足group
|
||||
* 所期望的组件组合。
|
||||
*/
|
||||
compAddOrRemove: Map<number, ecs.CompAddOrRemove[]> = new Map();
|
||||
|
||||
/** 编号获取组件 */
|
||||
tid2comp: Map<number, ecs.IComp> = new Map();
|
||||
|
||||
/**
|
||||
* 实体对象缓存池
|
||||
*/
|
||||
entityPool: Map<string, ECSEntity[]> = new Map();
|
||||
|
||||
/**
|
||||
* 通过实体id查找实体对象
|
||||
*/
|
||||
eid2Entity: Map<number, ECSEntity> = new Map();
|
||||
|
||||
/**
|
||||
* 缓存的group
|
||||
*
|
||||
* key是组件的筛选规则,一个筛选规则对应一个group
|
||||
*/
|
||||
groups: Map<number, ECSGroup> = new Map();
|
||||
}
|
||||
9
assets/script/core/libs/ecs/ECSModel.ts.meta
Normal file
9
assets/script/core/libs/ecs/ECSModel.ts.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.23",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "1d60e80c-eabf-46b5-ae0e-a7754005218c",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
231
assets/script/core/libs/ecs/ECSSystem.ts
Normal file
231
assets/script/core/libs/ecs/ECSSystem.ts
Normal file
@@ -0,0 +1,231 @@
|
||||
import { ecs } from "./ECS";
|
||||
import { ECSEntity } from "./ECSEntity";
|
||||
import { ECSGroup } from "./ECSGroup";
|
||||
|
||||
/**
|
||||
* 创建group,每个group只关心对应组件的添加和删除
|
||||
* @param matcher 实体筛选器
|
||||
*/
|
||||
export function createGroup<E extends ECSEntity = ECSEntity>(matcher: ecs.IMatcher): ECSGroup<E> {
|
||||
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<E>;
|
||||
}
|
||||
|
||||
export abstract class ECSComblockSystem<E extends ECSEntity = ECSEntity> {
|
||||
protected group: ECSGroup<E>;
|
||||
protected dt: number = 0;
|
||||
|
||||
private enteredEntities: Map<number, E> = null!;
|
||||
private removedEntities: Map<number, E> = 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<number, E>();
|
||||
this.removedEntities = new Map<number, E>();
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
9
assets/script/core/libs/ecs/ECSSystem.ts.meta
Normal file
9
assets/script/core/libs/ecs/ECSSystem.ts.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.23",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "9261f456-8364-4163-9931-6c526c635402",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
9
assets/script/core/libs/ecs/IECS.ts.meta
Normal file
9
assets/script/core/libs/ecs/IECS.ts.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.23",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "70bdbf80-64ba-465d-baca-d19506dee907",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
|
||||
/**
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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";
|
||||
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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";
|
||||
|
||||
/** 向目标移动,移动过程中目标位置变化会自动修正移动目标点,直到未修正前移动到目标点停止 */
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
|
||||
Reference in New Issue
Block a user