@ste_tisci/ecs 0.1.7 → 0.1.8
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 +33 -0
- package/dist/index.cjs +66 -22
- package/dist/index.d.cts +43 -0
- package/dist/index.d.ts +43 -0
- package/dist/index.js +66 -22
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -75,6 +75,39 @@ function gameLoop() {
|
|
|
75
75
|
gameLoop();
|
|
76
76
|
```
|
|
77
77
|
|
|
78
|
+
## Hot systems: `queryIds`
|
|
79
|
+
|
|
80
|
+
`query` yields one result object plus one `{ id }` object per component per entity, which is
|
|
81
|
+
convenient but allocates on every iteration. For systems that run every frame over many
|
|
82
|
+
entities, `queryIds` returns the matching entity IDs instead, and `indices` translates an
|
|
83
|
+
entity ID into its slot in the SoA arrays:
|
|
84
|
+
|
|
85
|
+
```typescript
|
|
86
|
+
function movementSystem(World) {
|
|
87
|
+
const { Position, Velocity } = World.components;
|
|
88
|
+
const pos = World.indices.Position;
|
|
89
|
+
const vel = World.indices.Velocity;
|
|
90
|
+
|
|
91
|
+
for (const eid of World.queryIds('Position', 'Velocity')) {
|
|
92
|
+
Position.x[pos[eid]] += Velocity.x[vel[eid]];
|
|
93
|
+
Position.y[pos[eid]] += Velocity.y[vel[eid]];
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Both APIs return the same entities; `queryIds` is roughly 3x faster on a packed query and 5x
|
|
99
|
+
on a fragmented one, at the cost of resolving indices yourself.
|
|
100
|
+
|
|
101
|
+
## Iteration and mutation
|
|
102
|
+
|
|
103
|
+
`query` iterates a snapshot taken when the generator starts, so calling `removeComponent` or
|
|
104
|
+
`destroyEntity` from inside the loop cannot skip an entity. Entities created mid-iteration are
|
|
105
|
+
not visited by the query in flight; the next call picks them up.
|
|
106
|
+
|
|
107
|
+
`queryIds` builds its whole result before returning, so the same guarantee holds. Note that
|
|
108
|
+
destroying an entity invalidates its entry in `indices`, so if a loop destroys entities it
|
|
109
|
+
must not read their storage index afterwards.
|
|
110
|
+
|
|
78
111
|
## Error Handling
|
|
79
112
|
|
|
80
113
|
Every operation validates its inputs and throws a descriptive `Error` instead of failing silently or corrupting state:
|
package/dist/index.cjs
CHANGED
|
@@ -60,13 +60,16 @@ function SparseSet() {
|
|
|
60
60
|
function getSize() {
|
|
61
61
|
return size;
|
|
62
62
|
}
|
|
63
|
-
|
|
63
|
+
function getSparse() {
|
|
64
|
+
return sparse;
|
|
65
|
+
}
|
|
66
|
+
return { has, add, remove, getIndex, getDense, getSize, getSparse };
|
|
64
67
|
}
|
|
65
68
|
|
|
66
69
|
// src/ComponentStore.ts
|
|
67
70
|
var INITIAL_CAPACITY = 8;
|
|
68
71
|
function grow(buffer, requiredIndex) {
|
|
69
|
-
let capacity = buffer.length;
|
|
72
|
+
let capacity = buffer.length || INITIAL_CAPACITY;
|
|
70
73
|
while (capacity <= requiredIndex) capacity *= 2;
|
|
71
74
|
const grown = new Float32Array(capacity);
|
|
72
75
|
grown.set(buffer);
|
|
@@ -74,8 +77,8 @@ function grow(buffer, requiredIndex) {
|
|
|
74
77
|
}
|
|
75
78
|
function ComponentStore() {
|
|
76
79
|
const componentSet = SparseSet();
|
|
77
|
-
const componentData =
|
|
78
|
-
const isNumeric =
|
|
80
|
+
const componentData = /* @__PURE__ */ Object.create(null);
|
|
81
|
+
const isNumeric = /* @__PURE__ */ Object.create(null);
|
|
79
82
|
function add(eid, data) {
|
|
80
83
|
if (componentSet.has(eid)) throw new Error(`Entity ${eid} already has this component`);
|
|
81
84
|
const ID = componentSet.getSize();
|
|
@@ -120,7 +123,10 @@ function ComponentStore() {
|
|
|
120
123
|
function getSize() {
|
|
121
124
|
return componentSet.getSize();
|
|
122
125
|
}
|
|
123
|
-
|
|
126
|
+
function getSparse() {
|
|
127
|
+
return componentSet.getSparse();
|
|
128
|
+
}
|
|
129
|
+
return { add, remove, getIndex, getDense, getData, getSize, getSparse };
|
|
124
130
|
}
|
|
125
131
|
|
|
126
132
|
// src/EntityManager.ts
|
|
@@ -164,7 +170,10 @@ function EntityManager(registry) {
|
|
|
164
170
|
if (!exists(eid)) throw new Error(`Entity ${eid} does not exist`);
|
|
165
171
|
return bitMasks[eid];
|
|
166
172
|
}
|
|
167
|
-
|
|
173
|
+
function tryGetMask(eid) {
|
|
174
|
+
return eid >= 0 && eid < nextID ? bitMasks[eid] : void 0;
|
|
175
|
+
}
|
|
176
|
+
return { exists, hasComponent, create, remove, addComponent, removeComponent, getMask, tryGetMask };
|
|
168
177
|
}
|
|
169
178
|
|
|
170
179
|
// src/utils/ComponentRegistry.ts
|
|
@@ -196,14 +205,17 @@ function createComponentRegistry() {
|
|
|
196
205
|
function ECS() {
|
|
197
206
|
const ComponentRegistry = createComponentRegistry();
|
|
198
207
|
const Entities = EntityManager(ComponentRegistry);
|
|
199
|
-
const internalStores =
|
|
200
|
-
const components =
|
|
208
|
+
const internalStores = /* @__PURE__ */ Object.create(null);
|
|
209
|
+
const components = /* @__PURE__ */ Object.create(null);
|
|
210
|
+
const indices = /* @__PURE__ */ Object.create(null);
|
|
201
211
|
function defineComponents(...names) {
|
|
202
212
|
for (const name of names) {
|
|
203
|
-
if (
|
|
213
|
+
if (Object.hasOwn(internalStores, name))
|
|
214
|
+
throw new Error(`Component ${String(name)} is already defined`);
|
|
204
215
|
ComponentRegistry.register(name);
|
|
205
216
|
internalStores[name] = ComponentStore();
|
|
206
217
|
components[name] = internalStores[name].getData();
|
|
218
|
+
indices[name] = internalStores[name].getSparse();
|
|
207
219
|
}
|
|
208
220
|
}
|
|
209
221
|
function createEntity() {
|
|
@@ -231,33 +243,65 @@ function ECS() {
|
|
|
231
243
|
Entities.removeComponent(eid, name);
|
|
232
244
|
internalStores[name].remove(eid);
|
|
233
245
|
}
|
|
234
|
-
function
|
|
235
|
-
if (componentName.length === 0) return;
|
|
246
|
+
function planQuery(componentName) {
|
|
236
247
|
let targetMask = 0n;
|
|
237
248
|
for (const name of componentName) {
|
|
238
249
|
targetMask |= 1n << BigInt(ComponentRegistry.getID(name));
|
|
239
250
|
}
|
|
240
|
-
let
|
|
241
|
-
let
|
|
251
|
+
let pivot = internalStores[componentName[0]];
|
|
252
|
+
let pivotSize = pivot.getSize();
|
|
242
253
|
for (let i = 1; i < componentName.length; i++) {
|
|
243
254
|
const store = internalStores[componentName[i]];
|
|
244
255
|
const size = store.getSize();
|
|
245
|
-
if (size <
|
|
246
|
-
|
|
247
|
-
|
|
256
|
+
if (size < pivotSize) {
|
|
257
|
+
pivot = store;
|
|
258
|
+
pivotSize = size;
|
|
248
259
|
}
|
|
249
260
|
}
|
|
250
|
-
|
|
251
|
-
|
|
261
|
+
return { targetMask, pivot };
|
|
262
|
+
}
|
|
263
|
+
function* query(...componentName) {
|
|
264
|
+
if (componentName.length === 0) return;
|
|
265
|
+
const { targetMask, pivot } = planQuery(componentName);
|
|
266
|
+
const count = componentName.length;
|
|
267
|
+
const sparses = new Array(count);
|
|
268
|
+
for (let i = 0; i < count; i++) sparses[i] = internalStores[componentName[i]].getSparse();
|
|
269
|
+
const entities = [...pivot.getDense()];
|
|
270
|
+
for (let i = 0; i < entities.length; i++) {
|
|
271
|
+
const eid = entities[i];
|
|
272
|
+
const mask = Entities.tryGetMask(eid);
|
|
273
|
+
if (mask === void 0 || (mask & targetMask) !== targetMask) continue;
|
|
252
274
|
const result = {};
|
|
253
|
-
for (
|
|
254
|
-
|
|
255
|
-
result[name] = { id };
|
|
275
|
+
for (let c = 0; c < count; c++) {
|
|
276
|
+
result[componentName[c]] = { id: sparses[c][eid] };
|
|
256
277
|
}
|
|
257
278
|
yield result;
|
|
258
279
|
}
|
|
259
280
|
}
|
|
260
|
-
|
|
281
|
+
function queryIds(...componentName) {
|
|
282
|
+
const matches = [];
|
|
283
|
+
if (componentName.length === 0) return matches;
|
|
284
|
+
const { targetMask, pivot } = planQuery(componentName);
|
|
285
|
+
const dense = pivot.getDense();
|
|
286
|
+
for (let i = 0; i < dense.length; i++) {
|
|
287
|
+
const eid = dense[i];
|
|
288
|
+
const mask = Entities.tryGetMask(eid);
|
|
289
|
+
if (mask === void 0 || (mask & targetMask) !== targetMask) continue;
|
|
290
|
+
matches.push(eid);
|
|
291
|
+
}
|
|
292
|
+
return matches;
|
|
293
|
+
}
|
|
294
|
+
return {
|
|
295
|
+
defineComponents,
|
|
296
|
+
createEntity,
|
|
297
|
+
destroyEntity,
|
|
298
|
+
addComponent,
|
|
299
|
+
removeComponent,
|
|
300
|
+
query,
|
|
301
|
+
queryIds,
|
|
302
|
+
components,
|
|
303
|
+
indices
|
|
304
|
+
};
|
|
261
305
|
}
|
|
262
306
|
// Annotate the CommonJS export names for ESM import in node:
|
|
263
307
|
0 && (module.exports = {
|
package/dist/index.d.cts
CHANGED
|
@@ -22,6 +22,16 @@ type StoreDataMap<T> = {
|
|
|
22
22
|
[K in keyof T]: ComponentDataArrays<T[K]>;
|
|
23
23
|
};
|
|
24
24
|
|
|
25
|
+
/**
|
|
26
|
+
* Maps component names to their raw sparse lookup table.
|
|
27
|
+
* Each table translates an entity ID into that entity's index in the component's
|
|
28
|
+
* data arrays, which is what queryIds callers need to read the SoA storage.
|
|
29
|
+
* @template T - Record type defining all component types
|
|
30
|
+
*/
|
|
31
|
+
type IndexMap<T> = {
|
|
32
|
+
[K in keyof T]: number[];
|
|
33
|
+
};
|
|
34
|
+
|
|
25
35
|
/**
|
|
26
36
|
* Type helper for the query result.
|
|
27
37
|
* Creates an object with an entry for each requested component, holding its store index.
|
|
@@ -91,7 +101,40 @@ interface IECS<T> {
|
|
|
91
101
|
*/
|
|
92
102
|
query: <C extends (keyof T)[]>(...componentName: C) => Generator<QueryResult<C>>;
|
|
93
103
|
|
|
104
|
+
/**
|
|
105
|
+
* Allocation-free counterpart to query, for hot systems.
|
|
106
|
+
* Returns the IDs of every entity holding all the specified components, instead of
|
|
107
|
+
* yielding one result object plus one { id } object per component per entity.
|
|
108
|
+
* Translate an entity ID into a storage index through `indices`:
|
|
109
|
+
*
|
|
110
|
+
* ```ts
|
|
111
|
+
* const { Position, Velocity } = World.components;
|
|
112
|
+
* const pos = World.indices.Position;
|
|
113
|
+
* const vel = World.indices.Velocity;
|
|
114
|
+
*
|
|
115
|
+
* for (const eid of World.queryIds('Position', 'Velocity')) {
|
|
116
|
+
* Position.x[pos[eid]] += Velocity.x[vel[eid]];
|
|
117
|
+
* }
|
|
118
|
+
* ```
|
|
119
|
+
*
|
|
120
|
+
* The array is complete before it is returned, so adding or removing components while
|
|
121
|
+
* looping over it cannot skip an entity. Destroying an entity mid-loop does invalidate
|
|
122
|
+
* its storage index, so guard that case yourself.
|
|
123
|
+
*
|
|
124
|
+
* @template C - Array of component names to query for
|
|
125
|
+
* @param componentName - List of component names that the entity must include
|
|
126
|
+
* @returns A fresh array of matching entity IDs
|
|
127
|
+
*/
|
|
128
|
+
queryIds: <C extends (keyof T)[]>(...componentName: C) => number[];
|
|
129
|
+
|
|
94
130
|
components: StoreDataMap<T>;
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Maps each component name to its sparse lookup table, translating an entity ID into
|
|
134
|
+
* that entity's index in the component's data arrays. Needed to read `components`
|
|
135
|
+
* when iterating the output of queryIds.
|
|
136
|
+
*/
|
|
137
|
+
indices: IndexMap<T>;
|
|
95
138
|
}
|
|
96
139
|
|
|
97
140
|
declare function ECS<T extends Record<string, Record<string, any>>>(): IECS<T>;
|
package/dist/index.d.ts
CHANGED
|
@@ -22,6 +22,16 @@ type StoreDataMap<T> = {
|
|
|
22
22
|
[K in keyof T]: ComponentDataArrays<T[K]>;
|
|
23
23
|
};
|
|
24
24
|
|
|
25
|
+
/**
|
|
26
|
+
* Maps component names to their raw sparse lookup table.
|
|
27
|
+
* Each table translates an entity ID into that entity's index in the component's
|
|
28
|
+
* data arrays, which is what queryIds callers need to read the SoA storage.
|
|
29
|
+
* @template T - Record type defining all component types
|
|
30
|
+
*/
|
|
31
|
+
type IndexMap<T> = {
|
|
32
|
+
[K in keyof T]: number[];
|
|
33
|
+
};
|
|
34
|
+
|
|
25
35
|
/**
|
|
26
36
|
* Type helper for the query result.
|
|
27
37
|
* Creates an object with an entry for each requested component, holding its store index.
|
|
@@ -91,7 +101,40 @@ interface IECS<T> {
|
|
|
91
101
|
*/
|
|
92
102
|
query: <C extends (keyof T)[]>(...componentName: C) => Generator<QueryResult<C>>;
|
|
93
103
|
|
|
104
|
+
/**
|
|
105
|
+
* Allocation-free counterpart to query, for hot systems.
|
|
106
|
+
* Returns the IDs of every entity holding all the specified components, instead of
|
|
107
|
+
* yielding one result object plus one { id } object per component per entity.
|
|
108
|
+
* Translate an entity ID into a storage index through `indices`:
|
|
109
|
+
*
|
|
110
|
+
* ```ts
|
|
111
|
+
* const { Position, Velocity } = World.components;
|
|
112
|
+
* const pos = World.indices.Position;
|
|
113
|
+
* const vel = World.indices.Velocity;
|
|
114
|
+
*
|
|
115
|
+
* for (const eid of World.queryIds('Position', 'Velocity')) {
|
|
116
|
+
* Position.x[pos[eid]] += Velocity.x[vel[eid]];
|
|
117
|
+
* }
|
|
118
|
+
* ```
|
|
119
|
+
*
|
|
120
|
+
* The array is complete before it is returned, so adding or removing components while
|
|
121
|
+
* looping over it cannot skip an entity. Destroying an entity mid-loop does invalidate
|
|
122
|
+
* its storage index, so guard that case yourself.
|
|
123
|
+
*
|
|
124
|
+
* @template C - Array of component names to query for
|
|
125
|
+
* @param componentName - List of component names that the entity must include
|
|
126
|
+
* @returns A fresh array of matching entity IDs
|
|
127
|
+
*/
|
|
128
|
+
queryIds: <C extends (keyof T)[]>(...componentName: C) => number[];
|
|
129
|
+
|
|
94
130
|
components: StoreDataMap<T>;
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Maps each component name to its sparse lookup table, translating an entity ID into
|
|
134
|
+
* that entity's index in the component's data arrays. Needed to read `components`
|
|
135
|
+
* when iterating the output of queryIds.
|
|
136
|
+
*/
|
|
137
|
+
indices: IndexMap<T>;
|
|
95
138
|
}
|
|
96
139
|
|
|
97
140
|
declare function ECS<T extends Record<string, Record<string, any>>>(): IECS<T>;
|
package/dist/index.js
CHANGED
|
@@ -34,13 +34,16 @@ function SparseSet() {
|
|
|
34
34
|
function getSize() {
|
|
35
35
|
return size;
|
|
36
36
|
}
|
|
37
|
-
|
|
37
|
+
function getSparse() {
|
|
38
|
+
return sparse;
|
|
39
|
+
}
|
|
40
|
+
return { has, add, remove, getIndex, getDense, getSize, getSparse };
|
|
38
41
|
}
|
|
39
42
|
|
|
40
43
|
// src/ComponentStore.ts
|
|
41
44
|
var INITIAL_CAPACITY = 8;
|
|
42
45
|
function grow(buffer, requiredIndex) {
|
|
43
|
-
let capacity = buffer.length;
|
|
46
|
+
let capacity = buffer.length || INITIAL_CAPACITY;
|
|
44
47
|
while (capacity <= requiredIndex) capacity *= 2;
|
|
45
48
|
const grown = new Float32Array(capacity);
|
|
46
49
|
grown.set(buffer);
|
|
@@ -48,8 +51,8 @@ function grow(buffer, requiredIndex) {
|
|
|
48
51
|
}
|
|
49
52
|
function ComponentStore() {
|
|
50
53
|
const componentSet = SparseSet();
|
|
51
|
-
const componentData =
|
|
52
|
-
const isNumeric =
|
|
54
|
+
const componentData = /* @__PURE__ */ Object.create(null);
|
|
55
|
+
const isNumeric = /* @__PURE__ */ Object.create(null);
|
|
53
56
|
function add(eid, data) {
|
|
54
57
|
if (componentSet.has(eid)) throw new Error(`Entity ${eid} already has this component`);
|
|
55
58
|
const ID = componentSet.getSize();
|
|
@@ -94,7 +97,10 @@ function ComponentStore() {
|
|
|
94
97
|
function getSize() {
|
|
95
98
|
return componentSet.getSize();
|
|
96
99
|
}
|
|
97
|
-
|
|
100
|
+
function getSparse() {
|
|
101
|
+
return componentSet.getSparse();
|
|
102
|
+
}
|
|
103
|
+
return { add, remove, getIndex, getDense, getData, getSize, getSparse };
|
|
98
104
|
}
|
|
99
105
|
|
|
100
106
|
// src/EntityManager.ts
|
|
@@ -138,7 +144,10 @@ function EntityManager(registry) {
|
|
|
138
144
|
if (!exists(eid)) throw new Error(`Entity ${eid} does not exist`);
|
|
139
145
|
return bitMasks[eid];
|
|
140
146
|
}
|
|
141
|
-
|
|
147
|
+
function tryGetMask(eid) {
|
|
148
|
+
return eid >= 0 && eid < nextID ? bitMasks[eid] : void 0;
|
|
149
|
+
}
|
|
150
|
+
return { exists, hasComponent, create, remove, addComponent, removeComponent, getMask, tryGetMask };
|
|
142
151
|
}
|
|
143
152
|
|
|
144
153
|
// src/utils/ComponentRegistry.ts
|
|
@@ -170,14 +179,17 @@ function createComponentRegistry() {
|
|
|
170
179
|
function ECS() {
|
|
171
180
|
const ComponentRegistry = createComponentRegistry();
|
|
172
181
|
const Entities = EntityManager(ComponentRegistry);
|
|
173
|
-
const internalStores =
|
|
174
|
-
const components =
|
|
182
|
+
const internalStores = /* @__PURE__ */ Object.create(null);
|
|
183
|
+
const components = /* @__PURE__ */ Object.create(null);
|
|
184
|
+
const indices = /* @__PURE__ */ Object.create(null);
|
|
175
185
|
function defineComponents(...names) {
|
|
176
186
|
for (const name of names) {
|
|
177
|
-
if (
|
|
187
|
+
if (Object.hasOwn(internalStores, name))
|
|
188
|
+
throw new Error(`Component ${String(name)} is already defined`);
|
|
178
189
|
ComponentRegistry.register(name);
|
|
179
190
|
internalStores[name] = ComponentStore();
|
|
180
191
|
components[name] = internalStores[name].getData();
|
|
192
|
+
indices[name] = internalStores[name].getSparse();
|
|
181
193
|
}
|
|
182
194
|
}
|
|
183
195
|
function createEntity() {
|
|
@@ -205,33 +217,65 @@ function ECS() {
|
|
|
205
217
|
Entities.removeComponent(eid, name);
|
|
206
218
|
internalStores[name].remove(eid);
|
|
207
219
|
}
|
|
208
|
-
function
|
|
209
|
-
if (componentName.length === 0) return;
|
|
220
|
+
function planQuery(componentName) {
|
|
210
221
|
let targetMask = 0n;
|
|
211
222
|
for (const name of componentName) {
|
|
212
223
|
targetMask |= 1n << BigInt(ComponentRegistry.getID(name));
|
|
213
224
|
}
|
|
214
|
-
let
|
|
215
|
-
let
|
|
225
|
+
let pivot = internalStores[componentName[0]];
|
|
226
|
+
let pivotSize = pivot.getSize();
|
|
216
227
|
for (let i = 1; i < componentName.length; i++) {
|
|
217
228
|
const store = internalStores[componentName[i]];
|
|
218
229
|
const size = store.getSize();
|
|
219
|
-
if (size <
|
|
220
|
-
|
|
221
|
-
|
|
230
|
+
if (size < pivotSize) {
|
|
231
|
+
pivot = store;
|
|
232
|
+
pivotSize = size;
|
|
222
233
|
}
|
|
223
234
|
}
|
|
224
|
-
|
|
225
|
-
|
|
235
|
+
return { targetMask, pivot };
|
|
236
|
+
}
|
|
237
|
+
function* query(...componentName) {
|
|
238
|
+
if (componentName.length === 0) return;
|
|
239
|
+
const { targetMask, pivot } = planQuery(componentName);
|
|
240
|
+
const count = componentName.length;
|
|
241
|
+
const sparses = new Array(count);
|
|
242
|
+
for (let i = 0; i < count; i++) sparses[i] = internalStores[componentName[i]].getSparse();
|
|
243
|
+
const entities = [...pivot.getDense()];
|
|
244
|
+
for (let i = 0; i < entities.length; i++) {
|
|
245
|
+
const eid = entities[i];
|
|
246
|
+
const mask = Entities.tryGetMask(eid);
|
|
247
|
+
if (mask === void 0 || (mask & targetMask) !== targetMask) continue;
|
|
226
248
|
const result = {};
|
|
227
|
-
for (
|
|
228
|
-
|
|
229
|
-
result[name] = { id };
|
|
249
|
+
for (let c = 0; c < count; c++) {
|
|
250
|
+
result[componentName[c]] = { id: sparses[c][eid] };
|
|
230
251
|
}
|
|
231
252
|
yield result;
|
|
232
253
|
}
|
|
233
254
|
}
|
|
234
|
-
|
|
255
|
+
function queryIds(...componentName) {
|
|
256
|
+
const matches = [];
|
|
257
|
+
if (componentName.length === 0) return matches;
|
|
258
|
+
const { targetMask, pivot } = planQuery(componentName);
|
|
259
|
+
const dense = pivot.getDense();
|
|
260
|
+
for (let i = 0; i < dense.length; i++) {
|
|
261
|
+
const eid = dense[i];
|
|
262
|
+
const mask = Entities.tryGetMask(eid);
|
|
263
|
+
if (mask === void 0 || (mask & targetMask) !== targetMask) continue;
|
|
264
|
+
matches.push(eid);
|
|
265
|
+
}
|
|
266
|
+
return matches;
|
|
267
|
+
}
|
|
268
|
+
return {
|
|
269
|
+
defineComponents,
|
|
270
|
+
createEntity,
|
|
271
|
+
destroyEntity,
|
|
272
|
+
addComponent,
|
|
273
|
+
removeComponent,
|
|
274
|
+
query,
|
|
275
|
+
queryIds,
|
|
276
|
+
components,
|
|
277
|
+
indices
|
|
278
|
+
};
|
|
235
279
|
}
|
|
236
280
|
export {
|
|
237
281
|
ECS
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ste_tisci/ecs",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.8",
|
|
4
4
|
"description": "A minimalistic game engine with zero dependencies based on the Entity Component System (ECS) architecture implemented in TypeScript",
|
|
5
5
|
"main": "./dist/index.cjs",
|
|
6
6
|
"module": "./dist/index.js",
|