@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 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
- - **Minimal Bundle size** - Compact bundle size without dependencies.(34kb 📦)
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 ( returns entity proxy for easy reading )
166
- world.on("entityCreated", ({ entity }) => {
167
- console.log("Entity created:", entity);
158
+ // Listen to world events
159
+ world.on("entityCreated", ({ entityId }) => {
160
+ console.log("Entity created:", entityId);
168
161
  });
169
162
 
170
- world.on("entityDestroyed", ({ entity }) => {
171
- console.log("Entity destroyed:", entity);
163
+ world.on("entityDestroyed", ({ entityId }) => {
164
+ console.log("Entity destroyed:", entityId);
172
165
  });
173
166
 
174
- world.on("componentAdded", ({ entity, component }) => {
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", ({ entity, component }) => {
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", { entity: someEntity });
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
- for(const entityId of this.movementQuery.ids){
453
- // Process movement using entityId
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
- for (const entityId of this.movementQuery.ids) {
472
- // Process movement
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
- [k: number]: ComponentSchema;
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
  }
@@ -1,14 +1,12 @@
1
- export type Event<V> = {
2
- [key: string]: V;
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: keyof E, callback: EventCallback<E>): void;
8
- off(type: keyof E, callback: EventCallback<E>): void;
9
- once(type: keyof E, callback: EventCallback<E>): void;
10
- clearEvent(type: keyof E): void;
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: keyof E, callback: EventCallback<E>): boolean;
13
- emit(type: keyof E, data: E[keyof E]): void;
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
- private _checkExistingEntities;
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
- entity: Entity;
9
+ entityId: number;
10
10
  };
11
11
  entityDestroyed: {
12
- entity: Entity;
12
+ entityId: number;
13
13
  };
14
14
  componentAdded: {
15
- entity: Entity;
15
+ entityId: number;
16
16
  component: keyof ComponentSchema;
17
17
  };
18
18
  componentRemoved: {
19
- entity: Entity;
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>;
@@ -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=(n=>{t(n),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 d{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 c{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 u extends o{entityMap=new c;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 l extends o{componentSet={};world;constructor(e){super(),this.world=e}clearComponentSchema(e){this.componentSet[e]&&delete this.componentSet[e]}addComponent(e,t,s){const n=this.componentSet[e];n?n[t]=s:this.componentSet[e]={[t]:s},this.emit("add",{entityId:e,component:t})}getComponent(e,t){if(this.hasComponent(e,t))return this.componentSet[e][t]}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.emit("remove",{entityId:e,component:t}))}}class a extends o{config;entityMap;entityInstancesCache;world;constructor(e,t){super(),this.config=e,this.world=t,this.entityInstancesCache=new Map,this.entityMap=new c,this.on("removed",s=>{this.entityInstancesCache.delete(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 a.getHash(this.config)}get ids(){return this.entityMap}getCachedEntity(e){const t=this.entityInstancesCache.get(e);if(t)return t;{const s=this.world.getEntity(e);return s?(this.entityInstancesCache.set(e,s),s):void 0}}get entities(){const e=[],t=this.entityMap.size()>100;return this.entityMap.forEach(s=>{if(t){const n=this.getCachedEntity(s);n&&e.push(n)}else{const n=this.world.getEntity(s);n&&e.push(n)}}),e}include(...e){return this.world.include(...e)}exclude(...e){return this.world.exclude(...e)}_checkExistingEntities(){for(let e of this.ids)this.world.exist(e)||(this.emit("removed",e),this.entityMap.remove(e))}checkEntities(){this.entityInstancesCache.clear();for(let e of this.world.entityIds)this.hasComponents(e)&&(this.entityMap.add(e),this.emit("added",e));this._checkExistingEntities()}static getHash(e){const t=e.include?.map(i=>i.trim()).filter(i=>i).join("_"),s=e.exclude?.map(i=>i.trim()).filter(i=>i).join("_"),n="in_"+t+"_out_"+s;let h=0;for(const i of n)h=(h<<5)-h+i.charCodeAt(0),h|=0;return h}}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;constructor(){super(),this.entityManager=new u(this),this.componentManager=new l(this),this.systemManager=new d,this.entityManager.on("create",e=>{const t=this.getEntity(e);t&&(this.emit("entityCreated",{entity:t}),this._updateQueries())}),this.entityManager.on("destroy",e=>{const t=this.getEntity(e);t&&(this.emit("entityCreated",{entity:t}),this._updateQueries(),this.componentManager.clearComponentSchema(e))}),this.componentManager.on("add",e=>{const t=this.getEntity(e.entityId);t&&(this.emit("componentAdded",{entity:t,component:e.component}),this._updateQueries())}),this.componentManager.on("remove",e=>{const t=this.getEntity(e.entityId);t&&(this.emit("componentRemoved",{entity:t,component:e.component}),this._updateQueries())}),this.queries=new Map}getEntity(e){if(this.entityManager.exist(e))return new m(this,e)}get entityIds(){return this.entityManager.entities}query(e){const t=a.getHash(e);let n=this.queries.get(t);return n||(n=new a(e,this),this.queries.set(t,n),this._updateQueries()),n}_updateQueries(){this.queries.forEach(e=>e.checkEntities())}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=l;exports.EntityManager=u;exports.EventRegistry=o;exports.Query=a;exports.SparseSet=c;exports.SystemManager=d;exports.Time=g;exports.World=f;
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;
@@ -1,66 +1,66 @@
1
1
  class h {
2
2
  _listeners = /* @__PURE__ */ new Map();
3
- on(t, e) {
4
- if (this.contains(t, e))
3
+ on(e, t) {
4
+ if (this.contains(e, t))
5
5
  return;
6
- const s = this._listeners.get(t);
7
- s ? s.add(e) : this._listeners.set(t, /* @__PURE__ */ new Set([e]));
6
+ const s = this._listeners.get(e);
7
+ s ? s.add(t) : this._listeners.set(e, /* @__PURE__ */ new Set([t]));
8
8
  }
9
- off(t, e) {
10
- if (!this.contains(t, e))
9
+ off(e, t) {
10
+ if (!this.contains(e, t))
11
11
  return;
12
- const s = this._listeners.get(t);
13
- s && s.delete(e);
12
+ const s = this._listeners.get(e);
13
+ s && s.delete(t);
14
14
  }
15
- once(t, e) {
16
- const s = ((n) => {
17
- e(n), this.off(t, s);
18
- });
19
- this.on(t, s);
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(t) {
22
- this._listeners.get(t) && this._listeners.get(t)?.clear();
21
+ clearEvent(e) {
22
+ this._listeners.get(e) && this._listeners.get(e)?.clear();
23
23
  }
24
24
  clearAllEvents() {
25
- this._listeners.forEach((t) => t.clear()), this._listeners.clear();
25
+ this._listeners.forEach((e) => e.clear()), this._listeners.clear();
26
26
  }
27
- contains(t, e) {
28
- return this._listeners.get(t) ? this._listeners.get(t).has(e) : !1;
27
+ contains(e, t) {
28
+ return this._listeners.get(e) ? this._listeners.get(e).has(t) : !1;
29
29
  }
30
- emit(t, e) {
31
- this._listeners.get(t) && this._listeners.get(t)?.forEach((s) => {
32
- s(e);
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 d {
36
+ class c {
37
37
  systemList = [];
38
- addSystem(t) {
39
- this.systemList.push(t), t.init?.(), this.reorder();
38
+ addSystem(e) {
39
+ this.systemList.push(e), e.init?.(), this.reorder();
40
40
  }
41
41
  reorder() {
42
- this.systemList.sort((t, e) => t.priority - e.priority);
42
+ this.systemList.sort((e, t) => e.priority - t.priority);
43
43
  }
44
- has(t) {
45
- return this.systemList.indexOf(t) > 0;
44
+ has(e) {
45
+ return this.systemList.indexOf(e) > 0;
46
46
  }
47
- removeSystem(t) {
48
- if (!this.has(t)) return;
49
- const e = this.systemList.indexOf(t);
50
- e >= 0 && (this.systemList.splice(e, 1), t.exit?.(), this.reorder());
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 c {
53
+ class d {
54
54
  denseValues = [];
55
55
  sparse = /* @__PURE__ */ new Map();
56
56
  [Symbol.iterator]() {
57
- let t = this.values.length;
58
- const e = {
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: () => (e.value = this.values[--t], e.done = t < 0, e)
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(t) {
73
- this.has(t) || (this.denseValues.push(t), this.sparse.set(t, this.denseValues.length - 1));
72
+ add(e) {
73
+ this.has(e) || (this.denseValues.push(e), this.sparse.set(e, this.denseValues.length - 1));
74
74
  }
75
- indexOf(t) {
76
- const e = this.sparse.get(t);
77
- return e !== void 0 ? e : -1;
75
+ indexOf(e) {
76
+ const t = this.sparse.get(e);
77
+ return t !== void 0 ? t : -1;
78
78
  }
79
- remove(t) {
80
- if (!this.has(t)) return;
81
- const e = this.sparse.get(t);
82
- this.sparse.delete(t);
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 !== t && (this.denseValues[e] = s, this.sparse.set(s, e)), this.denseValues.pop();
84
+ s !== e && (this.denseValues[t] = s, this.sparse.set(s, t)), this.denseValues.pop();
85
85
  }
86
- forEach(t) {
87
- for (let e of this)
88
- t(e);
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 t of this)
95
- this.remove(t);
94
+ for (let e of this)
95
+ this.remove(e);
96
96
  }
97
- has(t) {
98
- return this.sparse.has(t);
97
+ has(e) {
98
+ return this.sparse.has(e);
99
99
  }
100
100
  }
101
- class u {
101
+ class l {
102
102
  id;
103
103
  _world;
104
- constructor(t, e) {
105
- this.id = e, this._world = t;
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(t, e) {
113
- this._world.addComponent(this.id, t, e);
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(t) {
120
- this._world.removeComponent(this.id, t);
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(t) {
128
- return this._world.componentManager.hasComponent(this.id, t);
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(t) {
136
- return this._world.componentManager.getComponent(this.id, t);
135
+ get(e) {
136
+ return this._world.componentManager.getComponent(this.id, e);
137
137
  }
138
138
  }
139
- class l extends h {
140
- entityMap = new c();
139
+ class u extends h {
140
+ entityMap = new d();
141
141
  nextId = 0;
142
142
  _world;
143
- constructor(t) {
144
- super(), this._world = t;
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 t = this.nextId++;
151
- return this.entities.add(t), this.emit("create", t), t;
150
+ const e = this.nextId++;
151
+ return this.entities.add(e), this.emit("create", e), e;
152
152
  }
153
- exist(t) {
154
- return this.entities.has(t);
153
+ exist(e) {
154
+ return this.entities.has(e);
155
155
  }
156
156
  size() {
157
157
  return this.entities.size();
158
158
  }
159
- destroy(t) {
160
- return this.entities.remove(t), this.emit("destroy", t), t;
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
- constructor(t) {
167
- super(), this.world = t;
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
- clearComponentSchema(t) {
170
- this.componentSet[t] && delete this.componentSet[t];
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
- addComponent(t, e, s) {
173
- const n = this.componentSet[t];
174
- n ? n[e] = s : this.componentSet[t] = { [e]: s }, this.emit("add", { entityId: t, component: e });
178
+ getComponent(e, t) {
179
+ if (this.hasComponent(e, t))
180
+ return this.componentSet[e][t];
175
181
  }
176
- getComponent(t, e) {
177
- if (this.hasComponent(t, e))
178
- return this.componentSet[t][e];
182
+ cleanDirtyEntities() {
183
+ this.dirtyEntities.clear();
179
184
  }
180
- hasComponent(t, e) {
181
- const s = this.componentSet[t];
182
- return s ? e in s : !1;
185
+ hasComponent(e, t) {
186
+ const s = this.componentSet[e];
187
+ return s ? t in s : !1;
183
188
  }
184
- removeComponent(t, e) {
185
- if (!this.componentSet[t]) return;
186
- const s = this.componentSet[t];
187
- s && s[e] !== void 0 && (delete s[e], Object.keys(s).length === 0 && delete this.componentSet[t], this.emit("remove", { entityId: t, component: e }));
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
- constructor(t, e) {
196
- super(), this.config = t, this.world = e, this.entityInstancesCache = /* @__PURE__ */ new Map(), this.entityMap = new c(), this.on("removed", (s) => {
197
- this.entityInstancesCache.delete(s);
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(t) {
201
- const e = this.world.componentManager;
206
+ hasComponents(e) {
207
+ const t = this.world.componentManager;
202
208
  return this.config.include?.every(
203
- (s) => e.getComponent(t, s)
209
+ (s) => t.getComponent(e, s)
204
210
  ) && this.config.exclude?.every(
205
- (s) => !e.getComponent(t, 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
- getCachedEntity(t) {
218
- const e = this.entityInstancesCache.get(t);
219
- if (e)
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
- return s ? (this.entityInstancesCache.set(t, s), s) : void 0;
224
- }
227
+ s && e.push(s);
228
+ }), e;
225
229
  }
226
- get entities() {
227
- const t = [], e = this.entityMap.size() > 100;
228
- return this.entityMap.forEach((s) => {
229
- if (e) {
230
- const n = this.getCachedEntity(s);
231
- n && t.push(n);
232
- } else {
233
- const n = this.world.getEntity(s);
234
- n && t.push(n);
235
- }
236
- }), t;
237
- }
238
- include(...t) {
239
- return this.world.include(...t);
240
- }
241
- exclude(...t) {
242
- return this.world.exclude(...t);
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 i of n)
258
- o = (o << 5) - o + i.charCodeAt(0), o |= 0;
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 t = 0;
265
+ let e = 0;
274
266
  if (this.running) {
275
- const e = performance.now();
276
- t = (e - this._oldTime) / 1e3, this._oldTime = e, this.elapsed += t;
267
+ const t = performance.now();
268
+ e = (t - this._oldTime) / 1e3, this._oldTime = t, this.elapsed += e;
277
269
  }
278
- this.delta = t, this.emit("update"), this._requestId = requestAnimationFrame(this._loop.bind(this));
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 l(this), this.componentManager = new m(this), this.systemManager = new d(), this.entityManager.on("create", (t) => {
295
- const e = this.getEntity(t);
296
- e && (this.emit("entityCreated", { entity: e }), this._updateQueries());
297
- }), this.entityManager.on("destroy", (t) => {
298
- const e = this.getEntity(t);
299
- e && (this.emit("entityCreated", { entity: e }), this._updateQueries(), this.componentManager.clearComponentSchema(t));
300
- }), this.componentManager.on(
301
- "add",
302
- (t) => {
303
- const e = this.getEntity(t.entityId);
304
- e && (this.emit("componentAdded", {
305
- entity: e,
306
- component: t.component
307
- }), this._updateQueries());
308
- }
309
- ), this.componentManager.on(
310
- "remove",
311
- (t) => {
312
- const e = this.getEntity(t.entityId);
313
- e && (this.emit("componentRemoved", {
314
- entity: e,
315
- component: t.component
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(t) {
328
- const e = a.getHash(t);
329
- let n = this.queries.get(e);
330
- return n || (n = new a(t, this), this.queries.set(e, n), this._updateQueries()), n;
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.queries.forEach((t) => t.checkEntities());
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(t) {
336
- return this.entityManager.exist(t);
324
+ exist(e) {
325
+ return this.entityManager.exist(e);
337
326
  }
338
- include(...t) {
339
- return this.query({ include: t, exclude: [] });
327
+ include(...e) {
328
+ return this.query({ include: e, exclude: [] });
340
329
  }
341
- exclude(...t) {
342
- return this.query({ include: [], exclude: t });
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(t) {
348
- this.entityManager.destroy(t);
336
+ destroy(e) {
337
+ this.entityManager.destroy(e);
349
338
  }
350
- addSystem(t) {
351
- this.systemManager.addSystem(t);
339
+ addSystem(e) {
340
+ this.systemManager.addSystem(e);
352
341
  }
353
- removeSystem(t) {
354
- this.systemManager.removeSystem(t);
342
+ removeSystem(e) {
343
+ this.systemManager.removeSystem(e);
355
344
  }
356
- addComponent(t, e, s) {
357
- this.componentManager.addComponent(t, e, s);
345
+ addComponent(e, t, s) {
346
+ this.componentManager.addComponent(e, t, s);
358
347
  }
359
- getComponent(t, e) {
360
- return this.componentManager.getComponent(t, e);
348
+ getComponent(e, t) {
349
+ return this.componentManager.getComponent(e, t);
361
350
  }
362
- removeComponent(t, e) {
363
- this.componentManager.removeComponent(t, e);
351
+ removeComponent(e, t) {
352
+ this.componentManager.removeComponent(e, t);
364
353
  }
365
354
  update() {
366
- this.systemManager.systemList.forEach((t) => {
367
- t.update();
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
- l as EntityManager,
362
+ u as EntityManager,
374
363
  h as EventRegistry,
375
364
  a as Query,
376
- c as SparseSet,
377
- d as SystemManager,
365
+ d as SparseSet,
366
+ c as SystemManager,
378
367
  g as Time,
379
368
  f as World
380
369
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jael-ecs/core",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "description": "Entity Component System library with typescript",
5
5
  "keywords": [
6
6
  "ecs",