@cc-component/cc-core 1.3.0 → 1.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/core/lib/ecs/.ecs.md +1 -0
- package/assets/core/lib/ecs/ECS.ts +329 -0
- package/assets/core/{home/BaseBoxView.ts.meta → lib/ecs/ECS.ts.meta} +1 -1
- package/assets/core/lib/ecs/ECSComp.ts +35 -0
- package/assets/core/{home/DouYinShow.ts.meta → lib/ecs/ECSComp.ts.meta} +1 -1
- package/assets/core/lib/ecs/ECSEntity.ts +302 -0
- package/assets/core/lib/ecs/ECSEntity.ts.meta +9 -0
- package/assets/core/lib/ecs/ECSGroup.ts +83 -0
- package/assets/core/lib/ecs/ECSGroup.ts.meta +9 -0
- package/assets/core/lib/ecs/ECSMask.ts +57 -0
- package/assets/core/lib/ecs/ECSMask.ts.meta +9 -0
- package/assets/core/lib/ecs/ECSMatcher.ts +215 -0
- package/assets/core/lib/ecs/ECSMatcher.ts.meta +9 -0
- package/assets/core/lib/ecs/ECSModel.ts +82 -0
- package/assets/core/lib/ecs/ECSModel.ts.meta +9 -0
- package/assets/core/lib/ecs/ECSSystem.ts +221 -0
- package/assets/core/lib/ecs/ECSSystem.ts.meta +9 -0
- package/assets/core/lib/ecs/IECS.ts.meta +9 -0
- package/assets/core/lib/ecs.meta +9 -0
- package/index.ts +1 -0
- package/package.json +1 -1
- package/assets/core/home/BaseBoxView.ts +0 -57
- package/assets/core/home/DouYinShow.ts +0 -27
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Entity-Component-System 框架
|
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
import { ECSComp } from "./ECSComp";
|
|
2
|
+
import { ECSEntity } from "./ECSEntity";
|
|
3
|
+
import { ECSMatcher } from "./ECSMatcher";
|
|
4
|
+
import { CompCtor, CompType, ECSModel, EntityCtor } from "./ECSModel";
|
|
5
|
+
import { ECSComblockSystem, ECSRootSystem, ECSSystem } from "./ECSSystem";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* ECSEntity对象在destroy后,会回收到ECSModel.entityPool实体对象池中
|
|
9
|
+
* ECSComp对象从ECSEntity.remove后,数据组件会回收到ECSModel.compPools组件对象池中
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Entity-Component-System(实体-组件-系统)框架
|
|
14
|
+
* 文档:https://gitee.com/dgflash/oops-framework/wikis/pages?sort_id=12033388&doc_id=2873565
|
|
15
|
+
*/
|
|
16
|
+
export namespace ecs {
|
|
17
|
+
/** 实体 - 一个概念上的定义,指的是游戏世界中的一个独特物体,是一系列组件的集合 */
|
|
18
|
+
export type Entity = ECSEntity;
|
|
19
|
+
/** 组件 - 一堆数据的集合,即不存在任何的行为,只用来存储状态 */
|
|
20
|
+
export type Comp = ECSComp;
|
|
21
|
+
/** 系统 - 关注实体上组件数据变化,处理游戏逻辑 */
|
|
22
|
+
export type System = ECSSystem;
|
|
23
|
+
/** 根系统 - 驱动游戏中所有系统工作 */
|
|
24
|
+
export type RootSystem = ECSRootSystem;
|
|
25
|
+
/** 处理游戏逻辑系统对象 - 继承此对象实现自定义业务逻辑 */
|
|
26
|
+
export type ComblockSystem = ECSComblockSystem;
|
|
27
|
+
|
|
28
|
+
/** 实体 - 一个概念上的定义,指的是游戏世界中的一个独特物体,是一系列组件的集合 */
|
|
29
|
+
export const Entity = ECSEntity;
|
|
30
|
+
/** 组件 - 一堆数据的集合,即不存在任何的行为,只用来存储状态 */
|
|
31
|
+
export const Comp = ECSComp;
|
|
32
|
+
/** 系统 - 关注实体上组件数据变化,处理游戏逻辑 */
|
|
33
|
+
export const System = ECSSystem;
|
|
34
|
+
/** 根系统 - 驱动游戏中所有系统工作 */
|
|
35
|
+
export const RootSystem = ECSRootSystem;
|
|
36
|
+
/** 处理游戏逻辑系统对象 - 继承此对象实现自定义业务逻辑 */
|
|
37
|
+
export const ComblockSystem = ECSComblockSystem;
|
|
38
|
+
|
|
39
|
+
//#region 接口
|
|
40
|
+
|
|
41
|
+
/** 组件接口 */
|
|
42
|
+
export interface IComp {
|
|
43
|
+
canRecycle: boolean;
|
|
44
|
+
ent: Entity;
|
|
45
|
+
tid: number;
|
|
46
|
+
|
|
47
|
+
reset(): void;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** 实体匹配器接口 */
|
|
51
|
+
export interface IMatcher {
|
|
52
|
+
mid: number;
|
|
53
|
+
indices: number[];
|
|
54
|
+
key: string;
|
|
55
|
+
isMatch(entity: Entity): boolean;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* 监听组件首次添加到实体上时,在ComblockSystem上实现这个接口
|
|
60
|
+
* 1. entityEnter会在update方法之前执行,实体进入后,不会再次进入entityEnter方法中
|
|
61
|
+
* 2. 当实体从当前System移除,下次再次符合条件进入System也会执行上述流程
|
|
62
|
+
* @example
|
|
63
|
+
export class RoleUpgradeSystem extends ecs.ComblockSystem implements ecs.IEntityEnterSystem {
|
|
64
|
+
filter(): ecs.IMatcher {
|
|
65
|
+
return ecs.allOf(RoleUpgradeComp, RoleModelLevelComp);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
entityEnter(e: Role): void {
|
|
69
|
+
e.remove(RoleUpgradeComp);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
*/
|
|
73
|
+
export interface IEntityEnterSystem<E extends Entity = Entity> {
|
|
74
|
+
entityEnter(entity: E): void;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** 监听组件从实体上移除时,在ComblockSystem上实现这个接口 */
|
|
78
|
+
export interface IEntityRemoveSystem<E extends Entity = Entity> {
|
|
79
|
+
entityRemove(entity: E): void;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** 监听系统第一次执行update处理实体时,在ComblockSystem上实现这个接口 */
|
|
83
|
+
export interface ISystemFirstUpdate<E extends Entity = Entity> {
|
|
84
|
+
firstUpdate(entity: E): void;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/** 监听系统执行update处理实体时,在ComblockSystem上实现这个接口 */
|
|
88
|
+
export interface ISystemUpdate<E extends Entity = Entity> {
|
|
89
|
+
update(entity: E): void;
|
|
90
|
+
}
|
|
91
|
+
//#endregion
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* 注册组件到ecs系统中
|
|
95
|
+
* @param name 由于js打包会改变类名,所以这里必须手动传入组件的名称
|
|
96
|
+
* @param canNew 标识是否可以new对象。想继承自Cocos Creator的组件就不能去new,需要写成@ecs.register('name', false)
|
|
97
|
+
* @example
|
|
98
|
+
// 注册实体
|
|
99
|
+
@ecs.register('Role')
|
|
100
|
+
export class Role extends ecs.Entity {
|
|
101
|
+
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// 注册数据组件
|
|
105
|
+
@ecs.register('RoleModel')
|
|
106
|
+
export class RoleModelComp extends ecs.Comp {
|
|
107
|
+
id: number = -1;
|
|
108
|
+
|
|
109
|
+
reset() {
|
|
110
|
+
this.id = -1;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// 注册系统组件
|
|
115
|
+
@ecs.register('Initialize')
|
|
116
|
+
export class InitResSystem extends ecs.ComblockSystem implements ecs.IEntityEnterSystem {
|
|
117
|
+
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// 注册显示对象组件
|
|
121
|
+
@ccclass('RoleViewComp')
|
|
122
|
+
@ecs.register('RoleView', false)
|
|
123
|
+
export class RoleViewComp extends CCComp {
|
|
124
|
+
onLoad(){
|
|
125
|
+
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
*/
|
|
129
|
+
export function register<T>(name: string, canNew: boolean = true) {
|
|
130
|
+
return function (ctor: any) {
|
|
131
|
+
// 注册系统
|
|
132
|
+
if (ctor.s) {
|
|
133
|
+
var system = ECSModel.systems.get(name);
|
|
134
|
+
if (system == null) {
|
|
135
|
+
system = new ecs.System();
|
|
136
|
+
ECSModel.systems.set(name, system);
|
|
137
|
+
}
|
|
138
|
+
system.add(new ctor);
|
|
139
|
+
}
|
|
140
|
+
// 注册实体
|
|
141
|
+
else if (ctor.tid == undefined) {
|
|
142
|
+
ECSModel.entityCtors.set(ctor as EntityCtor<T>, name);
|
|
143
|
+
}
|
|
144
|
+
// 注册组件
|
|
145
|
+
else {
|
|
146
|
+
if (ctor.tid === -1) {
|
|
147
|
+
ctor.tid = ECSModel.compTid++;
|
|
148
|
+
ctor.compName = name;
|
|
149
|
+
ECSModel.compCtors.push(ctor); // 注册不同类型的组件
|
|
150
|
+
if (canNew) {
|
|
151
|
+
ECSModel.compPools.set(ctor.tid, []);
|
|
152
|
+
}
|
|
153
|
+
ECSModel.compAddOrRemove.set(ctor.tid, []);
|
|
154
|
+
}
|
|
155
|
+
else {
|
|
156
|
+
throw new Error(`ECS 组件重复注册: ${name}.`);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* 创建一个新的实体对象或从缓存中获取一个实体对象
|
|
164
|
+
* @param ctor 实体类
|
|
165
|
+
*/
|
|
166
|
+
export function getEntity<T extends Entity>(ctor: EntityCtor<T>): T {
|
|
167
|
+
// 获取实体对象名
|
|
168
|
+
var entityName = ECSModel.entityCtors.get(ctor);
|
|
169
|
+
if (entityName == undefined)
|
|
170
|
+
console.error(`${ctor.name} 实体没有注册`);
|
|
171
|
+
|
|
172
|
+
// 获取实体对象池
|
|
173
|
+
var entitys = ECSModel.entityPool.get(entityName!) || [];
|
|
174
|
+
var entity: any = entitys.pop();
|
|
175
|
+
|
|
176
|
+
// 缓存中没有同类实体,则创建一个新的
|
|
177
|
+
if (!entity) {
|
|
178
|
+
entity = new ctor();
|
|
179
|
+
entity.eid = ECSModel.eid++; // 实体唯一编号
|
|
180
|
+
entity.name = entityName;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// 触发实体初始化逻辑
|
|
184
|
+
if (entity.init) {
|
|
185
|
+
entity.isValid = true;
|
|
186
|
+
entity.init();
|
|
187
|
+
}
|
|
188
|
+
else
|
|
189
|
+
console.error(`${ctor.name} 实体缺少 init 方法初始化默认组件`);
|
|
190
|
+
|
|
191
|
+
ECSModel.eid2Entity.set(entity.eid, entity);
|
|
192
|
+
return entity as T;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* 动态查询实体
|
|
197
|
+
* @param matcher 匹配器
|
|
198
|
+
* @example
|
|
199
|
+
* ecs.query(ecs.allOf(Comp1, Comp2));
|
|
200
|
+
*/
|
|
201
|
+
export function query<E extends Entity = Entity>(matcher: IMatcher): E[] {
|
|
202
|
+
let group = ECSModel.groups.get(matcher.mid);
|
|
203
|
+
if (!group) {
|
|
204
|
+
group = ECSModel.createGroup(matcher);
|
|
205
|
+
ECSModel.eid2Entity.forEach(group.onComponentAddOrRemove, group);
|
|
206
|
+
}
|
|
207
|
+
return group.matchEntities as E[];
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/** 清理所有的实体 */
|
|
211
|
+
export function clear() {
|
|
212
|
+
ECSModel.eid2Entity.forEach((entity) => {
|
|
213
|
+
entity.destroy();
|
|
214
|
+
});
|
|
215
|
+
ECSModel.groups.forEach((group) => {
|
|
216
|
+
group.clear();
|
|
217
|
+
});
|
|
218
|
+
ECSModel.compAddOrRemove.forEach(callbackLst => {
|
|
219
|
+
callbackLst.length = 0;
|
|
220
|
+
});
|
|
221
|
+
ECSModel.eid2Entity.clear();
|
|
222
|
+
ECSModel.groups.clear();
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* 通过实体唯一编号获得实体对象
|
|
227
|
+
* @param eid 实体唯一编号
|
|
228
|
+
*/
|
|
229
|
+
export function getEntityByEid<E extends Entity = Entity>(eid: number): E {
|
|
230
|
+
return ECSModel.eid2Entity.get(eid) as E;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/** 当前活动中的实体数量 */
|
|
234
|
+
export function activeEntityCount() {
|
|
235
|
+
return ECSModel.eid2Entity.size;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/** 创建实体 */
|
|
239
|
+
function createEntity<E extends Entity = Entity>(): E {
|
|
240
|
+
let entity = new Entity();
|
|
241
|
+
entity.eid = ECSModel.eid++; // 实体id也是有限的资源
|
|
242
|
+
ECSModel.eid2Entity.set(entity.eid, entity);
|
|
243
|
+
return entity as E;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* 指定一个组件创建实体,返回组件对象。
|
|
248
|
+
* @param ctor
|
|
249
|
+
*/
|
|
250
|
+
function createEntityWithComp<T extends IComp>(ctor: CompCtor<T>): T {
|
|
251
|
+
let entity = createEntity();
|
|
252
|
+
return entity.add(ctor);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
//#region 过滤器
|
|
256
|
+
/**
|
|
257
|
+
* 表示只关心这些组件的添加和删除动作。虽然实体可能有这些组件之外的组件,但是它们的添加和删除没有被关注,所以不会存在对关注之外的组件
|
|
258
|
+
* 进行添加操作引发Group重复添加实体。
|
|
259
|
+
* @param args
|
|
260
|
+
* @example
|
|
261
|
+
* ecs.allOf(AComponent, BComponent, CComponent);
|
|
262
|
+
*/
|
|
263
|
+
export function allOf(...args: CompType<IComp>[]) {
|
|
264
|
+
return new ECSMatcher().allOf(...args);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* 组件间是或的关系,表示关注拥有任意一个这些组件的实体
|
|
269
|
+
* @param args 组件类
|
|
270
|
+
* @example
|
|
271
|
+
* ecs.anyOf(AComponent, BComponent);
|
|
272
|
+
*/
|
|
273
|
+
export function anyOf(...args: CompType<IComp>[]) {
|
|
274
|
+
return new ECSMatcher().anyOf(...args);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* 表示关注只拥有这些组件的实体
|
|
279
|
+
* 注:不是特殊情况不建议使用onlyOf。因为onlyOf会监听所有组件的添加和删除事件
|
|
280
|
+
* @param args 组件类
|
|
281
|
+
* @example
|
|
282
|
+
// 不包含CComponent或者DComponent
|
|
283
|
+
ecs.allOf(AComponent, BComponent).excludeOf(CComponent, DComponent);
|
|
284
|
+
|
|
285
|
+
// 不同时包含CComponent和DComponent
|
|
286
|
+
ecs.allOf(AComponent, BComponent).excludeOf(CComponent).excludeOf(DComponent);
|
|
287
|
+
*/
|
|
288
|
+
export function onlyOf(...args: CompType<IComp>[]) {
|
|
289
|
+
return new ECSMatcher().onlyOf(...args);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* 不包含指定的任意一个组件
|
|
294
|
+
* @param args 组件类
|
|
295
|
+
* @example
|
|
296
|
+
// 表示不包含组件A或者组件B
|
|
297
|
+
ecs.excludeOf(A, B);
|
|
298
|
+
*/
|
|
299
|
+
export function excludeOf(...args: CompType<IComp>[]) {
|
|
300
|
+
return new ECSMatcher().excludeOf(...args);
|
|
301
|
+
}
|
|
302
|
+
//#endregion
|
|
303
|
+
|
|
304
|
+
//#region 单例组件
|
|
305
|
+
/**
|
|
306
|
+
* 获取单例组件
|
|
307
|
+
* @param ctor 组件类
|
|
308
|
+
*/
|
|
309
|
+
export function getSingleton<T extends IComp>(ctor: CompCtor<T>) {
|
|
310
|
+
if (!ECSModel.tid2comp.has(ctor.tid)) {
|
|
311
|
+
let comp = createEntityWithComp(ctor) as T;
|
|
312
|
+
ECSModel.tid2comp.set(ctor.tid, comp);
|
|
313
|
+
}
|
|
314
|
+
return ECSModel.tid2comp.get(ctor.tid) as T;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* 注册单例组件 - 主要用于那些不能手动创建对象的组件
|
|
319
|
+
* @param obj
|
|
320
|
+
*/
|
|
321
|
+
export function addSingleton(obj: IComp) {
|
|
322
|
+
let tid = (obj.constructor as CompCtor<IComp>).tid;
|
|
323
|
+
if (!ECSModel.tid2comp.has(tid)) {
|
|
324
|
+
ECSModel.tid2comp.set(tid, obj);
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
//#endregion
|
|
329
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* @Author: dgflash
|
|
3
|
+
* @Date: 2022-09-01 18:00:28
|
|
4
|
+
* @LastEditors: dgflash
|
|
5
|
+
* @LastEditTime: 2022-09-05 14:03:54
|
|
6
|
+
*/
|
|
7
|
+
import { ecs } from "./ECS";
|
|
8
|
+
import { ECSEntity } from "./ECSEntity";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* 组件抽象类
|
|
12
|
+
* 注:建议组件里面只放数据可能在实际写代码会碰到一些比较麻烦的问题,如果是单纯对组件内的数据操作可以在组件里面写方法
|
|
13
|
+
*/
|
|
14
|
+
export abstract class ECSComp implements ecs.IComp {
|
|
15
|
+
/** 组件的类型编号,-1表示未给该组件分配编号 */
|
|
16
|
+
static tid: number = -1;
|
|
17
|
+
/** 组件名 */
|
|
18
|
+
static compName: string;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* 是否可回收组件对象,默认情况下都是可回收的
|
|
22
|
+
* 注:如果该组件对象是由ecs系统外部创建的,则不可回收,需要用户自己手动进行回收
|
|
23
|
+
*/
|
|
24
|
+
canRecycle: boolean = true;
|
|
25
|
+
/** 拥有该组件的实体 */
|
|
26
|
+
ent!: ECSEntity;
|
|
27
|
+
/** 组件的类型编号 */
|
|
28
|
+
tid: number = -1;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* 组件被回收时会调用这个接口。可以在这里重置数据,或者解除引用
|
|
32
|
+
* 注:不要偷懒,除非你能确定并保证组件在复用时,里面的数据是先赋值然后再使用
|
|
33
|
+
*/
|
|
34
|
+
abstract reset(): void;
|
|
35
|
+
}
|
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
import { ecs } from "./ECS";
|
|
2
|
+
import { ECSMask } from "./ECSMask";
|
|
3
|
+
import { CompCtor, CompType, ECSModel } from "./ECSModel";
|
|
4
|
+
|
|
5
|
+
//#region 辅助方法
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* 实体身上组件有增删操作,广播通知对应的观察者
|
|
9
|
+
* @param entity 实体对象
|
|
10
|
+
* @param componentTypeId 组件类型id
|
|
11
|
+
*/
|
|
12
|
+
function broadcastCompAddOrRemove(entity: ECSEntity, componentTypeId: number) {
|
|
13
|
+
let events = ECSModel.compAddOrRemove.get(componentTypeId);
|
|
14
|
+
for (let i = events!.length - 1; i >= 0; i--) {
|
|
15
|
+
events;
|
|
16
|
+
}
|
|
17
|
+
// 判断是不是删了单例组件
|
|
18
|
+
if (ECSModel.tid2comp.has(componentTypeId)) {
|
|
19
|
+
ECSModel.tid2comp.delete(componentTypeId);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* 创建组件对象
|
|
25
|
+
* @param ctor
|
|
26
|
+
*/
|
|
27
|
+
function createComp<T extends ecs.IComp>(ctor: CompCtor<T>): T {
|
|
28
|
+
const cct = ECSModel.compCtors[ctor.tid];
|
|
29
|
+
if (!cct) {
|
|
30
|
+
throw Error(`没有找到该组件的构造函数,检查${ctor.compName}是否为不可构造的组件`);
|
|
31
|
+
}
|
|
32
|
+
let comps = ECSModel.compPools.get(ctor.tid)!;
|
|
33
|
+
let component = comps.pop() || new (cct as CompCtor<T>);
|
|
34
|
+
return component as T;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* 销毁实体
|
|
39
|
+
*
|
|
40
|
+
* 缓存销毁的实体,下次新建实体时会优先从缓存中拿
|
|
41
|
+
* @param entity
|
|
42
|
+
*/
|
|
43
|
+
function destroyEntity(entity: ECSEntity) {
|
|
44
|
+
if (ECSModel.eid2Entity.has(entity.eid)) {
|
|
45
|
+
let entitys = ECSModel.entityPool.get(entity.name);
|
|
46
|
+
if (entitys == null) {
|
|
47
|
+
entitys = [];
|
|
48
|
+
ECSModel.entityPool.set(entity.name, entitys);
|
|
49
|
+
}
|
|
50
|
+
entitys.push(entity);
|
|
51
|
+
ECSModel.eid2Entity.delete(entity.eid);
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
console.warn('试图销毁不存在的实体');
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
//#endregion
|
|
59
|
+
|
|
60
|
+
/** ECS实体对象 */
|
|
61
|
+
export class ECSEntity {
|
|
62
|
+
/** 实体唯一标识,不要手动修改 */
|
|
63
|
+
eid: number = -1;
|
|
64
|
+
/** 实体对象名 */
|
|
65
|
+
name: string = "";
|
|
66
|
+
/** 实体是否有效 */
|
|
67
|
+
isValid: boolean = true;
|
|
68
|
+
/** 组件过滤数据 */
|
|
69
|
+
private mask = new ECSMask();
|
|
70
|
+
/** 当前实体身上附加的组件构造函数 */
|
|
71
|
+
private compTid2Ctor: Map<number, CompType<ecs.IComp>> = new Map();
|
|
72
|
+
/** 配合 entity.remove(Comp, false), 记录组件实例上的缓存数据,在添加时恢复原数据 */
|
|
73
|
+
private compTid2Obj: Map<number, ecs.IComp> = new Map();
|
|
74
|
+
|
|
75
|
+
private _parent: ECSEntity | null = null;
|
|
76
|
+
/** 父实体 */
|
|
77
|
+
get parent(): ECSEntity | null {
|
|
78
|
+
return this._parent;
|
|
79
|
+
}
|
|
80
|
+
set parent(value: ECSEntity | null) {
|
|
81
|
+
this._parent = value;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** 子实体 */
|
|
85
|
+
private childs: Map<number, ECSEntity> = null!;
|
|
86
|
+
|
|
87
|
+
/** 获取子实体 */
|
|
88
|
+
getChild<T>(eid: number) {
|
|
89
|
+
return this.childs.get(eid) as T;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* 添加子实体
|
|
94
|
+
* @param entity 被添加的实体对象
|
|
95
|
+
* @returns 子实体的唯一编号, -1表示添加失败
|
|
96
|
+
*/
|
|
97
|
+
addChild(entity: ECSEntity): number {
|
|
98
|
+
if (this.childs == null) this.childs = new Map<number, ECSEntity>();
|
|
99
|
+
|
|
100
|
+
if (this.childs.has(entity.eid)) {
|
|
101
|
+
console.warn(`子实体${entity.name}已存在`);
|
|
102
|
+
return -1;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
entity._parent = this;
|
|
106
|
+
this.childs.set(entity.eid, entity);
|
|
107
|
+
return entity.eid;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* 移除子实体
|
|
112
|
+
* @param entity 被移除的实体对象
|
|
113
|
+
* @param isDestroy 被移除的实体是否释放,默认为释放
|
|
114
|
+
* @returns
|
|
115
|
+
*/
|
|
116
|
+
removeChild(entity: ECSEntity, isDestroy = true) {
|
|
117
|
+
if (this.childs == null) return;
|
|
118
|
+
|
|
119
|
+
entity.parent = null;
|
|
120
|
+
this.childs.delete(entity.eid);
|
|
121
|
+
if (isDestroy) entity.destroy();
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* 根据组件类动态创建组件,并通知关心的系统。如果实体存在了这个组件,那么会先删除之前的组件然后添加新的
|
|
126
|
+
*
|
|
127
|
+
* 注意:不要直接new Component,new来的Component不会从Component的缓存池拿缓存的数据
|
|
128
|
+
* @param componentTypeId 组件类
|
|
129
|
+
* @param isReAdd true-表示用户指定这个实体可能已经存在了该组件,那么再次add组件的时候会先移除该组件然后再添加一遍。false-表示不重复添加组件
|
|
130
|
+
*/
|
|
131
|
+
add<T extends ecs.IComp>(obj: T): ECSEntity;
|
|
132
|
+
add<T extends ecs.IComp>(ctor: CompType<T>, isReAdd?: boolean): T;
|
|
133
|
+
add<T extends ecs.IComp>(ctor: CompType<T> | T, isReAdd: boolean = false): T | ECSEntity {
|
|
134
|
+
if (typeof ctor === 'function') {
|
|
135
|
+
let compTid = ctor.tid;
|
|
136
|
+
if (ctor.tid === -1) {
|
|
137
|
+
throw Error(`【${this.name}】实体【${ctor.compName}】组件未注册`);
|
|
138
|
+
}
|
|
139
|
+
if (this.compTid2Ctor.has(compTid)) { // 判断是否有该组件,如果有则先移除
|
|
140
|
+
if (isReAdd) {
|
|
141
|
+
this.remove(ctor);
|
|
142
|
+
}
|
|
143
|
+
else {
|
|
144
|
+
console.log(`【${this.name}】实体【${ctor.compName}】组件已存在`);
|
|
145
|
+
// @ts-ignore
|
|
146
|
+
return this[ctor.compName] as T;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
this.mask.set(compTid);
|
|
150
|
+
|
|
151
|
+
let comp: T;
|
|
152
|
+
if (this.compTid2Obj.has(compTid)) {
|
|
153
|
+
comp = this.compTid2Obj.get(compTid) as T;
|
|
154
|
+
this.compTid2Obj.delete(compTid);
|
|
155
|
+
}
|
|
156
|
+
else {
|
|
157
|
+
// 创建组件对象
|
|
158
|
+
comp = createComp(ctor) as T;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// 将组件对象直接附加到实体对象身上,方便直接获取
|
|
162
|
+
// @ts-ignore
|
|
163
|
+
this[ctor.compName] = comp;
|
|
164
|
+
this.compTid2Ctor.set(compTid, ctor);
|
|
165
|
+
comp.tid = compTid;
|
|
166
|
+
comp.ent = this;
|
|
167
|
+
// 广播实体添加组件的消息
|
|
168
|
+
broadcastCompAddOrRemove(this, compTid);
|
|
169
|
+
|
|
170
|
+
return comp;
|
|
171
|
+
}
|
|
172
|
+
else {
|
|
173
|
+
let tmpCtor = (ctor.constructor as CompCtor<T>);
|
|
174
|
+
let compTid = tmpCtor.tid;
|
|
175
|
+
// console.assert(compTid !== -1 || !compTid, '组件未注册!');
|
|
176
|
+
// console.assert(this.compTid2Ctor.has(compTid), '已存在该组件!');
|
|
177
|
+
if (compTid === -1 || compTid == null) throw Error(`【${this.name}】实体【${tmpCtor.name}】组件未注册`);
|
|
178
|
+
if (this.compTid2Ctor.has(compTid)) throw Error(`【${this.name}】实体【${tmpCtor.name}】组件已经存在`);
|
|
179
|
+
|
|
180
|
+
this.mask.set(compTid);
|
|
181
|
+
//@ts-ignore
|
|
182
|
+
this[tmpCtor.compName] = ctor;
|
|
183
|
+
this.compTid2Ctor.set(compTid, tmpCtor);
|
|
184
|
+
//@ts-ignore
|
|
185
|
+
ctor.tid = compTid;
|
|
186
|
+
//@ts-ignore
|
|
187
|
+
ctor.canRecycle = false;
|
|
188
|
+
//@ts-ignore
|
|
189
|
+
ctor.ent = this;
|
|
190
|
+
broadcastCompAddOrRemove(this, compTid);
|
|
191
|
+
|
|
192
|
+
return this;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* 批量添加组件
|
|
198
|
+
* @param ctors 组件类
|
|
199
|
+
* @returns
|
|
200
|
+
*/
|
|
201
|
+
addComponents<T extends ecs.IComp>(...ctors: CompType<T>[]) {
|
|
202
|
+
for (let ctor of ctors) {
|
|
203
|
+
this.add(ctor);
|
|
204
|
+
}
|
|
205
|
+
return this;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* 获取一个组件实例
|
|
210
|
+
* @param ctor 组件类
|
|
211
|
+
*/
|
|
212
|
+
get(ctor: number): number;
|
|
213
|
+
get<T extends ecs.IComp>(ctor: CompCtor<T>): T;
|
|
214
|
+
get<T extends ecs.IComp>(ctor: CompCtor<T> | number): T {
|
|
215
|
+
// @ts-ignore
|
|
216
|
+
return this[ctor.compName];
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* 组件是否在实体存在内
|
|
221
|
+
* @param ctor 组件类
|
|
222
|
+
*/
|
|
223
|
+
has(ctor: CompType<ecs.IComp>): boolean {
|
|
224
|
+
if (typeof ctor == "number") {
|
|
225
|
+
return this.mask.has(ctor);
|
|
226
|
+
}
|
|
227
|
+
else {
|
|
228
|
+
return this.compTid2Ctor.has(ctor.tid);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* 从实体上删除指定组件
|
|
234
|
+
* @param ctor 组件构造函数或者组件Tag
|
|
235
|
+
* @param isRecycle 是否回收该组件对象。对于有些组件上有大量数据,当要描述移除组件但是不想清除组件上的数据是可以
|
|
236
|
+
* 设置该参数为false,这样该组件对象会缓存在实体身上,下次重新添加组件时会将该组件对象添加回来,不会重新从组件缓存
|
|
237
|
+
* 池中拿一个组件来用。
|
|
238
|
+
*/
|
|
239
|
+
remove(ctor: CompType<ecs.IComp>, isRecycle: boolean = true) {
|
|
240
|
+
let hasComp = false;
|
|
241
|
+
//@ts-ignore
|
|
242
|
+
let componentTypeId = ctor.tid;
|
|
243
|
+
//@ts-ignore
|
|
244
|
+
let compName = ctor.compName;
|
|
245
|
+
if (this.mask.has(componentTypeId)) {
|
|
246
|
+
hasComp = true;
|
|
247
|
+
//@ts-ignore
|
|
248
|
+
let comp = this[ctor.compName];
|
|
249
|
+
//@ts-ignore
|
|
250
|
+
comp.ent = null;
|
|
251
|
+
if (isRecycle) {
|
|
252
|
+
comp.reset();
|
|
253
|
+
|
|
254
|
+
// 回收组件到指定缓存池中
|
|
255
|
+
if (comp.canRecycle) {
|
|
256
|
+
const compPoolsType = ECSModel.compPools.get(componentTypeId)!;
|
|
257
|
+
compPoolsType.push(comp);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
else {
|
|
261
|
+
this.compTid2Obj.set(componentTypeId, comp); // 用于缓存显示对象组件
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// 删除实体上的组件逻辑
|
|
266
|
+
if (hasComp) {
|
|
267
|
+
//@ts-ignore
|
|
268
|
+
this[compName] = null;
|
|
269
|
+
this.mask.delete(componentTypeId);
|
|
270
|
+
this.compTid2Ctor.delete(componentTypeId);
|
|
271
|
+
broadcastCompAddOrRemove(this, componentTypeId);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/** 销毁实体,实体会被回收到实体缓存池中 */
|
|
276
|
+
destroy() {
|
|
277
|
+
this.isValid = false;
|
|
278
|
+
|
|
279
|
+
// 如果有父模块,则移除父模块上记录的子模块
|
|
280
|
+
if (this._parent) {
|
|
281
|
+
this._parent.removeChild(this, false);
|
|
282
|
+
this._parent = null;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// 移除模块上所有子模块
|
|
286
|
+
if (this.childs) {
|
|
287
|
+
this.childs.forEach(e => {
|
|
288
|
+
this.removeChild(e);
|
|
289
|
+
});
|
|
290
|
+
this.childs = null!;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// 移除实体上所有组件
|
|
294
|
+
this.compTid2Ctor.forEach(this._remove, this);
|
|
295
|
+
destroyEntity(this);
|
|
296
|
+
this.compTid2Obj.clear();
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
private _remove(comp: CompType<ecs.IComp>) {
|
|
300
|
+
this.remove(comp, true);
|
|
301
|
+
}
|
|
302
|
+
}
|