@vworlds/vecs 1.0.20 → 1.0.21
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 +51 -48
- package/dist/component.d.ts +5 -2
- package/dist/component.js +4 -57
- package/dist/component.js.map +1 -1
- package/dist/component_meta.d.ts +15 -8
- package/dist/component_meta.js +6 -4
- package/dist/component_meta.js.map +1 -1
- package/dist/dsl.d.ts +56 -11
- package/dist/dsl.js +21 -31
- package/dist/dsl.js.map +1 -1
- package/dist/entity/entity.base.d.ts +4 -3
- package/dist/entity/entity.base.js +4 -4
- package/dist/entity/entity.base.js.map +1 -1
- package/dist/entity/entity.components.d.ts +16 -7
- package/dist/entity/entity.components.js +48 -23
- package/dist/entity/entity.components.js.map +1 -1
- package/dist/entity/entity.d.ts +5 -1
- package/dist/entity/entity.js +5 -0
- package/dist/entity/entity.js.map +1 -1
- package/dist/entity/entity.lifecycle.js +25 -25
- package/dist/entity/entity.lifecycle.js.map +1 -1
- package/dist/entity/entity.queries.js +0 -6
- package/dist/entity/entity.queries.js.map +1 -1
- package/dist/entity/entity.relationships.d.ts +1 -1
- package/dist/entity/entity.relationships.js +7 -5
- package/dist/entity/entity.relationships.js.map +1 -1
- package/dist/entity/index.d.ts +1 -1
- package/dist/entity/index.js.map +1 -1
- package/dist/filter.d.ts +6 -6
- package/dist/filter.js +2 -1
- package/dist/filter.js.map +1 -1
- package/dist/index.d.ts +8 -4
- package/dist/index.js +5 -3
- package/dist/index.js.map +1 -1
- package/dist/inject.d.ts +18 -17
- package/dist/inject.js +34 -34
- package/dist/inject.js.map +1 -1
- package/dist/module.d.ts +0 -2
- package/dist/module.js +0 -3
- package/dist/module.js.map +1 -1
- package/dist/modules/relationship_types.d.ts +34 -0
- package/dist/modules/relationship_types.js +36 -0
- package/dist/modules/relationship_types.js.map +1 -0
- package/dist/modules/relationships.d.ts +24 -0
- package/dist/modules/relationships.js +82 -0
- package/dist/modules/relationships.js.map +1 -0
- package/dist/modules/singleton.js +1 -2
- package/dist/modules/singleton.js.map +1 -1
- package/dist/package.json +1 -1
- package/dist/query/callbacks.d.ts +17 -0
- package/dist/query/callbacks.js +113 -0
- package/dist/query/callbacks.js.map +1 -0
- package/dist/query/constants.d.ts +1 -0
- package/dist/query/constants.js +2 -0
- package/dist/query/constants.js.map +1 -0
- package/dist/query/{query.03.tracking.d.ts → group.d.ts} +11 -9
- package/dist/query/group.js +31 -0
- package/dist/query/group.js.map +1 -0
- package/dist/query/grouped_query.d.ts +26 -0
- package/dist/query/grouped_query.js +121 -0
- package/dist/query/grouped_query.js.map +1 -0
- package/dist/query/grouped_query_registry.d.ts +4 -0
- package/dist/query/grouped_query_registry.js +11 -0
- package/dist/query/grouped_query_registry.js.map +1 -0
- package/dist/query/grouping.d.ts +24 -0
- package/dist/query/{query.07.groups.js → grouping.js} +29 -60
- package/dist/query/grouping.js.map +1 -0
- package/dist/query/index.d.ts +6 -6
- package/dist/query/index.js +6 -4
- package/dist/query/index.js.map +1 -1
- package/dist/query/query.d.ts +58 -28
- package/dist/query/query.js +327 -88
- package/dist/query/query.js.map +1 -1
- package/dist/system.d.ts +18 -34
- package/dist/system.js +33 -49
- package/dist/system.js.map +1 -1
- package/dist/terms/all_term.d.ts +2 -6
- package/dist/terms/all_term.js +2 -7
- package/dist/terms/all_term.js.map +1 -1
- package/dist/terms/any_term.d.ts +2 -6
- package/dist/terms/any_term.js +2 -7
- package/dist/terms/any_term.js.map +1 -1
- package/dist/terms/build.d.ts +9 -6
- package/dist/terms/build.js +17 -14
- package/dist/terms/build.js.map +1 -1
- package/dist/terms/component_term.d.ts +3 -3
- package/dist/terms/component_term.js.map +1 -1
- package/dist/terms/composite_build.d.ts +32 -0
- package/dist/terms/composite_build.js +28 -0
- package/dist/terms/composite_build.js.map +1 -0
- package/dist/terms/only_term.d.ts +3 -3
- package/dist/terms/only_term.js.map +1 -1
- package/dist/terms/predicate_term.d.ts +3 -3
- package/dist/terms/predicate_term.js +3 -3
- package/dist/terms/predicate_term.js.map +1 -1
- package/dist/terms/target_term.d.ts +3 -2
- package/dist/terms/target_term.js +11 -17
- package/dist/terms/target_term.js.map +1 -1
- package/dist/util/dense_set.js +1 -1
- package/dist/util/id_pool.d.ts +2 -0
- package/dist/util/id_pool.js +10 -0
- package/dist/util/id_pool.js.map +1 -1
- package/dist/util/ordered_set.js +15 -10
- package/dist/util/ordered_set.js.map +1 -1
- package/dist/world/world.components.d.ts +5 -4
- package/dist/world/world.components.js +16 -16
- package/dist/world/world.components.js.map +1 -1
- package/dist/world/world.entities.d.ts +2 -2
- package/dist/world/world.entities.js.map +1 -1
- package/dist/world/world.js +2 -4
- package/dist/world/world.js.map +1 -1
- package/dist/world/world.modules.d.ts +2 -2
- package/dist/world/world.modules.js +6 -1
- package/dist/world/world.modules.js.map +1 -1
- package/dist/world/world.pools.d.ts +2 -1
- package/dist/world/world.pools.js.map +1 -1
- package/dist/world/world.queries.d.ts +3 -5
- package/dist/world/world.queries.js +11 -8
- package/dist/world/world.queries.js.map +1 -1
- package/package.json +1 -1
- package/dist/query/query.00.base.d.ts +0 -23
- package/dist/query/query.00.base.js +0 -77
- package/dist/query/query.00.base.js.map +0 -1
- package/dist/query/query.01.reactive.d.ts +0 -7
- package/dist/query/query.01.reactive.js +0 -58
- package/dist/query/query.01.reactive.js.map +0 -1
- package/dist/query/query.02.lifecycle.d.ts +0 -6
- package/dist/query/query.02.lifecycle.js +0 -63
- package/dist/query/query.02.lifecycle.js.map +0 -1
- package/dist/query/query.03.tracking.js +0 -31
- package/dist/query/query.03.tracking.js.map +0 -1
- package/dist/query/query.04.callbacks.d.ts +0 -14
- package/dist/query/query.04.callbacks.js +0 -65
- package/dist/query/query.04.callbacks.js.map +0 -1
- package/dist/query/query.05.updates.d.ts +0 -14
- package/dist/query/query.05.updates.js +0 -81
- package/dist/query/query.05.updates.js.map +0 -1
- package/dist/query/query.06.predicate.d.ts +0 -13
- package/dist/query/query.06.predicate.js +0 -40
- package/dist/query/query.06.predicate.js.map +0 -1
- package/dist/query/query.07.groups.d.ts +0 -41
- package/dist/query/query.07.groups.js.map +0 -1
- package/dist/relationship.d.ts +0 -19
- package/dist/relationship.js +0 -18
- package/dist/relationship.js.map +0 -1
package/README.md
CHANGED
|
@@ -74,7 +74,7 @@ const cleanup: IPhase = world.addPhase("cleanup");
|
|
|
74
74
|
world
|
|
75
75
|
.system("Move")
|
|
76
76
|
.phase(update)
|
|
77
|
-
.
|
|
77
|
+
.with(Position, Velocity)
|
|
78
78
|
.each([Position, Velocity], (e, [pos, vel]) => {
|
|
79
79
|
pos.x += vel.vx;
|
|
80
80
|
pos.y += vel.vy;
|
|
@@ -85,7 +85,7 @@ world
|
|
|
85
85
|
world
|
|
86
86
|
.system("Health")
|
|
87
87
|
.phase(cleanup)
|
|
88
|
-
.
|
|
88
|
+
.with(Health)
|
|
89
89
|
.update(Health, (entity, health) => {
|
|
90
90
|
if (health.hp <= 0) {
|
|
91
91
|
entity.destroy();
|
|
@@ -145,7 +145,7 @@ const world = new World();
|
|
|
145
145
|
|
|
146
146
|
#### Component registration
|
|
147
147
|
|
|
148
|
-
Components are ordinary classes. They do not inherit from a vecs base class, and vecs constructs them with `new ComponentClass()`, so constructors should take no parameters. Register every component class before using it in `add`, `set`, `get`, `
|
|
148
|
+
Components are ordinary classes. They do not inherit from a vecs base class, and vecs constructs them with `new ComponentClass()`, so constructors should take no parameters. Register every component class before using it in `add`, `set`, `get`, `with`, `filter`, hook registration, or `setExclusiveComponents`.
|
|
149
149
|
|
|
150
150
|
```ts
|
|
151
151
|
class Position {
|
|
@@ -160,7 +160,7 @@ const positionComponent = world.component(Position);
|
|
|
160
160
|
world.component(Position, 42);
|
|
161
161
|
|
|
162
162
|
// Access component metadata (numeric id, cleanup policy, etc.):
|
|
163
|
-
const
|
|
163
|
+
const componentMeta = world.component(Position).meta; // ComponentMeta
|
|
164
164
|
|
|
165
165
|
// Give the component a name so it can be looked up by string later:
|
|
166
166
|
world.component(Position).name = "Position";
|
|
@@ -222,7 +222,7 @@ world.component("Position"); // resolves the same component entity
|
|
|
222
222
|
|
|
223
223
|
#### Component entity deletion cleanup
|
|
224
224
|
|
|
225
|
-
Every component key is backed by a component entity. Destroying that component entity is controlled by `world.component(C).
|
|
225
|
+
Every component key is backed by a component entity. Destroying that component entity is controlled by `world.component(C).meta.onDelete`:
|
|
226
226
|
|
|
227
227
|
| Policy | Meaning |
|
|
228
228
|
| ---------------------- | -------------------------------------------------------------------------------------------------- |
|
|
@@ -233,7 +233,7 @@ Every component key is backed by a component entity. Destroying that component e
|
|
|
233
233
|
```ts
|
|
234
234
|
import { CleanupPolicy } from "@vworlds/vecs";
|
|
235
235
|
|
|
236
|
-
world.component(Temporary).
|
|
236
|
+
world.component(Temporary).meta.onDelete = CleanupPolicy.Remove;
|
|
237
237
|
world.component(Temporary).destroy(); // strips Temporary from all carriers
|
|
238
238
|
```
|
|
239
239
|
|
|
@@ -280,7 +280,7 @@ Systems with no explicit phase are placed in the built-in `"update"` phase.
|
|
|
280
280
|
world
|
|
281
281
|
.system("MySystem")
|
|
282
282
|
.phase("update")
|
|
283
|
-
.
|
|
283
|
+
.with(A, B)
|
|
284
284
|
.enter(...)
|
|
285
285
|
.update(...)
|
|
286
286
|
.each(...)
|
|
@@ -353,7 +353,7 @@ Tick source objects and systems can both be used as sources. Disabling a source
|
|
|
353
353
|
```ts
|
|
354
354
|
const enemies = world
|
|
355
355
|
.query("Enemies")
|
|
356
|
-
.
|
|
356
|
+
.with(Enemy, Health)
|
|
357
357
|
.enter((e) => console.log("enemy spawned", e.eid));
|
|
358
358
|
|
|
359
359
|
world.start();
|
|
@@ -380,7 +380,9 @@ world.filter({ all: [Position, Velocity] }).forEach([Position, Velocity], (e, [p
|
|
|
380
380
|
});
|
|
381
381
|
|
|
382
382
|
// Manual hint for queries the type extractor can't see through:
|
|
383
|
-
world
|
|
383
|
+
world
|
|
384
|
+
.filter({ with: { any: [Position, Velocity] }, hint: [Position] })
|
|
385
|
+
.forEach([Position], (e, [pos]) => pos.x);
|
|
384
386
|
```
|
|
385
387
|
|
|
386
388
|
A `Filter` requires no name, no `world.start()`, and no `destroy()` — create it anywhere and discard freely.
|
|
@@ -433,7 +435,7 @@ entity.get(Position) === shared; // true
|
|
|
433
435
|
| Shared instances possible | `entity.attach(instance)` stores the exact passed object; code should use the entity passed by vecs callbacks. |
|
|
434
436
|
| Manual dirty marking | After mutating fields directly, call `entity.modified(C)` to notify hooks, queries, and systems. |
|
|
435
437
|
|
|
436
|
-
Use `world.component(C).
|
|
438
|
+
Use `world.component(C).meta` when you need metadata such as the numeric type id or component name. Metadata is world-specific.
|
|
437
439
|
|
|
438
440
|
---
|
|
439
441
|
|
|
@@ -495,14 +497,14 @@ item.parent(EquippedBy); // player
|
|
|
495
497
|
player.children(EquippedBy).has(item); // true
|
|
496
498
|
```
|
|
497
499
|
|
|
498
|
-
Relationship target cleanup is controlled by `
|
|
500
|
+
Relationship target cleanup is controlled by `meta.onDeleteTarget`:
|
|
499
501
|
|
|
500
502
|
| Policy | Meaning |
|
|
501
503
|
| ---------------------- | -------------------------------------------------------------------------------------------- |
|
|
502
504
|
| `CleanupPolicy.Remove` | Default. When a target is destroyed, remove the relationship component from each source. |
|
|
503
505
|
| `CleanupPolicy.Delete` | When a target is destroyed, destroy each source entity that targets it through the relation. |
|
|
504
506
|
|
|
505
|
-
`ChildOf` is registered by every `World` and uses `CleanupPolicy.Delete`. Custom relationships default to `Remove`; set `world.component(MyRelationship).
|
|
507
|
+
`ChildOf` is registered by every `World` and uses `CleanupPolicy.Delete`. Custom relationships default to `Remove`; set `world.component(MyRelationship).meta.onDeleteTarget = CleanupPolicy.Delete` if target deletion should cascade.
|
|
506
508
|
|
|
507
509
|
`onDeleteTarget` is independent from `onDelete`: `onDeleteTarget` runs when a relationship target is destroyed, while `onDelete` runs when the relationship component entity itself is destroyed.
|
|
508
510
|
|
|
@@ -511,8 +513,8 @@ Retarget relationships with `entity.set(RelationshipClass, { target })`. `entity
|
|
|
511
513
|
Relationship queries use `target` to follow a relationship from the candidate entity to its target and test that target with another DSL expression. Use `parent` for the built-in `ChildOf` hierarchy:
|
|
512
514
|
|
|
513
515
|
```ts
|
|
514
|
-
world.query("body-friends").
|
|
515
|
-
world.query("positioned-children").
|
|
516
|
+
world.query("body-friends").with({ all: [Body, { target: [FriendOf, [Position, Velocity]] }] });
|
|
517
|
+
world.query("positioned-children").with({ all: [Position, { parent: Body }] });
|
|
516
518
|
```
|
|
517
519
|
|
|
518
520
|
Tracked queries stay live when either side changes. Adding or removing `FriendOf` on the candidate re-routes the candidate normally; adding or removing `Position` / `Velocity` on the target also refreshes every candidate that points at that target. Nested `target` works the same way across multiple hops.
|
|
@@ -530,7 +532,7 @@ Component injection supports lowercase `down` in `forEach` and system `each` inj
|
|
|
530
532
|
```ts
|
|
531
533
|
world
|
|
532
534
|
.query("parents")
|
|
533
|
-
.
|
|
535
|
+
.with(Body)
|
|
534
536
|
.forEach([Body, { down: [ChildOf, [Position]] }], (parent, [body, childPos]) => {
|
|
535
537
|
// One call per ChildOf child of parent. childPos is undefined when that child lacks Position.
|
|
536
538
|
});
|
|
@@ -547,17 +549,18 @@ Two details matter:
|
|
|
547
549
|
|
|
548
550
|
Systems are created via `world.system(name)` and configured through a fluent builder. Every method returns `this` for chaining. `System` extends `Query`, so the membership / enter / exit / update / sort APIs are shared.
|
|
549
551
|
|
|
550
|
-
#### `.
|
|
552
|
+
#### `.with(...specs)`
|
|
551
553
|
|
|
552
554
|
Declare which entities the system tracks.
|
|
553
555
|
|
|
554
556
|
```ts
|
|
555
|
-
.
|
|
556
|
-
.
|
|
557
|
-
.
|
|
558
|
-
.
|
|
559
|
-
.
|
|
560
|
-
.
|
|
557
|
+
.with(Position, Velocity) // explicit component list
|
|
558
|
+
.with([Position, Velocity]) // array shorthand
|
|
559
|
+
.with({ all: [Position, { any: [Sprite, Container] }] }) // compound
|
|
560
|
+
.with({ not: Invisible })
|
|
561
|
+
.without(Invisible) // shorthand for .with({ not: Invisible })
|
|
562
|
+
.with({ target: [FriendOf, Position] }) // relationship target has Position
|
|
563
|
+
.with({ parent: Body }) // ChildOf target has Body
|
|
561
564
|
```
|
|
562
565
|
|
|
563
566
|
**Query operators:**
|
|
@@ -578,31 +581,31 @@ Declare which entities the system tracks.
|
|
|
578
581
|
|
|
579
582
|
`target` and `source` use tuple form only: `{ target: [RelationshipClass, innerDSL] }` / `{ source: [RelationshipClass, innerDSL] }`. The first element must be a component that extends `Relationship`; passing a normal component throws. `target` evaluates the inner DSL against the relationship target; `source` evaluates it against each child/source and matches when at least one child/source matches. Because `source` and `children` are non-reactive, use them with `world.filter(...)`, not tracked queries or systems.
|
|
580
583
|
|
|
581
|
-
Component injection also supports lowercase relationship markers. `
|
|
584
|
+
Component injection also supports lowercase relationship markers. `target` works in `forEach`, `each`, `enter`, and `exit` injection lists. It follows the relationship target and flattens the requested target components into the callback tuple:
|
|
582
585
|
|
|
583
586
|
```ts
|
|
584
587
|
world
|
|
585
588
|
.query("friends")
|
|
586
|
-
.
|
|
587
|
-
.forEach([Body, {
|
|
589
|
+
.with({ target: [FriendOf, Position] })
|
|
590
|
+
.forEach([Body, { target: [FriendOf, [Position, Velocity]] }], (e, [body, pos, vel]) => {
|
|
588
591
|
// body is from e; pos and vel are from e.parent(FriendOf)
|
|
589
592
|
// target-side injected components are undefined if the target is absent or lacks them.
|
|
590
593
|
});
|
|
591
594
|
```
|
|
592
595
|
|
|
593
|
-
On `exit`, direct component injection is snapshot-stable when the exiting component was just removed. `
|
|
596
|
+
On `exit`, direct component injection is snapshot-stable when the exiting component was just removed. `target` injection resolves the target live at callback time, so target-side slots can be `undefined` if the exit was caused by the target losing that component.
|
|
594
597
|
|
|
595
598
|
Lowercase `down` is allowed only in `forEach` and system `each`, where fan-out has a clear meaning. Only one `down` marker is allowed per injection tuple.
|
|
596
599
|
|
|
597
600
|
#### Iter cursor
|
|
598
601
|
|
|
599
|
-
Pass `Iter` as the first argument to `each`, `forEach`, `enter`, `exit`, `update`, or `
|
|
602
|
+
Pass `Iter` as the first argument to `each`, `forEach`, `enter`, `exit`, `update`, or `orderBy` to receive a reusable cursor object instead of the bare entity. The cursor exposes:
|
|
600
603
|
|
|
601
604
|
- `it.entity` — the visited entity.
|
|
602
|
-
- `it.src` — a same-length array of source entities: the visited entity for a directly-injected component, the relationship target for
|
|
605
|
+
- `it.src` — a same-length array of source entities: the visited entity for a directly-injected component, the relationship target for a `target`-injected one, or the specific child for a `down`-injected one.
|
|
603
606
|
|
|
604
607
|
```ts
|
|
605
|
-
system.each(Iter, [Body, {
|
|
608
|
+
system.each(Iter, [Body, { target: [ChildOf, [Position]] }], (it, [body, pos]) => {
|
|
606
609
|
it.entity; // the visited entity
|
|
607
610
|
it.src[0]; // entity `body` was read from (the visited entity)
|
|
608
611
|
it.src[1]; // entity `pos` was read from (the ChildOf target)
|
|
@@ -611,14 +614,14 @@ system.each(Iter, [Body, { up: [ChildOf, [Position]] }], (it, [body, pos]) => {
|
|
|
611
614
|
|
|
612
615
|
When `Iter` is **not** requested, the original code path runs with zero per-entity overhead. Dispatch to the cursor or non-cursor path happens once at setup time — not per entity. A single `Iter` instance is mutated before each callback; read what you need inside the callback but do not retain the cursor or its `src` array across callbacks.
|
|
613
616
|
|
|
614
|
-
`enter` and `exit` allocate a fresh `Iter` per event (they fire reentrantly during command routing). `each` / `forEach` / `update` reuse a single `Iter` instance for the whole pass; `
|
|
617
|
+
`enter` and `exit` allocate a fresh `Iter` per event (they fire reentrantly during command routing). `each` / `forEach` / `update` reuse a single `Iter` instance for the whole pass; `orderBy` reuses two (one per side of the comparison). Because a plain `Query.update` callback fires synchronously during command routing, a nested mutation triggered from inside it can re-enter `update` and overwrite that reused cursor and tuple mid-callback — snapshot any `it`, `it.src`, or tuple values you need before triggering further mutations.
|
|
615
618
|
|
|
616
619
|
Class-valued query terms are components. Register component classes before using them in a `QueryDSL`; an unregistered class throws. Use `{ test: fn }` for arbitrary predicate functions.
|
|
617
620
|
|
|
618
|
-
**Type inference.** `
|
|
621
|
+
**Type inference.** `with()` records inferrable component classes as a type parameter `R` on the system. Callbacks in `.orderBy()`, `.each()`, and `.update()` injection treat those components as non-nullable — no `!` needed. For complex `with()` expressions the type system can't introspect, supply a `hint` field:
|
|
619
622
|
|
|
620
623
|
```ts
|
|
621
|
-
.
|
|
624
|
+
.with({ with: { all: [Position, Velocity] }, hint: [Position, Velocity] })
|
|
622
625
|
.each([Position, Velocity], (e, [pos, vel]) => {
|
|
623
626
|
pos.x += vel.vx; // pos and vel are non-null
|
|
624
627
|
});
|
|
@@ -646,7 +649,7 @@ Fires once when an entity first matches the system.
|
|
|
646
649
|
|
|
647
650
|
#### `.exit(callback)` / `.exit(inject, callback)`
|
|
648
651
|
|
|
649
|
-
Fires when an entity leaves the system (component removed or entity destroyed). Direct components removed in the same frame are still resolvable in `inject`; `
|
|
652
|
+
Fires when an entity leaves the system (component removed or entity destroyed). Direct components removed in the same frame are still resolvable in `inject`; `target` injection resolves relationship targets live.
|
|
650
653
|
|
|
651
654
|
```ts
|
|
652
655
|
.exit([Sprite], (e, [sprite]) => sprite.destroy());
|
|
@@ -664,34 +667,34 @@ Fires when `entity.modified(ComponentClass)` is called for the watched component
|
|
|
664
667
|
});
|
|
665
668
|
```
|
|
666
669
|
|
|
667
|
-
If `
|
|
670
|
+
If `with()` has not been called, `update` automatically expands the implicit component predicate to require the watched component.
|
|
668
671
|
|
|
669
672
|
#### `.each(components, callback)`
|
|
670
673
|
|
|
671
674
|
Fires every tick for **every tracked entity**, regardless of whether anything changed. Use it for per-entity logic that must run every frame. Implies `.track()`. Only one `each` per system.
|
|
672
675
|
|
|
673
676
|
```ts
|
|
674
|
-
.
|
|
677
|
+
.with(Position, Velocity)
|
|
675
678
|
.each([Position, Velocity], (e, [pos, vel]) => {
|
|
676
679
|
pos.x += vel.vx;
|
|
677
680
|
});
|
|
678
681
|
```
|
|
679
682
|
|
|
680
|
-
#### `.
|
|
683
|
+
#### `.orderBy(components, compare)`
|
|
681
684
|
|
|
682
685
|
Store matched entities in a custom order determined by `compare`. Implies `.track()`. Iterating the system, `forEach`, and `each` walks entities in sorted order.
|
|
683
686
|
|
|
684
687
|
```ts
|
|
685
688
|
world
|
|
686
689
|
.system("Render")
|
|
687
|
-
.
|
|
688
|
-
.
|
|
690
|
+
.with(Position, Sprite)
|
|
691
|
+
.orderBy([Position], (_entityA, [posA], _entityB, [posB]) => posA.z - posB.z)
|
|
689
692
|
.each([Position, Sprite], (e, [pos, sprite]) => sprite.draw(pos.x, pos.y));
|
|
690
693
|
```
|
|
691
694
|
|
|
692
695
|
#### `.track()`
|
|
693
696
|
|
|
694
|
-
Enable entity tracking without an `each` callback — exposes matched entities via `system.count`, `system.has(e)`, and direct iteration. `each` and `
|
|
697
|
+
Enable entity tracking without an `each` callback — exposes matched entities via `system.count`, `system.has(e)`, and direct iteration. `each` and `orderBy` imply `track` automatically. When called after `world.start()`, immediately backfills existing matched entities.
|
|
695
698
|
|
|
696
699
|
#### `.run(callback)`
|
|
697
700
|
|
|
@@ -708,7 +711,7 @@ Fires every tick when the system's phase runs, regardless of entity state. Use f
|
|
|
708
711
|
Pause and resume a system at runtime. While disabled the system is effectively invisible: the inbox is cleared immediately, any new `enter`, `exit`, or `update` events are silently dropped, `run` and `each` callbacks do not fire, and the system skips its `_run` entirely. Entity membership in the underlying query is still maintained, so the tracked set remains correct and the system resumes cleanly when re-enabled. Events that occurred while the system was disabled are **not** replayed.
|
|
709
712
|
|
|
710
713
|
```ts
|
|
711
|
-
const ai = world.system("AI").
|
|
714
|
+
const ai = world.system("AI").with(Enemy).run(tickAI);
|
|
712
715
|
|
|
713
716
|
// Pause AI processing during a cutscene:
|
|
714
717
|
ai.disable();
|
|
@@ -732,8 +735,8 @@ Permanently remove this system from the world. Calls `disable()` first (clearing
|
|
|
732
735
|
```ts
|
|
733
736
|
const projectiles = world
|
|
734
737
|
.query("Projectiles")
|
|
735
|
-
.
|
|
736
|
-
.
|
|
738
|
+
.with(Position, Velocity)
|
|
739
|
+
.orderBy([Position], (_entityA, [a], _entityB, [b]) => a.z - b.z)
|
|
737
740
|
.enter([Position], (e, [pos]) => {
|
|
738
741
|
pos.x = spawnX;
|
|
739
742
|
});
|
|
@@ -747,12 +750,12 @@ console.log(projectiles.count, "active projectiles");
|
|
|
747
750
|
|
|
748
751
|
| Method | Description |
|
|
749
752
|
| ------------------------------------------------------- | --------------------------------------------------------------------------------------------------- |
|
|
750
|
-
| `.
|
|
751
|
-
| `.
|
|
753
|
+
| `.with(...specs)` | Add membership predicates using `QuerySpec` expressions. |
|
|
754
|
+
| `.without(dsl)` | Add a negated membership predicate; shorthand for `.with({ not: dsl })`. |
|
|
752
755
|
| `.enter(callback)` / `.enter(inject, callback)` | Fires when an entity joins the query. |
|
|
753
756
|
| `.exit(callback)` / `.exit(inject, callback)` | Fires when an entity leaves the query. |
|
|
754
757
|
| `.update(C, callback)` / `.update(C, inject, callback)` | Fires when `C` is modified on a tracked entity. Callback receives `(entity, component, injected?)`. |
|
|
755
|
-
| `.
|
|
758
|
+
| `.orderBy(components, compare)` | Store matched entities in sorted order. Comparator receives `(entityA, tupleA, entityB, tupleB)`. |
|
|
756
759
|
| `.track()` | Enable tracking. Backfills when called after `start()`. |
|
|
757
760
|
| `.belongs(e)` | Returns `true` if the entity satisfies the predicate. |
|
|
758
761
|
| `.count` | Number of currently tracked entities. |
|
|
@@ -767,7 +770,7 @@ console.log(projectiles.count, "active projectiles");
|
|
|
767
770
|
`destroy()` permanently removes a standalone query from the world. Entity references are silently purged (no `exit` callbacks fire), the tracked set is cleared, and the `world` reference is set to `undefined`. Any further use of the object is **undefined behavior**.
|
|
768
771
|
|
|
769
772
|
```ts
|
|
770
|
-
const q = world.query("Temporary").
|
|
773
|
+
const q = world.query("Temporary").with(Position);
|
|
771
774
|
// ... use q.count, q.has(e), or iterate q ...
|
|
772
775
|
q.destroy();
|
|
773
776
|
```
|
|
@@ -791,14 +794,14 @@ const f = world.filter([Position, Velocity]);
|
|
|
791
794
|
|
|
792
795
|
`forEach` runs inside a deferred scope, so mutations made by the callback are batched and become visible after iteration finishes.
|
|
793
796
|
|
|
794
|
-
**Type inference.** Component classes the type system can extract from the DSL (plain component classes, plain arrays, `only`, and `all` of those) are non-nullable in the callback tuple. For the rest, supply a `
|
|
797
|
+
**Type inference.** Component classes the type system can extract from the DSL (plain component classes, plain arrays, `only`, and `all` of those) are non-nullable in the callback tuple. For the rest, supply a `hint` field:
|
|
795
798
|
|
|
796
799
|
```ts
|
|
797
800
|
// Auto-deduced — both non-null:
|
|
798
801
|
world.filter([Position, Velocity]).forEach([Position, Velocity], (e, [pos, vel]) => { ... });
|
|
799
802
|
|
|
800
803
|
// Manual hint for any / not / test:
|
|
801
|
-
world.filter({ any: [Position, Velocity] }, [Position]).forEach([Position], (e, [pos]) => pos.x);
|
|
804
|
+
world.filter({ with: { any: [Position, Velocity] }, hint: [Position] }).forEach([Position], (e, [pos]) => pos.x);
|
|
802
805
|
```
|
|
803
806
|
|
|
804
807
|
A `Filter` holds no tracked set, makes no registration calls, and needs no `destroy()`.
|
package/dist/component.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Entity } from "./entity/index.js";
|
|
2
|
-
import type { ComponentType } from "./component_meta.js";
|
|
3
|
-
export { CleanupPolicy, ComponentMeta, type ComponentClass, type ComponentInstance, type ComponentRef, type ComponentRefArray, type ComponentType, type Hook, } from "./component_meta.js";
|
|
2
|
+
import type { ComponentInstance, ComponentType } from "./component_meta.js";
|
|
3
|
+
export { CleanupPolicy, ComponentMeta, Relationship, type ComponentClass, type ComponentInstance, type ComponentRef, type ComponentRefArray, type ComponentType, type Hook, } from "./component_meta.js";
|
|
4
4
|
/** A component entity created by {@link World.component}. */
|
|
5
5
|
export declare class Component<T extends ComponentType = ComponentType> extends Entity {
|
|
6
6
|
/**
|
|
@@ -19,6 +19,7 @@ export declare class Component<T extends ComponentType = ComponentType> extends
|
|
|
19
19
|
* ```
|
|
20
20
|
*/
|
|
21
21
|
onAdd(handler: (entity: Entity, c: InstanceType<T>) => void): this;
|
|
22
|
+
onAdd(handler: (entity: Entity, c: ComponentInstance) => void): this;
|
|
22
23
|
/**
|
|
23
24
|
* Register a callback fired each time this component is **removed** from an
|
|
24
25
|
* entity, or when the owning entity is destroyed.
|
|
@@ -36,6 +37,7 @@ export declare class Component<T extends ComponentType = ComponentType> extends
|
|
|
36
37
|
* ```
|
|
37
38
|
*/
|
|
38
39
|
onRemove(handler: (entity: Entity, c: InstanceType<T>) => void): this;
|
|
40
|
+
onRemove(handler: (entity: Entity, c: ComponentInstance) => void): this;
|
|
39
41
|
/**
|
|
40
42
|
* Register a callback fired each time this component's data is **set or
|
|
41
43
|
* modified**.
|
|
@@ -59,4 +61,5 @@ export declare class Component<T extends ComponentType = ComponentType> extends
|
|
|
59
61
|
* ```
|
|
60
62
|
*/
|
|
61
63
|
onSet(handler: (entity: Entity, c: InstanceType<T>) => void): this;
|
|
64
|
+
onSet(handler: (entity: Entity, c: ComponentInstance) => void): this;
|
|
62
65
|
}
|
package/dist/component.js
CHANGED
|
@@ -1,70 +1,17 @@
|
|
|
1
1
|
import { Entity } from "./entity/index.js";
|
|
2
|
-
export { CleanupPolicy, ComponentMeta, } from "./component_meta.js";
|
|
2
|
+
export { CleanupPolicy, ComponentMeta, Relationship, } from "./component_meta.js";
|
|
3
3
|
/** A component entity created by {@link World.component}. */
|
|
4
4
|
export class Component extends Entity {
|
|
5
|
-
/**
|
|
6
|
-
* Register a callback fired each time this component is **added** to an entity.
|
|
7
|
-
*
|
|
8
|
-
* The handler is called with the owning entity and the freshly created
|
|
9
|
-
* component instance. Use it to initialise external state (e.g. scene objects)
|
|
10
|
-
* that should exist for the lifetime of the component.
|
|
11
|
-
*
|
|
12
|
-
* @param handler - Callback invoked with `(entity, componentInstance)`.
|
|
13
|
-
* @returns This `Component` entity, for chaining.
|
|
14
|
-
*
|
|
15
|
-
* @example
|
|
16
|
-
* ```ts
|
|
17
|
-
* world.component(Sprite).onAdd((entity, sprite) => sprite.initialize(scene));
|
|
18
|
-
* ```
|
|
19
|
-
*/
|
|
20
5
|
onAdd(handler) {
|
|
21
|
-
|
|
6
|
+
super.onAdd(handler);
|
|
22
7
|
return this;
|
|
23
8
|
}
|
|
24
|
-
/**
|
|
25
|
-
* Register a callback fired each time this component is **removed** from an
|
|
26
|
-
* entity, or when the owning entity is destroyed.
|
|
27
|
-
*
|
|
28
|
-
* The handler is called with the owning entity and the component instance that
|
|
29
|
-
* is about to be detached. Use it to clean up any external state associated
|
|
30
|
-
* with the component.
|
|
31
|
-
*
|
|
32
|
-
* @param handler - Callback invoked with `(entity, componentInstance)`.
|
|
33
|
-
* @returns This `Component` entity, for chaining.
|
|
34
|
-
*
|
|
35
|
-
* @example
|
|
36
|
-
* ```ts
|
|
37
|
-
* world.component(Sprite).onRemove((entity, sprite) => sprite.destroy(scene));
|
|
38
|
-
* ```
|
|
39
|
-
*/
|
|
40
9
|
onRemove(handler) {
|
|
41
|
-
|
|
10
|
+
super.onRemove(handler);
|
|
42
11
|
return this;
|
|
43
12
|
}
|
|
44
|
-
/**
|
|
45
|
-
* Register a callback fired each time this component's data is **set or
|
|
46
|
-
* modified**.
|
|
47
|
-
*
|
|
48
|
-
* Fires when:
|
|
49
|
-
* - `entity.set(C, props)` applies data to the component.
|
|
50
|
-
* - `entity.attach(instance)` stores an existing instance.
|
|
51
|
-
* - `entity.modified(C)` is called after in-place mutation.
|
|
52
|
-
* - A system's `getMut(C)` marks the component dirty.
|
|
53
|
-
*
|
|
54
|
-
* The handler receives the owning entity and the current component instance.
|
|
55
|
-
* Component instances do not carry entity references, which is why the entity
|
|
56
|
-
* is passed explicitly.
|
|
57
|
-
*
|
|
58
|
-
* @param handler - Callback invoked with `(entity, componentInstance)`.
|
|
59
|
-
* @returns This `Component` entity, for chaining.
|
|
60
|
-
*
|
|
61
|
-
* @example
|
|
62
|
-
* ```ts
|
|
63
|
-
* world.component(Transform).onSet((entity, t) => renderer.syncTransform(entity.eid, t));
|
|
64
|
-
* ```
|
|
65
|
-
*/
|
|
66
13
|
onSet(handler) {
|
|
67
|
-
|
|
14
|
+
super.onSet(handler);
|
|
68
15
|
return this;
|
|
69
16
|
}
|
|
70
17
|
}
|
package/dist/component.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"component.js","sourceRoot":"","sources":["../src/component.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAG3C,OAAO,EACL,aAAa,EACb,aAAa,
|
|
1
|
+
{"version":3,"file":"component.js","sourceRoot":"","sources":["../src/component.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAG3C,OAAO,EACL,aAAa,EACb,aAAa,EACb,YAAY,GAOb,MAAM,qBAAqB,CAAC;AAE7B,6DAA6D;AAC7D,MAAM,OAAO,SAAmD,SAAQ,MAAM;IAkB5D,KAAK,CAAC,OAAyC;QAC7D,KAAK,CAAC,KAAK,CAAC,OAAyD,CAAC,CAAC;QACvE,OAAO,IAAI,CAAC;IACd,CAAC;IAoBe,QAAQ,CAAC,OAAyC;QAChE,KAAK,CAAC,QAAQ,CAAC,OAAyD,CAAC,CAAC;QAC1E,OAAO,IAAI,CAAC;IACd,CAAC;IA0Be,KAAK,CAAC,OAAyC;QAC7D,KAAK,CAAC,KAAK,CAAC,OAAyD,CAAC,CAAC;QACvE,OAAO,IAAI,CAAC;IACd,CAAC;CACF"}
|
package/dist/component_meta.d.ts
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
import { BitPtr } from "./util/bitset.js";
|
|
2
|
-
import type { Entity, EntityClass } from "./entity/index.js";
|
|
2
|
+
import type { EID, Entity, EntityClass } from "./entity/index.js";
|
|
3
3
|
export declare enum CleanupPolicy {
|
|
4
4
|
Throw = "throw",
|
|
5
5
|
Remove = "remove",
|
|
6
6
|
Delete = "delete"
|
|
7
7
|
}
|
|
8
|
+
/** Base class for relationship components that point at another entity. */
|
|
9
|
+
export declare abstract class Relationship {
|
|
10
|
+
target: Entity;
|
|
11
|
+
}
|
|
8
12
|
/** A component instance. Components are plain objects created with a no-arg constructor. */
|
|
9
13
|
export type ComponentInstance = object;
|
|
10
14
|
/** A component constructor used as a component key. */
|
|
@@ -12,7 +16,7 @@ export type ComponentType<T extends ComponentInstance = ComponentInstance> = new
|
|
|
12
16
|
/** A component class constructor that can be instantiated by entity.add/set. */
|
|
13
17
|
export type ComponentClass<T extends ComponentInstance = ComponentInstance> = new () => T;
|
|
14
18
|
/** A component class constructor, component eid, or entity component key. */
|
|
15
|
-
export type ComponentRef =
|
|
19
|
+
export type ComponentRef = EID | ComponentType | EntityClass | Entity;
|
|
16
20
|
/**
|
|
17
21
|
* Lifecycle hook for a registered component class. Exposed by
|
|
18
22
|
* {@link World.component}.
|
|
@@ -54,10 +58,13 @@ export interface Hook<C extends ComponentInstance = ComponentInstance> {
|
|
|
54
58
|
*/
|
|
55
59
|
onRemove(handler: (entity: Entity, c: C) => void): Hook<C>;
|
|
56
60
|
/**
|
|
57
|
-
* Register a handler invoked when a component's data
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
* component.
|
|
61
|
+
* Register a handler invoked when a component's data is set or modified.
|
|
62
|
+
*
|
|
63
|
+
* Fires when:
|
|
64
|
+
* - `entity.set(C, props)` applies data to the component.
|
|
65
|
+
* - `entity.attach(instance)` stores an existing instance.
|
|
66
|
+
* - `entity.modified(C)` is called after in-place mutation.
|
|
67
|
+
* - A system's `getMut(C)` marks the component dirty.
|
|
61
68
|
*
|
|
62
69
|
* @param handler - Receives the entity and component instance whose data changed.
|
|
63
70
|
* @returns This hook, for chaining.
|
|
@@ -76,7 +83,7 @@ export declare class ComponentMeta implements Hook<ComponentInstance> {
|
|
|
76
83
|
/** The component class constructor this meta represents. */
|
|
77
84
|
readonly Class: ComponentType;
|
|
78
85
|
/** Component entity id assigned at registration time. */
|
|
79
|
-
readonly eid:
|
|
86
|
+
readonly eid: EID;
|
|
80
87
|
/** Pre-computed bit-pointer into the entity archetype {@link Bitset}. */
|
|
81
88
|
readonly bitPtr: BitPtr;
|
|
82
89
|
/** Whether this component is a relationship pointing at another entity. */
|
|
@@ -86,7 +93,7 @@ export declare class ComponentMeta implements Hook<ComponentInstance> {
|
|
|
86
93
|
/** Applied to entities targeting a deleted entity through this relationship. */
|
|
87
94
|
onDeleteTarget: CleanupPolicy;
|
|
88
95
|
private _usageCount;
|
|
89
|
-
constructor(Class: ComponentType,
|
|
96
|
+
constructor(Class: ComponentType, ceid: EID);
|
|
90
97
|
/** Number of entities currently carrying this component eid. */
|
|
91
98
|
get usageCount(): number;
|
|
92
99
|
/** @inheritdoc */
|
package/dist/component_meta.js
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { BitPtr } from "./util/bitset.js";
|
|
2
|
-
import { Relationship } from "./relationship.js";
|
|
3
2
|
export var CleanupPolicy;
|
|
4
3
|
(function (CleanupPolicy) {
|
|
5
4
|
CleanupPolicy["Throw"] = "throw";
|
|
6
5
|
CleanupPolicy["Remove"] = "remove";
|
|
7
6
|
CleanupPolicy["Delete"] = "delete";
|
|
8
7
|
})(CleanupPolicy || (CleanupPolicy = {}));
|
|
8
|
+
/** Base class for relationship components that point at another entity. */
|
|
9
|
+
export class Relationship {
|
|
10
|
+
}
|
|
9
11
|
/**
|
|
10
12
|
* Bookkeeping record produced for each component eid.
|
|
11
13
|
*
|
|
@@ -15,7 +17,7 @@ export var CleanupPolicy;
|
|
|
15
17
|
* the meta object.
|
|
16
18
|
*/
|
|
17
19
|
export class ComponentMeta {
|
|
18
|
-
constructor(Class,
|
|
20
|
+
constructor(Class, ceid) {
|
|
19
21
|
/** Applied to entities carrying this component when this component entity is deleted. */
|
|
20
22
|
this.onDelete = CleanupPolicy.Throw;
|
|
21
23
|
/** Applied to entities targeting a deleted entity through this relationship. */
|
|
@@ -28,8 +30,8 @@ export class ComponentMeta {
|
|
|
28
30
|
this._exclusive = undefined;
|
|
29
31
|
this._usageCount = 0;
|
|
30
32
|
this.Class = Class;
|
|
31
|
-
this.eid =
|
|
32
|
-
this.bitPtr = new BitPtr(
|
|
33
|
+
this.eid = ceid;
|
|
34
|
+
this.bitPtr = new BitPtr(ceid);
|
|
33
35
|
this.isRelationship = Class.prototype instanceof Relationship;
|
|
34
36
|
}
|
|
35
37
|
/** Number of entities currently carrying this component eid. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"component_meta.js","sourceRoot":"","sources":["../src/component_meta.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"component_meta.js","sourceRoot":"","sources":["../src/component_meta.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAG1C,MAAM,CAAN,IAAY,aAIX;AAJD,WAAY,aAAa;IACvB,gCAAe,CAAA;IACf,kCAAiB,CAAA;IACjB,kCAAiB,CAAA;AACnB,CAAC,EAJW,aAAa,KAAb,aAAa,QAIxB;AAED,2EAA2E;AAC3E,MAAM,OAAgB,YAAY;CAEjC;AAyED;;;;;;;GAOG;AACH,MAAM,OAAO,aAAa;IA+BxB,YAAY,KAAoB,EAAE,IAAS;QArB3C,yFAAyF;QAClF,aAAQ,GAAG,aAAa,CAAC,KAAK,CAAC;QACtC,gFAAgF;QACzE,mBAAc,GAAG,aAAa,CAAC,MAAM,CAAC;QAE7C;;;;WAIG;QACI,eAAU,GAAgC,SAAS,CAAC;QASnD,gBAAW,GAAG,CAAC,CAAC;QAGtB,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,CAAC,GAAG,GAAG,IAAI,CAAC;QAChB,IAAI,CAAC,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,CAAC;QAC/B,IAAI,CAAC,cAAc,GAAG,KAAK,CAAC,SAAS,YAAY,YAAY,CAAC;IAChE,CAAC;IAED,gEAAgE;IAChE,IAAW,UAAU;QACnB,OAAO,IAAI,CAAC,WAAW,CAAC;IAC1B,CAAC;IAED,gBAAgB;IACT,eAAe;QACpB,IAAI,CAAC,WAAW,EAAE,CAAC;IACrB,CAAC;IAED,gBAAgB;IACT,eAAe;QACpB,IAAI,IAAI,CAAC,WAAW,GAAG,CAAC,EAAE,CAAC;YACzB,IAAI,CAAC,WAAW,EAAE,CAAC;QACrB,CAAC;IACH,CAAC;IAED,kBAAkB;IACX,KAAK,CAAC,OAAuD;QAClE,CAAC,IAAI,CAAC,cAAc,KAAnB,IAAI,CAAC,cAAc,GAAK,EAAE,EAAC,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QAC9C,OAAO,IAAI,CAAC;IACd,CAAC;IAED,kBAAkB;IACX,QAAQ,CAAC,OAAuD;QACrE,CAAC,IAAI,CAAC,iBAAiB,KAAtB,IAAI,CAAC,iBAAiB,GAAK,EAAE,EAAC,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QACjD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,kBAAkB;IACX,KAAK,CAAC,OAAuD;QAClE,CAAC,IAAI,CAAC,cAAc,KAAnB,IAAI,CAAC,cAAc,GAAK,EAAE,EAAC,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QAC9C,OAAO,IAAI,CAAC;IACd,CAAC;CACF"}
|