@jael-ecs/core 1.1.0 → 1.2.0
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/README.md +31 -33
- package/dist/ComponentManager.d.ts +4 -4
- package/dist/EventRegistry.d.ts +9 -11
- package/dist/Query.d.ts +4 -4
- package/dist/World.d.ts +5 -4
- package/dist/jael-build.cjs +1 -1
- package/dist/jael-build.js +187 -198
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -25,6 +25,7 @@ _A modern, performant, and user-friendly Entity Component System library written
|
|
|
25
25
|
- [EventRegistry](#event-registry)
|
|
26
26
|
- [Best Practices](#best-practices)
|
|
27
27
|
- [Advanced Usage](#advanced-usage)
|
|
28
|
+
- [Planned Features](#planned-features)
|
|
28
29
|
- [Contributing](#contributing)
|
|
29
30
|
- [Acknowledgments](#acknowledgments)
|
|
30
31
|
|
|
@@ -32,7 +33,8 @@ _A modern, performant, and user-friendly Entity Component System library written
|
|
|
32
33
|
|
|
33
34
|
- **User Friendly API** - Clean, fluent api that's easy to learn
|
|
34
35
|
- **High Performance** - Optimized SparseSet implementation for fast entity lookups
|
|
35
|
-
- **
|
|
36
|
+
- **Query System** - Optimized cache query system for entity packets
|
|
37
|
+
- **Minimal Bundle size** - Compact bundle size without dependencies.(38kb 📦)
|
|
36
38
|
|
|
37
39
|
## Installation
|
|
38
40
|
|
|
@@ -88,15 +90,6 @@ const movementSystem: System = {
|
|
|
88
90
|
position.x += velocity.dx * (Time.delta || 0.016);
|
|
89
91
|
position.y += velocity.dy * (Time.delta || 0.016);
|
|
90
92
|
});
|
|
91
|
-
|
|
92
|
-
// Get entity ids from query
|
|
93
|
-
for (const entityId of query.ids) {
|
|
94
|
-
const position = this.world.getComponent<Position>(entityId, "position");
|
|
95
|
-
const velocity = this.world.getComponent<Velocity>(entityId, "velocity");
|
|
96
|
-
|
|
97
|
-
position.x += velocity.dx * (Time.delta || 0.016);
|
|
98
|
-
position.y += velocity.dy * (Time.delta || 0.016);
|
|
99
|
-
}
|
|
100
93
|
},
|
|
101
94
|
};
|
|
102
95
|
|
|
@@ -162,21 +155,21 @@ world.removeSystem(yourSystem);
|
|
|
162
155
|
#### Events
|
|
163
156
|
|
|
164
157
|
```typescript
|
|
165
|
-
// Listen to world events
|
|
166
|
-
world.on("entityCreated", ({
|
|
167
|
-
console.log("Entity created:",
|
|
158
|
+
// Listen to world events
|
|
159
|
+
world.on("entityCreated", ({ entityId }) => {
|
|
160
|
+
console.log("Entity created:", entityId);
|
|
168
161
|
});
|
|
169
162
|
|
|
170
|
-
world.on("entityDestroyed", ({
|
|
171
|
-
console.log("Entity destroyed:",
|
|
163
|
+
world.on("entityDestroyed", ({ entityId }) => {
|
|
164
|
+
console.log("Entity destroyed:", entityId);
|
|
172
165
|
});
|
|
173
166
|
|
|
174
|
-
world.on("componentAdded", ({
|
|
175
|
-
console.log(`Component ${component} added to entity`);
|
|
167
|
+
world.on("componentAdded", ({ entityId, component }) => {
|
|
168
|
+
console.log(`Component ${component} added to entity ${entityId}`);
|
|
176
169
|
});
|
|
177
170
|
|
|
178
|
-
world.on("componentRemoved", ({
|
|
179
|
-
console.log(`Component ${component} removed from entity`);
|
|
171
|
+
world.on("componentRemoved", ({ entityId, component }) => {
|
|
172
|
+
console.log(`Component ${component} removed from entity ${entityId}`);
|
|
180
173
|
});
|
|
181
174
|
|
|
182
175
|
world.on("updated", () => {
|
|
@@ -205,8 +198,7 @@ const posExist = entity.has("position");
|
|
|
205
198
|
// Get curren value of the component
|
|
206
199
|
const compSchema = entity.get("position");
|
|
207
200
|
|
|
208
|
-
entity.id // Returns unique entity id from proxy
|
|
209
|
-
|
|
201
|
+
entity.id; // Returns unique entity id from proxy
|
|
210
202
|
```
|
|
211
203
|
|
|
212
204
|
### System
|
|
@@ -286,18 +278,16 @@ const complexQuery2 = world.include("position", "health").exclude("static");
|
|
|
286
278
|
// Iterate through entities as proxy
|
|
287
279
|
query.entities.forEach((entity) => {
|
|
288
280
|
// Process Entity proxy
|
|
289
|
-
})
|
|
281
|
+
});
|
|
290
282
|
|
|
291
283
|
// Iterate through entities ids
|
|
292
|
-
for(const entityId of query.ids){
|
|
284
|
+
for (const entityId of query.ids) {
|
|
293
285
|
// Process Entity id
|
|
294
286
|
}
|
|
295
287
|
|
|
296
|
-
|
|
297
|
-
|
|
298
288
|
// Get the first value of the query
|
|
299
289
|
const first = query.entities[0];
|
|
300
|
-
const firstId = query.ids.first()
|
|
290
|
+
const firstId = query.ids.first();
|
|
301
291
|
|
|
302
292
|
// Check query size
|
|
303
293
|
const count = query.size();
|
|
@@ -387,7 +377,7 @@ world.on("entityCreated", (data) => {
|
|
|
387
377
|
});
|
|
388
378
|
|
|
389
379
|
// Emit events (handled internally by World)
|
|
390
|
-
world.emit("entityCreated", {
|
|
380
|
+
world.emit("entityCreated", { entityId });
|
|
391
381
|
|
|
392
382
|
// Remove listeners
|
|
393
383
|
world.off("entityCreated", handler);
|
|
@@ -449,9 +439,9 @@ class MovementSystem implements System {
|
|
|
449
439
|
}
|
|
450
440
|
|
|
451
441
|
update() {
|
|
452
|
-
|
|
453
|
-
//
|
|
454
|
-
}
|
|
442
|
+
this.movementQuery.entities.forEach((entity) => {
|
|
443
|
+
// Handle entity movement
|
|
444
|
+
})
|
|
455
445
|
}
|
|
456
446
|
}
|
|
457
447
|
|
|
@@ -468,9 +458,9 @@ const movementSystem: MovementSystem = {
|
|
|
468
458
|
}
|
|
469
459
|
|
|
470
460
|
update(){
|
|
471
|
-
|
|
472
|
-
//
|
|
473
|
-
}
|
|
461
|
+
this.movementQuery?.entities.forEach((entity) => {
|
|
462
|
+
// Handle entity movement
|
|
463
|
+
})
|
|
474
464
|
}
|
|
475
465
|
}
|
|
476
466
|
|
|
@@ -513,6 +503,13 @@ world.on("playerScored", ({ points }) => {
|
|
|
513
503
|
});
|
|
514
504
|
```
|
|
515
505
|
|
|
506
|
+
## Planned Features
|
|
507
|
+
|
|
508
|
+
- Implement basic one level tag manager.
|
|
509
|
+
- Instancing / Prefab system with searilzation.
|
|
510
|
+
- Entity with childrens and parents.
|
|
511
|
+
- React wrapper (?)
|
|
512
|
+
|
|
516
513
|
## Contributing
|
|
517
514
|
|
|
518
515
|
Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.
|
|
@@ -541,6 +538,7 @@ npm run build
|
|
|
541
538
|
## Acknowledgments
|
|
542
539
|
|
|
543
540
|
- Inspiration from ECS frameworks like [ECSY](https://github.com/ecsyjs/ecsy) and [Bevy](https://github.com/bevyengine/bevy)
|
|
541
|
+
- Deep digging documentation at [Austin Morlan Post](https://austinmorlan.com/posts/entity_component_system/)
|
|
544
542
|
- TypeScript for providing excellent type safety and developer experience
|
|
545
543
|
|
|
546
544
|
---
|
|
@@ -12,14 +12,14 @@ export interface ComponentManagerEvents {
|
|
|
12
12
|
};
|
|
13
13
|
}
|
|
14
14
|
export declare class ComponentManager extends EventRegistry<ComponentManagerEvents> {
|
|
15
|
-
componentSet
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
world: World;
|
|
15
|
+
private componentSet;
|
|
16
|
+
private world;
|
|
17
|
+
dirtyEntities: Set<number>;
|
|
19
18
|
constructor(world: World);
|
|
20
19
|
clearComponentSchema(entityId: number): void;
|
|
21
20
|
addComponent<K extends keyof ComponentSchema>(entityId: number, key: K, value: ComponentSchema[K]): void;
|
|
22
21
|
getComponent<K extends keyof ComponentSchema>(entityId: number, key: K): ComponentSchema[K] | undefined;
|
|
22
|
+
cleanDirtyEntities(): void;
|
|
23
23
|
hasComponent<K extends keyof ComponentSchema>(entityId: number, key: K): boolean;
|
|
24
24
|
removeComponent<K extends keyof ComponentSchema>(entityId: number, key: K): void;
|
|
25
25
|
}
|
package/dist/EventRegistry.d.ts
CHANGED
|
@@ -1,14 +1,12 @@
|
|
|
1
|
-
export
|
|
2
|
-
[key: string]:
|
|
3
|
-
}
|
|
4
|
-
export type EventCallback<V> = (event: V[keyof V]) => void;
|
|
5
|
-
export default class EventRegistry<E extends Event<any> = {}> {
|
|
1
|
+
export default class EventRegistry<E extends {
|
|
2
|
+
[key: string]: any;
|
|
3
|
+
} = {}> {
|
|
6
4
|
private _listeners;
|
|
7
|
-
on(type:
|
|
8
|
-
off(type:
|
|
9
|
-
once(type:
|
|
10
|
-
clearEvent(type:
|
|
5
|
+
on<K extends string>(type: K, callback: (e: E[K]) => void): void;
|
|
6
|
+
off<K extends string>(type: K, callback: (e: E[K]) => void): void;
|
|
7
|
+
once<K extends string>(type: K, callback: (e: E[K]) => void): void;
|
|
8
|
+
clearEvent<K extends string>(type: K): void;
|
|
11
9
|
clearAllEvents(): void;
|
|
12
|
-
contains(type:
|
|
13
|
-
emit(type:
|
|
10
|
+
contains<K extends string>(type: K, callback: (e: E[K]) => void): boolean;
|
|
11
|
+
emit<K extends string>(type: K, data?: E[K]): void;
|
|
14
12
|
}
|
package/dist/Query.d.ts
CHANGED
|
@@ -13,18 +13,18 @@ export interface QueryEvents {
|
|
|
13
13
|
export declare class Query extends EventRegistry<QueryEvents> {
|
|
14
14
|
private config;
|
|
15
15
|
private entityMap;
|
|
16
|
-
private entityInstancesCache;
|
|
17
16
|
private world;
|
|
17
|
+
private lastVersion;
|
|
18
|
+
dirty: boolean;
|
|
18
19
|
constructor(config: QueryConfig, world: World);
|
|
19
20
|
hasComponents(entityId: number): boolean;
|
|
20
21
|
size(): number;
|
|
21
22
|
get hash(): number;
|
|
22
23
|
get ids(): SparseSet<number>;
|
|
23
|
-
private getCachedEntity;
|
|
24
24
|
get entities(): Entity[];
|
|
25
25
|
include(...comps: string[]): Query;
|
|
26
26
|
exclude(...comps: string[]): Query;
|
|
27
|
-
|
|
28
|
-
checkEntities(): void;
|
|
27
|
+
markDirty(): void;
|
|
28
|
+
checkEntities(entities?: Set<number>): void;
|
|
29
29
|
static getHash(config: QueryConfig): number;
|
|
30
30
|
}
|
package/dist/World.d.ts
CHANGED
|
@@ -6,17 +6,17 @@ import { SparseSet } from './SparseSet';
|
|
|
6
6
|
import { SystemManager, System } from './SystemManager';
|
|
7
7
|
export interface WorldEvents {
|
|
8
8
|
entityCreated: {
|
|
9
|
-
|
|
9
|
+
entityId: number;
|
|
10
10
|
};
|
|
11
11
|
entityDestroyed: {
|
|
12
|
-
|
|
12
|
+
entityId: number;
|
|
13
13
|
};
|
|
14
14
|
componentAdded: {
|
|
15
|
-
|
|
15
|
+
entityId: number;
|
|
16
16
|
component: keyof ComponentSchema;
|
|
17
17
|
};
|
|
18
18
|
componentRemoved: {
|
|
19
|
-
|
|
19
|
+
entityId: number;
|
|
20
20
|
component: keyof ComponentSchema;
|
|
21
21
|
};
|
|
22
22
|
updated: void;
|
|
@@ -26,6 +26,7 @@ export default class World extends EventRegistry<WorldEvents> {
|
|
|
26
26
|
componentManager: ComponentManager;
|
|
27
27
|
systemManager: SystemManager;
|
|
28
28
|
queries: Map<number, Query>;
|
|
29
|
+
version: number;
|
|
29
30
|
constructor();
|
|
30
31
|
getEntity(id: number): Entity | undefined;
|
|
31
32
|
get entityIds(): SparseSet<number>;
|
package/dist/jael-build.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});class o{_listeners=new Map;on(e,t){if(this.contains(e,t))return;const s=this._listeners.get(e);s?s.add(t):this._listeners.set(e,new Set([t]))}off(e,t){if(!this.contains(e,t))return;const s=this._listeners.get(e);s&&s.delete(t)}once(e,t){const s=
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});class o{_listeners=new Map;on(e,t){if(this.contains(e,t))return;const s=this._listeners.get(e);s?s.add(t):this._listeners.set(e,new Set([t]))}off(e,t){if(!this.contains(e,t))return;const s=this._listeners.get(e);s&&s.delete(t)}once(e,t){const s=i=>{t(i),this.off(e,s)};this.on(e,s)}clearEvent(e){this._listeners.get(e)&&this._listeners.get(e)?.clear()}clearAllEvents(){this._listeners.forEach(e=>e.clear()),this._listeners.clear()}contains(e,t){return this._listeners.get(e)?this._listeners.get(e).has(t):!1}emit(e,t){this._listeners.get(e)&&this._listeners.get(e)?.forEach(s=>{s(t)})}}class c{systemList=[];addSystem(e){this.systemList.push(e),e.init?.(),this.reorder()}reorder(){this.systemList.sort((e,t)=>e.priority-t.priority)}has(e){return this.systemList.indexOf(e)>0}removeSystem(e){if(!this.has(e))return;const t=this.systemList.indexOf(e);t>=0&&(this.systemList.splice(t,1),e.exit?.(),this.reorder())}}class d{denseValues=[];sparse=new Map;[Symbol.iterator](){let e=this.values.length;const t={value:void 0,done:!1};return{next:()=>(t.value=this.values[--e],t.done=e<0,t)}}get values(){return this.denseValues}first(){return this.denseValues[0]}add(e){this.has(e)||(this.denseValues.push(e),this.sparse.set(e,this.denseValues.length-1))}indexOf(e){const t=this.sparse.get(e);return t!==void 0?t:-1}remove(e){if(!this.has(e))return;const t=this.sparse.get(e);this.sparse.delete(e);const s=this.denseValues[this.denseValues.length-1];s!==e&&(this.denseValues[t]=s,this.sparse.set(s,t)),this.denseValues.pop()}forEach(e){for(let t of this)e(t)}size(){return this.denseValues.length}clear(){for(let e of this)this.remove(e)}has(e){return this.sparse.has(e)}}class m{id;_world;constructor(e,t){this.id=t,this._world=e}add(e,t){this._world.addComponent(this.id,e,t)}remove(e){this._world.removeComponent(this.id,e)}has(e){return this._world.componentManager.hasComponent(this.id,e)}get(e){return this._world.componentManager.getComponent(this.id,e)}}class l extends o{entityMap=new d;nextId=0;_world;constructor(e){super(),this._world=e}get entities(){return this.entityMap}create(){const e=this.nextId++;return this.entities.add(e),this.emit("create",e),e}exist(e){return this.entities.has(e)}size(){return this.entities.size()}destroy(e){return this.entities.remove(e),this.emit("destroy",e),e}}class u extends o{componentSet={};world;dirtyEntities=new Set;constructor(e){super(),this.world=e}clearComponentSchema(e){this.componentSet[e]&&delete this.componentSet[e]}addComponent(e,t,s){if(!this.world.exist(e))return;const i=this.componentSet[e];i?i[t]=s:this.componentSet[e]={[t]:s},this.dirtyEntities.add(e),this.emit("add",{entityId:e,component:t})}getComponent(e,t){if(this.hasComponent(e,t))return this.componentSet[e][t]}cleanDirtyEntities(){this.dirtyEntities.clear()}hasComponent(e,t){const s=this.componentSet[e];return s?t in s:!1}removeComponent(e,t){if(!this.componentSet[e])return;const s=this.componentSet[e];s&&s[t]!==void 0&&(delete s[t],Object.keys(s).length===0&&delete this.componentSet[e],this.dirtyEntities.add(e),this.emit("remove",{entityId:e,component:t}))}}class h extends o{config;entityMap;world;lastVersion=0;dirty;constructor(e,t){super(),this.config=e,this.world=t,this.entityMap=new d,this.dirty=!1,this.world.on("entityDestroyed",({entityId:s})=>{this.entityMap.has(s)&&(this.emit("removed",s),this.entityMap.remove(s))})}hasComponents(e){const t=this.world.componentManager;return this.config.include?.every(s=>t.getComponent(e,s))&&this.config.exclude?.every(s=>!t.getComponent(e,s))}size(){return this.ids.size()}get hash(){return h.getHash(this.config)}get ids(){return this.entityMap}get entities(){const e=[];return this.entityMap.forEach(t=>{const s=this.world.getEntity(t);s&&e.push(s)}),e}include(...e){return this.world.include(...e)}exclude(...e){return this.world.exclude(...e)}markDirty(){this.dirty=!0}checkEntities(e){if(!this.dirty||this.world.version===this.lastVersion)return;const t=e||this.world.entityIds;for(let s of t)this.hasComponents(s)&&(this.entityMap.add(s),this.emit("added",s));this.dirty=!1,this.lastVersion=this.world.version}static getHash(e){const t=e.include?.map(n=>n.trim()).filter(n=>n).join("_"),s=e.exclude?.map(n=>n.trim()).filter(n=>n).join("_"),i="in_"+t+"_out_"+s;let a=0;for(const n of i)a=(a<<5)-a+n.charCodeAt(0),a|=0;return a}}class p extends o{_startTime=0;_oldTime=0;_requestId=0;running=!1;delta=0;elapsed=0;constructor(){super()}_loop(){let e=0;if(this.running){const t=performance.now();e=(t-this._oldTime)/1e3,this._oldTime=t,this.elapsed+=e}this.delta=e,this.emit("update"),this._requestId=requestAnimationFrame(this._loop.bind(this))}start(){this._startTime=performance.now(),this._oldTime=this._startTime,this.elapsed=0,this.delta=0,this.running=!0,this._loop()}stop(){this.running=!1,cancelAnimationFrame(this._requestId),this._requestId=0}}let g=new p;class f extends o{entityManager;componentManager;systemManager;queries;version;constructor(){super(),this.entityManager=new l(this),this.componentManager=new u(this),this.systemManager=new c,this.version=0,this.entityManager.on("create",e=>{this.emit("entityCreated",{entityId:e}),this._updateQueries()}),this.entityManager.on("destroy",e=>{this.emit("entityDestroyed",{entityId:e}),this._updateQueries(),this.componentManager.clearComponentSchema(e)}),this.componentManager.on("add",({entityId:e,component:t})=>{this.emit("componentAdded",{entityId:e,component:t}),this._updateQueries()}),this.componentManager.on("remove",({entityId:e,component:t})=>{this.getEntity(e)&&this.emit("componentRemoved",{entityId:e,component:t}),this._updateQueries()}),this.queries=new Map}getEntity(e){return this.exist(e)?new m(this,e):void 0}get entityIds(){return this.entityManager.entities}query(e){const t=h.getHash(e);let i=this.queries.get(t);return i||(i=new h(e,this),this.queries.set(t,i),this._updateQueries()),i}_updateQueries(){const e=this.componentManager.dirtyEntities;this.queries.forEach(t=>{t.markDirty(),e.size>0&&t.checkEntities()}),this.componentManager.cleanDirtyEntities(),this.version++}exist(e){return this.entityManager.exist(e)}include(...e){return this.query({include:e,exclude:[]})}exclude(...e){return this.query({include:[],exclude:e})}create(){return this.entityManager.create()}destroy(e){this.entityManager.destroy(e)}addSystem(e){this.systemManager.addSystem(e)}removeSystem(e){this.systemManager.removeSystem(e)}addComponent(e,t,s){this.componentManager.addComponent(e,t,s)}getComponent(e,t){return this.componentManager.getComponent(e,t)}removeComponent(e,t){this.componentManager.removeComponent(e,t)}update(){this.systemManager.systemList.forEach(e=>{e.update()}),this.emit("updated")}}exports.ComponentManager=u;exports.EntityManager=l;exports.EventRegistry=o;exports.Query=h;exports.SparseSet=d;exports.SystemManager=c;exports.Time=g;exports.World=f;
|
package/dist/jael-build.js
CHANGED
|
@@ -1,66 +1,66 @@
|
|
|
1
1
|
class h {
|
|
2
2
|
_listeners = /* @__PURE__ */ new Map();
|
|
3
|
-
on(
|
|
4
|
-
if (this.contains(
|
|
3
|
+
on(e, t) {
|
|
4
|
+
if (this.contains(e, t))
|
|
5
5
|
return;
|
|
6
|
-
const s = this._listeners.get(
|
|
7
|
-
s ? s.add(
|
|
6
|
+
const s = this._listeners.get(e);
|
|
7
|
+
s ? s.add(t) : this._listeners.set(e, /* @__PURE__ */ new Set([t]));
|
|
8
8
|
}
|
|
9
|
-
off(
|
|
10
|
-
if (!this.contains(
|
|
9
|
+
off(e, t) {
|
|
10
|
+
if (!this.contains(e, t))
|
|
11
11
|
return;
|
|
12
|
-
const s = this._listeners.get(
|
|
13
|
-
s && s.delete(
|
|
12
|
+
const s = this._listeners.get(e);
|
|
13
|
+
s && s.delete(t);
|
|
14
14
|
}
|
|
15
|
-
once(
|
|
16
|
-
const s = (
|
|
17
|
-
|
|
18
|
-
}
|
|
19
|
-
this.on(
|
|
15
|
+
once(e, t) {
|
|
16
|
+
const s = (i) => {
|
|
17
|
+
t(i), this.off(e, s);
|
|
18
|
+
};
|
|
19
|
+
this.on(e, s);
|
|
20
20
|
}
|
|
21
|
-
clearEvent(
|
|
22
|
-
this._listeners.get(
|
|
21
|
+
clearEvent(e) {
|
|
22
|
+
this._listeners.get(e) && this._listeners.get(e)?.clear();
|
|
23
23
|
}
|
|
24
24
|
clearAllEvents() {
|
|
25
|
-
this._listeners.forEach((
|
|
25
|
+
this._listeners.forEach((e) => e.clear()), this._listeners.clear();
|
|
26
26
|
}
|
|
27
|
-
contains(
|
|
28
|
-
return this._listeners.get(
|
|
27
|
+
contains(e, t) {
|
|
28
|
+
return this._listeners.get(e) ? this._listeners.get(e).has(t) : !1;
|
|
29
29
|
}
|
|
30
|
-
emit(
|
|
31
|
-
this._listeners.get(
|
|
32
|
-
s(
|
|
30
|
+
emit(e, t) {
|
|
31
|
+
this._listeners.get(e) && this._listeners.get(e)?.forEach((s) => {
|
|
32
|
+
s(t);
|
|
33
33
|
});
|
|
34
34
|
}
|
|
35
35
|
}
|
|
36
|
-
class
|
|
36
|
+
class c {
|
|
37
37
|
systemList = [];
|
|
38
|
-
addSystem(
|
|
39
|
-
this.systemList.push(
|
|
38
|
+
addSystem(e) {
|
|
39
|
+
this.systemList.push(e), e.init?.(), this.reorder();
|
|
40
40
|
}
|
|
41
41
|
reorder() {
|
|
42
|
-
this.systemList.sort((
|
|
42
|
+
this.systemList.sort((e, t) => e.priority - t.priority);
|
|
43
43
|
}
|
|
44
|
-
has(
|
|
45
|
-
return this.systemList.indexOf(
|
|
44
|
+
has(e) {
|
|
45
|
+
return this.systemList.indexOf(e) > 0;
|
|
46
46
|
}
|
|
47
|
-
removeSystem(
|
|
48
|
-
if (!this.has(
|
|
49
|
-
const
|
|
50
|
-
|
|
47
|
+
removeSystem(e) {
|
|
48
|
+
if (!this.has(e)) return;
|
|
49
|
+
const t = this.systemList.indexOf(e);
|
|
50
|
+
t >= 0 && (this.systemList.splice(t, 1), e.exit?.(), this.reorder());
|
|
51
51
|
}
|
|
52
52
|
}
|
|
53
|
-
class
|
|
53
|
+
class d {
|
|
54
54
|
denseValues = [];
|
|
55
55
|
sparse = /* @__PURE__ */ new Map();
|
|
56
56
|
[Symbol.iterator]() {
|
|
57
|
-
let
|
|
58
|
-
const
|
|
57
|
+
let e = this.values.length;
|
|
58
|
+
const t = {
|
|
59
59
|
value: void 0,
|
|
60
60
|
done: !1
|
|
61
61
|
};
|
|
62
62
|
return {
|
|
63
|
-
next: () => (
|
|
63
|
+
next: () => (t.value = this.values[--e], t.done = e < 0, t)
|
|
64
64
|
};
|
|
65
65
|
}
|
|
66
66
|
get values() {
|
|
@@ -69,140 +69,146 @@ class c {
|
|
|
69
69
|
first() {
|
|
70
70
|
return this.denseValues[0];
|
|
71
71
|
}
|
|
72
|
-
add(
|
|
73
|
-
this.has(
|
|
72
|
+
add(e) {
|
|
73
|
+
this.has(e) || (this.denseValues.push(e), this.sparse.set(e, this.denseValues.length - 1));
|
|
74
74
|
}
|
|
75
|
-
indexOf(
|
|
76
|
-
const
|
|
77
|
-
return
|
|
75
|
+
indexOf(e) {
|
|
76
|
+
const t = this.sparse.get(e);
|
|
77
|
+
return t !== void 0 ? t : -1;
|
|
78
78
|
}
|
|
79
|
-
remove(
|
|
80
|
-
if (!this.has(
|
|
81
|
-
const
|
|
82
|
-
this.sparse.delete(
|
|
79
|
+
remove(e) {
|
|
80
|
+
if (!this.has(e)) return;
|
|
81
|
+
const t = this.sparse.get(e);
|
|
82
|
+
this.sparse.delete(e);
|
|
83
83
|
const s = this.denseValues[this.denseValues.length - 1];
|
|
84
|
-
s !==
|
|
84
|
+
s !== e && (this.denseValues[t] = s, this.sparse.set(s, t)), this.denseValues.pop();
|
|
85
85
|
}
|
|
86
|
-
forEach(
|
|
87
|
-
for (let
|
|
88
|
-
t
|
|
86
|
+
forEach(e) {
|
|
87
|
+
for (let t of this)
|
|
88
|
+
e(t);
|
|
89
89
|
}
|
|
90
90
|
size() {
|
|
91
91
|
return this.denseValues.length;
|
|
92
92
|
}
|
|
93
93
|
clear() {
|
|
94
|
-
for (let
|
|
95
|
-
this.remove(
|
|
94
|
+
for (let e of this)
|
|
95
|
+
this.remove(e);
|
|
96
96
|
}
|
|
97
|
-
has(
|
|
98
|
-
return this.sparse.has(
|
|
97
|
+
has(e) {
|
|
98
|
+
return this.sparse.has(e);
|
|
99
99
|
}
|
|
100
100
|
}
|
|
101
|
-
class
|
|
101
|
+
class l {
|
|
102
102
|
id;
|
|
103
103
|
_world;
|
|
104
|
-
constructor(
|
|
105
|
-
this.id =
|
|
104
|
+
constructor(e, t) {
|
|
105
|
+
this.id = t, this._world = e;
|
|
106
106
|
}
|
|
107
107
|
/**
|
|
108
108
|
* Add component to current entity.
|
|
109
109
|
* @param compType Component name
|
|
110
110
|
* @param compValue Component value
|
|
111
111
|
*/
|
|
112
|
-
add(
|
|
113
|
-
this._world.addComponent(this.id,
|
|
112
|
+
add(e, t) {
|
|
113
|
+
this._world.addComponent(this.id, e, t);
|
|
114
114
|
}
|
|
115
115
|
/**
|
|
116
116
|
* Remove component of current entity.
|
|
117
117
|
* @param compType Component name
|
|
118
118
|
*/
|
|
119
|
-
remove(
|
|
120
|
-
this._world.removeComponent(this.id,
|
|
119
|
+
remove(e) {
|
|
120
|
+
this._world.removeComponent(this.id, e);
|
|
121
121
|
}
|
|
122
122
|
/**
|
|
123
123
|
* Check if current entity has a component.
|
|
124
124
|
* @param compType Component name
|
|
125
125
|
* @returns boolean
|
|
126
126
|
*/
|
|
127
|
-
has(
|
|
128
|
-
return this._world.componentManager.hasComponent(this.id,
|
|
127
|
+
has(e) {
|
|
128
|
+
return this._world.componentManager.hasComponent(this.id, e);
|
|
129
129
|
}
|
|
130
130
|
/**
|
|
131
131
|
* Get passed component schema of current entity.
|
|
132
132
|
* @param compType Component name
|
|
133
133
|
* @returns Return component schema with T(any as default) as type
|
|
134
134
|
*/
|
|
135
|
-
get(
|
|
136
|
-
return this._world.componentManager.getComponent(this.id,
|
|
135
|
+
get(e) {
|
|
136
|
+
return this._world.componentManager.getComponent(this.id, e);
|
|
137
137
|
}
|
|
138
138
|
}
|
|
139
|
-
class
|
|
140
|
-
entityMap = new
|
|
139
|
+
class u extends h {
|
|
140
|
+
entityMap = new d();
|
|
141
141
|
nextId = 0;
|
|
142
142
|
_world;
|
|
143
|
-
constructor(
|
|
144
|
-
super(), this._world =
|
|
143
|
+
constructor(e) {
|
|
144
|
+
super(), this._world = e;
|
|
145
145
|
}
|
|
146
146
|
get entities() {
|
|
147
147
|
return this.entityMap;
|
|
148
148
|
}
|
|
149
149
|
create() {
|
|
150
|
-
const
|
|
151
|
-
return this.entities.add(
|
|
150
|
+
const e = this.nextId++;
|
|
151
|
+
return this.entities.add(e), this.emit("create", e), e;
|
|
152
152
|
}
|
|
153
|
-
exist(
|
|
154
|
-
return this.entities.has(
|
|
153
|
+
exist(e) {
|
|
154
|
+
return this.entities.has(e);
|
|
155
155
|
}
|
|
156
156
|
size() {
|
|
157
157
|
return this.entities.size();
|
|
158
158
|
}
|
|
159
|
-
destroy(
|
|
160
|
-
return this.entities.remove(
|
|
159
|
+
destroy(e) {
|
|
160
|
+
return this.entities.remove(e), this.emit("destroy", e), e;
|
|
161
161
|
}
|
|
162
162
|
}
|
|
163
163
|
class m extends h {
|
|
164
164
|
componentSet = {};
|
|
165
165
|
world;
|
|
166
|
-
|
|
167
|
-
|
|
166
|
+
dirtyEntities = /* @__PURE__ */ new Set();
|
|
167
|
+
constructor(e) {
|
|
168
|
+
super(), this.world = e;
|
|
169
|
+
}
|
|
170
|
+
clearComponentSchema(e) {
|
|
171
|
+
this.componentSet[e] && delete this.componentSet[e];
|
|
168
172
|
}
|
|
169
|
-
|
|
170
|
-
this.
|
|
173
|
+
addComponent(e, t, s) {
|
|
174
|
+
if (!this.world.exist(e)) return;
|
|
175
|
+
const i = this.componentSet[e];
|
|
176
|
+
i ? i[t] = s : this.componentSet[e] = { [t]: s }, this.dirtyEntities.add(e), this.emit("add", { entityId: e, component: t });
|
|
171
177
|
}
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
178
|
+
getComponent(e, t) {
|
|
179
|
+
if (this.hasComponent(e, t))
|
|
180
|
+
return this.componentSet[e][t];
|
|
175
181
|
}
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
return this.componentSet[t][e];
|
|
182
|
+
cleanDirtyEntities() {
|
|
183
|
+
this.dirtyEntities.clear();
|
|
179
184
|
}
|
|
180
|
-
hasComponent(
|
|
181
|
-
const s = this.componentSet[
|
|
182
|
-
return s ?
|
|
185
|
+
hasComponent(e, t) {
|
|
186
|
+
const s = this.componentSet[e];
|
|
187
|
+
return s ? t in s : !1;
|
|
183
188
|
}
|
|
184
|
-
removeComponent(
|
|
185
|
-
if (!this.componentSet[
|
|
186
|
-
const s = this.componentSet[
|
|
187
|
-
s && s[
|
|
189
|
+
removeComponent(e, t) {
|
|
190
|
+
if (!this.componentSet[e]) return;
|
|
191
|
+
const s = this.componentSet[e];
|
|
192
|
+
s && s[t] !== void 0 && (delete s[t], Object.keys(s).length === 0 && delete this.componentSet[e], this.dirtyEntities.add(e), this.emit("remove", { entityId: e, component: t }));
|
|
188
193
|
}
|
|
189
194
|
}
|
|
190
195
|
class a extends h {
|
|
191
196
|
config;
|
|
192
197
|
entityMap;
|
|
193
|
-
entityInstancesCache;
|
|
194
198
|
world;
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
199
|
+
lastVersion = 0;
|
|
200
|
+
dirty;
|
|
201
|
+
constructor(e, t) {
|
|
202
|
+
super(), this.config = e, this.world = t, this.entityMap = new d(), this.dirty = !1, this.world.on("entityDestroyed", ({ entityId: s }) => {
|
|
203
|
+
this.entityMap.has(s) && (this.emit("removed", s), this.entityMap.remove(s));
|
|
198
204
|
});
|
|
199
205
|
}
|
|
200
|
-
hasComponents(
|
|
201
|
-
const
|
|
206
|
+
hasComponents(e) {
|
|
207
|
+
const t = this.world.componentManager;
|
|
202
208
|
return this.config.include?.every(
|
|
203
|
-
(s) =>
|
|
209
|
+
(s) => t.getComponent(e, s)
|
|
204
210
|
) && this.config.exclude?.every(
|
|
205
|
-
(s) => !
|
|
211
|
+
(s) => !t.getComponent(e, s)
|
|
206
212
|
);
|
|
207
213
|
}
|
|
208
214
|
size() {
|
|
@@ -214,48 +220,34 @@ class a extends h {
|
|
|
214
220
|
get ids() {
|
|
215
221
|
return this.entityMap;
|
|
216
222
|
}
|
|
217
|
-
|
|
218
|
-
const e =
|
|
219
|
-
|
|
220
|
-
return e;
|
|
221
|
-
{
|
|
223
|
+
get entities() {
|
|
224
|
+
const e = [];
|
|
225
|
+
return this.entityMap.forEach((t) => {
|
|
222
226
|
const s = this.world.getEntity(t);
|
|
223
|
-
|
|
224
|
-
}
|
|
227
|
+
s && e.push(s);
|
|
228
|
+
}), e;
|
|
225
229
|
}
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
_checkExistingEntities() {
|
|
245
|
-
for (let t of this.ids)
|
|
246
|
-
this.world.exist(t) || (this.emit("removed", t), this.entityMap.remove(t));
|
|
247
|
-
}
|
|
248
|
-
checkEntities() {
|
|
249
|
-
this.entityInstancesCache.clear();
|
|
250
|
-
for (let t of this.world.entityIds)
|
|
251
|
-
this.hasComponents(t) && (this.entityMap.add(t), this.emit("added", t));
|
|
252
|
-
this._checkExistingEntities();
|
|
253
|
-
}
|
|
254
|
-
static getHash(t) {
|
|
255
|
-
const e = t.include?.map((i) => i.trim()).filter((i) => i).join("_"), s = t.exclude?.map((i) => i.trim()).filter((i) => i).join("_"), n = "in_" + e + "_out_" + s;
|
|
230
|
+
include(...e) {
|
|
231
|
+
return this.world.include(...e);
|
|
232
|
+
}
|
|
233
|
+
exclude(...e) {
|
|
234
|
+
return this.world.exclude(...e);
|
|
235
|
+
}
|
|
236
|
+
markDirty() {
|
|
237
|
+
this.dirty = !0;
|
|
238
|
+
}
|
|
239
|
+
checkEntities(e) {
|
|
240
|
+
if (!this.dirty || this.world.version === this.lastVersion) return;
|
|
241
|
+
const t = e || this.world.entityIds;
|
|
242
|
+
for (let s of t)
|
|
243
|
+
this.hasComponents(s) && (this.entityMap.add(s), this.emit("added", s));
|
|
244
|
+
this.dirty = !1, this.lastVersion = this.world.version;
|
|
245
|
+
}
|
|
246
|
+
static getHash(e) {
|
|
247
|
+
const t = e.include?.map((n) => n.trim()).filter((n) => n).join("_"), s = e.exclude?.map((n) => n.trim()).filter((n) => n).join("_"), i = "in_" + t + "_out_" + s;
|
|
256
248
|
let o = 0;
|
|
257
|
-
for (const
|
|
258
|
-
o = (o << 5) - o +
|
|
249
|
+
for (const n of i)
|
|
250
|
+
o = (o << 5) - o + n.charCodeAt(0), o |= 0;
|
|
259
251
|
return o;
|
|
260
252
|
}
|
|
261
253
|
}
|
|
@@ -270,12 +262,12 @@ class p extends h {
|
|
|
270
262
|
super();
|
|
271
263
|
}
|
|
272
264
|
_loop() {
|
|
273
|
-
let
|
|
265
|
+
let e = 0;
|
|
274
266
|
if (this.running) {
|
|
275
|
-
const
|
|
276
|
-
|
|
267
|
+
const t = performance.now();
|
|
268
|
+
e = (t - this._oldTime) / 1e3, this._oldTime = t, this.elapsed += e;
|
|
277
269
|
}
|
|
278
|
-
this.delta =
|
|
270
|
+
this.delta = e, this.emit("update"), this._requestId = requestAnimationFrame(this._loop.bind(this));
|
|
279
271
|
}
|
|
280
272
|
start() {
|
|
281
273
|
this._startTime = performance.now(), this._oldTime = this._startTime, this.elapsed = 0, this.delta = 0, this.running = !0, this._loop();
|
|
@@ -290,91 +282,88 @@ class f extends h {
|
|
|
290
282
|
componentManager;
|
|
291
283
|
systemManager;
|
|
292
284
|
queries;
|
|
285
|
+
version;
|
|
293
286
|
constructor() {
|
|
294
|
-
super(), this.entityManager = new
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
e
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
(
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
}), this._updateQueries());
|
|
317
|
-
}
|
|
318
|
-
), this.queries = /* @__PURE__ */ new Map();
|
|
319
|
-
}
|
|
320
|
-
getEntity(t) {
|
|
321
|
-
if (this.entityManager.exist(t))
|
|
322
|
-
return new u(this, t);
|
|
287
|
+
super(), this.entityManager = new u(this), this.componentManager = new m(this), this.systemManager = new c(), this.version = 0, this.entityManager.on("create", (e) => {
|
|
288
|
+
this.emit("entityCreated", {
|
|
289
|
+
entityId: e
|
|
290
|
+
}), this._updateQueries();
|
|
291
|
+
}), this.entityManager.on("destroy", (e) => {
|
|
292
|
+
this.emit("entityDestroyed", {
|
|
293
|
+
entityId: e
|
|
294
|
+
}), this._updateQueries(), this.componentManager.clearComponentSchema(e);
|
|
295
|
+
}), this.componentManager.on("add", ({ entityId: e, component: t }) => {
|
|
296
|
+
this.emit("componentAdded", {
|
|
297
|
+
entityId: e,
|
|
298
|
+
component: t
|
|
299
|
+
}), this._updateQueries();
|
|
300
|
+
}), this.componentManager.on("remove", ({ entityId: e, component: t }) => {
|
|
301
|
+
this.getEntity(e) && this.emit("componentRemoved", {
|
|
302
|
+
entityId: e,
|
|
303
|
+
component: t
|
|
304
|
+
}), this._updateQueries();
|
|
305
|
+
}), this.queries = /* @__PURE__ */ new Map();
|
|
306
|
+
}
|
|
307
|
+
getEntity(e) {
|
|
308
|
+
return this.exist(e) ? new l(this, e) : void 0;
|
|
323
309
|
}
|
|
324
310
|
get entityIds() {
|
|
325
311
|
return this.entityManager.entities;
|
|
326
312
|
}
|
|
327
|
-
query(
|
|
328
|
-
const
|
|
329
|
-
let
|
|
330
|
-
return
|
|
313
|
+
query(e) {
|
|
314
|
+
const t = a.getHash(e);
|
|
315
|
+
let i = this.queries.get(t);
|
|
316
|
+
return i || (i = new a(e, this), this.queries.set(t, i), this._updateQueries()), i;
|
|
331
317
|
}
|
|
332
318
|
_updateQueries() {
|
|
333
|
-
this.
|
|
319
|
+
const e = this.componentManager.dirtyEntities;
|
|
320
|
+
this.queries.forEach((t) => {
|
|
321
|
+
t.markDirty(), e.size > 0 && t.checkEntities();
|
|
322
|
+
}), this.componentManager.cleanDirtyEntities(), this.version++;
|
|
334
323
|
}
|
|
335
|
-
exist(
|
|
336
|
-
return this.entityManager.exist(
|
|
324
|
+
exist(e) {
|
|
325
|
+
return this.entityManager.exist(e);
|
|
337
326
|
}
|
|
338
|
-
include(...
|
|
339
|
-
return this.query({ include:
|
|
327
|
+
include(...e) {
|
|
328
|
+
return this.query({ include: e, exclude: [] });
|
|
340
329
|
}
|
|
341
|
-
exclude(...
|
|
342
|
-
return this.query({ include: [], exclude:
|
|
330
|
+
exclude(...e) {
|
|
331
|
+
return this.query({ include: [], exclude: e });
|
|
343
332
|
}
|
|
344
333
|
create() {
|
|
345
334
|
return this.entityManager.create();
|
|
346
335
|
}
|
|
347
|
-
destroy(
|
|
348
|
-
this.entityManager.destroy(
|
|
336
|
+
destroy(e) {
|
|
337
|
+
this.entityManager.destroy(e);
|
|
349
338
|
}
|
|
350
|
-
addSystem(
|
|
351
|
-
this.systemManager.addSystem(
|
|
339
|
+
addSystem(e) {
|
|
340
|
+
this.systemManager.addSystem(e);
|
|
352
341
|
}
|
|
353
|
-
removeSystem(
|
|
354
|
-
this.systemManager.removeSystem(
|
|
342
|
+
removeSystem(e) {
|
|
343
|
+
this.systemManager.removeSystem(e);
|
|
355
344
|
}
|
|
356
|
-
addComponent(
|
|
357
|
-
this.componentManager.addComponent(
|
|
345
|
+
addComponent(e, t, s) {
|
|
346
|
+
this.componentManager.addComponent(e, t, s);
|
|
358
347
|
}
|
|
359
|
-
getComponent(
|
|
360
|
-
return this.componentManager.getComponent(
|
|
348
|
+
getComponent(e, t) {
|
|
349
|
+
return this.componentManager.getComponent(e, t);
|
|
361
350
|
}
|
|
362
|
-
removeComponent(
|
|
363
|
-
this.componentManager.removeComponent(
|
|
351
|
+
removeComponent(e, t) {
|
|
352
|
+
this.componentManager.removeComponent(e, t);
|
|
364
353
|
}
|
|
365
354
|
update() {
|
|
366
|
-
this.systemManager.systemList.forEach((
|
|
367
|
-
|
|
355
|
+
this.systemManager.systemList.forEach((e) => {
|
|
356
|
+
e.update();
|
|
368
357
|
}), this.emit("updated");
|
|
369
358
|
}
|
|
370
359
|
}
|
|
371
360
|
export {
|
|
372
361
|
m as ComponentManager,
|
|
373
|
-
|
|
362
|
+
u as EntityManager,
|
|
374
363
|
h as EventRegistry,
|
|
375
364
|
a as Query,
|
|
376
|
-
|
|
377
|
-
|
|
365
|
+
d as SparseSet,
|
|
366
|
+
c as SystemManager,
|
|
378
367
|
g as Time,
|
|
379
368
|
f as World
|
|
380
369
|
};
|