@vworlds/vecs 1.0.3 → 1.0.4
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 +135 -14
- package/dist/dsl.d.ts +71 -0
- package/dist/dsl.js +58 -0
- package/dist/dsl.js.map +1 -0
- package/dist/entity.d.ts +28 -10
- package/dist/entity.js +34 -23
- package/dist/entity.js.map +1 -1
- package/dist/filter.d.ts +59 -0
- package/dist/filter.js +53 -0
- package/dist/filter.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/package.json +1 -1
- package/dist/query.d.ts +217 -0
- package/dist/query.js +238 -0
- package/dist/query.js.map +1 -0
- package/dist/system.d.ts +16 -169
- package/dist/system.js +49 -198
- package/dist/system.js.map +1 -1
- package/dist/world.d.ts +73 -28
- package/dist/world.js +67 -46
- package/dist/world.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -14,18 +14,20 @@ yarn add @vworlds/vecs
|
|
|
14
14
|
|
|
15
15
|
| Concept | What it is |
|
|
16
16
|
|---|---|
|
|
17
|
-
| **World** | Central container. Owns all entities, runs all systems. |
|
|
17
|
+
| **World** | Central container. Owns all entities, runs all systems and queries. |
|
|
18
18
|
| **Component** | A plain data class. Extend `Component` and attach instances to entities. |
|
|
19
19
|
| **Entity** | An integer id with a set of components. Create via the world. |
|
|
20
|
-
| **
|
|
20
|
+
| **Query** | A reactive, always-updated set of entities that match a predicate. |
|
|
21
|
+
| **System** | A `Query` with per-tick runtime logic (phases, `update`, `each`, `run`). |
|
|
22
|
+
| **Filter** | A non-reactive, one-shot scan: walks all world entities on each `forEach` call. |
|
|
21
23
|
|
|
22
24
|
### Lifecycle in brief
|
|
23
25
|
|
|
24
26
|
```
|
|
25
|
-
registerComponent() × N → system() × N → start() → progress() every frame
|
|
27
|
+
registerComponent() × N → system() / query() × N → start() → progress() every frame
|
|
26
28
|
```
|
|
27
29
|
|
|
28
|
-
After `start()`,
|
|
30
|
+
After `start()`, component registration is disabled. Systems and queries can still be created — standalone queries backfill existing matched entities immediately.
|
|
29
31
|
|
|
30
32
|
---
|
|
31
33
|
|
|
@@ -112,17 +114,12 @@ world.start(); // freeze registration, sort systems into phases
|
|
|
112
114
|
// ─── Create entities ───────────────────────────────────────────────────────
|
|
113
115
|
|
|
114
116
|
const bullet = world.createEntity();
|
|
115
|
-
|
|
116
|
-
pos.x = 0;
|
|
117
|
-
pos.y = 0;
|
|
117
|
+
bullet.set(Position, { x: 0, y: 0 });
|
|
118
118
|
|
|
119
|
-
const vel = bullet.
|
|
120
|
-
vel.vx = 5;
|
|
121
|
-
vel.vy = 0;
|
|
119
|
+
const vel = bullet.set(Velocity, { vx: 5, vy: 0 });
|
|
122
120
|
vel.modified(); // first update: notify Move system
|
|
123
121
|
|
|
124
|
-
const hp = bullet.
|
|
125
|
-
hp.hp = 3;
|
|
122
|
+
const hp = bullet.set(Health, { hp: 3 });
|
|
126
123
|
hp.modified();
|
|
127
124
|
|
|
128
125
|
// ─── Game loop ─────────────────────────────────────────────────────────────
|
|
@@ -197,9 +194,54 @@ world.system("MySystem")
|
|
|
197
194
|
.update(...)
|
|
198
195
|
.exit(...);
|
|
199
196
|
|
|
200
|
-
world.start(); //
|
|
197
|
+
world.start(); // distributes systems to phases, freezes component registration
|
|
201
198
|
```
|
|
202
199
|
|
|
200
|
+
#### Queries
|
|
201
|
+
|
|
202
|
+
A standalone `Query` is a reactive entity set without a phase or per-tick callbacks. Use it when you need the matched set kept up-to-date automatically — for example to enumerate scene nodes or find the nearest enemy.
|
|
203
|
+
|
|
204
|
+
```ts
|
|
205
|
+
const enemies = world.query("Enemies")
|
|
206
|
+
.requires(Enemy, Health)
|
|
207
|
+
.enter((e) => console.log("enemy spawned", e.eid))
|
|
208
|
+
.exit((e) => console.log("enemy died", e.eid));
|
|
209
|
+
|
|
210
|
+
world.start();
|
|
211
|
+
// enemies.entities is kept up-to-date automatically
|
|
212
|
+
|
|
213
|
+
// Can also be created after start(); existing matched entities are backfilled:
|
|
214
|
+
const lateQuery = world.query("Walls").requires(Wall);
|
|
215
|
+
// lateQuery.entities immediately contains all current Wall entities
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
#### Filters
|
|
219
|
+
|
|
220
|
+
A `Filter` is a non-reactive, one-shot scan. It holds no tracked entity set — each `forEach` call walks all world entities at that moment. Use it for ad-hoc lookups that don't need to stay live.
|
|
221
|
+
|
|
222
|
+
```ts
|
|
223
|
+
// Entity only:
|
|
224
|
+
world.filter([Position]).forEach((e) => console.log(e.eid));
|
|
225
|
+
|
|
226
|
+
// With component injection:
|
|
227
|
+
world.filter([Position, Velocity])
|
|
228
|
+
.forEach([Position, Velocity], (e, [pos, vel]) => {
|
|
229
|
+
pos.x += vel.vx;
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
// Full DSL, with auto-deduced required components:
|
|
233
|
+
world.filter({ AND: [{ HAS: Position }, { HAS: Velocity }] })
|
|
234
|
+
.forEach([Position, Velocity], (e, [pos, vel]) => {
|
|
235
|
+
pos.x += vel.vx; // pos and vel are non-null — deduced from AND of HAS
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
// Manual type hint for queries the extractor can't see through:
|
|
239
|
+
world.filter({ OR: [Position, Velocity] }, [Position])
|
|
240
|
+
.forEach([Position], (e, [pos]) => pos.x);
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
Unlike `Query`, a `Filter` requires no name, no `world.start()`, and no `destroy()` — create it anywhere and discard it freely.
|
|
244
|
+
|
|
203
245
|
#### Phases
|
|
204
246
|
|
|
205
247
|
```ts
|
|
@@ -275,6 +317,7 @@ const e = world.createEntity();
|
|
|
275
317
|
| `eid` | Unique numeric entity id. |
|
|
276
318
|
| `world` | The `World` that owns this entity. |
|
|
277
319
|
| `add(Class)` | Attach a component; returns the typed instance. Idempotent. |
|
|
320
|
+
| `set(Class, props)` | Like `add`, but also assigns the given partial properties onto the instance. Returns the typed instance. |
|
|
278
321
|
| `get(Class)` | Return the component instance, or `undefined` if not present. |
|
|
279
322
|
| `remove(Class)` | Detach a component (triggers `onRemove` hooks and `exit` callbacks). |
|
|
280
323
|
| `destroy()` | Remove all components and unregister the entity. Recurses to children. |
|
|
@@ -436,7 +479,9 @@ Iterating `system.entities` after a phase run yields entities in the sorted orde
|
|
|
436
479
|
|
|
437
480
|
#### `.track()`
|
|
438
481
|
|
|
439
|
-
Enable entity tracking without an `each` callback — matched entities are exposed via `system.entities` as they enter and leave. `each` and `sort` imply `track` automatically; call this directly only when you need the set without a per-tick callback.
|
|
482
|
+
Enable entity tracking without an `each` callback — matched entities are exposed via `system.entities` (or `query.entities`) as they enter and leave. `each` and `sort` imply `track` automatically; call this directly only when you need the tracked set without a per-tick callback.
|
|
483
|
+
|
|
484
|
+
When called after `world.start()`, `track()` immediately backfills existing entities that satisfy the query predicate.
|
|
440
485
|
|
|
441
486
|
#### `.run(callback)`
|
|
442
487
|
|
|
@@ -450,6 +495,82 @@ Called every tick when the system's phase runs, regardless of entity state. Use
|
|
|
450
495
|
|
|
451
496
|
---
|
|
452
497
|
|
|
498
|
+
### Query
|
|
499
|
+
|
|
500
|
+
A standalone query is created via `world.query(name)` and configured through the same fluent builder API as `System` (`requires`, `query`, `enter`, `exit`, `sort`, `track`, `forEach`, `entities`). It has no phase and no per-tick callbacks.
|
|
501
|
+
|
|
502
|
+
```ts
|
|
503
|
+
const projectiles = world.query("Projectiles")
|
|
504
|
+
.requires(Position, Velocity)
|
|
505
|
+
.sort([Position], ([a], [b]) => a.z - b.z)
|
|
506
|
+
.enter([Position], (e, [pos]) => { pos.x = spawnX; });
|
|
507
|
+
|
|
508
|
+
world.start();
|
|
509
|
+
|
|
510
|
+
// Anywhere in game code:
|
|
511
|
+
projectiles.forEach((e) => { /* ... */ });
|
|
512
|
+
console.log(projectiles.entities.size, "active projectiles");
|
|
513
|
+
```
|
|
514
|
+
|
|
515
|
+
| Method | Description |
|
|
516
|
+
|---|---|
|
|
517
|
+
| `.requires(...components)` | Set the membership predicate and start tracking. |
|
|
518
|
+
| `.query(expr)` | Set the membership predicate using the {@link SystemQuery} DSL. |
|
|
519
|
+
| `.enter(callback)` / `.enter(inject, callback)` | Fires when an entity joins the query. |
|
|
520
|
+
| `.exit(callback)` / `.exit(inject, callback)` | Fires when an entity leaves the query. |
|
|
521
|
+
| `.sort(components, compare)` | Store matched entities in sorted order. |
|
|
522
|
+
| `.track()` | Enable tracking (implied by `sort`; backfills when called after `start`). |
|
|
523
|
+
| `.belongs(e)` | Returns `true` if the entity satisfies the predicate. |
|
|
524
|
+
| `.forEach(callback)` | Iterate all currently tracked entities (entity only). |
|
|
525
|
+
| `.forEach(components, callback)` | Iterate with component injection — same signature as `Filter.forEach`. |
|
|
526
|
+
| `.entities` | `ReadonlySet<Entity>` of all currently tracked entities. |
|
|
527
|
+
| `.destroy()` | Remove the query from the world and all entities. See below. |
|
|
528
|
+
|
|
529
|
+
#### `.destroy()`
|
|
530
|
+
|
|
531
|
+
Permanently removes a standalone query from the world. All entity references are silently purged (no exit callbacks fire), the tracked entity set is cleared, and the query's `world` reference is set to `undefined`. After this call, any use of the query object is **undefined behavior**.
|
|
532
|
+
|
|
533
|
+
```ts
|
|
534
|
+
const q = world.query("Temporary").requires(Position);
|
|
535
|
+
// ... use q.entities ...
|
|
536
|
+
q.destroy(); // unregisters from world and all entities
|
|
537
|
+
```
|
|
538
|
+
|
|
539
|
+
`System` does **not** support `destroy()` — calling it throws. Systems are owned by the world for the lifetime of the session. Use a standalone `Query` when you need a temporary reactive set.
|
|
540
|
+
|
|
541
|
+
Both `System` and `Query` share the same query DSL, enter/exit callbacks, sort, and `entities` set — `System` extends `Query` and layers phase execution on top.
|
|
542
|
+
|
|
543
|
+
---
|
|
544
|
+
|
|
545
|
+
### Filter
|
|
546
|
+
|
|
547
|
+
A `Filter` is created via `world.filter(dsl)` and provides a non-reactive `forEach`. It accepts the same [`QueryDSL`](#-requirescomponents-and-queryq) expressions as systems and queries.
|
|
548
|
+
|
|
549
|
+
```ts
|
|
550
|
+
const f = world.filter([Position, Velocity]);
|
|
551
|
+
```
|
|
552
|
+
|
|
553
|
+
| Method | Description |
|
|
554
|
+
|---|---|
|
|
555
|
+
| `.forEach(callback)` | Walk all world entities; invoke callback for each matching one. |
|
|
556
|
+
| `.forEach(components, callback)` | Same, with component injection and non-null types for required components. |
|
|
557
|
+
|
|
558
|
+
**Type inference** works the same way as for `requires()` on systems/queries: component classes extractable from the DSL (`HAS`, `HAS_ONLY`, plain arrays, and `AND` of those) are non-nullable in the callback tuple. Pass a `_guaranteed` second argument to `world.filter()` as a manual override when inference can't reach:
|
|
559
|
+
|
|
560
|
+
```ts
|
|
561
|
+
// Auto-deduced — both non-null:
|
|
562
|
+
world.filter([Position, Velocity])
|
|
563
|
+
.forEach([Position, Velocity], (e, [pos, vel]) => { ... });
|
|
564
|
+
|
|
565
|
+
// Manual hint for OR / NOT / PARENT / custom function:
|
|
566
|
+
world.filter({ OR: [Position, Velocity] }, [Position])
|
|
567
|
+
.forEach([Position], (e, [pos]) => pos.x);
|
|
568
|
+
```
|
|
569
|
+
|
|
570
|
+
A `Filter` holds no tracked set, makes no registration calls, and needs no `destroy()`.
|
|
571
|
+
|
|
572
|
+
---
|
|
573
|
+
|
|
453
574
|
## Build & Test
|
|
454
575
|
|
|
455
576
|
```
|
package/dist/dsl.d.ts
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { Component, ComponentClassArray, ComponentClassOrType } from "./component.js";
|
|
2
|
+
import type { Entity } from "./entity.js";
|
|
3
|
+
import type { World } from "./world.js";
|
|
4
|
+
/** A function that tests whether a given entity belongs to a query. */
|
|
5
|
+
export type EntityTestFunc = (e: Entity) => boolean;
|
|
6
|
+
/**
|
|
7
|
+
* A composable query expression used to declare which entities a
|
|
8
|
+
* {@link Query} or {@link System} should track.
|
|
9
|
+
*
|
|
10
|
+
* Queries can be nested arbitrarily:
|
|
11
|
+
*
|
|
12
|
+
* ```ts
|
|
13
|
+
* // Entities that have Position AND (Sprite OR Container):
|
|
14
|
+
* world.system("render").query({
|
|
15
|
+
* AND: [Position, { OR: [Sprite, Container] }]
|
|
16
|
+
* });
|
|
17
|
+
*
|
|
18
|
+
* // Entities that have a parent with Player AND Container:
|
|
19
|
+
* world.system("attach").query({
|
|
20
|
+
* PARENT: { AND: [Player, Container] }
|
|
21
|
+
* });
|
|
22
|
+
* ```
|
|
23
|
+
*
|
|
24
|
+
* Short forms:
|
|
25
|
+
* - A single class or type id is equivalent to `{ HAS: [C] }`.
|
|
26
|
+
* - An array `[A, B]` is equivalent to `{ HAS: [A, B] }`.
|
|
27
|
+
* - Pass an {@link EntityTestFunc} directly for fully custom membership logic.
|
|
28
|
+
*/
|
|
29
|
+
export type QueryDSL = ComponentClassArray | ComponentClassOrType | EntityTestFunc | {
|
|
30
|
+
HAS: ComponentClassArray | ComponentClassOrType;
|
|
31
|
+
} | {
|
|
32
|
+
HAS_ONLY: ComponentClassArray | ComponentClassOrType;
|
|
33
|
+
} | {
|
|
34
|
+
AND: readonly QueryDSL[];
|
|
35
|
+
} | {
|
|
36
|
+
OR: readonly QueryDSL[];
|
|
37
|
+
} | {
|
|
38
|
+
NOT: QueryDSL;
|
|
39
|
+
} | {
|
|
40
|
+
PARENT: QueryDSL;
|
|
41
|
+
};
|
|
42
|
+
export declare function HAS(world: World, ...components: ComponentClassArray): EntityTestFunc;
|
|
43
|
+
/**
|
|
44
|
+
* Resolves component nullability based on what was declared in `requires` (or
|
|
45
|
+
* the `_guaranteed` hint). Components in `R` are non-nullable; others are
|
|
46
|
+
* `InstanceType<C> | undefined`.
|
|
47
|
+
*/
|
|
48
|
+
export type MaybeRequired<C, R extends (typeof Component)[]> = C extends typeof Component ? C extends R[number] ? InstanceType<C> : InstanceType<C> | undefined : never;
|
|
49
|
+
/**
|
|
50
|
+
* Statically extracts the component classes that are **guaranteed present** on
|
|
51
|
+
* every entity matched by a {@link QueryDSL} expression.
|
|
52
|
+
*
|
|
53
|
+
* Rules:
|
|
54
|
+
* - Plain class `C` → `[C]`
|
|
55
|
+
* - Plain array `[A, B]` → `[A, B]`
|
|
56
|
+
* - `{HAS: ...}` / `{HAS_ONLY: ...}` → recurse into the payload
|
|
57
|
+
* - `{AND: [q1, q2, ...]}` → concatenation of each branch's extraction
|
|
58
|
+
* - `{OR: ...}` / `{NOT: ...}` / `{PARENT: ...}` → `[]` (no guarantee)
|
|
59
|
+
* - `EntityTestFunc` / numeric type id → `[]` (opaque)
|
|
60
|
+
*/
|
|
61
|
+
export type ExtractRequired<Q> = Q extends typeof Component ? [Q] : Q extends readonly (typeof Component)[] ? Q : Q extends {
|
|
62
|
+
HAS: infer H;
|
|
63
|
+
} ? ExtractRequired<H> : Q extends {
|
|
64
|
+
HAS_ONLY: infer H;
|
|
65
|
+
} ? ExtractRequired<H> : Q extends {
|
|
66
|
+
AND: infer A extends readonly QueryDSL[];
|
|
67
|
+
} ? ExtractAndChain<A> : [];
|
|
68
|
+
type ExtractAndChain<A extends readonly QueryDSL[]> = A extends readonly [infer First, ...infer Rest extends readonly QueryDSL[]] ? [...ExtractRequired<First>, ...ExtractAndChain<Rest>] : [];
|
|
69
|
+
/** Convert a {@link QueryDSL} expression into a runtime entity-test predicate. */
|
|
70
|
+
export declare function buildEntityTest(world: World, q: QueryDSL): EntityTestFunc;
|
|
71
|
+
export {};
|
package/dist/dsl.js
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { Component, calculateComponentBitmask, } from "./component.js";
|
|
2
|
+
export function HAS(world, ...components) {
|
|
3
|
+
const testBitmask = calculateComponentBitmask(components, world);
|
|
4
|
+
return (e) => e.componentBitmask.hasBitset(testBitmask);
|
|
5
|
+
}
|
|
6
|
+
function HAS_ONLY(world, ...components) {
|
|
7
|
+
const testBitmask = calculateComponentBitmask(components, world);
|
|
8
|
+
return (e) => e.componentBitmask.equal(testBitmask);
|
|
9
|
+
}
|
|
10
|
+
function NOT(func) {
|
|
11
|
+
return (e) => !func(e);
|
|
12
|
+
}
|
|
13
|
+
function AND(...funcs) {
|
|
14
|
+
return (e) => funcs.every((f) => f(e));
|
|
15
|
+
}
|
|
16
|
+
function OR(...funcs) {
|
|
17
|
+
return (e) => funcs.some((f) => f(e));
|
|
18
|
+
}
|
|
19
|
+
function PARENT(func) {
|
|
20
|
+
return (e) => (e.parent && func(e.parent)) || false;
|
|
21
|
+
}
|
|
22
|
+
/** Convert a {@link QueryDSL} expression into a runtime entity-test predicate. */
|
|
23
|
+
export function buildEntityTest(world, q) {
|
|
24
|
+
if (typeof q === "number" ||
|
|
25
|
+
(typeof q === "function" && q.prototype instanceof Component)) {
|
|
26
|
+
return HAS(world, q);
|
|
27
|
+
}
|
|
28
|
+
else if (typeof q === "function") {
|
|
29
|
+
return q;
|
|
30
|
+
}
|
|
31
|
+
if (q instanceof Array) {
|
|
32
|
+
return HAS(world, ...q);
|
|
33
|
+
}
|
|
34
|
+
if ("HAS" in q) {
|
|
35
|
+
return buildEntityTest(world, q.HAS);
|
|
36
|
+
}
|
|
37
|
+
if ("HAS_ONLY" in q) {
|
|
38
|
+
const v = q.HAS_ONLY;
|
|
39
|
+
if (v instanceof Array) {
|
|
40
|
+
return HAS_ONLY(world, ...v);
|
|
41
|
+
}
|
|
42
|
+
return HAS_ONLY(world, v);
|
|
43
|
+
}
|
|
44
|
+
if ("AND" in q) {
|
|
45
|
+
return AND(...q.AND.map((sq) => buildEntityTest(world, sq)));
|
|
46
|
+
}
|
|
47
|
+
if ("OR" in q) {
|
|
48
|
+
return OR(...q.OR.map((sq) => buildEntityTest(world, sq)));
|
|
49
|
+
}
|
|
50
|
+
if ("NOT" in q) {
|
|
51
|
+
return NOT(buildEntityTest(world, q.NOT));
|
|
52
|
+
}
|
|
53
|
+
if ("PARENT" in q) {
|
|
54
|
+
return PARENT(buildEntityTest(world, q.PARENT));
|
|
55
|
+
}
|
|
56
|
+
throw "Unrecognized query term";
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=dsl.js.map
|
package/dist/dsl.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dsl.js","sourceRoot":"","sources":["../src/dsl.ts"],"names":[],"mappings":"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"}
|
package/dist/entity.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Component } from "./component.js";
|
|
2
2
|
import type { World } from "./world.js";
|
|
3
|
-
import { type
|
|
3
|
+
import { type Query } from "./query.js";
|
|
4
4
|
import { Events } from "./util/events.js";
|
|
5
5
|
import { Bitset } from "./util/bitset.js";
|
|
6
6
|
type EntityEvents = Events<{
|
|
@@ -33,12 +33,12 @@ export declare class Entity {
|
|
|
33
33
|
private deletedComponents;
|
|
34
34
|
/**
|
|
35
35
|
* Bitmask representing the set of component types currently attached to this
|
|
36
|
-
* entity. Used by the world to efficiently match entities against
|
|
37
|
-
*
|
|
36
|
+
* entity. Used by the world to efficiently match entities against query
|
|
37
|
+
* predicates.
|
|
38
38
|
*/
|
|
39
39
|
readonly componentBitmask: Bitset;
|
|
40
|
-
private readonly
|
|
41
|
-
private readonly
|
|
40
|
+
private readonly queries;
|
|
41
|
+
private readonly newQueries;
|
|
42
42
|
/**
|
|
43
43
|
* A free-form property bag that modules can use to associate arbitrary data
|
|
44
44
|
* with an entity without registering a component.
|
|
@@ -86,6 +86,22 @@ export declare class Entity {
|
|
|
86
86
|
* @param markAsModified - Whether to queue an update notification.
|
|
87
87
|
*/
|
|
88
88
|
add(type: number, markAsModified?: boolean): Component;
|
|
89
|
+
/**
|
|
90
|
+
* Add a component of type `Class` (if not already present) and assign the
|
|
91
|
+
* provided properties onto the instance, then return it.
|
|
92
|
+
*
|
|
93
|
+
* @param Class - The component class to instantiate.
|
|
94
|
+
* @param props - Optional properties to assign onto the component instance.
|
|
95
|
+
* @returns The new (or existing) component instance with the given properties applied.
|
|
96
|
+
*/
|
|
97
|
+
set<C extends typeof Component>(Class: C, props: Partial<InstanceType<C>>): InstanceType<C>;
|
|
98
|
+
/**
|
|
99
|
+
* Add a component by its numeric type id and assign the provided properties.
|
|
100
|
+
*
|
|
101
|
+
* @param type - Numeric component type id.
|
|
102
|
+
* @param props - Optional properties to assign onto the component instance.
|
|
103
|
+
*/
|
|
104
|
+
set(type: number, props: Partial<Component>): Component;
|
|
89
105
|
/**
|
|
90
106
|
* Remove the component of the given class from this entity.
|
|
91
107
|
*
|
|
@@ -102,7 +118,7 @@ export declare class Entity {
|
|
|
102
118
|
* @param type - Numeric component type id.
|
|
103
119
|
*/
|
|
104
120
|
remove(type: number): void;
|
|
105
|
-
/** @internal Called by
|
|
121
|
+
/** @internal Called by queries to deliver update notifications. */
|
|
106
122
|
_notifyModified(component: Component): void;
|
|
107
123
|
/**
|
|
108
124
|
* Retrieve the component of type `Class`, or `undefined` if not present.
|
|
@@ -124,13 +140,15 @@ export declare class Entity {
|
|
|
124
140
|
*/
|
|
125
141
|
get events(): EntityEvents;
|
|
126
142
|
/** @internal */
|
|
127
|
-
|
|
143
|
+
_hasQuery(q: Query): boolean;
|
|
144
|
+
/** @internal Removes a query from this entity's tracking sets without firing any callbacks. */
|
|
145
|
+
_purgeQuery(q: Query): void;
|
|
128
146
|
/** @internal */
|
|
129
|
-
|
|
147
|
+
_addQuery(q: Query): void;
|
|
130
148
|
/** @internal */
|
|
131
|
-
|
|
149
|
+
_removeQuery(q: Query): void;
|
|
132
150
|
/** @internal */
|
|
133
|
-
|
|
151
|
+
_updateQueries(): void;
|
|
134
152
|
/** `true` when the entity has no components attached. */
|
|
135
153
|
get empty(): boolean;
|
|
136
154
|
private _destroy;
|
package/dist/entity.js
CHANGED
|
@@ -31,12 +31,12 @@ export class Entity {
|
|
|
31
31
|
this.deletedComponents = new ArrayMap(); //maps deleted component types to Components
|
|
32
32
|
/**
|
|
33
33
|
* Bitmask representing the set of component types currently attached to this
|
|
34
|
-
* entity. Used by the world to efficiently match entities against
|
|
35
|
-
*
|
|
34
|
+
* entity. Used by the world to efficiently match entities against query
|
|
35
|
+
* predicates.
|
|
36
36
|
*/
|
|
37
37
|
this.componentBitmask = new Bitset();
|
|
38
|
-
this.
|
|
39
|
-
this.
|
|
38
|
+
this.queries = new Set();
|
|
39
|
+
this.newQueries = [];
|
|
40
40
|
/**
|
|
41
41
|
* A free-form property bag that modules can use to associate arbitrary data
|
|
42
42
|
* with an entity without registering a component.
|
|
@@ -71,6 +71,10 @@ export class Entity {
|
|
|
71
71
|
this.world._queueUpdatedComponent(c);
|
|
72
72
|
return c;
|
|
73
73
|
}
|
|
74
|
+
set(typeOrClass, props) {
|
|
75
|
+
const c = this.add(typeOrClass);
|
|
76
|
+
return Object.assign(c, props);
|
|
77
|
+
}
|
|
74
78
|
remove(typeOrClass) {
|
|
75
79
|
const type = this.world.getComponentType(typeOrClass);
|
|
76
80
|
const c = this.components.get(type);
|
|
@@ -81,10 +85,10 @@ export class Entity {
|
|
|
81
85
|
this.world._notifyComponentRemoved(this, c);
|
|
82
86
|
}
|
|
83
87
|
}
|
|
84
|
-
/** @internal Called by
|
|
88
|
+
/** @internal Called by queries to deliver update notifications. */
|
|
85
89
|
_notifyModified(component) {
|
|
86
|
-
this.
|
|
87
|
-
|
|
90
|
+
this.queries.forEach((q) => {
|
|
91
|
+
q.notifyModified(component);
|
|
88
92
|
});
|
|
89
93
|
}
|
|
90
94
|
/**
|
|
@@ -119,28 +123,35 @@ export class Entity {
|
|
|
119
123
|
return this._events;
|
|
120
124
|
}
|
|
121
125
|
/** @internal */
|
|
122
|
-
|
|
123
|
-
return this.
|
|
126
|
+
_hasQuery(q) {
|
|
127
|
+
return this.queries.has(q);
|
|
128
|
+
}
|
|
129
|
+
/** @internal Removes a query from this entity's tracking sets without firing any callbacks. */
|
|
130
|
+
_purgeQuery(q) {
|
|
131
|
+
this.queries.delete(q);
|
|
132
|
+
const idx = this.newQueries.indexOf(q);
|
|
133
|
+
if (idx !== -1)
|
|
134
|
+
this.newQueries.splice(idx, 1);
|
|
124
135
|
}
|
|
125
136
|
/** @internal */
|
|
126
|
-
|
|
127
|
-
if (!this.
|
|
128
|
-
this.
|
|
129
|
-
|
|
137
|
+
_addQuery(q) {
|
|
138
|
+
if (!this.queries.has(q)) {
|
|
139
|
+
this.newQueries.push(q);
|
|
140
|
+
q._enter(this);
|
|
130
141
|
}
|
|
131
142
|
}
|
|
132
143
|
/** @internal */
|
|
133
|
-
|
|
134
|
-
if (this.
|
|
135
|
-
|
|
144
|
+
_removeQuery(q) {
|
|
145
|
+
if (this.queries.delete(q)) {
|
|
146
|
+
q._exit(this);
|
|
136
147
|
}
|
|
137
148
|
}
|
|
138
149
|
/** @internal */
|
|
139
|
-
|
|
140
|
-
this.
|
|
141
|
-
this.
|
|
150
|
+
_updateQueries() {
|
|
151
|
+
this.newQueries.forEach((q) => {
|
|
152
|
+
this.queries.add(q);
|
|
142
153
|
});
|
|
143
|
-
this.
|
|
154
|
+
this.newQueries.length = 0;
|
|
144
155
|
}
|
|
145
156
|
/** `true` when the entity has no components attached. */
|
|
146
157
|
get empty() {
|
|
@@ -150,10 +161,10 @@ export class Entity {
|
|
|
150
161
|
if (this.destroyed)
|
|
151
162
|
return;
|
|
152
163
|
this.destroyed = true;
|
|
153
|
-
this.
|
|
154
|
-
|
|
164
|
+
this.queries.forEach((q) => {
|
|
165
|
+
q._exit(this);
|
|
155
166
|
});
|
|
156
|
-
this.
|
|
167
|
+
this.queries.clear();
|
|
157
168
|
if (this._events) {
|
|
158
169
|
this._events.emit("destroy");
|
|
159
170
|
this._events.removeAllListeners("destroy");
|
package/dist/entity.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"entity.js","sourceRoot":"","sources":["../src/entity.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAE/C,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC1C,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAI1C;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,OAAO,MAAM;IA0BjB;IACE,+CAA+C;IAC/B,KAAY;IAC5B,0DAA0D;IAC1C,GAAW;QAFX,UAAK,GAAL,KAAK,CAAO;QAEZ,QAAG,GAAH,GAAG,CAAQ;QA7BrB,eAAU,GAAG,IAAI,QAAQ,EAAa,CAAC,CAAC,oCAAoC;QAC5E,sBAAiB,GAAG,IAAI,QAAQ,EAAa,CAAC,CAAC,4CAA4C;QAEnG;;;;WAIG;QACa,qBAAgB,GAAG,IAAI,MAAM,EAAE,CAAC;QAC/B,YAAO,GAAG,IAAI,GAAG,
|
|
1
|
+
{"version":3,"file":"entity.js","sourceRoot":"","sources":["../src/entity.ts"],"names":[],"mappings":"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"}
|
package/dist/filter.d.ts
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { Component } from "./component.js";
|
|
2
|
+
import type { Entity } from "./entity.js";
|
|
3
|
+
import type { World } from "./world.js";
|
|
4
|
+
import { type MaybeRequired, type QueryDSL } from "./dsl.js";
|
|
5
|
+
/**
|
|
6
|
+
* A non-reactive, one-shot entity filter.
|
|
7
|
+
*
|
|
8
|
+
* Unlike {@link Query}, a `Filter` holds no tracked entity set and registers
|
|
9
|
+
* nothing with the world. Every {@link forEach} call walks all world entities
|
|
10
|
+
* and invokes the callback for those that match the predicate captured at
|
|
11
|
+
* construction time.
|
|
12
|
+
*
|
|
13
|
+
* Create via {@link World.filter}:
|
|
14
|
+
*
|
|
15
|
+
* ```ts
|
|
16
|
+
* const f = world.filter([Position, Velocity]);
|
|
17
|
+
*
|
|
18
|
+
* // Entity only:
|
|
19
|
+
* f.forEach((e) => console.log(e.eid));
|
|
20
|
+
*
|
|
21
|
+
* // With component injection:
|
|
22
|
+
* f.forEach([Position, Velocity], (e, [pos, vel]) => {
|
|
23
|
+
* pos.x += vel.vx;
|
|
24
|
+
* });
|
|
25
|
+
* ```
|
|
26
|
+
*
|
|
27
|
+
* ### Type parameter `R`
|
|
28
|
+
* Tracks which component classes are guaranteed present on every matched
|
|
29
|
+
* entity — inferred automatically from the DSL by {@link World.filter}, or
|
|
30
|
+
* supplied manually via the optional `_guaranteed` argument. Components in `R`
|
|
31
|
+
* appear as non-nullable in `forEach` callback tuples.
|
|
32
|
+
*/
|
|
33
|
+
export declare class Filter<R extends (typeof Component)[] = []> {
|
|
34
|
+
private readonly world;
|
|
35
|
+
private readonly belongs;
|
|
36
|
+
constructor(world: World, dsl: QueryDSL);
|
|
37
|
+
/**
|
|
38
|
+
* Iterate all world entities and call `callback` for each one that matches
|
|
39
|
+
* the DSL this filter was created with.
|
|
40
|
+
*
|
|
41
|
+
* @param callback - Receives only the entity.
|
|
42
|
+
*/
|
|
43
|
+
forEach(callback: (e: Entity) => void): void;
|
|
44
|
+
/**
|
|
45
|
+
* Iterate all world entities and call `callback` for each one that matches
|
|
46
|
+
* the DSL, with component injection.
|
|
47
|
+
*
|
|
48
|
+
* Components declared via {@link World.filter}'s DSL (or `_guaranteed`) are
|
|
49
|
+
* non-nullable in the resolved tuple; any other requested component may be
|
|
50
|
+
* `undefined` if the entity lacks it.
|
|
51
|
+
*
|
|
52
|
+
* @param components - Component classes to resolve from each matching entity.
|
|
53
|
+
* @param callback - Receives the entity and a tuple of resolved component
|
|
54
|
+
* instances.
|
|
55
|
+
*/
|
|
56
|
+
forEach<J extends (typeof Component)[]>(components: readonly [...J], callback: (e: Entity, resolved: {
|
|
57
|
+
[K in keyof J]: MaybeRequired<J[K], R>;
|
|
58
|
+
}) => void): void;
|
|
59
|
+
}
|
package/dist/filter.js
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { buildEntityTest, } from "./dsl.js";
|
|
2
|
+
/**
|
|
3
|
+
* A non-reactive, one-shot entity filter.
|
|
4
|
+
*
|
|
5
|
+
* Unlike {@link Query}, a `Filter` holds no tracked entity set and registers
|
|
6
|
+
* nothing with the world. Every {@link forEach} call walks all world entities
|
|
7
|
+
* and invokes the callback for those that match the predicate captured at
|
|
8
|
+
* construction time.
|
|
9
|
+
*
|
|
10
|
+
* Create via {@link World.filter}:
|
|
11
|
+
*
|
|
12
|
+
* ```ts
|
|
13
|
+
* const f = world.filter([Position, Velocity]);
|
|
14
|
+
*
|
|
15
|
+
* // Entity only:
|
|
16
|
+
* f.forEach((e) => console.log(e.eid));
|
|
17
|
+
*
|
|
18
|
+
* // With component injection:
|
|
19
|
+
* f.forEach([Position, Velocity], (e, [pos, vel]) => {
|
|
20
|
+
* pos.x += vel.vx;
|
|
21
|
+
* });
|
|
22
|
+
* ```
|
|
23
|
+
*
|
|
24
|
+
* ### Type parameter `R`
|
|
25
|
+
* Tracks which component classes are guaranteed present on every matched
|
|
26
|
+
* entity — inferred automatically from the DSL by {@link World.filter}, or
|
|
27
|
+
* supplied manually via the optional `_guaranteed` argument. Components in `R`
|
|
28
|
+
* appear as non-nullable in `forEach` callback tuples.
|
|
29
|
+
*/
|
|
30
|
+
export class Filter {
|
|
31
|
+
constructor(world, dsl) {
|
|
32
|
+
this.world = world;
|
|
33
|
+
this.belongs = buildEntityTest(world, dsl);
|
|
34
|
+
}
|
|
35
|
+
forEach(componentsOrCallback, callback) {
|
|
36
|
+
if (typeof componentsOrCallback === "function") {
|
|
37
|
+
this.world._forEachEntity((e) => {
|
|
38
|
+
if (this.belongs(e))
|
|
39
|
+
componentsOrCallback(e);
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
const types = componentsOrCallback.map((C) => this.world.getComponentType(C));
|
|
44
|
+
this.world._forEachEntity((e) => {
|
|
45
|
+
if (!this.belongs(e))
|
|
46
|
+
return;
|
|
47
|
+
const resolved = types.map((t) => e.get(t));
|
|
48
|
+
callback(e, resolved);
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=filter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"filter.js","sourceRoot":"","sources":["../src/filter.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,eAAe,GAIhB,MAAM,UAAU,CAAC;AAElB;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,MAAM,OAAO,MAAM;IAGjB,YACmB,KAAY,EAC7B,GAAa;QADI,UAAK,GAAL,KAAK,CAAO;QAG7B,IAAI,CAAC,OAAO,GAAG,eAAe,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;IAC7C,CAAC;IA8BM,OAAO,CACZ,oBAEyB,EACzB,QAGS;QAET,IAAI,OAAO,oBAAoB,KAAK,UAAU,EAAE;YAC9C,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC,CAAC,EAAE,EAAE;gBAC9B,IAAI,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC;oBAAE,oBAAoB,CAAC,CAAC,CAAC,CAAC;YAC/C,CAAC,CAAC,CAAC;SACJ;aAAM;YACL,MAAM,KAAK,GAAG,oBAAoB,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAC3C,IAAI,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAC/B,CAAC;YACF,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC,CAAC,EAAE,EAAE;gBAC9B,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC;oBAAE,OAAO;gBAC7B,MAAM,QAAQ,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;gBAC5C,QAAS,CAAC,CAAC,EAAE,QAAe,CAAC,CAAC;YAChC,CAAC,CAAC,CAAC;SACJ;IACH,CAAC;CACF"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export { type System } from "./system.js";
|
|
2
|
+
export { Query } from "./query.js";
|
|
2
3
|
export { World } from "./world.js";
|
|
4
|
+
export { Filter } from "./filter.js";
|
|
3
5
|
export { Component, type ComponentMeta } from "./component.js";
|
|
4
6
|
export { type Entity } from "./entity.js";
|
|
5
7
|
export { type IPhase } from "./phase.js";
|