@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,83 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* @Author: dgflash
|
|
3
|
+
* @Date: 2022-09-01 18:00:28
|
|
4
|
+
* @LastEditors: dgflash
|
|
5
|
+
* @LastEditTime: 2022-09-05 14:21:54
|
|
6
|
+
*/
|
|
7
|
+
import { ecs } from "./ECS";
|
|
8
|
+
import { ECSEntity } from "./ECSEntity";
|
|
9
|
+
|
|
10
|
+
export class ECSGroup<E extends ECSEntity = ECSEntity> {
|
|
11
|
+
/** 实体筛选规则 */
|
|
12
|
+
private matcher: ecs.IMatcher;
|
|
13
|
+
|
|
14
|
+
private _matchEntities: Map<number, E> = new Map();
|
|
15
|
+
|
|
16
|
+
private _entitiesCache: E[] | null = null;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* 符合规则的实体
|
|
20
|
+
*/
|
|
21
|
+
get matchEntities() {
|
|
22
|
+
if (this._entitiesCache === null) {
|
|
23
|
+
this._entitiesCache = Array.from(this._matchEntities.values());
|
|
24
|
+
}
|
|
25
|
+
return this._entitiesCache;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* 当前group中实体的数量
|
|
30
|
+
*
|
|
31
|
+
* 注:不要手动修改这个属性值。
|
|
32
|
+
* 注:其实可以通过this._matchEntities.size获得实体数量,但是需要封装get方法。为了减少一次方法的调用所以才直接创建一个count属性
|
|
33
|
+
*/
|
|
34
|
+
count = 0;
|
|
35
|
+
|
|
36
|
+
/** 获取matchEntities中第一个实体 */
|
|
37
|
+
get entity(): E {
|
|
38
|
+
return this.matchEntities[0];
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
private _enteredEntities: Map<number, E> | null = null;
|
|
42
|
+
private _removedEntities: Map<number, E> | null = null;
|
|
43
|
+
|
|
44
|
+
constructor(matcher: ecs.IMatcher) {
|
|
45
|
+
this.matcher = matcher;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
onComponentAddOrRemove(entity: E) {
|
|
49
|
+
if (this.matcher.isMatch(entity)) { // Group只关心指定组件在实体身上的添加和删除动作。
|
|
50
|
+
this._matchEntities.set(entity.eid, entity);
|
|
51
|
+
this._entitiesCache = null;
|
|
52
|
+
this.count++;
|
|
53
|
+
|
|
54
|
+
if (this._enteredEntities) {
|
|
55
|
+
this._enteredEntities.set(entity.eid, entity);
|
|
56
|
+
this._removedEntities!.delete(entity.eid);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
else if (this._matchEntities.has(entity.eid)) { // 如果Group中有这个实体,但是这个实体已经不满足匹配规则,则从Group中移除该实体
|
|
60
|
+
this._matchEntities.delete(entity.eid);
|
|
61
|
+
this._entitiesCache = null;
|
|
62
|
+
this.count--;
|
|
63
|
+
|
|
64
|
+
if (this._enteredEntities) {
|
|
65
|
+
this._enteredEntities.delete(entity.eid);
|
|
66
|
+
this._removedEntities!.set(entity.eid, entity);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
watchEntityEnterAndRemove(enteredEntities: Map<number, E>, removedEntities: Map<number, E>) {
|
|
72
|
+
this._enteredEntities = enteredEntities;
|
|
73
|
+
this._removedEntities = removedEntities;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
clear() {
|
|
77
|
+
this._matchEntities.clear();
|
|
78
|
+
this._entitiesCache = null;
|
|
79
|
+
this.count = 0;
|
|
80
|
+
this._enteredEntities?.clear();
|
|
81
|
+
this._removedEntities?.clear();
|
|
82
|
+
}
|
|
83
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* @Author: dgflash
|
|
3
|
+
* @Date: 2022-05-12 14:18:44
|
|
4
|
+
* @LastEditors: dgflash
|
|
5
|
+
* @LastEditTime: 2022-05-24 11:09:49
|
|
6
|
+
*/
|
|
7
|
+
import { ECSModel } from "./ECSModel";
|
|
8
|
+
|
|
9
|
+
export class ECSMask {
|
|
10
|
+
private mask: Uint32Array;
|
|
11
|
+
private size: number = 0;
|
|
12
|
+
|
|
13
|
+
constructor() {
|
|
14
|
+
let length = Math.ceil(ECSModel.compTid / 31);
|
|
15
|
+
this.mask = new Uint32Array(length);
|
|
16
|
+
this.size = length;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
set(num: number) {
|
|
20
|
+
// https://stackoverflow.com/questions/34896909/is-it-correct-to-set-bit-31-in-javascript
|
|
21
|
+
// this.mask[((num / 32) >>> 0)] |= ((1 << (num % 32)) >>> 0);
|
|
22
|
+
this.mask[((num / 31) >>> 0)] |= (1 << (num % 31));
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
delete(num: number) {
|
|
26
|
+
this.mask[((num / 31) >>> 0)] &= ~(1 << (num % 31));
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
has(num: number) {
|
|
30
|
+
return !!(this.mask[((num / 31) >>> 0)] & (1 << (num % 31)));
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
or(other: ECSMask) {
|
|
34
|
+
for (let i = 0; i < this.size; i++) {
|
|
35
|
+
// &操作符最大也只能对2^30进行操作,如果对2^31&2^31会得到负数。当然可以(2^31&2^31) >>> 0,这样多了一步右移操作。
|
|
36
|
+
if (this.mask[i] & other.mask[i]) {
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
and(other: ECSMask) {
|
|
44
|
+
for (let i = 0; i < this.size; i++) {
|
|
45
|
+
if ((this.mask[i] & other.mask[i]) != this.mask[i]) {
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return true;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
clear() {
|
|
53
|
+
for (let i = 0; i < this.size; i++) {
|
|
54
|
+
this.mask[i] = 0;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
import { ecs } from "./ECS";
|
|
2
|
+
import { ECSEntity } from "./ECSEntity";
|
|
3
|
+
import { ECSMask } from "./ECSMask";
|
|
4
|
+
import { CompCtor, CompType, ECSModel } from "./ECSModel";
|
|
5
|
+
|
|
6
|
+
let macherId: number = 1;
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* 筛选规则间是“与”的关系
|
|
10
|
+
* 比如:ecs.Macher.allOf(...).excludeOf(...)表达的是allOf && excludeOf,即实体有“这些组件” 并且 “没有这些组件”
|
|
11
|
+
*/
|
|
12
|
+
export class ECSMatcher implements ecs.IMatcher {
|
|
13
|
+
protected rules: BaseOf[] = [];
|
|
14
|
+
protected _indices: number[] | null = null;
|
|
15
|
+
public isMatch!: (entity: ECSEntity) => boolean;
|
|
16
|
+
public mid: number = -1;
|
|
17
|
+
|
|
18
|
+
private _key: string | null = null;
|
|
19
|
+
public get key(): string {
|
|
20
|
+
if (!this._key) {
|
|
21
|
+
let s = '';
|
|
22
|
+
for (let i = 0; i < this.rules.length; i++) {
|
|
23
|
+
s += this.rules[i].getKey()
|
|
24
|
+
if (i < this.rules.length - 1) {
|
|
25
|
+
s += ' && '
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
this._key = s;
|
|
29
|
+
}
|
|
30
|
+
return this._key;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
constructor() {
|
|
34
|
+
this.mid = macherId++;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* 匹配器关注的组件索引。在创建Group时,Context根据组件id去给Group关联组件的添加和移除事件。
|
|
39
|
+
*/
|
|
40
|
+
get indices() {
|
|
41
|
+
if (this._indices === null) {
|
|
42
|
+
this._indices = [];
|
|
43
|
+
this.rules.forEach((rule) => {
|
|
44
|
+
Array.prototype.push.apply(this._indices, rule.indices);
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
return this._indices;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* 组件间是或的关系,表示关注拥有任意一个这些组件的实体。
|
|
52
|
+
* @param args 组件索引
|
|
53
|
+
*/
|
|
54
|
+
anyOf(...args: CompType<ecs.IComp>[]): ECSMatcher {
|
|
55
|
+
this.rules.push(new AnyOf(...args));
|
|
56
|
+
this.bindMatchMethod();
|
|
57
|
+
return this;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* 组件间是与的关系,表示关注拥有所有这些组件的实体。
|
|
62
|
+
* @param args 组件索引
|
|
63
|
+
*/
|
|
64
|
+
allOf(...args: CompType<ecs.IComp>[]): ECSMatcher {
|
|
65
|
+
this.rules.push(new AllOf(...args));
|
|
66
|
+
this.bindMatchMethod();
|
|
67
|
+
return this;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* 表示关注只拥有这些组件的实体
|
|
72
|
+
*
|
|
73
|
+
* 注意:
|
|
74
|
+
* 不是特殊情况不建议使用onlyOf。因为onlyOf会监听所有组件的添加和删除事件。
|
|
75
|
+
* @param args 组件索引
|
|
76
|
+
*/
|
|
77
|
+
onlyOf(...args: CompType<ecs.IComp>[]): ECSMatcher {
|
|
78
|
+
this.rules.push(new AllOf(...args));
|
|
79
|
+
let otherTids: CompType<ecs.IComp>[] = [];
|
|
80
|
+
for (let ctor of ECSModel.compCtors) {
|
|
81
|
+
if (args.indexOf(ctor) < 0) {
|
|
82
|
+
otherTids.push(ctor);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
this.rules.push(new ExcludeOf(...otherTids));
|
|
86
|
+
this.bindMatchMethod();
|
|
87
|
+
return this;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* 不包含指定的任意一个组件
|
|
92
|
+
* @param args
|
|
93
|
+
*/
|
|
94
|
+
excludeOf(...args: CompType<ecs.IComp>[]) {
|
|
95
|
+
this.rules.push(new ExcludeOf(...args));
|
|
96
|
+
this.bindMatchMethod();
|
|
97
|
+
return this;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
private bindMatchMethod() {
|
|
101
|
+
if (this.rules.length === 1) {
|
|
102
|
+
this.isMatch = this.isMatch1;
|
|
103
|
+
}
|
|
104
|
+
else if (this.rules.length === 2) {
|
|
105
|
+
this.isMatch = this.isMatch2;
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
this.isMatch = this.isMatchMore;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
private isMatch1(entity: ECSEntity): boolean {
|
|
113
|
+
return this.rules[0].isMatch(entity);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
private isMatch2(entity: ECSEntity): boolean {
|
|
117
|
+
return this.rules[0].isMatch(entity) && this.rules[1].isMatch(entity);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
private isMatchMore(entity: ECSEntity): boolean {
|
|
121
|
+
for (let rule of this.rules) {
|
|
122
|
+
if (!rule.isMatch(entity)) {
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return true;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
clone(): ECSMatcher {
|
|
130
|
+
let newMatcher = new ECSMatcher();
|
|
131
|
+
newMatcher.mid = macherId++;
|
|
132
|
+
this.rules.forEach(rule => newMatcher.rules.push(rule));
|
|
133
|
+
return newMatcher;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
abstract class BaseOf {
|
|
138
|
+
indices: number[] = [];
|
|
139
|
+
|
|
140
|
+
protected mask = new ECSMask();
|
|
141
|
+
|
|
142
|
+
constructor(...args: CompType<ecs.IComp>[]) {
|
|
143
|
+
let componentTypeId = -1;
|
|
144
|
+
let len = args.length;
|
|
145
|
+
for (let i = 0; i < len; i++) {
|
|
146
|
+
if (typeof (args[i]) === "number") {
|
|
147
|
+
componentTypeId = args[i] as number;
|
|
148
|
+
}
|
|
149
|
+
else {
|
|
150
|
+
componentTypeId = (args[i] as CompCtor<ecs.IComp>).tid;
|
|
151
|
+
}
|
|
152
|
+
if (componentTypeId == -1) {
|
|
153
|
+
throw Error('存在没有注册的组件!');
|
|
154
|
+
}
|
|
155
|
+
this.mask.set(componentTypeId);
|
|
156
|
+
|
|
157
|
+
if (this.indices.indexOf(componentTypeId) < 0) { // 去重
|
|
158
|
+
this.indices.push(componentTypeId);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
if (len > 1) {
|
|
162
|
+
this.indices.sort((a, b) => { return a - b; }); // 对组件类型id进行排序,这样关注相同组件的系统就能共用同一个group
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
toString(): string {
|
|
167
|
+
return this.indices.join('-'); // 生成group的key
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
abstract getKey(): string;
|
|
171
|
+
|
|
172
|
+
abstract isMatch(entity: ECSEntity): boolean;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* 用于描述包含任意一个这些组件的实体
|
|
177
|
+
*/
|
|
178
|
+
class AnyOf extends BaseOf {
|
|
179
|
+
public isMatch(entity: ECSEntity): boolean {
|
|
180
|
+
// @ts-ignore
|
|
181
|
+
return this.mask.or(entity.mask);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
getKey(): string {
|
|
185
|
+
return 'anyOf:' + this.toString();
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* 用于描述包含了“这些”组件的实体,这个实体除了包含这些组件还可以包含其他组件
|
|
191
|
+
*/
|
|
192
|
+
class AllOf extends BaseOf {
|
|
193
|
+
public isMatch(entity: ECSEntity): boolean {
|
|
194
|
+
// @ts-ignore
|
|
195
|
+
return this.mask.and(entity.mask);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
getKey(): string {
|
|
199
|
+
return 'allOf:' + this.toString();
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* 不包含指定的任意一个组件
|
|
205
|
+
*/
|
|
206
|
+
class ExcludeOf extends BaseOf {
|
|
207
|
+
public getKey(): string {
|
|
208
|
+
return 'excludeOf:' + this.toString();
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
public isMatch(entity: ECSEntity): boolean {
|
|
212
|
+
// @ts-ignore
|
|
213
|
+
return !this.mask.or(entity.mask);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* @Author: dgflash
|
|
3
|
+
* @Date: 2022-05-12 14:18:44
|
|
4
|
+
* @LastEditors: dgflash
|
|
5
|
+
* @LastEditTime: 2022-09-05 16:37:10
|
|
6
|
+
*/
|
|
7
|
+
import { ecs } from "./ECS";
|
|
8
|
+
import { ECSEntity } from "./ECSEntity";
|
|
9
|
+
import { ECSGroup } from "./ECSGroup";
|
|
10
|
+
|
|
11
|
+
type CompAddOrRemove = (entity: ecs.Entity) => void;
|
|
12
|
+
|
|
13
|
+
/** 组件类型 */
|
|
14
|
+
export type CompType<T> = CompCtor<T> | number;
|
|
15
|
+
|
|
16
|
+
/** 实体构造器接口 */
|
|
17
|
+
export interface EntityCtor<T> {
|
|
18
|
+
new(): T;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** 组件构造器接口 */
|
|
22
|
+
export interface CompCtor<T> {
|
|
23
|
+
new(): T;
|
|
24
|
+
/** 组件编号 */
|
|
25
|
+
tid: number;
|
|
26
|
+
/** 组件名 */
|
|
27
|
+
compName: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** ECS框架内部数据 */
|
|
31
|
+
export class ECSModel {
|
|
32
|
+
/** 实体自增id */
|
|
33
|
+
static eid = 1;
|
|
34
|
+
/** 实体造函数 */
|
|
35
|
+
static entityCtors: Map<EntityCtor<any>, string> = new Map();
|
|
36
|
+
/** 实体对象缓存池 */
|
|
37
|
+
static entityPool: Map<string, ECSEntity[]> = new Map();
|
|
38
|
+
/** 通过实体id查找实体对象 */
|
|
39
|
+
static eid2Entity: Map<number, ECSEntity> = new Map();
|
|
40
|
+
|
|
41
|
+
/** 组件类型id */
|
|
42
|
+
static compTid = 0;
|
|
43
|
+
/** 组件缓存池 */
|
|
44
|
+
static compPools: Map<number, ecs.IComp[]> = new Map();
|
|
45
|
+
/** 组件构造函数,用于ecs.register注册时,记录不同类型的组件 */
|
|
46
|
+
static compCtors: (CompCtor<any> | number)[] = [];
|
|
47
|
+
/**
|
|
48
|
+
* 每个组件的添加和删除的动作都要派送到“关心”它们的group上。goup对当前拥有或者之前(删除前)拥有该组件的实体进行组件规则判断。判断该实体是否满足group
|
|
49
|
+
* 所期望的组件组合。
|
|
50
|
+
*/
|
|
51
|
+
static compAddOrRemove: Map<number, CompAddOrRemove[]> = new Map();
|
|
52
|
+
|
|
53
|
+
/** 编号获取组件 */
|
|
54
|
+
static tid2comp: Map<number, ecs.IComp> = new Map();
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* 缓存的group
|
|
58
|
+
*
|
|
59
|
+
* key是组件的筛选规则,一个筛选规则对应一个group
|
|
60
|
+
*/
|
|
61
|
+
static groups: Map<number, ECSGroup> = new Map();
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* 创建group,每个group只关心对应组件的添加和删除
|
|
65
|
+
* @param matcher 实体筛选器
|
|
66
|
+
*/
|
|
67
|
+
static createGroup<E extends ECSEntity = ECSEntity>(matcher: ecs.IMatcher): ECSGroup<E> {
|
|
68
|
+
let group = ECSModel.groups.get(matcher.mid);
|
|
69
|
+
if (!group) {
|
|
70
|
+
group = new ECSGroup(matcher);
|
|
71
|
+
ECSModel.groups.set(matcher.mid, group);
|
|
72
|
+
let careComponentTypeIds = matcher.indices;
|
|
73
|
+
for (let i = 0; i < careComponentTypeIds.length; i++) {
|
|
74
|
+
ECSModel.compAddOrRemove.get(careComponentTypeIds[i])!.push(group.onComponentAddOrRemove.bind(group));
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return group as unknown as ECSGroup<E>;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** 系统组件 */
|
|
81
|
+
static systems: Map<string, ecs.System> = new Map<string, ecs.System>();
|
|
82
|
+
}
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import { ecs } from "./ECS";
|
|
2
|
+
import { ECSEntity } from "./ECSEntity";
|
|
3
|
+
import { ECSGroup } from "./ECSGroup";
|
|
4
|
+
import { ECSModel } from "./ECSModel";
|
|
5
|
+
|
|
6
|
+
/** 继承此类实现具体业务逻辑的系统 */
|
|
7
|
+
export abstract class ECSComblockSystem<E extends ECSEntity = ECSEntity> {
|
|
8
|
+
static s: boolean = true;
|
|
9
|
+
|
|
10
|
+
protected group: ECSGroup<E>;
|
|
11
|
+
protected dt: number = 0;
|
|
12
|
+
|
|
13
|
+
private enteredEntities: Map<number, E> = null!;
|
|
14
|
+
private removedEntities: Map<number, E> = null!;
|
|
15
|
+
|
|
16
|
+
private hasEntityEnter: boolean = false;
|
|
17
|
+
private hasEntityRemove: boolean = false;
|
|
18
|
+
private hasUpdate: boolean = false;
|
|
19
|
+
|
|
20
|
+
private tmpExecute: ((dt: number) => void) | null = null;
|
|
21
|
+
private execute!: (dt: number) => void;
|
|
22
|
+
|
|
23
|
+
/** 构造函数 */
|
|
24
|
+
constructor() {
|
|
25
|
+
let hasOwnProperty = Object.hasOwnProperty;
|
|
26
|
+
let prototype = Object.getPrototypeOf(this);
|
|
27
|
+
let hasEntityEnter = hasOwnProperty.call(prototype, 'entityEnter');
|
|
28
|
+
let hasEntityRemove = hasOwnProperty.call(prototype, 'entityRemove');
|
|
29
|
+
let hasFirstUpdate = hasOwnProperty.call(prototype, 'firstUpdate');
|
|
30
|
+
let hasUpdate = hasOwnProperty.call(prototype, 'update');
|
|
31
|
+
|
|
32
|
+
this.hasEntityEnter = hasEntityEnter;
|
|
33
|
+
this.hasEntityRemove = hasEntityRemove;
|
|
34
|
+
this.hasUpdate = hasUpdate;
|
|
35
|
+
|
|
36
|
+
if (hasEntityEnter || hasEntityRemove) {
|
|
37
|
+
this.enteredEntities = new Map<number, E>();
|
|
38
|
+
this.removedEntities = new Map<number, E>();
|
|
39
|
+
|
|
40
|
+
this.execute = this.execute1;
|
|
41
|
+
this.group = ECSModel.createGroup(this.filter());
|
|
42
|
+
this.group.watchEntityEnterAndRemove(this.enteredEntities, this.removedEntities);
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
this.execute = this.execute0;
|
|
46
|
+
this.group = ECSModel.createGroup(this.filter());
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (hasFirstUpdate) {
|
|
50
|
+
this.tmpExecute = this.execute;
|
|
51
|
+
this.execute = this.updateOnce;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** 系统实始化 */
|
|
56
|
+
init(): void {
|
|
57
|
+
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** 系统释放事件 */
|
|
61
|
+
onDestroy(): void {
|
|
62
|
+
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** 是否存在实体 */
|
|
66
|
+
hasEntity(): boolean {
|
|
67
|
+
return this.group.count > 0;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* 先执行entityEnter,最后执行firstUpdate
|
|
72
|
+
* @param dt
|
|
73
|
+
* @returns
|
|
74
|
+
*/
|
|
75
|
+
private updateOnce(dt: number) {
|
|
76
|
+
if (this.group.count === 0) {
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
this.dt = dt;
|
|
81
|
+
|
|
82
|
+
// 处理刚进来的实体
|
|
83
|
+
if (this.enteredEntities.size > 0) {
|
|
84
|
+
var entities = this.enteredEntities.values();
|
|
85
|
+
for (let entity of entities) {
|
|
86
|
+
(this as unknown as ecs.IEntityEnterSystem).entityEnter(entity);
|
|
87
|
+
}
|
|
88
|
+
this.enteredEntities.clear();
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// 只执行firstUpdate
|
|
92
|
+
for (let entity of this.group.matchEntities) {
|
|
93
|
+
(this as unknown as ecs.ISystemFirstUpdate).firstUpdate(entity);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
this.execute = this.tmpExecute!;
|
|
97
|
+
this.execute(dt);
|
|
98
|
+
this.tmpExecute = null;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* 只执行update
|
|
103
|
+
* @param dt
|
|
104
|
+
* @returns
|
|
105
|
+
*/
|
|
106
|
+
private execute0(dt: number): void {
|
|
107
|
+
if (this.group.count === 0) return;
|
|
108
|
+
|
|
109
|
+
this.dt = dt;
|
|
110
|
+
|
|
111
|
+
// 执行update
|
|
112
|
+
if (this.hasUpdate) {
|
|
113
|
+
for (let entity of this.group.matchEntities) {
|
|
114
|
+
(this as unknown as ecs.ISystemUpdate).update(entity);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* 先执行entityRemove,再执行entityEnter,最后执行update
|
|
121
|
+
* @param dt
|
|
122
|
+
* @returns
|
|
123
|
+
*/
|
|
124
|
+
private execute1(dt: number): void {
|
|
125
|
+
let entities;
|
|
126
|
+
if (this.removedEntities.size > 0) {
|
|
127
|
+
if (this.hasEntityRemove) {
|
|
128
|
+
entities = this.removedEntities.values();
|
|
129
|
+
for (let entity of entities) {
|
|
130
|
+
(this as unknown as ecs.IEntityRemoveSystem).entityRemove(entity);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
this.removedEntities.clear();
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (this.group.count === 0) return;
|
|
137
|
+
|
|
138
|
+
this.dt = dt;
|
|
139
|
+
|
|
140
|
+
// 处理刚进来的实体
|
|
141
|
+
if (this.enteredEntities!.size > 0) {
|
|
142
|
+
if (this.hasEntityEnter) {
|
|
143
|
+
entities = this.enteredEntities!.values();
|
|
144
|
+
for (let entity of entities) {
|
|
145
|
+
(this as unknown as ecs.IEntityEnterSystem).entityEnter(entity);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
this.enteredEntities!.clear();
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// 执行update
|
|
152
|
+
if (this.hasUpdate) {
|
|
153
|
+
for (let entity of this.group.matchEntities) {
|
|
154
|
+
(this as unknown as ecs.ISystemUpdate).update(entity);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* 实体过滤规则
|
|
161
|
+
*
|
|
162
|
+
* 根据提供的组件过滤实体。
|
|
163
|
+
*/
|
|
164
|
+
abstract filter(): ecs.IMatcher;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/** 根System,对游戏中的System遍历从这里开始,一个System组合中只能有一个RootSystem,可以有多个并行的RootSystem */
|
|
168
|
+
export class ECSRootSystem {
|
|
169
|
+
private executeSystemFlows: ECSComblockSystem[] = [];
|
|
170
|
+
private systemCnt: number = 0;
|
|
171
|
+
|
|
172
|
+
add(system: ECSSystem | ECSComblockSystem) {
|
|
173
|
+
if (system instanceof ECSSystem) {
|
|
174
|
+
// 将嵌套的System都“摊平”,放在根System中进行遍历,减少execute的频繁进入退出。
|
|
175
|
+
Array.prototype.push.apply(this.executeSystemFlows, system.comblockSystems);
|
|
176
|
+
}
|
|
177
|
+
else {
|
|
178
|
+
this.executeSystemFlows.push(system as ECSComblockSystem);
|
|
179
|
+
}
|
|
180
|
+
this.systemCnt = this.executeSystemFlows.length;
|
|
181
|
+
return this;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
init() {
|
|
185
|
+
// 自动注册系统组件
|
|
186
|
+
ECSModel.systems.forEach(sys => this.add(sys));
|
|
187
|
+
|
|
188
|
+
// 初始化组件
|
|
189
|
+
this.executeSystemFlows.forEach(sys => sys.init());
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
execute(dt: number) {
|
|
193
|
+
for (let i = 0; i < this.systemCnt; i++) {
|
|
194
|
+
// @ts-ignore
|
|
195
|
+
this.executeSystemFlows[i].execute(dt);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
clear() {
|
|
200
|
+
this.executeSystemFlows.forEach(sys => sys.onDestroy());
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/** 系统组合器,用于将多个相同功能模块的系统逻辑上放在一起,系统也可以嵌套系统 */
|
|
205
|
+
export class ECSSystem {
|
|
206
|
+
private _comblockSystems: ECSComblockSystem[] = [];
|
|
207
|
+
get comblockSystems() {
|
|
208
|
+
return this._comblockSystems;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
add(system: ECSSystem | ECSComblockSystem) {
|
|
212
|
+
if (system instanceof ECSSystem) {
|
|
213
|
+
Array.prototype.push.apply(this._comblockSystems, system._comblockSystems);
|
|
214
|
+
system._comblockSystems.length = 0;
|
|
215
|
+
}
|
|
216
|
+
else {
|
|
217
|
+
this._comblockSystems.push(system as ECSComblockSystem);
|
|
218
|
+
}
|
|
219
|
+
return this;
|
|
220
|
+
}
|
|
221
|
+
}
|