@vworlds/vecs 1.0.45 → 1.0.47
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 +7 -1
- package/dist/component.d.ts +57 -60
- package/dist/component.js +24 -17
- package/dist/component.js.map +1 -1
- package/dist/component_meta.d.ts +67 -79
- package/dist/component_meta.js +331 -37
- package/dist/component_meta.js.map +1 -1
- package/dist/dsl.js +1 -1
- package/dist/dsl.js.map +1 -1
- package/dist/entity/entity.base.js +4 -0
- package/dist/entity/entity.base.js.map +1 -1
- package/dist/entity/entity.components.d.ts +39 -41
- package/dist/entity/entity.components.js +249 -82
- package/dist/entity/entity.components.js.map +1 -1
- package/dist/entity/entity.d.ts +1 -2
- package/dist/entity/entity.js +2 -3
- package/dist/entity/entity.js.map +1 -1
- package/dist/entity/entity.lifecycle.d.ts +7 -1
- package/dist/entity/entity.lifecycle.js +40 -50
- package/dist/entity/entity.lifecycle.js.map +1 -1
- package/dist/entity/entity.queries.js +30 -0
- package/dist/entity/entity.queries.js.map +1 -1
- package/dist/entity/entity.relationships.d.ts +4 -0
- package/dist/entity/entity.relationships.js +18 -10
- package/dist/entity/entity.relationships.js.map +1 -1
- package/dist/index.d.ts +7 -4
- package/dist/index.js +4 -2
- package/dist/index.js.map +1 -1
- package/dist/inject.d.ts +63 -14
- package/dist/inject.js +165 -89
- package/dist/inject.js.map +1 -1
- package/dist/modules/identity.js +2 -2
- package/dist/modules/identity.js.map +1 -1
- package/dist/modules/implements.js +2 -2
- package/dist/modules/implements.js.map +1 -1
- package/dist/modules/pipeline.js +5 -5
- package/dist/modules/pipeline.js.map +1 -1
- package/dist/modules/relationship_types.d.ts +70 -6
- package/dist/modules/relationship_types.js +70 -6
- package/dist/modules/relationship_types.js.map +1 -1
- package/dist/modules/relationships.d.ts +31 -9
- package/dist/modules/relationships.js +120 -44
- package/dist/modules/relationships.js.map +1 -1
- package/dist/modules/singleton.js +2 -2
- package/dist/modules/singleton.js.map +1 -1
- package/dist/modules/snapshot.d.ts +26 -0
- package/dist/modules/snapshot.js +45 -0
- package/dist/modules/snapshot.js.map +1 -0
- package/dist/modules/snapshot_types.d.ts +85 -0
- package/dist/modules/snapshot_types.js +130 -0
- package/dist/modules/snapshot_types.js.map +1 -0
- package/dist/package.json +2 -2
- package/dist/query/callbacks.d.ts +9 -7
- package/dist/query/callbacks.js +27 -41
- package/dist/query/callbacks.js.map +1 -1
- package/dist/query/grouped_query.js +7 -7
- package/dist/query/grouped_query.js.map +1 -1
- package/dist/query/grouping.d.ts +6 -2
- package/dist/query/grouping.js +84 -15
- package/dist/query/grouping.js.map +1 -1
- package/dist/query/query.d.ts +4 -4
- package/dist/query/query.js +37 -14
- package/dist/query/query.js.map +1 -1
- package/dist/snapshot/capture.d.ts +1 -0
- package/dist/snapshot/capture.js +212 -0
- package/dist/snapshot/capture.js.map +1 -0
- package/dist/snapshot/clone.d.ts +1 -0
- package/dist/snapshot/clone.js +107 -0
- package/dist/snapshot/clone.js.map +1 -0
- package/dist/snapshot/index.d.ts +5 -0
- package/dist/snapshot/index.js +21 -0
- package/dist/snapshot/index.js.map +1 -0
- package/dist/snapshot/preflight.d.ts +1 -0
- package/dist/snapshot/preflight.js +40 -0
- package/dist/snapshot/preflight.js.map +1 -0
- package/dist/snapshot/restore.d.ts +1 -0
- package/dist/snapshot/restore.js +111 -0
- package/dist/snapshot/restore.js.map +1 -0
- package/dist/snapshot/types.d.ts +135 -0
- package/dist/snapshot/types.js +26 -0
- package/dist/snapshot/types.js.map +1 -0
- package/dist/system.d.ts +3 -1
- package/dist/system.js +33 -23
- package/dist/system.js.map +1 -1
- package/dist/terms/all_term.js +19 -0
- package/dist/terms/all_term.js.map +1 -1
- package/dist/terms/any_term.js +19 -0
- package/dist/terms/any_term.js.map +1 -1
- package/dist/terms/not_term.js +4 -0
- package/dist/terms/not_term.js.map +1 -1
- package/dist/terms/target_term.d.ts +1 -2
- package/dist/terms/target_term.js +2 -4
- package/dist/terms/target_term.js.map +1 -1
- package/dist/terms/term.d.ts +2 -0
- package/dist/terms/term.js +59 -10
- package/dist/terms/term.js.map +1 -1
- package/dist/util/array_map.d.ts +43 -12
- package/dist/util/array_map.js +90 -41
- package/dist/util/array_map.js.map +1 -1
- package/dist/util/dense_set.js +26 -0
- package/dist/util/dense_set.js.map +1 -1
- package/dist/world/world.base.js +12 -0
- package/dist/world/world.base.js.map +1 -1
- package/dist/world/world.d.ts +7 -0
- package/dist/world/world.deferred.js +17 -0
- package/dist/world/world.deferred.js.map +1 -1
- package/dist/world/world.entities.d.ts +13 -2
- package/dist/world/world.entities.js +16 -2
- package/dist/world/world.entities.js.map +1 -1
- package/dist/world/world.js +15 -3
- package/dist/world/world.js.map +1 -1
- package/dist/world/world.pipeline.d.ts +2 -2
- package/dist/world/world.pipeline.js +4 -2
- package/dist/world/world.pipeline.js.map +1 -1
- package/dist/world/world.queries.d.ts +7 -0
- package/dist/world/world.queries.js +52 -0
- package/dist/world/world.queries.js.map +1 -1
- package/dist/world/world.transfers.d.ts +137 -0
- package/dist/world/world.transfers.js +188 -0
- package/dist/world/world.transfers.js.map +1 -0
- package/docs/README.md +18 -6
- package/docs/components.md +90 -53
- package/docs/concepts.md +21 -17
- package/docs/design-guide.md +125 -94
- package/docs/entities.md +62 -76
- package/docs/execution-model.md +26 -10
- package/docs/getting-started.md +6 -6
- package/docs/glossary.md +19 -10
- package/docs/modules.md +8 -8
- package/docs/queries-and-filters.md +15 -10
- package/docs/relationships.md +245 -124
- package/docs/snapshots.md +181 -0
- package/docs/systems.md +64 -21
- package/docs/utilities.md +10 -6
- package/package.json +2 -2
- package/skills/vecs/SKILL.md +5 -4
package/docs/components.md
CHANGED
|
@@ -5,8 +5,9 @@ exclusivity, cleanup policies, interface aliases, and singletons.
|
|
|
5
5
|
|
|
6
6
|
## Components are plain data classes
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
An ordinary component is a class with field initializers and a no-argument constructor. Relationship
|
|
9
|
+
components are the exception: they extend `Relationship` so vecs can recognize their private edge
|
|
10
|
+
storage. Vecs instantiates either form with `new ComponentClass()`:
|
|
10
11
|
|
|
11
12
|
```ts
|
|
12
13
|
class Position {
|
|
@@ -71,27 +72,30 @@ Pass `idPools` to the `World` constructor to change ranges (each entry is an `Id
|
|
|
71
72
|
released this frame is not immediately recycled. `world.peekNextEid(poolName)` returns the next id
|
|
72
73
|
a pool would allocate without reserving it. Id 0 is reserved.
|
|
73
74
|
|
|
74
|
-
## Hooks:
|
|
75
|
+
## Hooks: component and relationship edges
|
|
75
76
|
|
|
76
77
|
The `Component` entity exposes lifecycle hooks fired for every entity carrying the component:
|
|
77
78
|
|
|
78
79
|
```ts
|
|
79
80
|
world
|
|
80
81
|
.component(Sprite)
|
|
81
|
-
.
|
|
82
|
-
.
|
|
83
|
-
.
|
|
82
|
+
.on("add", (entity, sprite) => sprite.initialize(scene, entity))
|
|
83
|
+
.on("set", (entity, sprite) => sprite.syncToScene(entity))
|
|
84
|
+
.on("remove", (entity, sprite) => sprite.destroy(scene, entity));
|
|
84
85
|
```
|
|
85
86
|
|
|
86
|
-
| Hook
|
|
87
|
-
|
|
|
88
|
-
| `
|
|
89
|
-
| `
|
|
90
|
-
| `
|
|
87
|
+
| Hook | Fires when… |
|
|
88
|
+
| -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
89
|
+
| `add` | The component is first attached (`add`, or `set` on an entity that lacked an ordinary component). |
|
|
90
|
+
| `set` | `entity.set(C, props)` applies ordinary data, `entity.attach(instance)` stores an instance, `entity.modified(C)` is called, or `getMut` / `ensure` marks it dirty. |
|
|
91
|
+
| `remove` | The component is detached (`entity.remove(C)`), or the owning entity is destroyed. |
|
|
91
92
|
|
|
92
|
-
Relationship component entities additionally expose `
|
|
93
|
-
|
|
94
|
-
|
|
93
|
+
Relationship component entities additionally expose the `targetAdd` and `targetRemove` events. Both
|
|
94
|
+
receive `(source, relationshipInstance, target)` after the edge and reverse index reach their new
|
|
95
|
+
state. An exclusive replacement fires `targetRemove` for the old edge before `targetAdd` for
|
|
96
|
+
the new edge. Adding or removing edges never fires `set`; it is reserved for ordinary fields on
|
|
97
|
+
an existing relationship. See [Relationships — edge hooks](./relationships.md#edge-hooks) for the
|
|
98
|
+
full ordering.
|
|
95
99
|
|
|
96
100
|
Handlers receive `(entity, componentInstance)` — component instances do not carry entity
|
|
97
101
|
references, so the entity is passed explicitly. Hooks fire synchronously when the corresponding
|
|
@@ -99,10 +103,10 @@ command is applied (see [Execution model](./execution-model.md#reactive-routing)
|
|
|
99
103
|
resource management and cross-cutting reactions that aren't worth a system; use systems when the
|
|
100
104
|
reaction belongs to a phase.
|
|
101
105
|
|
|
102
|
-
|
|
103
|
-
`
|
|
104
|
-
|
|
105
|
-
|
|
106
|
+
Multiple handlers may be registered for the same event: they fire in registration
|
|
107
|
+
order. Call `on(event, handler)` to append, `on(event, handler, true)` to
|
|
108
|
+
prepend, and `off(event, handler)` to remove a handler by reference. All three
|
|
109
|
+
return the component entity for chaining.
|
|
106
110
|
|
|
107
111
|
## Component traits: policy on component entities
|
|
108
112
|
|
|
@@ -112,25 +116,30 @@ components **component traits**:
|
|
|
112
116
|
|
|
113
117
|
```ts
|
|
114
118
|
world.component(Gravity).add(Singleton);
|
|
115
|
-
world.component(Inventory).add(OwnedTarget);
|
|
116
|
-
world.component(ChildOf).add(Traversable);
|
|
119
|
+
world.component(Inventory).add(Exclusive).add(OwnedTarget);
|
|
120
|
+
world.component(ChildOf).add(Traversable); // ChildOf is already Exclusive
|
|
117
121
|
world.component(Orange).set(Implements, { interfaces: [Fruit] });
|
|
118
122
|
```
|
|
119
123
|
|
|
120
|
-
| Trait/declaration | Applies to
|
|
121
|
-
| ----------------- |
|
|
122
|
-
| `Singleton` | Concrete components
|
|
123
|
-
| `Implements` | Non-relationship components
|
|
124
|
-
| `
|
|
125
|
-
| `
|
|
124
|
+
| Trait/declaration | Applies to | Effect |
|
|
125
|
+
| ----------------- | -------------------------------------------- | ---------------------------------------------------------------------------- |
|
|
126
|
+
| `Singleton` | Concrete components | Allows at most one carrier entity |
|
|
127
|
+
| `Implements` | Non-relationship components | Exposes concrete data through abstract aliases |
|
|
128
|
+
| `Exclusive` | Relationships | Restricts the relationship to one target with atomic replacement |
|
|
129
|
+
| `Traversable` | Exclusive relationships | Maintains hierarchy depth for cascade ordering |
|
|
130
|
+
| `OwnedTarget` | Exclusive relationships other than `ChildOf` | Owns each explicit target through `ChildOf` and destructive edge replacement |
|
|
131
|
+
| `OneOf` | Relationships | Constrains each target to be a direct `ChildOf` child of a declared parent |
|
|
126
132
|
|
|
127
133
|
Traits declare policy; their built-in modules install the hooks and maintain the metadata that
|
|
128
134
|
enforce it. Configure traits in feature-module setup rather than opportunistically inside systems.
|
|
129
135
|
Do not assume all traits have the same lifecycle:
|
|
130
136
|
|
|
131
137
|
- Add `Singleton` before component instances exist.
|
|
132
|
-
-
|
|
133
|
-
|
|
138
|
+
- Add `Exclusive` before relationship instances exist. `OwnedTarget` and `Traversable` require it.
|
|
139
|
+
- Add `OwnedTarget` before relationship instances exist; it never creates a target implicitly and
|
|
140
|
+
does not replace public edge hooks.
|
|
141
|
+
- Add `OneOf` before relationship instances exist; it rejects every target that is not a direct
|
|
142
|
+
`ChildOf` child of the declared parent.
|
|
134
143
|
- `Traversable` backfills existing relationship instances.
|
|
135
144
|
- Setting or removing `Implements` recomputes interface aliases.
|
|
136
145
|
|
|
@@ -145,8 +154,10 @@ class Audited {}
|
|
|
145
154
|
|
|
146
155
|
class AuditModule extends Module {
|
|
147
156
|
public init(): void {
|
|
148
|
-
this.world.component(Audited).
|
|
149
|
-
component.
|
|
157
|
+
this.world.component(Audited).on("add", (component) => {
|
|
158
|
+
component.on("set", (entity) =>
|
|
159
|
+
console.log("changed", component.meta.Class.name, entity.eid)
|
|
160
|
+
);
|
|
150
161
|
});
|
|
151
162
|
}
|
|
152
163
|
}
|
|
@@ -177,17 +188,22 @@ at any time. Exclusivity is how you model "exactly one of N" (one renderable per
|
|
|
177
188
|
physics shape per body) — and it makes type swaps clean, because replacing the component fires the
|
|
178
189
|
old one's `exit`/`onRemove` automatically.
|
|
179
190
|
|
|
191
|
+
This is distinct from the `Exclusive` relationship trait. `setExclusiveComponents(A, B)` chooses
|
|
192
|
+
which **component class** may exist; `world.component(R).add(Exclusive)` allows one instance of
|
|
193
|
+
relationship `R` as usual but limits it to one **target edge**, replacing that edge atomically. See
|
|
194
|
+
[Relationships — exclusive one-target relationships](./relationships.md#exclusive-one-target-relationships).
|
|
195
|
+
|
|
180
196
|
## Cleanup policies: destroying a component entity
|
|
181
197
|
|
|
182
198
|
Every component key is backed by a component entity. What happens when you destroy that entity
|
|
183
199
|
while other entities still carry the component is controlled by `meta.onDelete`
|
|
184
200
|
(`CleanupPolicy` in `lib/vecs/src/component_meta.ts`):
|
|
185
201
|
|
|
186
|
-
| Policy | Meaning
|
|
187
|
-
| ---------------------- |
|
|
188
|
-
| `CleanupPolicy.Throw` | Default. Destroying the component entity while other entities carry it throws and
|
|
189
|
-
| `CleanupPolicy.Remove` | Remove the component from every carrier, then destroy the component entity.
|
|
190
|
-
| `CleanupPolicy.Delete` | Destroy every carrier entity, then destroy the component entity.
|
|
202
|
+
| Policy | Meaning |
|
|
203
|
+
| ---------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
204
|
+
| `CleanupPolicy.Throw` | Default. Destroying the component entity while other entities carry it throws. The synchronous `destroy()` call throws before mutating anything, so the world stays usable; a `Throw` policy reached during a deferred drain or a relationship cascade throws and leaves the world partial — discard it. |
|
|
205
|
+
| `CleanupPolicy.Remove` | Remove the component from every carrier, then destroy the component entity. |
|
|
206
|
+
| `CleanupPolicy.Delete` | Destroy every carrier entity, then destroy the component entity. |
|
|
191
207
|
|
|
192
208
|
```ts
|
|
193
209
|
import { CleanupPolicy } from "@vworlds/vecs";
|
|
@@ -197,7 +213,29 @@ world.component(Temporary).destroy(); // strips Temporary from all carriers
|
|
|
197
213
|
```
|
|
198
214
|
|
|
199
215
|
The `Throw` default also applies to plain entities used as component keys (`entity.add(keyEntity)`).
|
|
200
|
-
|
|
216
|
+
|
|
217
|
+
## Snapshot policies: what a component may cross worlds with
|
|
218
|
+
|
|
219
|
+
A component type also declares how
|
|
220
|
+
[subtree snapshots](./snapshots.md) capture it, through `world.component(Type).snapshotPolicy(...)`.
|
|
221
|
+
The default — no declaration — deep-copies the instance's own enumerable fields, and **fails
|
|
222
|
+
loudly at capture** on a nested value that is not plain data, so a component carrying a class
|
|
223
|
+
instance must declare a policy saying how that instance is detached. `DontSnapshot` marks a
|
|
224
|
+
type as world-bound (an engine handle, a socket, a live `Query`), and `CustomSnapshot` with
|
|
225
|
+
`capture` / `materialize` callbacks is how a type that mixes declarative and runtime state hands over
|
|
226
|
+
only the declarative half:
|
|
227
|
+
|
|
228
|
+
```ts
|
|
229
|
+
world.component(PhysicsEngine).snapshotPolicy(DontSnapshot);
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
A policy is declared by the owning module and may change at any time: a capture binds the
|
|
233
|
+
materializer its policy resolved to, so an existing snapshot always restores the way it was
|
|
234
|
+
captured.
|
|
235
|
+
A synchronous `destroy()` throw (component still in use) leaves the world usable. A throw during
|
|
236
|
+
deferred drain or a cascade is irrecoverable — the component entity, its carriers, and the usage
|
|
237
|
+
count are in a partial state and the world must be discarded. Configure cleanup policies so cascades
|
|
238
|
+
are consistent; treat a mid-destroy throw as a programming error.
|
|
201
239
|
|
|
202
240
|
Relationship components have a second, independent policy — `meta.onDeleteTarget` — that governs
|
|
203
241
|
what happens to _sources_ when a relationship **target** is destroyed. See
|
|
@@ -285,7 +323,7 @@ the live object itself as a component** with `entity.attach(obj)` instead of kee
|
|
|
285
323
|
const obj = scene.add.arc(0, 0); // a Phaser GameObject
|
|
286
324
|
entity.attach(obj); // the GameObject is now a component, keyed by its class
|
|
287
325
|
|
|
288
|
-
world.component(Phaser.GameObjects.Arc).
|
|
326
|
+
world.component(Phaser.GameObjects.Arc).on("remove", (_entity, obj) => obj.destroy());
|
|
289
327
|
```
|
|
290
328
|
|
|
291
329
|
The entity's component set then _is_ the resource lifecycle: queries drive create/destroy, and
|
|
@@ -312,22 +350,21 @@ interface markers for polymorphic dispatch — is in the
|
|
|
312
350
|
A `Component<T>` is an `Entity` subclass, so the full [entity API](./entities.md#reference)
|
|
313
351
|
applies. On top of it:
|
|
314
352
|
|
|
315
|
-
| Member
|
|
316
|
-
|
|
|
317
|
-
| `
|
|
318
|
-
| `
|
|
319
|
-
| `
|
|
320
|
-
| `onSet(handler?)` | Get, set, or remove the single set/modified hook. |
|
|
321
|
-
| `meta` | The `ComponentMeta` bookkeeping record. |
|
|
353
|
+
| Member | Description |
|
|
354
|
+
| ----------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
355
|
+
| `on(event, handler, insert?)` | Register a handler for `add` / `remove` / `set` / `targetAdd` / `targetRemove` (plus the internal `policyAdd` / `policyRemove` / `targetPolicyAdd` / `targetPolicyRemove` events). `insert` defaults to `false` (append); `true` prepends. Rejects `target*` events on non-relationships. Returns the component. |
|
|
356
|
+
| `off(event, handler)` | Remove a previously-registered handler by reference. Idempotent. Returns the component. |
|
|
357
|
+
| `meta` | The `ComponentMeta` bookkeeping record. |
|
|
322
358
|
|
|
323
359
|
### `ComponentMeta` (`lib/vecs/src/component_meta.ts`)
|
|
324
360
|
|
|
325
|
-
| Field
|
|
326
|
-
|
|
|
327
|
-
| `Class`
|
|
328
|
-
| `eid`
|
|
329
|
-
| `bitPtr`
|
|
330
|
-
| `isRelationship`
|
|
331
|
-
| `
|
|
332
|
-
| `
|
|
333
|
-
| `
|
|
361
|
+
| Field | Description |
|
|
362
|
+
| ----------------------- | --------------------------------------------------------------------------------------------- |
|
|
363
|
+
| `Class` | The component class constructor. |
|
|
364
|
+
| `eid` | Component entity id assigned at registration. |
|
|
365
|
+
| `bitPtr` | Pre-computed bit pointer into entity archetype bitsets. |
|
|
366
|
+
| `isRelationship` | `true` when the class extends `Relationship`. |
|
|
367
|
+
| `relationshipExclusive` | `true` when the relationship carries the `Exclusive` trait. |
|
|
368
|
+
| `onDelete` | `CleanupPolicy` applied to carriers when the component entity is destroyed. Default `Throw`. |
|
|
369
|
+
| `onDeleteTarget` | `CleanupPolicy` applied to sources when a relationship target is destroyed. Default `Remove`. |
|
|
370
|
+
| `usageCount` | Number of entities currently carrying this component. |
|
package/docs/concepts.md
CHANGED
|
@@ -12,21 +12,22 @@ every idiom in the [Design guide](./design-guide.md) is a consequence of taking
|
|
|
12
12
|
|
|
13
13
|
## The pieces
|
|
14
14
|
|
|
15
|
-
| Concept
|
|
16
|
-
|
|
|
17
|
-
| **World**
|
|
18
|
-
| **Entity**
|
|
19
|
-
| **Component**
|
|
20
|
-
| **System**
|
|
21
|
-
| **Query**
|
|
22
|
-
| **Filter**
|
|
23
|
-
| **Hook**
|
|
24
|
-
| **Phase**
|
|
25
|
-
| **Relationship**
|
|
26
|
-
| **Component trait**
|
|
27
|
-
| **Module**
|
|
28
|
-
| **Exclusive components**
|
|
29
|
-
| **
|
|
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 component callbacks (`onAdd` / `onSet` / `onRemove`, plus relationship edge hooks). |
|
|
24
|
+
| **Phase** | Marker component for entities used as pipeline ordering anchors. |
|
|
25
|
+
| **Relationship** | A component holding explicit edges to one or more target entities, with reverse lookup and cleanup cascades. |
|
|
26
|
+
| **Component trait** | Policy attached to a component entity, configuring every use of that component type. |
|
|
27
|
+
| **Module** | A unit of packaging: a class whose `init()` registers components, systems, phases, and policy. |
|
|
28
|
+
| **Exclusive components** | A group of components where at most one may exist on any entity at a time. |
|
|
29
|
+
| **Exclusive relationship** | A relationship carrying `Exclusive`, restricting each source to at most one target. |
|
|
30
|
+
| **Implements** | Declares abstract/interface component aliases for a concrete component, for polymorphic queries. |
|
|
30
31
|
|
|
31
32
|
## Data vs. behavior
|
|
32
33
|
|
|
@@ -46,8 +47,11 @@ right components, and adding a capability to an entity is just adding a componen
|
|
|
46
47
|
|
|
47
48
|
Because a registered component is also an entity, it can itself carry components. Built-in
|
|
48
49
|
**component traits** use that meta-ECS layer to declare reusable policy: `Singleton` constrains a
|
|
49
|
-
component, `Implements` declares aliases,
|
|
50
|
-
The trait remains data; a module
|
|
50
|
+
component, `Implements` declares aliases, `Exclusive` constrains relationship cardinality, and
|
|
51
|
+
`Traversable` / `OwnedTarget` configure exclusive relationships. The trait remains data; a module
|
|
52
|
+
owns the hooks and metadata behavior it activates. Relationship `Exclusive` is unrelated to an
|
|
53
|
+
exclusive component group: one limits edge count, while the other limits coexisting component
|
|
54
|
+
types.
|
|
51
55
|
|
|
52
56
|
Prefer many small, single-purpose components over one large one. An entity that never rotates
|
|
53
57
|
simply omits `Rotation`; absence encodes "not applicable". See
|