@vworlds/vecs 1.0.25 → 1.0.26
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 +29 -778
- package/dist/component.d.ts +1 -1
- package/dist/component.js +1 -1
- package/dist/component.js.map +1 -1
- package/dist/component_meta.d.ts +5 -1
- package/dist/component_meta.js +10 -0
- package/dist/component_meta.js.map +1 -1
- package/dist/dsl.d.ts +2 -2
- package/dist/entity/entity.base.d.ts +2 -18
- package/dist/entity/entity.base.js +2 -20
- package/dist/entity/entity.base.js.map +1 -1
- package/dist/entity/entity.components.d.ts +1 -0
- package/dist/entity/entity.components.js +50 -0
- package/dist/entity/entity.components.js.map +1 -1
- package/dist/entity/entity.lifecycle.d.ts +3 -3
- package/dist/entity/entity.lifecycle.js +6 -9
- package/dist/entity/entity.lifecycle.js.map +1 -1
- package/dist/entity/entity.relationships.js +2 -2
- package/dist/entity/entity.relationships.js.map +1 -1
- package/dist/filter.d.ts +1 -0
- package/dist/filter.js +3 -2
- package/dist/filter.js.map +1 -1
- package/dist/index.d.ts +4 -2
- package/dist/index.js +4 -2
- package/dist/index.js.map +1 -1
- package/dist/inject.d.ts +3 -2
- package/dist/inject.js.map +1 -1
- package/dist/modules/implements.d.ts +14 -0
- package/dist/modules/implements.js +98 -0
- package/dist/modules/implements.js.map +1 -0
- package/dist/package.json +1 -4
- package/dist/query/callbacks.d.ts +6 -2
- package/dist/query/callbacks.js +5 -2
- package/dist/query/callbacks.js.map +1 -1
- package/dist/query/query.d.ts +14 -1
- package/dist/query/query.js +26 -15
- package/dist/query/query.js.map +1 -1
- package/dist/system.d.ts +5 -4
- package/dist/system.js +17 -6
- package/dist/system.js.map +1 -1
- package/dist/util/array_map.d.ts +70 -12
- package/dist/util/array_map.js +113 -26
- package/dist/util/array_map.js.map +1 -1
- package/dist/util/bitset.js +0 -17
- package/dist/util/bitset.js.map +1 -1
- package/dist/util/events.d.ts +42 -12
- package/dist/util/events.js +94 -43
- package/dist/util/events.js.map +1 -1
- package/dist/util/ordered_set.js +43 -19
- package/dist/util/ordered_set.js.map +1 -1
- package/dist/world/world.deferred.js +2 -0
- package/dist/world/world.deferred.js.map +1 -1
- package/dist/world/world.entities.d.ts +8 -1
- package/dist/world/world.entities.js +25 -6
- package/dist/world/world.entities.js.map +1 -1
- package/dist/world/world.js +8 -1
- package/dist/world/world.js.map +1 -1
- package/dist/world/world.queries.js +6 -1
- package/dist/world/world.queries.js.map +1 -1
- package/dist/world/world.storage.d.ts +2 -2
- package/dist/world/world.storage.js +6 -3
- package/dist/world/world.storage.js.map +1 -1
- package/docs/README.md +50 -0
- package/docs/components.md +267 -0
- package/docs/concepts.md +86 -0
- package/docs/design-guide.md +506 -0
- package/docs/entities.md +175 -0
- package/docs/execution-model.md +173 -0
- package/docs/getting-started.md +215 -0
- package/docs/glossary.md +113 -0
- package/docs/modules.md +108 -0
- package/docs/queries-and-filters.md +187 -0
- package/docs/relationships.md +148 -0
- package/docs/systems.md +311 -0
- package/docs/utilities.md +139 -0
- package/package.json +1 -4
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
# Components
|
|
2
|
+
|
|
3
|
+
Define plain-data components, register them with the world, and configure their lifecycle: hooks,
|
|
4
|
+
exclusivity, cleanup policies, interface aliases, and singletons.
|
|
5
|
+
|
|
6
|
+
## Components are plain data classes
|
|
7
|
+
|
|
8
|
+
A component is an ordinary class with field initializers and a no-argument constructor. It does
|
|
9
|
+
not inherit from a vecs base class — vecs instantiates it with `new ComponentClass()`:
|
|
10
|
+
|
|
11
|
+
```ts
|
|
12
|
+
class Position {
|
|
13
|
+
x = 0;
|
|
14
|
+
y = 0;
|
|
15
|
+
}
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
| Rule | Description |
|
|
19
|
+
| ------------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
|
|
20
|
+
| Plain class | Ordinary classes with field initializers; methods are allowed but behavior belongs in systems. |
|
|
21
|
+
| No-arg construction | vecs calls `new ComponentClass()`; omit the constructor or take no parameters. |
|
|
22
|
+
| Explicit registration | Call `world.component(C)` before using the class as a component anywhere. |
|
|
23
|
+
| Shared instances possible | `entity.attach(instance)` stores the exact passed object (see [Entities](./entities.md#attach-storing-an-existing-instance)). |
|
|
24
|
+
| Manual dirty marking | After mutating fields directly, call `entity.modified(C)` so hooks, queries, and systems react. |
|
|
25
|
+
|
|
26
|
+
Keep components small and single-purpose — `Position {x,y}` and `Rotation {angle}`, not a
|
|
27
|
+
`Transform` god-component. Absence of a component encodes "not applicable", and small components
|
|
28
|
+
compose into precise queries. The reasoning is developed in the
|
|
29
|
+
[Design guide](./design-guide.md#3-components).
|
|
30
|
+
|
|
31
|
+
## Registration: `world.component`
|
|
32
|
+
|
|
33
|
+
Register every component class before using it in `add`, `set`, `get`, `with`, `filter`, hook
|
|
34
|
+
registration, or `setExclusiveComponents`. Registration can happen at any time:
|
|
35
|
+
|
|
36
|
+
```ts
|
|
37
|
+
// Auto-assigned component id (drawn from the "component" id pool, 1–899 by default):
|
|
38
|
+
const positionComponent = world.component(Position);
|
|
39
|
+
|
|
40
|
+
// Explicit numeric id (e.g. a server-assigned stable id):
|
|
41
|
+
world.component(Position, 42);
|
|
42
|
+
|
|
43
|
+
// Access component metadata (numeric id, cleanup policy, usage count, …):
|
|
44
|
+
const meta = world.component(Position).meta;
|
|
45
|
+
|
|
46
|
+
// Name the component entity, then resolve it by string later:
|
|
47
|
+
world.component(Position).name = "Position";
|
|
48
|
+
world.component("Position"); // returns the same Component entity
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
`world.component(Class)` (`lib/vecs/src/world/world.components.ts`) returns the **`Component`
|
|
52
|
+
entity** for that class, creating and registering it on the first call. Subsequent calls with the
|
|
53
|
+
same class return the same entity. The same class can be registered independently in multiple
|
|
54
|
+
worlds — metadata is world-specific. Passing an explicit id for an already-registered class (with
|
|
55
|
+
a different id) throws, as does passing a name with no matching component entity.
|
|
56
|
+
|
|
57
|
+
### Id pools
|
|
58
|
+
|
|
59
|
+
Entity ids are drawn from configurable pools. The defaults
|
|
60
|
+
(`DEFAULT_POOLS` in `lib/vecs/src/world/world.pools.ts`):
|
|
61
|
+
|
|
62
|
+
| Pool | Range | Used by |
|
|
63
|
+
| ----------- | --------- | ----------------- |
|
|
64
|
+
| `component` | 1 – 899 | `world.component` |
|
|
65
|
+
| `module` | 900 – 999 | `world.module` |
|
|
66
|
+
| `entity` | 1000 – ∞ | `world.entity()` |
|
|
67
|
+
|
|
68
|
+
Pass `idPools` to the `World` constructor to change ranges (each entry is an `IdPoolConfig` with
|
|
69
|
+
`name`, `min`, optional `max`, and optional `reuseWaitFrames`). Freed ids wait
|
|
70
|
+
`reuseWaitFrames` frames (default `DEFAULT_ID_REUSE_WAIT_FRAMES = 16`) before reuse, so an id
|
|
71
|
+
released this frame is not immediately recycled. `world.peekNextEid(poolName)` returns the next id
|
|
72
|
+
a pool would allocate without reserving it. Id 0 is reserved.
|
|
73
|
+
|
|
74
|
+
## Hooks: `onAdd`, `onSet`, `onRemove`
|
|
75
|
+
|
|
76
|
+
The `Component` entity exposes lifecycle hooks fired for every entity carrying the component:
|
|
77
|
+
|
|
78
|
+
```ts
|
|
79
|
+
world
|
|
80
|
+
.component(Sprite)
|
|
81
|
+
.onAdd((entity, sprite) => sprite.initialize(scene, entity))
|
|
82
|
+
.onSet((entity, sprite) => sprite.syncToScene(entity))
|
|
83
|
+
.onRemove((entity, sprite) => sprite.destroy(scene, entity));
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
| Hook | Fires when… |
|
|
87
|
+
| ---------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
88
|
+
| `onAdd` | The component is first attached (`add`, or `set` on an entity that lacked it). |
|
|
89
|
+
| `onSet` | `entity.set(C, props)` applies data, `entity.attach(instance)` stores an instance, `entity.modified(C)` is called, or `getMut` marks it dirty. |
|
|
90
|
+
| `onRemove` | The component is detached (`entity.remove(C)`), or the owning entity is destroyed. |
|
|
91
|
+
|
|
92
|
+
Handlers receive `(entity, componentInstance)` — component instances do not carry entity
|
|
93
|
+
references, so the entity is passed explicitly. Hooks fire synchronously when the corresponding
|
|
94
|
+
command is applied (see [Execution model](./execution-model.md#reactive-routing)). Use hooks for
|
|
95
|
+
resource management and cross-cutting reactions that aren't worth a system; use systems when the
|
|
96
|
+
reaction belongs to a phase.
|
|
97
|
+
|
|
98
|
+
## Exclusive component groups
|
|
99
|
+
|
|
100
|
+
`world.setExclusiveComponents(A, B, C)` declares a mutually exclusive group: setting one
|
|
101
|
+
automatically removes the others first.
|
|
102
|
+
|
|
103
|
+
```ts
|
|
104
|
+
world.setExclusiveComponents(Walking, Running, Idle);
|
|
105
|
+
|
|
106
|
+
const e = world.entity();
|
|
107
|
+
e.add(Walking);
|
|
108
|
+
e.add(Running); // Walking is removed first
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Each call defines one independent group. A component belongs to at most one group; calling
|
|
112
|
+
`setExclusiveComponents` again with the same class overwrites its group membership. Safe to call
|
|
113
|
+
at any time. Exclusivity is how you model "exactly one of N" (one renderable per entity, one
|
|
114
|
+
physics shape per body) — and it makes type swaps clean, because replacing the component fires the
|
|
115
|
+
old one's `exit`/`onRemove` automatically.
|
|
116
|
+
|
|
117
|
+
## Cleanup policies: destroying a component entity
|
|
118
|
+
|
|
119
|
+
Every component key is backed by a component entity. What happens when you destroy that entity
|
|
120
|
+
while other entities still carry the component is controlled by `meta.onDelete`
|
|
121
|
+
(`CleanupPolicy` in `lib/vecs/src/component_meta.ts`):
|
|
122
|
+
|
|
123
|
+
| Policy | Meaning |
|
|
124
|
+
| ---------------------- | -------------------------------------------------------------------------------------------------- |
|
|
125
|
+
| `CleanupPolicy.Throw` | Default. Destroying the component entity while other entities carry it throws and changes nothing. |
|
|
126
|
+
| `CleanupPolicy.Remove` | Remove the component from every carrier, then destroy the component entity. |
|
|
127
|
+
| `CleanupPolicy.Delete` | Destroy every carrier entity, then destroy the component entity. |
|
|
128
|
+
|
|
129
|
+
```ts
|
|
130
|
+
import { CleanupPolicy } from "@vworlds/vecs";
|
|
131
|
+
|
|
132
|
+
world.component(Temporary).meta.onDelete = CleanupPolicy.Remove;
|
|
133
|
+
world.component(Temporary).destroy(); // strips Temporary from all carriers
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
The `Throw` default also applies to plain entities used as component keys (`entity.add(keyEntity)`).
|
|
137
|
+
A failed destroy leaves the component entity, its carriers, and the usage count untouched.
|
|
138
|
+
|
|
139
|
+
Relationship components have a second, independent policy — `meta.onDeleteTarget` — that governs
|
|
140
|
+
what happens to _sources_ when a relationship **target** is destroyed. See
|
|
141
|
+
[Relationships](./relationships.md#cleanup-when-a-target-dies).
|
|
142
|
+
|
|
143
|
+
## Interface aliases with `Implements`
|
|
144
|
+
|
|
145
|
+
Use the built-in `Implements` component when one concrete component should be queryable through
|
|
146
|
+
abstract parent or interface component types. The concrete component stays the single source of
|
|
147
|
+
truth; vecs stores the same instance under each alias, so `get(Parent) === get(Concrete)`:
|
|
148
|
+
|
|
149
|
+
```ts
|
|
150
|
+
import { Implements, World } from "@vworlds/vecs";
|
|
151
|
+
|
|
152
|
+
abstract class Food {
|
|
153
|
+
calories = 0;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
abstract class Fruit extends Food {
|
|
157
|
+
color = "";
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
class Orange extends Fruit {
|
|
161
|
+
acidity = 8;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const world = new World();
|
|
165
|
+
world.component(Food);
|
|
166
|
+
world.component(Fruit).set(Implements, { interfaces: [Food] });
|
|
167
|
+
world.component(Orange).set(Implements, { interfaces: [Fruit] });
|
|
168
|
+
|
|
169
|
+
const orange = world.entity().set(Orange, { calories: 7 });
|
|
170
|
+
|
|
171
|
+
orange.get(Food) === orange.get(Orange); // true
|
|
172
|
+
orange.has(Fruit); // true
|
|
173
|
+
|
|
174
|
+
world
|
|
175
|
+
.query("foods")
|
|
176
|
+
.with(Food)
|
|
177
|
+
.forEach([Food], (_entity, [food]) => {
|
|
178
|
+
food.calories; // Orange data, read through the Food alias
|
|
179
|
+
});
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
Rules (enforced by `ImplementsModule`, `lib/vecs/src/modules/implements.ts`):
|
|
183
|
+
|
|
184
|
+
- **Aliases are read/query only.** `get`, `has`, `with`, `filter`, and injection accept an
|
|
185
|
+
interface type; mutation must go through the concrete component. `add`, `set`, `remove`,
|
|
186
|
+
`modified`, `getMut`, and `attach` on an interface throw at runtime (and `add`/`set` are
|
|
187
|
+
rejected by TypeScript for abstract classes).
|
|
188
|
+
- **Declarations are transitive.** `Orange` implements `Fruit`, `Fruit` implements `Food`, so an
|
|
189
|
+
entity with `Orange` matches `with(Food)`. Cyclic declarations throw.
|
|
190
|
+
- **One implementer per interface per entity.** Setting a second concrete component that
|
|
191
|
+
implements the same interface evicts the first concrete component and all of its aliases.
|
|
192
|
+
- Relationship components cannot be implementers or interfaces.
|
|
193
|
+
|
|
194
|
+
## Singletons and world-global data
|
|
195
|
+
|
|
196
|
+
Tag a component class with `Singleton` to enforce that at most one entity holds it:
|
|
197
|
+
|
|
198
|
+
```ts
|
|
199
|
+
import { Singleton } from "@vworlds/vecs";
|
|
200
|
+
|
|
201
|
+
world.component(Gravity).add(Singleton); // adding Gravity to a second entity now throws
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
For world-global state, store the component **on its own component entity** with the `world.get` /
|
|
205
|
+
`world.set` shorthand:
|
|
206
|
+
|
|
207
|
+
```ts
|
|
208
|
+
world.set(Gravity, { y: -9.8 }); // world.component(Gravity).set(Gravity, {...})
|
|
209
|
+
world.get(Gravity)?.y; // world.component(Gravity).get(Gravity)
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
`SingletonModule` enforces the constraint (`lib/vecs/src/modules/singleton.ts`); see
|
|
213
|
+
[Modules](./modules.md#built-in-modules).
|
|
214
|
+
|
|
215
|
+
## Companion components for non-ECS resources
|
|
216
|
+
|
|
217
|
+
To attach a non-ECS resource (a renderer object, a WASM handle, a DOM node) to an entity, wrap it
|
|
218
|
+
in a local component instead of keeping a side `Map<eid, resource>`:
|
|
219
|
+
|
|
220
|
+
```ts
|
|
221
|
+
class VGameObject {
|
|
222
|
+
obj!: Phaser.GameObjects.GameObject;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
world.component(VGameObject).onRemove((_entity, vgo) => vgo.obj.destroy());
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
The entity's component set then _is_ the resource lifecycle: queries drive create/destroy, and
|
|
229
|
+
`onRemove` releases the resource whether the component is removed explicitly or the entity is
|
|
230
|
+
destroyed. The full pattern — including create/update/teardown phase placement — is in the
|
|
231
|
+
[Design guide](./design-guide.md#7-patterns-reach-for-these).
|
|
232
|
+
|
|
233
|
+
## Reference
|
|
234
|
+
|
|
235
|
+
### `World` component methods (`lib/vecs/src/world/world.components.ts`)
|
|
236
|
+
|
|
237
|
+
| Method | Description |
|
|
238
|
+
| ------------------------------------ | -------------------------------------------------------------------------------------------------- |
|
|
239
|
+
| `component(Class, ceid?)` | Register or look up the `Component` entity for `Class`; optional explicit numeric id. |
|
|
240
|
+
| `component(name)` | Resolve an already-registered component entity by its `name`. Throws if absent or not a component. |
|
|
241
|
+
| `get(Class)` | Singleton read: `component(Class).get(Class)`. |
|
|
242
|
+
| `set(Class, props)` | Singleton write: `component(Class).set(Class, props)`. |
|
|
243
|
+
| `setExclusiveComponents(...classes)` | Declare one mutually exclusive group. |
|
|
244
|
+
|
|
245
|
+
### `Component` entity (`lib/vecs/src/component.ts`)
|
|
246
|
+
|
|
247
|
+
A `Component<T>` is an `Entity` subclass, so the full [entity API](./entities.md#reference)
|
|
248
|
+
applies. On top of it:
|
|
249
|
+
|
|
250
|
+
| Member | Description |
|
|
251
|
+
| ------------------- | ---------------------------------------------------------------- |
|
|
252
|
+
| `onAdd(handler)` | Register an add hook. Returns the component entity for chaining. |
|
|
253
|
+
| `onRemove(handler)` | Register a remove hook. |
|
|
254
|
+
| `onSet(handler)` | Register a set/modified hook. |
|
|
255
|
+
| `meta` | The `ComponentMeta` bookkeeping record. |
|
|
256
|
+
|
|
257
|
+
### `ComponentMeta` (`lib/vecs/src/component_meta.ts`)
|
|
258
|
+
|
|
259
|
+
| Field | Description |
|
|
260
|
+
| ---------------- | --------------------------------------------------------------------------------------------- |
|
|
261
|
+
| `Class` | The component class constructor. |
|
|
262
|
+
| `eid` | Component entity id assigned at registration. |
|
|
263
|
+
| `bitPtr` | Pre-computed bit pointer into entity archetype bitsets. |
|
|
264
|
+
| `isRelationship` | `true` when the class extends `Relationship`. |
|
|
265
|
+
| `onDelete` | `CleanupPolicy` applied to carriers when the component entity is destroyed. Default `Throw`. |
|
|
266
|
+
| `onDeleteTarget` | `CleanupPolicy` applied to sources when a relationship target is destroyed. Default `Remove`. |
|
|
267
|
+
| `usageCount` | Number of entities currently carrying this component. |
|
package/docs/concepts.md
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# Concepts
|
|
2
|
+
|
|
3
|
+
The vecs mental model: what the pieces are, how they relate, and the one rule that makes the rest
|
|
4
|
+
fall into place.
|
|
5
|
+
|
|
6
|
+
## The golden rule
|
|
7
|
+
|
|
8
|
+
Components are **data**, systems are **behavior**, and **lifecycle is driven by the presence and
|
|
9
|
+
change of components — not by imperative bookkeeping**. You do not call `start()` on things or keep
|
|
10
|
+
side maps of what exists; you attach and remove components, and reactive systems respond. Nearly
|
|
11
|
+
every idiom in the [Design guide](./design-guide.md) is a consequence of taking this seriously.
|
|
12
|
+
|
|
13
|
+
## The pieces
|
|
14
|
+
|
|
15
|
+
| Concept | What it is |
|
|
16
|
+
| ------------------------ | ----------------------------------------------------------------------------------------------------------- |
|
|
17
|
+
| **World** | Central container. Owns every entity, registered component, query, system, and the flat system pipeline. |
|
|
18
|
+
| **Entity** | A numeric id (`eid`) with a set of components attached. Created via the world. |
|
|
19
|
+
| **Component** | A registered plain data class with a no-argument constructor. No behavior. |
|
|
20
|
+
| **System** | A `Query` plus per-tick logic (`update`, `each`, `run`), assigned to one pipeline phase. |
|
|
21
|
+
| **Query** | A reactive, always-up-to-date set of entities matching a predicate, with `enter`/`exit`/`update` callbacks. |
|
|
22
|
+
| **Filter** | A non-reactive, one-shot scan: walks all world entities on each `forEach` call. |
|
|
23
|
+
| **Hook** | Lightweight `onAdd` / `onRemove` / `onSet` callbacks registered per component class. |
|
|
24
|
+
| **Phase** | Marker component for entities used as pipeline ordering anchors. |
|
|
25
|
+
| **Relationship** | A component whose `target` field points at another entity, with reverse lookup and cleanup cascades. |
|
|
26
|
+
| **Module** | A unit of packaging: a class whose `init()` registers components, systems, phases, and policy. |
|
|
27
|
+
| **Exclusive components** | A group of components where at most one may exist on any entity at a time. |
|
|
28
|
+
| **Implements** | Declares abstract/interface component aliases for a concrete component, for polymorphic queries. |
|
|
29
|
+
|
|
30
|
+
## Data vs. behavior
|
|
31
|
+
|
|
32
|
+
A component is a bag of fields:
|
|
33
|
+
|
|
34
|
+
```ts
|
|
35
|
+
class Position {
|
|
36
|
+
x = 0;
|
|
37
|
+
y = 0;
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
It carries no methods that act on the world, no reference to its entity, and no lifecycle of its
|
|
42
|
+
own. All behavior lives in systems (and, for small cross-cutting reactions, in hooks). This split
|
|
43
|
+
is what makes composition work: any system can operate on any entity that happens to carry the
|
|
44
|
+
right components, and adding a capability to an entity is just adding a component.
|
|
45
|
+
|
|
46
|
+
Prefer many small, single-purpose components over one large one. An entity that never rotates
|
|
47
|
+
simply omits `Rotation`; absence encodes "not applicable". See
|
|
48
|
+
[Components](./components.md).
|
|
49
|
+
|
|
50
|
+
## How a tick works, in brief
|
|
51
|
+
|
|
52
|
+
```text
|
|
53
|
+
world.component(C) / world.system(name) / world.query(name) × N
|
|
54
|
+
↓
|
|
55
|
+
world.progress(now, delta) — once per frame
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
`world.progress` runs every system in phase order. Inside a system body the world is **deferred**:
|
|
59
|
+
entity mutations are queued, then flushed after the system finishes, which routes `enter` /
|
|
60
|
+
`update` / `exit` events into the queries and systems that care. Newly created systems are inserted
|
|
61
|
+
into the pipeline the next time `progress` runs; standalone queries backfill already-matching
|
|
62
|
+
entities when they are built. The full story — phase taxonomy, ordering guarantees, flush
|
|
63
|
+
boundaries — is in [Execution model](./execution-model.md).
|
|
64
|
+
|
|
65
|
+
## Lifecycle is component presence
|
|
66
|
+
|
|
67
|
+
Because queries react to component presence and change, the component set _is_ the lifecycle:
|
|
68
|
+
|
|
69
|
+
- "This entity is renderable" — it has a renderable component; the render system's `enter` fires.
|
|
70
|
+
- "This resource must be released" — remove its component (or destroy the entity); `onRemove`
|
|
71
|
+
fires.
|
|
72
|
+
- "This value changed" — `set`/`modified` fires `onSet` hooks and `update` callbacks.
|
|
73
|
+
|
|
74
|
+
So instead of factories, registries, and `Map<eid, resource>` side tables, you write systems gated
|
|
75
|
+
on component combinations. The canonical example is the companion-component pattern in the
|
|
76
|
+
[Design guide](./design-guide.md#7-patterns-reach-for-these).
|
|
77
|
+
|
|
78
|
+
## Where each topic lives
|
|
79
|
+
|
|
80
|
+
- Registering, configuring, and hooking components → [Components](./components.md)
|
|
81
|
+
- Creating, mutating, and destroying entities → [Entities](./entities.md)
|
|
82
|
+
- System membership, callbacks, and cadence → [Systems](./systems.md)
|
|
83
|
+
- The query DSL, live queries, filters, grouping → [Queries and filters](./queries-and-filters.md)
|
|
84
|
+
- Entity-to-entity links and hierarchy → [Relationships](./relationships.md)
|
|
85
|
+
- Packaging features → [Modules](./modules.md)
|
|
86
|
+
- Terminology → [Glossary](./glossary.md)
|