@jael-ecs/core 1.0.3 → 1.1.1

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
@@ -32,7 +32,8 @@ _A modern, performant, and user-friendly Entity Component System library written
32
32
 
33
33
  - **User Friendly API** - Clean, fluent api that's easy to learn
34
34
  - **High Performance** - Optimized SparseSet implementation for fast entity lookups
35
- - **Minimal Bundle size** - Compact bundle size without dependencies.(34kb 📦)
35
+ - **Query System** - Optimized cache query system for entity packets
36
+ - **Minimal Bundle size** - Compact bundle size without dependencies.(38kb 📦)
36
37
 
37
38
  ## Installation
38
39
 
@@ -60,13 +61,19 @@ interface Velocity {
60
61
  }
61
62
 
62
63
  // Create entities
63
- const player = world.create();
64
- world.addComponent(player, "position", { x: 0, y: 0 });
65
- world.addComponent(player, "velocity", { dx: 1, dy: 1 });
64
+ const playerId = world.create();
65
+ world.addComponent(playerId, "position", { x: 0, y: 0 });
66
+ world.addComponent(playerId, "velocity", { dx: 1, dy: 1 });
66
67
 
67
- const enemy = world.create();
68
- world.addComponent(enemy, "position", { x: 10, y: 10 });
69
- world.addComponent(enemy, "velocity", { dx: -1, dy: 0 });
68
+ const enemyId = world.create();
69
+ world.addComponent(enemyId, "position", { x: 10, y: 10 });
70
+ world.addComponent(enemyId, "velocity", { dx: -1, dy: 0 });
71
+
72
+ // Using Entity Proxy
73
+ const playerId = world.create();
74
+ const player = world.getEntity(playerId);
75
+ player.add("position", { x: 0, y: 0 });
76
+ player.add("velocity", { dx: 1, dy: 1 });
70
77
 
71
78
  // Create a system
72
79
  const movementSystem: System = {
@@ -74,10 +81,20 @@ const movementSystem: System = {
74
81
  update() {
75
82
  const query = world.include("position", "velocity");
76
83
 
77
- for (const entity of query.entities) {
84
+ // Get direct proxy access
85
+ query.entities.forEach((entity) => {
78
86
  const position = entity.get<Position>("position");
79
87
  const velocity = entity.get<Velocity>("velocity");
80
88
 
89
+ position.x += velocity.dx * (Time.delta || 0.016);
90
+ position.y += velocity.dy * (Time.delta || 0.016);
91
+ });
92
+
93
+ // Get entity ids from query
94
+ for (const entityId of query.ids) {
95
+ const position = this.world.getComponent<Position>(entityId, "position");
96
+ const velocity = this.world.getComponent<Velocity>(entityId, "velocity");
97
+
81
98
  position.x += velocity.dx * (Time.delta || 0.016);
82
99
  position.y += velocity.dy * (Time.delta || 0.016);
83
100
  }
@@ -111,29 +128,26 @@ The central hub that manages entities, components, and systems.
111
128
 
112
129
  ```typescript
113
130
  // Create a new entity
114
- const entity = world.create();
131
+ const entityId = world.create();
115
132
 
116
133
  // Destroy an entity
117
- world.destroy(entity);
134
+ world.destroy(entityId);
118
135
 
119
136
  // Check if entity exists
120
- const exists = world.exist(entity);
137
+ const exists = world.exist(entityId);
121
138
  ```
122
139
 
123
140
  #### Component Management
124
141
 
125
142
  ```typescript
126
143
  // Add component
127
- world.addComponent(entity, "position", { x: 0, y: 0 });
144
+ world.addComponent(entityId, "position", { x: 0, y: 0 });
128
145
 
129
146
  // Remove component
130
- world.removeComponent(entity, "position");
147
+ world.removeComponent(entityId, "position");
131
148
 
132
149
  // Get component
133
- const position = entity.get<Position>("position");
134
-
135
- // Check if entity has component
136
- const hasPosition = entity.has("position");
150
+ const position = world.getComponent<Position>(entityId, "position");
137
151
  ```
138
152
 
139
153
  #### System Management
@@ -149,7 +163,7 @@ world.removeSystem(yourSystem);
149
163
  #### Events
150
164
 
151
165
  ```typescript
152
- // Listen to world events
166
+ // Listen to world events ( returns entity proxy for easy reading )
153
167
  world.on("entityCreated", ({ entity }) => {
154
168
  console.log("Entity created:", entity);
155
169
  });
@@ -173,11 +187,12 @@ world.on("updated", () => {
173
187
 
174
188
  ### Entity
175
189
 
176
- Base entity class for intuitive component management
190
+ Base entity class for intuitive component management as proxy around id
177
191
 
178
192
  ```typescript
179
193
  // Create entity
180
- const entity = world.create();
194
+ const entityId = world.create();
195
+ const entity = world.getEntity(entityId); // Returns Entity class proxy
181
196
 
182
197
  // Add component
183
198
  entity.add("position", { x: 0, y: 0 });
@@ -190,6 +205,9 @@ const posExist = entity.has("position");
190
205
 
191
206
  // Get curren value of the component
192
207
  const compSchema = entity.get("position");
208
+
209
+ entity.id // Returns unique entity id from proxy
210
+
193
211
  ```
194
212
 
195
213
  ### System
@@ -199,7 +217,7 @@ Systems contain the game logic that processes entities with specific components.
199
217
  ```typescript
200
218
  interface System {
201
219
  priority: number; // Execution order (lower = earlier)
202
- init?(): void // Runs when added to the world
220
+ init?(): void; // Runs when added to the world
203
221
  exit?(): void; // Cleanup when removed
204
222
  update(): void; // Main update logic
205
223
  }
@@ -218,13 +236,13 @@ const renderSystem: System = {
218
236
  update(dt) {
219
237
  const renderableQuery = world.include("position", "sprite");
220
238
 
221
- for (const entity of renderableQuery.entities) {
239
+ renderableQuery.entities.forEach((entity) => {
222
240
  const position = entity.get<Position>("position");
223
241
  const sprite = entity.get<Sprite>("sprite");
224
242
 
225
243
  // Render entity
226
244
  drawSprite(sprite, position.x, position.y);
227
- }
245
+ })
228
246
  },
229
247
 
230
248
  exit() {
@@ -266,29 +284,36 @@ const complexQuery2 = world.include("position", "health").exclude("static");
266
284
  #### Accessing Results
267
285
 
268
286
  ```typescript
269
- // Iterate through entities
270
- for (const entity of query.entities) {
271
- // Process entity
287
+ // Iterate through entities as proxy
288
+ query.entities.forEach((entity) => {
289
+ // Process Entity proxy
290
+ })
291
+
292
+ // Iterate through entities ids
293
+ for(const entityId of query.ids){
294
+ // Process Entity id
272
295
  }
273
296
 
297
+
298
+
274
299
  // Get the first value of the query
275
- const first = query.entities.first();
300
+ const first = query.entities[0];
301
+ const firstId = query.ids.first()
276
302
 
277
303
  // Check query size
278
- const count = query.entities.size();
304
+ const count = query.size();
279
305
 
280
306
  // Check if query has any entities
281
- const isEmpty = query.entities.size() === 0;
307
+ const isEmpty = query.size() === 0;
282
308
 
283
309
  // Subscribe to query events
284
- query.on('added', (entity: Entity) => {
285
- // Entity added
286
- })
287
-
288
- query.on('removed', (entity: Entity) => {
310
+ query.on("added", (entityId: number) => {
289
311
  // Entity added
290
- })
312
+ });
291
313
 
314
+ query.on("removed", (entityId: number) => {
315
+ // Entity removed
316
+ });
292
317
  ```
293
318
 
294
319
  ### SparseSet
@@ -353,8 +378,8 @@ interface WorldEvents {
353
378
  }
354
379
 
355
380
  interface QueryEvents {
356
- added: Entity; //Entity added to query entities
357
- removed: Entity; //Entity removed to query entities
381
+ added: number; //EntityId added to query entities
382
+ removed: number; //EntityId removed to query entities
358
383
  }
359
384
 
360
385
  // Listen to events
@@ -369,7 +394,7 @@ world.emit("entityCreated", { entity: someEntity });
369
394
  world.off("entityCreated", handler);
370
395
 
371
396
  // Romeve all listeners of a type
372
- world.clearEvent('type')
397
+ world.clearEvent("type");
373
398
 
374
399
  // Remove all listeners
375
400
  world.clearAllEvents();
@@ -425,8 +450,8 @@ class MovementSystem implements System {
425
450
  }
426
451
 
427
452
  update() {
428
- for (const entity of this.movementQuery.entities) {
429
- // Process movement
453
+ for(const entityId of this.movementQuery.ids){
454
+ // Process movement using entityId
430
455
  }
431
456
  }
432
457
  }
@@ -438,13 +463,13 @@ type MovementSystem = { movementQuery: Query | null } & System;
438
463
  const movementSystem: MovementSystem = {
439
464
  movementQuery: null;
440
465
  priority: 1;
441
-
466
+
442
467
  init(){
443
468
  this.movementQuery = world.include('position', 'velocity');
444
469
  }
445
470
 
446
471
  update(){
447
- for (const entity of this.movementQuery.entities) {
472
+ for (const entityId of this.movementQuery.ids) {
448
473
  // Process movement
449
474
  }
450
475
  }
@@ -461,7 +486,7 @@ update() {
461
486
 
462
487
  ```typescript
463
488
  // Remember to clean up when removing entities
464
- world.destroy(entity); // Automatically removes all components
489
+ world.destroy(entityId); // Automatically removes all components
465
490
 
466
491
  // Clean up systems if they have resources
467
492
  system.exit?.(); // Called automatically when removed from world
@@ -1,24 +1,25 @@
1
- import { Entity } from './EntityManager';
2
1
  import { default as EventRegistry } from './EventRegistry';
3
2
  import { default as World } from './World';
4
3
  export type ComponentSchema = Record<string, any>;
5
4
  export interface ComponentManagerEvents {
6
5
  add: {
7
- entity: Entity;
6
+ entityId: number;
8
7
  component: keyof ComponentSchema;
9
8
  };
10
9
  remove: {
11
- entity: Entity;
10
+ entityId: number;
12
11
  component: keyof ComponentSchema;
13
12
  };
14
13
  }
15
14
  export declare class ComponentManager extends EventRegistry<ComponentManagerEvents> {
16
- componentSet: Map<number, ComponentSchema>;
15
+ componentSet: {
16
+ [k: number]: ComponentSchema;
17
+ };
17
18
  world: World;
18
19
  constructor(world: World);
19
- clearComponentSchema(entity: Entity): void;
20
- addComponent<K extends keyof ComponentSchema>(entity: Entity, key: K, value: ComponentSchema[K]): void;
21
- getComponent<K extends keyof ComponentSchema>(entity: Entity, key: K): ComponentSchema[K] | undefined;
22
- hasComponent<K extends keyof ComponentSchema>(entity: Entity, key: K): boolean;
23
- removeComponent<K extends keyof ComponentSchema>(entity: Entity, key: K): void;
20
+ clearComponentSchema(entityId: number): void;
21
+ addComponent<K extends keyof ComponentSchema>(entityId: number, key: K, value: ComponentSchema[K]): void;
22
+ getComponent<K extends keyof ComponentSchema>(entityId: number, key: K): ComponentSchema[K] | undefined;
23
+ hasComponent<K extends keyof ComponentSchema>(entityId: number, key: K): boolean;
24
+ removeComponent<K extends keyof ComponentSchema>(entityId: number, key: K): void;
24
25
  }
@@ -1,7 +1,7 @@
1
1
  import { default as EventRegistry } from './EventRegistry';
2
2
  import { SparseSet } from './SparseSet';
3
3
  import { default as World } from './World';
4
- declare class Entity {
4
+ export declare class Entity {
5
5
  readonly id: number;
6
6
  private _world;
7
7
  constructor(world: World, id: number);
@@ -30,18 +30,17 @@ declare class Entity {
30
30
  get<T = any>(compType: string): T;
31
31
  }
32
32
  export declare class EntityManager extends EventRegistry<EntityManagerEvents> {
33
- entityMap: SparseSet<Entity>;
33
+ entityMap: SparseSet<number>;
34
34
  nextId: number;
35
35
  _world: World;
36
36
  constructor(world: World);
37
- get entities(): SparseSet<Entity>;
38
- create(): Entity;
39
- exist(entity: Entity): boolean;
37
+ get entities(): SparseSet<number>;
38
+ create(): number;
39
+ exist(id: number): boolean;
40
40
  size(): number;
41
- destroy(entity: Entity): Entity;
41
+ destroy(id: number): number;
42
42
  }
43
43
  export interface EntityManagerEvents {
44
- create: Entity;
45
- destroy: Entity;
44
+ create: number;
45
+ destroy: number;
46
46
  }
47
- export { type Entity };
@@ -1,11 +1,14 @@
1
- export type Event<E> = (e: E[keyof E]) => void;
2
- export default class EventRegistry<E extends Record<string, any> = {}> {
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> = {}> {
3
6
  private _listeners;
4
- on(type: keyof E, callback: Event<E>): void;
5
- off(type: keyof E, callback: Event<E>): void;
6
- once(type: keyof E, callback: Event<E>): void;
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;
7
10
  clearEvent(type: keyof E): void;
8
11
  clearAllEvents(): void;
9
- contains(type: keyof E, callback: Event<E>): boolean;
12
+ contains(type: keyof E, callback: EventCallback<E>): boolean;
10
13
  emit(type: keyof E, data: E[keyof E]): void;
11
14
  }
package/dist/Query.d.ts CHANGED
@@ -7,16 +7,21 @@ export interface QueryConfig {
7
7
  exclude: string[];
8
8
  }
9
9
  export interface QueryEvents {
10
- added: Entity;
11
- removed: Entity;
10
+ added: number;
11
+ removed: number;
12
12
  }
13
13
  export declare class Query extends EventRegistry<QueryEvents> {
14
- config: QueryConfig;
15
- entityMap: SparseSet<Entity>;
16
- world: World;
14
+ private config;
15
+ private entityMap;
16
+ private entityInstancesCache;
17
+ private world;
17
18
  constructor(config: QueryConfig, world: World);
18
- hasComponents(entity: Entity): boolean;
19
- get entities(): SparseSet<Entity>;
19
+ hasComponents(entityId: number): boolean;
20
+ size(): number;
21
+ get hash(): number;
22
+ get ids(): SparseSet<number>;
23
+ private getCachedEntity;
24
+ get entities(): Entity[];
20
25
  include(...comps: string[]): Query;
21
26
  exclude(...comps: string[]): Query;
22
27
  private _checkExistingEntities;
package/dist/World.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { ComponentManager, ComponentSchema } from './ComponentManager';
2
- import { EntityManager, Entity } from './EntityManager';
2
+ import { Entity, EntityManager } from './EntityManager';
3
3
  import { default as EventRegistry } from './EventRegistry';
4
4
  import { Query, QueryConfig } from './Query';
5
5
  import { SparseSet } from './SparseSet';
@@ -27,17 +27,19 @@ export default class World extends EventRegistry<WorldEvents> {
27
27
  systemManager: SystemManager;
28
28
  queries: Map<number, Query>;
29
29
  constructor();
30
- get entities(): SparseSet<Entity>;
30
+ getEntity(id: number): Entity | undefined;
31
+ get entityIds(): SparseSet<number>;
31
32
  query(config: QueryConfig): Query;
32
33
  private _updateQueries;
33
- exist(entity: Entity): boolean;
34
+ exist(entityId: number): boolean;
34
35
  include(...comps: string[]): Query;
35
36
  exclude(...comps: string[]): Query;
36
- create(): Entity;
37
- destroy(entity: Entity): void;
37
+ create(): number;
38
+ destroy(entityId: number): void;
38
39
  addSystem(sys: System): void;
39
40
  removeSystem(sys: System): void;
40
- addComponent(entity: Entity, compKey: string, compValue: any): void;
41
- removeComponent(entity: Entity, compKey: string): void;
41
+ addComponent(entityId: number, compKey: string, compValue: any): void;
42
+ getComponent<T>(entityId: number, compKey: string): T;
43
+ removeComponent(entityId: number, compKey: string): void;
42
44
  update(): void;
43
45
  }
@@ -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=(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){return this.sparse.get(e)?this.sparse.get(e):-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,e,t)}remove(e){this._world.removeComponent(this,e)}has(e){return this._world.componentManager.hasComponent(this,e)}get(e){return this._world.componentManager.getComponent(this,e)}}class u extends o{entityMap=new d;nextId=0;_world;constructor(e){super(),this._world=e}get entities(){return this.entityMap}create(){const e=this.nextId++,t=new m(this._world,e);return this.entities.add(t),this.emit("create",t),t}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=new Map;world;constructor(e){super(),this.world=e}clearComponentSchema(e){this.componentSet.get(e.id)&&this.componentSet.delete(e.id)}addComponent(e,t,s){const i=this.componentSet.get(e.id);i?i[t]=s:this.componentSet.set(e.id,{[t]:s}),this.emit("add",{entity:e,component:t})}getComponent(e,t){return this.hasComponent(e,t)?this.componentSet.get(e.id)[t]:void 0}hasComponent(e,t){const s=this.componentSet.get(e.id);return s?t in s:!1}removeComponent(e,t){if(!this.componentSet.get(e.id))return;const s=this.componentSet.get(e.id);s&&s[t]!==void 0&&(delete s[t],Object.keys(s).length===0&&this.componentSet.delete(e.id),this.emit("remove",{entity:e,component:t}))}}class a extends o{config;entityMap;world;constructor(e,t){super(),this.config=e,this.world=t,this.entityMap=new d}hasComponents(e){return this.config.include?.every(t=>e.has(t))&&this.config.exclude?.every(t=>!e.has(t))}get entities(){return this.entityMap}include(...e){return this.world.include(...e)}exclude(...e){return this.world.exclude(...e)}_checkExistingEntities(){for(let e of this.entities)this.world.exist(e)||(this.emit("removed",e),this.entityMap.remove(e))}checkEntities(){for(let e of this.world.entities)e&&this.hasComponents(e)&&(this.entityMap.add(e),this.emit("added",e));this._checkExistingEntities()}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 h=0;for(const n of i)h=(h<<5)-h+n.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 c,this.entityManager.on("create",e=>{e&&(this.emit("entityCreated",{entity:e}),this._updateQueries())}),this.entityManager.on("destroy",e=>{e&&(this.emit("entityDestroyed",{entity:e}),this._updateQueries(),this.componentManager.clearComponentSchema(e))}),this.componentManager.on("add",e=>{this.emit("componentAdded",{entity:e.entity,component:e.component}),this._updateQueries()}),this.componentManager.on("remove",e=>{this.emit("componentRemoved",{entity:e.entity,component:e.component}),this._updateQueries()}),this.queries=new Map}get entities(){return this.entityManager.entities}query(e){const t=a.getHash(e);let i=this.queries.get(t);return i||(i=new a(e,this),this.queries.set(t,i),this._updateQueries()),i}_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)}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=d;exports.SystemManager=c;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=(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 u{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 a{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 m extends o{componentSet={};world;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 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 c extends o{config;entityMap;entityInstancesCache;world;constructor(e,t){super(),this.config=e,this.world=t,this.entityInstancesCache=new Map,this.entityMap=new d,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 c.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 l(this),this.componentManager=new m(this),this.systemManager=new u,this.entityManager.on("create",e=>{this.emit("entityCreated",{entity:new a(this,e)}),this._updateQueries()}),this.entityManager.on("destroy",e=>{this.emit("entityDestroyed",{entity:new a(this,e)}),this._updateQueries(),this.componentManager.clearComponentSchema(e)}),this.componentManager.on("add",({entityId:e,component:t})=>{this.emit("componentAdded",{entity:new a(this,e),component:t}),this._updateQueries()}),this.componentManager.on("remove",({entityId:e,component:t})=>{this.getEntity(e)&&this.emit("componentRemoved",{entity:new a(this,e),component:t}),this._updateQueries()}),this.queries=new Map}getEntity(e){return this.exist(e)?new a(this,e):void 0}get entityIds(){return this.entityManager.entities}query(e){const t=c.getHash(e);let n=this.queries.get(t);return n||(n=new c(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=m;exports.EntityManager=l;exports.EventRegistry=o;exports.Query=c;exports.SparseSet=d;exports.SystemManager=u;exports.Time=g;exports.World=f;
@@ -1,4 +1,4 @@
1
- class h {
1
+ class a {
2
2
  _listeners = /* @__PURE__ */ new Map();
3
3
  on(e, t) {
4
4
  if (this.contains(e, t))
@@ -13,8 +13,8 @@ class h {
13
13
  s && s.delete(t);
14
14
  }
15
15
  once(e, t) {
16
- const s = ((i) => {
17
- t(i), this.off(e, s);
16
+ const s = ((n) => {
17
+ t(n), this.off(e, s);
18
18
  });
19
19
  this.on(e, s);
20
20
  }
@@ -33,7 +33,7 @@ class h {
33
33
  });
34
34
  }
35
35
  }
36
- class c {
36
+ class u {
37
37
  systemList = [];
38
38
  addSystem(e) {
39
39
  this.systemList.push(e), e.init?.(), this.reorder();
@@ -73,7 +73,8 @@ class d {
73
73
  this.has(e) || (this.denseValues.push(e), this.sparse.set(e, this.denseValues.length - 1));
74
74
  }
75
75
  indexOf(e) {
76
- return this.sparse.get(e) ? this.sparse.get(e) : -1;
76
+ const t = this.sparse.get(e);
77
+ return t !== void 0 ? t : -1;
77
78
  }
78
79
  remove(e) {
79
80
  if (!this.has(e)) return;
@@ -97,7 +98,7 @@ class d {
97
98
  return this.sparse.has(e);
98
99
  }
99
100
  }
100
- class u {
101
+ class h {
101
102
  id;
102
103
  _world;
103
104
  constructor(e, t) {
@@ -109,14 +110,14 @@ class u {
109
110
  * @param compValue Component value
110
111
  */
111
112
  add(e, t) {
112
- this._world.addComponent(this, e, t);
113
+ this._world.addComponent(this.id, e, t);
113
114
  }
114
115
  /**
115
116
  * Remove component of current entity.
116
117
  * @param compType Component name
117
118
  */
118
119
  remove(e) {
119
- this._world.removeComponent(this, e);
120
+ this._world.removeComponent(this.id, e);
120
121
  }
121
122
  /**
122
123
  * Check if current entity has a component.
@@ -124,7 +125,7 @@ class u {
124
125
  * @returns boolean
125
126
  */
126
127
  has(e) {
127
- return this._world.componentManager.hasComponent(this, e);
128
+ return this._world.componentManager.hasComponent(this.id, e);
128
129
  }
129
130
  /**
130
131
  * Get passed component schema of current entity.
@@ -132,10 +133,10 @@ class u {
132
133
  * @returns Return component schema with T(any as default) as type
133
134
  */
134
135
  get(e) {
135
- return this._world.componentManager.getComponent(this, e);
136
+ return this._world.componentManager.getComponent(this.id, e);
136
137
  }
137
138
  }
138
- class l extends h {
139
+ class l extends a {
139
140
  entityMap = new d();
140
141
  nextId = 0;
141
142
  _world;
@@ -146,8 +147,8 @@ class l extends h {
146
147
  return this.entityMap;
147
148
  }
148
149
  create() {
149
- const e = this.nextId++, t = new u(this._world, e);
150
- 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;
151
152
  }
152
153
  exist(e) {
153
154
  return this.entities.has(e);
@@ -159,47 +160,82 @@ class l extends h {
159
160
  return this.entities.remove(e), this.emit("destroy", e), e;
160
161
  }
161
162
  }
162
- class m extends h {
163
- componentSet = /* @__PURE__ */ new Map();
163
+ class m extends a {
164
+ componentSet = {};
164
165
  world;
165
166
  constructor(e) {
166
167
  super(), this.world = e;
167
168
  }
168
169
  clearComponentSchema(e) {
169
- this.componentSet.get(e.id) && this.componentSet.delete(e.id);
170
+ this.componentSet[e] && delete this.componentSet[e];
170
171
  }
171
172
  addComponent(e, t, s) {
172
- const i = this.componentSet.get(
173
- e.id
174
- );
175
- i ? i[t] = s : this.componentSet.set(e.id, { [t]: s }), this.emit("add", { entity: e, component: t });
173
+ if (!this.world.exist(e)) return;
174
+ const n = this.componentSet[e];
175
+ n ? n[t] = s : this.componentSet[e] = { [t]: s }, this.emit("add", { entityId: e, component: t });
176
176
  }
177
177
  getComponent(e, t) {
178
- return this.hasComponent(e, t) ? this.componentSet.get(e.id)[t] : void 0;
178
+ if (this.hasComponent(e, t))
179
+ return this.componentSet[e][t];
179
180
  }
180
181
  hasComponent(e, t) {
181
- const s = this.componentSet.get(e.id);
182
+ const s = this.componentSet[e];
182
183
  return s ? t in s : !1;
183
184
  }
184
185
  removeComponent(e, t) {
185
- if (!this.componentSet.get(e.id)) return;
186
- const s = this.componentSet.get(e.id);
187
- s && s[t] !== void 0 && (delete s[t], Object.keys(s).length === 0 && this.componentSet.delete(e.id), this.emit("remove", { entity: e, component: t }));
186
+ if (!this.componentSet[e]) return;
187
+ const s = this.componentSet[e];
188
+ s && s[t] !== void 0 && (delete s[t], Object.keys(s).length === 0 && delete this.componentSet[e], this.emit("remove", { entityId: e, component: t }));
188
189
  }
189
190
  }
190
- class a extends h {
191
+ class c extends a {
191
192
  config;
192
193
  entityMap;
194
+ entityInstancesCache;
193
195
  world;
194
196
  constructor(e, t) {
195
- super(), this.config = e, this.world = t, this.entityMap = new d();
197
+ super(), this.config = e, this.world = t, this.entityInstancesCache = /* @__PURE__ */ new Map(), this.entityMap = new d(), this.on("removed", (s) => {
198
+ this.entityInstancesCache.delete(s);
199
+ });
196
200
  }
197
201
  hasComponents(e) {
198
- return this.config.include?.every((t) => e.has(t)) && this.config.exclude?.every((t) => !e.has(t));
202
+ const t = this.world.componentManager;
203
+ return this.config.include?.every(
204
+ (s) => t.getComponent(e, s)
205
+ ) && this.config.exclude?.every(
206
+ (s) => !t.getComponent(e, s)
207
+ );
199
208
  }
200
- get entities() {
209
+ size() {
210
+ return this.ids.size();
211
+ }
212
+ get hash() {
213
+ return c.getHash(this.config);
214
+ }
215
+ get ids() {
201
216
  return this.entityMap;
202
217
  }
218
+ getCachedEntity(e) {
219
+ const t = this.entityInstancesCache.get(e);
220
+ if (t)
221
+ return t;
222
+ {
223
+ const s = this.world.getEntity(e);
224
+ return s ? (this.entityInstancesCache.set(e, s), s) : void 0;
225
+ }
226
+ }
227
+ get entities() {
228
+ const e = [], t = this.entityMap.size() > 100;
229
+ return this.entityMap.forEach((s) => {
230
+ if (t) {
231
+ const n = this.getCachedEntity(s);
232
+ n && e.push(n);
233
+ } else {
234
+ const n = this.world.getEntity(s);
235
+ n && e.push(n);
236
+ }
237
+ }), e;
238
+ }
203
239
  include(...e) {
204
240
  return this.world.include(...e);
205
241
  }
@@ -207,23 +243,24 @@ class a extends h {
207
243
  return this.world.exclude(...e);
208
244
  }
209
245
  _checkExistingEntities() {
210
- for (let e of this.entities)
246
+ for (let e of this.ids)
211
247
  this.world.exist(e) || (this.emit("removed", e), this.entityMap.remove(e));
212
248
  }
213
249
  checkEntities() {
214
- for (let e of this.world.entities)
215
- e && this.hasComponents(e) && (this.entityMap.add(e), this.emit("added", e));
250
+ this.entityInstancesCache.clear();
251
+ for (let e of this.world.entityIds)
252
+ this.hasComponents(e) && (this.entityMap.add(e), this.emit("added", e));
216
253
  this._checkExistingEntities();
217
254
  }
218
255
  static getHash(e) {
219
- 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
+ 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;
220
257
  let o = 0;
221
- for (const n of i)
222
- o = (o << 5) - o + n.charCodeAt(0), o |= 0;
258
+ for (const i of n)
259
+ o = (o << 5) - o + i.charCodeAt(0), o |= 0;
223
260
  return o;
224
261
  }
225
262
  }
226
- class p extends h {
263
+ class p extends a {
227
264
  _startTime = 0;
228
265
  _oldTime = 0;
229
266
  _requestId = 0;
@@ -249,41 +286,42 @@ class p extends h {
249
286
  }
250
287
  }
251
288
  let g = new p();
252
- class f extends h {
289
+ class f extends a {
253
290
  entityManager;
254
291
  componentManager;
255
292
  systemManager;
256
293
  queries;
257
294
  constructor() {
258
- super(), this.entityManager = new l(this), this.componentManager = new m(this), this.systemManager = new c(), this.entityManager.on("create", (e) => {
259
- e && (this.emit("entityCreated", { entity: e }), this._updateQueries());
295
+ super(), this.entityManager = new l(this), this.componentManager = new m(this), this.systemManager = new u(), this.entityManager.on("create", (e) => {
296
+ this.emit("entityCreated", {
297
+ entity: new h(this, e)
298
+ }), this._updateQueries();
260
299
  }), this.entityManager.on("destroy", (e) => {
261
- e && (this.emit("entityDestroyed", { entity: e }), this._updateQueries(), this.componentManager.clearComponentSchema(e));
262
- }), this.componentManager.on(
263
- "add",
264
- (e) => {
265
- this.emit("componentAdded", {
266
- entity: e.entity,
267
- component: e.component
268
- }), this._updateQueries();
269
- }
270
- ), this.componentManager.on(
271
- "remove",
272
- (e) => {
273
- this.emit("componentRemoved", {
274
- entity: e.entity,
275
- component: e.component
276
- }), this._updateQueries();
277
- }
278
- ), this.queries = /* @__PURE__ */ new Map();
279
- }
280
- get entities() {
300
+ this.emit("entityDestroyed", {
301
+ entity: new h(this, e)
302
+ }), this._updateQueries(), this.componentManager.clearComponentSchema(e);
303
+ }), this.componentManager.on("add", ({ entityId: e, component: t }) => {
304
+ this.emit("componentAdded", {
305
+ entity: new h(this, e),
306
+ component: t
307
+ }), this._updateQueries();
308
+ }), this.componentManager.on("remove", ({ entityId: e, component: t }) => {
309
+ this.getEntity(e) && this.emit("componentRemoved", {
310
+ entity: new h(this, e),
311
+ component: t
312
+ }), this._updateQueries();
313
+ }), this.queries = /* @__PURE__ */ new Map();
314
+ }
315
+ getEntity(e) {
316
+ return this.exist(e) ? new h(this, e) : void 0;
317
+ }
318
+ get entityIds() {
281
319
  return this.entityManager.entities;
282
320
  }
283
321
  query(e) {
284
- const t = a.getHash(e);
285
- let i = this.queries.get(t);
286
- return i || (i = new a(e, this), this.queries.set(t, i), this._updateQueries()), i;
322
+ const t = c.getHash(e);
323
+ let n = this.queries.get(t);
324
+ return n || (n = new c(e, this), this.queries.set(t, n), this._updateQueries()), n;
287
325
  }
288
326
  _updateQueries() {
289
327
  this.queries.forEach((e) => e.checkEntities());
@@ -312,6 +350,9 @@ class f extends h {
312
350
  addComponent(e, t, s) {
313
351
  this.componentManager.addComponent(e, t, s);
314
352
  }
353
+ getComponent(e, t) {
354
+ return this.componentManager.getComponent(e, t);
355
+ }
315
356
  removeComponent(e, t) {
316
357
  this.componentManager.removeComponent(e, t);
317
358
  }
@@ -324,10 +365,10 @@ class f extends h {
324
365
  export {
325
366
  m as ComponentManager,
326
367
  l as EntityManager,
327
- h as EventRegistry,
328
- a as Query,
368
+ a as EventRegistry,
369
+ c as Query,
329
370
  d as SparseSet,
330
- c as SystemManager,
371
+ u as SystemManager,
331
372
  g as Time,
332
373
  f as World
333
374
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jael-ecs/core",
3
- "version": "1.0.3",
3
+ "version": "1.1.1",
4
4
  "description": "Entity Component System library with typescript",
5
5
  "keywords": [
6
6
  "ecs",