@relu-interactives/spatial-ecs 0.2.12 → 0.2.14

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.
Files changed (220) hide show
  1. package/dist/{System-CHCucemW.d.cts → System-BeuJ1xt5.d.cts} +291 -2
  2. package/dist/{System-CHCucemW.d.ts → System-BeuJ1xt5.d.ts} +291 -2
  3. package/dist/{Video-B9ZKmBIT.d.ts → Video-Cym2C52L.d.ts} +2 -241
  4. package/dist/{Video-ACTSfWjH.d.cts → Video-D4B-z74P.d.cts} +2 -241
  5. package/dist/{chunk-PH43YHUT.js → chunk-27RBPOVO.js} +3 -3
  6. package/dist/{chunk-PH43YHUT.js.map → chunk-27RBPOVO.js.map} +1 -1
  7. package/dist/{chunk-ZCEMOTH4.js → chunk-7QBONTBW.js} +4 -4
  8. package/dist/{chunk-ZCEMOTH4.js.map → chunk-7QBONTBW.js.map} +1 -1
  9. package/dist/{chunk-XPCVEXMY.js → chunk-FTIQQK62.js} +2 -2
  10. package/dist/chunk-FTIQQK62.js.map +1 -0
  11. package/dist/{chunk-ON4E5E5O.js → chunk-KDZ6VBRO.js} +4 -4
  12. package/dist/{chunk-ON4E5E5O.js.map → chunk-KDZ6VBRO.js.map} +1 -1
  13. package/dist/components/index.cjs.map +1 -1
  14. package/dist/components/index.d.cts +2 -2
  15. package/dist/components/index.d.ts +2 -2
  16. package/dist/components/index.js +1 -1
  17. package/dist/docs/api/Behaviour/classes/Behaviour.md +29 -29
  18. package/dist/docs/api/Component/classes/Component.md +1 -1
  19. package/dist/docs/api/components/classes/AnimationComponent.md +26 -26
  20. package/dist/docs/api/components/classes/AudioComponent.md +19 -19
  21. package/dist/docs/api/components/classes/CameraComponent.md +14 -14
  22. package/dist/docs/api/components/classes/EntityType.md +3 -3
  23. package/dist/docs/api/components/classes/Environment.md +7 -3
  24. package/dist/docs/api/components/classes/ImageComponent.md +7 -7
  25. package/dist/docs/api/components/classes/ImageTarget.md +7 -7
  26. package/dist/docs/api/components/classes/ImageTargetAnchor.md +4 -4
  27. package/dist/docs/api/components/classes/InstanceSlotComponent.md +4 -4
  28. package/dist/docs/api/components/classes/InstancedMeshComponent.md +10 -10
  29. package/dist/docs/api/components/classes/LightComponent.md +6 -6
  30. package/dist/docs/api/components/classes/MaterialComponent.md +11 -11
  31. package/dist/docs/api/components/classes/MeshComponent.md +3 -3
  32. package/dist/docs/api/components/classes/MeshGeometryComponent.md +9 -9
  33. package/dist/docs/api/components/classes/ModelComponent.md +4 -4
  34. package/dist/docs/api/components/classes/Name.md +3 -3
  35. package/dist/docs/api/components/classes/Object3DRef.md +4 -4
  36. package/dist/docs/api/components/classes/ParentId.md +3 -3
  37. package/dist/docs/api/components/classes/PhysicsComponent.md +6 -6
  38. package/dist/docs/api/components/classes/Postprocessing.md +7 -3
  39. package/dist/docs/api/components/classes/ScriptComponent.md +5 -5
  40. package/dist/docs/api/components/classes/Selectable.md +3 -3
  41. package/dist/docs/api/components/classes/SelectionTransformState.md +5 -5
  42. package/dist/docs/api/components/classes/SpriteComponent.md +7 -7
  43. package/dist/docs/api/components/classes/TransformComponent.md +12 -12
  44. package/dist/docs/api/components/classes/VideoComponent.md +28 -28
  45. package/dist/docs/api/components/functions/normalizePhysicsState.md +1 -1
  46. package/dist/docs/api/components/index.md +1 -1
  47. package/dist/docs/api/components/type-aliases/AnimationComponentOptions.md +9 -9
  48. package/dist/docs/api/components/type-aliases/CameraComponentOptions.md +13 -13
  49. package/dist/docs/api/components/type-aliases/CameraProjection.md +1 -1
  50. package/dist/docs/api/components/type-aliases/CameraRuntimeType.md +1 -1
  51. package/dist/docs/api/components/type-aliases/ColliderShape.md +1 -1
  52. package/dist/docs/api/components/type-aliases/ColliderState.md +20 -20
  53. package/dist/docs/api/components/type-aliases/CubeMeshParameters.md +4 -4
  54. package/dist/docs/api/components/type-aliases/EditableMeshObjectKind.md +1 -1
  55. package/dist/docs/api/components/type-aliases/EnvironmentFogState.md +8 -8
  56. package/dist/docs/api/components/type-aliases/EnvironmentFogType.md +1 -1
  57. package/dist/docs/api/components/type-aliases/EnvironmentGradientEffect.md +1 -1
  58. package/dist/docs/api/components/type-aliases/EnvironmentSkyboxState.md +7 -7
  59. package/dist/docs/api/components/type-aliases/EnvironmentSkyboxType.md +1 -1
  60. package/dist/docs/api/components/type-aliases/EnvironmentState.md +5 -5
  61. package/dist/docs/api/components/type-aliases/ImageTargetAnchorOptions.md +3 -3
  62. package/dist/docs/api/components/type-aliases/ImageTargetReference.md +7 -7
  63. package/dist/docs/api/components/type-aliases/InstanceTransform.md +13 -13
  64. package/dist/docs/api/components/type-aliases/InstancedMeshKind.md +1 -1
  65. package/dist/docs/api/components/type-aliases/LightValueState.md +11 -11
  66. package/dist/docs/api/components/type-aliases/MaterialTypeValue.md +1 -1
  67. package/dist/docs/api/components/type-aliases/MaterialValueState.md +28 -28
  68. package/dist/docs/api/components/type-aliases/MeshGeometryParameters.md +1 -1
  69. package/dist/docs/api/components/type-aliases/MeshGeometryParametersByKind.md +7 -7
  70. package/dist/docs/api/components/type-aliases/PhysicsState.md +3 -3
  71. package/dist/docs/api/components/type-aliases/PlaneMeshParameters.md +3 -3
  72. package/dist/docs/api/components/type-aliases/PostprocessingState.md +62 -62
  73. package/dist/docs/api/components/type-aliases/RadiusHeightMeshParameters.md +3 -3
  74. package/dist/docs/api/components/type-aliases/RadiusMeshParameters.md +2 -2
  75. package/dist/docs/api/components/type-aliases/RigidbodyState.md +17 -17
  76. package/dist/docs/api/components/type-aliases/RigidbodyType.md +1 -1
  77. package/dist/docs/api/components/type-aliases/ScriptEntry.md +9 -9
  78. package/dist/docs/api/components/type-aliases/TextureFilteringValue.md +1 -1
  79. package/dist/docs/api/components/type-aliases/TransformData.md +4 -4
  80. package/dist/docs/api/components/type-aliases/TransformModeValue.md +1 -1
  81. package/dist/docs/api/components/type-aliases/TransformSpaceValue.md +1 -1
  82. package/dist/docs/api/components/type-aliases/Vec3.md +4 -4
  83. package/dist/docs/api/components/variables/DEFAULT_COLLIDER_STATE.md +1 -1
  84. package/dist/docs/api/components/variables/DEFAULT_ENVIRONMENT_STATE.md +1 -1
  85. package/dist/docs/api/components/variables/DEFAULT_INSTANCED_MESH_PARAMETERS.md +1 -1
  86. package/dist/docs/api/components/variables/DEFAULT_INSTANCE_TRANSFORM.md +1 -1
  87. package/dist/docs/api/components/variables/DEFAULT_MESH_GEOMETRY_PARAMETERS.md +1 -1
  88. package/dist/docs/api/components/variables/DEFAULT_PHYSICS_STATE.md +1 -1
  89. package/dist/docs/api/components/variables/DEFAULT_POSTPROCESSING_STATE.md +1 -1
  90. package/dist/docs/api/components/variables/DEFAULT_RIGIDBODY_STATE.md +1 -1
  91. package/dist/docs/api/index/classes/AnimationStateMachine.md +16 -16
  92. package/dist/docs/api/index/classes/AnimationStateManager.md +13 -13
  93. package/dist/docs/api/index/classes/AnimationSystem.md +3 -3
  94. package/dist/docs/api/index/classes/AudioSystem.md +4 -4
  95. package/dist/docs/api/index/classes/CameraSyncSystem.md +2 -2
  96. package/dist/docs/api/index/classes/EnvironmentSyncSystem.md +4 -4
  97. package/dist/docs/api/index/classes/InputSystem.md +33 -33
  98. package/dist/docs/api/index/classes/InstancedMeshSystem.md +2 -2
  99. package/dist/docs/api/index/classes/LightSyncSystem.md +2 -2
  100. package/dist/docs/api/index/classes/MaterialSyncSystem.md +3 -3
  101. package/dist/docs/api/index/classes/MeshGeometrySystem.md +2 -2
  102. package/dist/docs/api/index/classes/ObjectManagementSystem.md +14 -14
  103. package/dist/docs/api/index/classes/ObjectVisibilitySystem.md +2 -2
  104. package/dist/docs/api/index/classes/PhysicsSyncSystem.md +4 -4
  105. package/dist/docs/api/index/classes/PostprocessingSyncSystem.md +4 -4
  106. package/dist/docs/api/index/classes/RenderSystem.md +3 -3
  107. package/dist/docs/api/index/classes/ScriptBehaviourSystem.md +4 -4
  108. package/dist/docs/api/index/classes/TransformSyncSystem.md +2 -2
  109. package/dist/docs/api/index/classes/VideoSystem.md +4 -4
  110. package/dist/docs/api/index/classes/World.md +186 -82
  111. package/dist/docs/api/index/functions/createAnimationComponent.md +1 -1
  112. package/dist/docs/api/index/functions/createAudioComponent.md +1 -1
  113. package/dist/docs/api/index/functions/createCameraComponent.md +1 -1
  114. package/dist/docs/api/index/functions/createEntityTypeComponent.md +1 -1
  115. package/dist/docs/api/index/functions/createEnvironmentComponent.md +1 -1
  116. package/dist/docs/api/index/functions/createImageComponent.md +1 -1
  117. package/dist/docs/api/index/functions/createImageTargetAnchorComponent.md +1 -1
  118. package/dist/docs/api/index/functions/createImageTargetComponent.md +1 -1
  119. package/dist/docs/api/index/functions/createInstanceSlotComponent.md +1 -1
  120. package/dist/docs/api/index/functions/createInstancedMeshComponent.md +1 -1
  121. package/dist/docs/api/index/functions/createLightComponent.md +1 -1
  122. package/dist/docs/api/index/functions/createMaterialComponent.md +1 -1
  123. package/dist/docs/api/index/functions/createMeshComponent.md +1 -1
  124. package/dist/docs/api/index/functions/createMeshGeometryComponent.md +1 -1
  125. package/dist/docs/api/index/functions/createModelComponent.md +1 -1
  126. package/dist/docs/api/index/functions/createNameComponent.md +1 -1
  127. package/dist/docs/api/index/functions/createObject3DRefComponent.md +1 -1
  128. package/dist/docs/api/index/functions/createParentIdComponent.md +1 -1
  129. package/dist/docs/api/index/functions/createPhysicsComponent.md +1 -1
  130. package/dist/docs/api/index/functions/createPostprocessingComponent.md +1 -1
  131. package/dist/docs/api/index/functions/createScriptComponent.md +1 -1
  132. package/dist/docs/api/index/functions/createSelectableComponent.md +1 -1
  133. package/dist/docs/api/index/functions/createSelectionTransformStateComponent.md +1 -1
  134. package/dist/docs/api/index/functions/createSpriteComponent.md +1 -1
  135. package/dist/docs/api/index/functions/createTransformComponent.md +1 -1
  136. package/dist/docs/api/index/functions/createVideoComponent.md +1 -1
  137. package/dist/docs/api/index/functions/hydrateWorldFromSnapshot.md +1 -1
  138. package/dist/docs/api/index/functions/hydrateWorldFromSnapshotDetailed.md +1 -1
  139. package/dist/docs/api/index/functions/registerDefaultComponentFactories.md +1 -1
  140. package/dist/docs/api/index/functions/registerDefaultFactories.md +1 -1
  141. package/dist/docs/api/index/interfaces/EcsSystem.md +3 -3
  142. package/dist/docs/api/index/interfaces/StateMachine.md +13 -13
  143. package/dist/docs/api/index/type-aliases/AnimationStateCallbacks.md +4 -4
  144. package/dist/docs/api/index/type-aliases/ComponentFactory.md +1 -1
  145. package/dist/docs/api/index/type-aliases/EcsComponentFactoryKind.md +1 -1
  146. package/dist/docs/api/index/type-aliases/EcsWorldComponentSnapshot.md +3 -3
  147. package/dist/docs/api/index/type-aliases/EcsWorldStateSnapshotLike.md +4 -4
  148. package/dist/docs/api/index/type-aliases/HydrateWorldFromSnapshotResult.md +3 -3
  149. package/dist/docs/api/index/type-aliases/KeyboardKey.md +1 -1
  150. package/dist/docs/api/index/type-aliases/MouseButton.md +1 -1
  151. package/dist/docs/api/index/type-aliases/SelectionContext.md +4 -4
  152. package/dist/docs/api/index/variables/HYDRATABLE_ENTITY_KINDS.md +1 -1
  153. package/dist/docs/api/index/variables/KeyboardKeys.md +89 -89
  154. package/dist/docs/api/types/type-aliases/AssetInput.md +3 -3
  155. package/dist/docs/api/types/type-aliases/CheckboxInput.md +12 -2
  156. package/dist/docs/api/types/type-aliases/ColorInput.md +1 -1
  157. package/dist/docs/api/types/type-aliases/ComponentInput.md +4 -4
  158. package/dist/docs/api/types/type-aliases/ComponentKind.md +1 -1
  159. package/dist/docs/api/types/type-aliases/DropdownInput.md +3 -3
  160. package/dist/docs/api/types/type-aliases/DropdownOption.md +3 -3
  161. package/dist/docs/api/types/type-aliases/EcsEntityEditorKind.md +1 -1
  162. package/dist/docs/api/types/type-aliases/EcsEntityKind.md +1 -1
  163. package/dist/docs/api/types/type-aliases/EntityId.md +1 -1
  164. package/dist/docs/api/types/type-aliases/EntityInput.md +3 -3
  165. package/dist/docs/api/types/type-aliases/FloatInput.md +1 -1
  166. package/dist/docs/api/types/type-aliases/IntersectionResult.md +3 -3
  167. package/dist/docs/api/types/type-aliases/ParentLookup.md +3 -3
  168. package/dist/docs/api/types/type-aliases/SelectionListener.md +1 -1
  169. package/dist/docs/api/types/type-aliases/SliderInput.md +5 -5
  170. package/dist/docs/api/types/type-aliases/TextInput.md +1 -1
  171. package/dist/docs/api/types/type-aliases/ToggleInput.md +1 -1
  172. package/dist/docs/api/types/type-aliases/Vec3.md +1 -1
  173. package/dist/docs/api/types/type-aliases/Vector2Input.md +3 -3
  174. package/dist/docs/api/types/type-aliases/Vector3Input.md +4 -4
  175. package/dist/docs/api/types/type-aliases/WorldEntityView.md +1 -1
  176. package/dist/docs/guide/accessing-components.md +135 -1
  177. package/dist/docs/guide/authoring-a-behaviour.md +1 -1
  178. package/dist/docs/guide/behaviour-data-binding/asset-input.md +54 -0
  179. package/dist/docs/guide/behaviour-data-binding/checkbox-input.md +38 -0
  180. package/dist/docs/guide/behaviour-data-binding/color-input.md +38 -0
  181. package/dist/docs/guide/behaviour-data-binding/component-input.md +79 -0
  182. package/dist/docs/guide/behaviour-data-binding/dropdown-input.md +53 -0
  183. package/dist/docs/guide/behaviour-data-binding/entity-input.md +100 -0
  184. package/dist/docs/guide/behaviour-data-binding/float-input.md +35 -0
  185. package/dist/docs/guide/behaviour-data-binding/index.md +158 -0
  186. package/dist/docs/guide/behaviour-data-binding/slider-input.md +37 -0
  187. package/dist/docs/guide/behaviour-data-binding/text-input.md +35 -0
  188. package/dist/docs/guide/behaviour-data-binding/toggle-input.md +36 -0
  189. package/dist/docs/guide/behaviour-data-binding/vector2-input.md +36 -0
  190. package/dist/docs/guide/behaviour-data-binding/vector3-input.md +41 -0
  191. package/dist/docs/guide/components/parent-id.md +25 -0
  192. package/dist/docs/guide/runtime-entity-management.md +302 -0
  193. package/dist/docs/guide/scene/environment.md +5 -6
  194. package/dist/docs/guide/scene/index.md +5 -10
  195. package/dist/docs/guide/scene/postprocessing.md +5 -6
  196. package/dist/docs/guide/systems/object-management-system.md +61 -0
  197. package/dist/docs/guide/world-api.md +37 -1
  198. package/dist/factories/index.cjs.map +1 -1
  199. package/dist/factories/index.d.cts +2 -2
  200. package/dist/factories/index.d.ts +2 -2
  201. package/dist/factories/index.js +3 -3
  202. package/dist/{index-B5JcGMJ5.d.ts → index-BfoOC2CG.d.cts} +2 -2
  203. package/dist/{index-kx5O2mVU.d.cts → index-BoJ3_6a3.d.ts} +2 -2
  204. package/dist/index.cjs +65 -0
  205. package/dist/index.cjs.map +1 -1
  206. package/dist/index.d.cts +5 -5
  207. package/dist/index.d.ts +5 -5
  208. package/dist/index.js +73 -8
  209. package/dist/index.js.map +1 -1
  210. package/dist/systems/index.cjs.map +1 -1
  211. package/dist/systems/index.d.cts +2 -2
  212. package/dist/systems/index.d.ts +2 -2
  213. package/dist/systems/index.js +3 -3
  214. package/dist/types.d.cts +1 -1
  215. package/dist/types.d.ts +1 -1
  216. package/dist/worldSnapshotUtils.d.cts +1 -1
  217. package/dist/worldSnapshotUtils.d.ts +1 -1
  218. package/package.json +1 -1
  219. package/dist/chunk-XPCVEXMY.js.map +0 -1
  220. package/dist/docs/guide/inspector-data-binding.md +0 -367
@@ -0,0 +1,158 @@
1
+ # Behaviour Data Binding
2
+
3
+ The `ScriptComponent.data` field is the bridge between the editor inspector and your `Behaviour` subclass. Anything you place on `data` is:
4
+
5
+ 1. Saved with the project.
6
+ 2. Rendered as a live inspector control in the editor.
7
+ 3. Available at runtime as `this.data` inside the script.
8
+
9
+ The **type annotation** on each field tells the editor which control to render. Import the input types from `@relu-interactives/spatial-ecs` and use them as `as` casts on the initial values in your data object.
10
+
11
+ ## Input types
12
+
13
+ | Type | Inspector control | Runtime value |
14
+ | --- | --- | --- |
15
+ | [`FloatInput`](./float-input) | Number input | `number` |
16
+ | [`TextInput`](./text-input) | Text input | `string` |
17
+ | [`ColorInput`](./color-input) | Color picker | `string` (hex, e.g. `"#ff0000"`) |
18
+ | [`Vector2Input`](./vector2-input) | XY number pair | `{ x: number; y: number }` |
19
+ | [`Vector3Input`](./vector3-input) | XYZ number triple | `{ x: number; y: number; z: number }` |
20
+ | [`SliderInput`](./slider-input) | Range slider | `{ min, max, step, value }` — read `field.value` at runtime |
21
+ | [`DropdownInput`](./dropdown-input) | Select dropdown | `{ options: DropdownOption[]; value: string }` — read `field.value` |
22
+ | [`CheckboxInput`](./checkbox-input) | Checkbox | `{ type: "checkbox"; value: boolean }` — read `.value` |
23
+ | [`ToggleInput`](./toggle-input) | Toggle switch | `boolean` |
24
+ | [`AssetInput`](./asset-input) | Asset file picker | `{ path: string; type: string }` — read `field.path` |
25
+ | [`EntityInput`](./entity-input) | Entity picker | `{ type: "entity"; value: WorldEntityView \| null }` — read `field.value` |
26
+ | [`ComponentInput`](./component-input) | Entity + component picker | `{ type: "component"; componentKind?: ComponentKind; value: Component \| null }` — read `field.value` |
27
+
28
+ ## Declaring inspector fields
29
+
30
+ Import the input types you need alongside `Behaviour`, then declare them directly on the `data` class property using `as` casts:
31
+
32
+ ```ts
33
+ import {
34
+ Behaviour,
35
+ type FloatInput,
36
+ type TextInput,
37
+ type ColorInput,
38
+ type Vector2Input,
39
+ type Vector3Input,
40
+ type SliderInput,
41
+ type DropdownInput,
42
+ type CheckboxInput,
43
+ type ToggleInput,
44
+ type AssetInput,
45
+ type EntityInput,
46
+ type ComponentInput,
47
+ } from "@relu-interactives/spatial-ecs";
48
+
49
+ export default class Demo extends Behaviour {
50
+ data = {
51
+ speed: 1 as FloatInput,
52
+ label: "Hello" as TextInput,
53
+ tint: "#ffffff" as ColorInput,
54
+ offset: { x: 0, y: 0 } as Vector2Input,
55
+ origin: { x: 0, y: 0, z: 0 } as Vector3Input,
56
+ range: { min: 0, max: 10, step: 0.1, value: 5 } as SliderInput,
57
+ axis: {
58
+ value: "y",
59
+ options: [
60
+ { label: "X", value: "x" },
61
+ { label: "Y", value: "y" },
62
+ { label: "Z", value: "z" },
63
+ ],
64
+ } as DropdownInput,
65
+ active: true as ToggleInput,
66
+ visible: { type: "checkbox", value: true } as CheckboxInput,
67
+ texture: { path: "", type: "image" } as AssetInput,
68
+ target: { type: "entity", value: null } as EntityInput,
69
+ camera: { type: "component", componentKind: "Camera", value: null } as ComponentInput,
70
+ };
71
+ }
72
+ ```
73
+
74
+ Each field in `data` maps directly to a live inspector control. The script above produces the following inspector panel:
75
+
76
+ ![Behaviour inspector controls](/images/behaviour-data-binding/behavior-inspector.gif)
77
+
78
+ ## Reading values at runtime
79
+
80
+ Most types map directly to their underlying primitive. The exceptions are `SliderInput`, `DropdownInput`, `AssetInput`, `EntityInput`, and `ComponentInput` — read the nested value:
81
+
82
+ ```ts
83
+ onUpdate() {
84
+ // Primitives — use directly
85
+ this.transform.rotation.y += this.data.speed * this.deltaTime;
86
+
87
+ // SliderInput → read .value
88
+ const volume = this.data.range.value;
89
+
90
+ // DropdownInput → read .value
91
+ const axis = this.data.axis.value as "x" | "y" | "z";
92
+
93
+ // AssetInput → read .path
94
+ const src = this.data.texture.path;
95
+
96
+ // EntityInput → cast to WorldEntityView | null
97
+ const entity = this.data.target.value as WorldEntityView | null;
98
+
99
+ // ComponentInput → cast to the concrete component type
100
+ const cam = this.data.camera.value as CameraComponent | null;
101
+ }
102
+ ```
103
+
104
+ ## Practical example — spinner with inspector controls
105
+
106
+ ```ts
107
+ import {
108
+ Behaviour,
109
+ type FloatInput,
110
+ type ColorInput,
111
+ type DropdownInput,
112
+ type ToggleInput,
113
+ } from "@relu-interactives/spatial-ecs";
114
+
115
+ export default class Spinner extends Behaviour {
116
+ data = {
117
+ speed: 1 as FloatInput,
118
+ color: "#00aaff" as ColorInput,
119
+ enabled: true as ToggleInput,
120
+ axis: {
121
+ value: "y",
122
+ options: [
123
+ { label: "X", value: "x" },
124
+ { label: "Y", value: "y" },
125
+ { label: "Z", value: "z" },
126
+ ],
127
+ } as DropdownInput,
128
+ };
129
+
130
+ onUpdate() {
131
+ if (!this.data.enabled) return;
132
+ const axis = this.data.axis.value as "x" | "y" | "z";
133
+ this.transform.rotation[axis] += this.data.speed * this.deltaTime;
134
+ }
135
+ }
136
+ ```
137
+
138
+ ## Defaulting fields
139
+
140
+ Defaults are set inline on the `data` property. When a saved project reloads, saved values take precedence — the inline defaults only apply when a field has never been saved. This means:
141
+
142
+ - Adding a new field with an inline default is safe — existing saves will pick it up on next load.
143
+ - The inspector always has a valid initial value when the script is first attached.
144
+ - For `SliderInput` and `DropdownInput`, the full object (including `options`) must be present in the inline default — the editor reads `options` to build the control.
145
+
146
+ ## Persistence
147
+
148
+ `ScriptComponent.data` is serialized and saved with the project. On reload, your `data` payload is restored verbatim. If you rename or restructure a field, migrate it inside `init()`:
149
+
150
+ ```ts
151
+ init() {
152
+ // migrate renamed field
153
+ if ((this.data as any).oldSpeed !== undefined) {
154
+ this.data.speed = (this.data as any).oldSpeed;
155
+ delete (this.data as any).oldSpeed;
156
+ }
157
+ }
158
+ ```
@@ -0,0 +1,37 @@
1
+ # SliderInput
2
+
3
+ A range slider. The inspector renders a horizontal slider with the current value displayed alongside it.
4
+
5
+ **Runtime value:** `{ min: number; max: number; step: number; value: number }` — read `field.value`
6
+
7
+ ![SliderInput inspector control](/images/behaviour-data-binding/slider-input.png)
8
+
9
+ ## Declaration
10
+
11
+ Provide the full object including `min`, `max`, `step`, and the initial `value`. The editor reads `min`/`max`/`step` to configure the slider control.
12
+
13
+ ```ts
14
+ import { Behaviour, type SliderInput } from "@relu-interactives/spatial-ecs";
15
+
16
+ export default class Demo extends Behaviour {
17
+ data = {
18
+ volume: { min: 0, max: 1, step: 0.01, value: 0.8 } as SliderInput,
19
+ quality: { min: 1, max: 10, step: 1, value: 5 } as SliderInput,
20
+ };
21
+ }
22
+ ```
23
+
24
+ ## Runtime usage
25
+
26
+ Read the `.value` property — the other fields (`min`, `max`, `step`) are for the editor control only:
27
+
28
+ ```ts
29
+ onUpdate() {
30
+ const volume = this.data.volume.value; // number between 0 and 1
31
+ }
32
+ ```
33
+
34
+ ## Notes
35
+
36
+ - Always include `min`, `max`, and `step` in the inline default — the editor needs them to render the control.
37
+ - `step` can be fractional for smooth sliders (e.g. `0.01`).
@@ -0,0 +1,35 @@
1
+ # TextInput
2
+
3
+ A single-line text field. The inspector renders a plain text input.
4
+
5
+ **Runtime value:** `string`
6
+
7
+ ![TextInput inspector control](/images/behaviour-data-binding/text-input.png)
8
+
9
+ ## Declaration
10
+
11
+ ```ts
12
+ import { Behaviour, type TextInput } from "@relu-interactives/spatial-ecs";
13
+
14
+ export default class Demo extends Behaviour {
15
+ data = {
16
+ label: "Hello world" as TextInput,
17
+ apiKey: "" as TextInput,
18
+ };
19
+ }
20
+ ```
21
+
22
+ ## Runtime usage
23
+
24
+ Use the value directly as a `string`:
25
+
26
+ ```ts
27
+ init() {
28
+ console.log(this.data.label); // "Hello world"
29
+ }
30
+ ```
31
+
32
+ ## Notes
33
+
34
+ - The default value is what the user sees when the script is first attached.
35
+ - Useful for URLs, display strings, or any freeform text parameter.
@@ -0,0 +1,36 @@
1
+ # ToggleInput
2
+
3
+ An on/off toggle switch. The inspector renders a pill-shaped toggle.
4
+
5
+ **Runtime value:** `boolean`
6
+
7
+ ![ToggleInput inspector control](/images/behaviour-data-binding/toggle-input.png)
8
+
9
+ ## Declaration
10
+
11
+ ```ts
12
+ import { Behaviour, type ToggleInput } from "@relu-interactives/spatial-ecs";
13
+
14
+ export default class Demo extends Behaviour {
15
+ data = {
16
+ enabled: true as ToggleInput,
17
+ loopAudio: false as ToggleInput,
18
+ };
19
+ }
20
+ ```
21
+
22
+ ## Runtime usage
23
+
24
+ Use the value directly as a `boolean`:
25
+
26
+ ```ts
27
+ onUpdate() {
28
+ if (!this.data.enabled) return;
29
+ // ... rest of update logic
30
+ }
31
+ ```
32
+
33
+ ## Notes
34
+
35
+ - Prefer `ToggleInput` for feature flags (e.g. "Enabled", "Loop") and [`CheckboxInput`](./checkbox-input) for property-style options.
36
+ - Both types resolve to a plain `boolean` at runtime.
@@ -0,0 +1,36 @@
1
+ # Vector2Input
2
+
3
+ An XY number pair. The inspector renders two side-by-side number inputs labelled **X** and **Y**.
4
+
5
+ **Runtime value:** `{ x: number; y: number }`
6
+
7
+ ![Vector2Input inspector control](/images/behaviour-data-binding/vector2-input.png)
8
+
9
+ ## Declaration
10
+
11
+ ```ts
12
+ import { Behaviour, type Vector2Input } from "@relu-interactives/spatial-ecs";
13
+
14
+ export default class Demo extends Behaviour {
15
+ data = {
16
+ offset: { x: 0, y: 0 } as Vector2Input,
17
+ uvScale: { x: 1, y: 1 } as Vector2Input,
18
+ };
19
+ }
20
+ ```
21
+
22
+ ## Runtime usage
23
+
24
+ Read `x` and `y` directly:
25
+
26
+ ```ts
27
+ onUpdate() {
28
+ this.transform.position.x += this.data.offset.x * this.deltaTime;
29
+ this.transform.position.y += this.data.offset.y * this.deltaTime;
30
+ }
31
+ ```
32
+
33
+ ## Notes
34
+
35
+ - Suitable for screen-space positions, UV offsets, or any 2D vector parameter.
36
+ - Use [`Vector3Input`](./vector3-input) for 3D world-space values.
@@ -0,0 +1,41 @@
1
+ # Vector3Input
2
+
3
+ An XYZ number triple. The inspector renders three side-by-side number inputs labelled **X**, **Y**, and **Z**.
4
+
5
+ **Runtime value:** `{ x: number; y: number; z: number }`
6
+
7
+ ![Vector3Input inspector control](/images/behaviour-data-binding/vector3-input.png)
8
+
9
+ ## Declaration
10
+
11
+ ```ts
12
+ import { Behaviour, type Vector3Input } from "@relu-interactives/spatial-ecs";
13
+
14
+ export default class Demo extends Behaviour {
15
+ data = {
16
+ spawnPoint: { x: 0, y: 1, z: 0 } as Vector3Input,
17
+ force: { x: 0, y: 0, z: 0 } as Vector3Input,
18
+ };
19
+ }
20
+ ```
21
+
22
+ ## Runtime usage
23
+
24
+ Read `x`, `y`, and `z` directly:
25
+
26
+ ```ts
27
+ import * as THREE from "three";
28
+
29
+ init() {
30
+ const point = new THREE.Vector3(
31
+ this.data.spawnPoint.x,
32
+ this.data.spawnPoint.y,
33
+ this.data.spawnPoint.z,
34
+ );
35
+ }
36
+ ```
37
+
38
+ ## Notes
39
+
40
+ - Ideal for world-space positions, directions, or scale factors.
41
+ - Values are plain numbers — no automatic unit conversion is applied.
@@ -103,6 +103,31 @@ export default class Example extends Behaviour {
103
103
  }
104
104
  ```
105
105
 
106
+ ### Navigate the hierarchy with World methods
107
+
108
+ Instead of querying the `ParentId` component directly, use `world.getEntitiesParent` and `world.getEntitiesChildren` for convenient hierarchy traversal:
109
+
110
+ ```typescript
111
+ import { Behaviour } from "@relu-interactives/spatial-ecs";
112
+
113
+ export default class HierarchyLogger extends Behaviour {
114
+ protected init() {
115
+ // Get parent entity
116
+ const parent = this.world.getEntitiesParent(this.entityId);
117
+ if (parent) {
118
+ console.log(`Parent: ${parent.name} (ID: ${parent.entityId})`);
119
+ }
120
+
121
+ // Get all children
122
+ const children = this.world.getEntitiesChildren(this.entityId);
123
+ console.log(`${children.length} child(ren):`);
124
+ for (const child of children) {
125
+ console.log(` - ${child.name} (ID: ${child.entityId})`);
126
+ }
127
+ }
128
+ }
129
+ ```
130
+
106
131
  ::: tip ObjectManagementSystem
107
132
  Do not call `THREE.Object3D.add` / `remove` directly to reparent. Write to `ParentId.value` instead — the `ObjectManagementSystem` reads this every frame and reconciles the scene graph. This keeps the ECS hierarchy state consistent with the three.js scene graph.
108
133
  :::
@@ -0,0 +1,302 @@
1
+ # Runtime Entity Management
2
+
3
+ From a `Behaviour` script you have full control over the entity lifecycle: spawning new objects, adding or removing components on existing ones, re-parenting entities in the hierarchy, and deleting with proper resource cleanup.
4
+
5
+ ## The ObjectManagementSystem
6
+
7
+ Every world exposes `ObjectManagementSystem` via `this.world.getObjectManagementSystem()`. It is the hub for all runtime entity and component creation:
8
+
9
+ ```ts
10
+ const oms = this.world.getObjectManagementSystem();
11
+ ```
12
+
13
+ Always null-check — `oms` returns `null` if the world has not been set up yet.
14
+
15
+ ---
16
+
17
+ ## 1. Spawning new objects
18
+
19
+ Use `requestCreate` to queue a new entity from a built-in kind. The factory runs on the next frame and the optional `onCreated` callback fires with the new `entityId`:
20
+
21
+ ```ts
22
+ import { Behaviour } from "@relu-interactives/spatial-ecs";
23
+
24
+ export default class Spawner extends Behaviour {
25
+ private spawnedId: number | null = null;
26
+
27
+ protected init() {
28
+ const oms = this.world.getObjectManagementSystem();
29
+
30
+ oms?.requestCreate("cube", {
31
+ name: "RuntimeCube",
32
+ transform: { position: { x: 0, y: 1, z: 0 } },
33
+ }, (entityId) => {
34
+ this.spawnedId = entityId;
35
+ });
36
+ }
37
+
38
+ dispose(world: any) {
39
+ if (this.spawnedId !== null) {
40
+ this.world.getObjectManagementSystem()?.deleteEntity(this.world, this.spawnedId, true);
41
+ }
42
+ }
43
+ }
44
+ ```
45
+
46
+ Supported kinds: `"cube"`, `"sphere"`, `"plane"`, `"capsule"`, `"cylinder"`, `"empty"`, `"model"`, `"image"`, `"sprite"`, `"video"`, `"audio"`, `"camera"`, `"ambient-light"`, `"directional-light"`, `"point-light"`, `"spot-light"`, `"area-light"`, `"instanced-mesh"`.
47
+
48
+ See the [Spawning reference](./spawning/) for per-kind payload schemas.
49
+
50
+ ---
51
+
52
+ ## 2. Adding components to existing entities
53
+
54
+ ### `world.addComponent` — simple data components
55
+
56
+ Use `world.addComponent` to attach data-only components that carry no runtime handles:
57
+
58
+ ```ts
59
+ import { Behaviour } from "@relu-interactives/spatial-ecs";
60
+ import { Name } from "@relu-interactives/spatial-ecs/components";
61
+
62
+ export default class Tagger extends Behaviour {
63
+ protected init() {
64
+ const entity = this.world.getEntityByName("Target");
65
+ if (!entity) return;
66
+
67
+ this.world.addComponent(entity.entityId, new Name({ value: "Tagged" }));
68
+ }
69
+ }
70
+ ```
71
+
72
+ ### `oms.createComponentNow` — factory-initialized components
73
+
74
+ For components that need runtime setup (Rapier physics bodies, Web Audio nodes, Three.js material instances, AnimationMixers) use `createComponentNow`. It calls the same registered factory used during scene construction, so all runtime handles are correctly wired up:
75
+
76
+ ```ts
77
+ import { Behaviour } from "@relu-interactives/spatial-ecs";
78
+
79
+ export default class AddPhysics extends Behaviour {
80
+ protected init() {
81
+ const oms = this.world.getObjectManagementSystem();
82
+ if (!oms) return;
83
+
84
+ oms.createComponentNow("physics", this.world, this.entityId, {
85
+ rigidbody: { type: "dynamic", mass: 1, gravityScale: 1 },
86
+ collider: { shape: "box" },
87
+ });
88
+ }
89
+ }
90
+ ```
91
+
92
+ **When to use which:**
93
+
94
+ | Scenario | Method |
95
+ |----------|--------|
96
+ | Name, EntityType, Transform (data only) | `world.addComponent(id, new ComponentClass(...))` |
97
+ | Physics with Rapier body/collider | `oms.createComponentNow("physics", world, id, config)` |
98
+ | Audio with Web Audio node | `oms.createComponentNow("audio", world, id, path, options, ctx)` |
99
+ | Material with Three.js material instance | `oms.createComponentNow("material", world, id, values)` |
100
+ | Animation with clip catalog | `oms.createComponentNow("animation", world, id)` |
101
+
102
+ **Registered factory kinds:**
103
+
104
+ `"animation"`, `"audio"`, `"camera"`, `"physics"`, `"environment"`, `"entityType"`, `"image"`, `"imageTarget"`, `"imageTargetAnchor"`, `"light"`, `"material"`, `"mesh"`, `"meshGeometry"`, `"model"`, `"name"`, `"object3DRef"`, `"parentId"`, `"postprocessing"`, `"script"`, `"sprite"`, `"transform"`, `"video"`, `"instancedMesh"`.
105
+
106
+ ---
107
+
108
+ ## 3. Removing components
109
+
110
+ Remove a component by passing its class to `world.removeComponent`. The associated runtime handles are not disposed — call system-specific cleanup first if needed:
111
+
112
+ ```ts
113
+ import { Behaviour } from "@relu-interactives/spatial-ecs";
114
+ import { PhysicsComponent } from "@relu-interactives/spatial-ecs/components";
115
+
116
+ export default class Freezer extends Behaviour {
117
+ protected init() {
118
+ // Remove physics from this entity to freeze it in place
119
+ this.world.removeComponent(this.entityId, PhysicsComponent);
120
+ }
121
+ }
122
+ ```
123
+
124
+ ---
125
+
126
+ ## 4. Re-parenting at runtime
127
+
128
+ `oms.setParent(world, entityId, parentId, options?)` updates the `ParentId` component and reconciles the Three.js scene graph in the same frame. By default (`preserveWorldTransform: true`) the entity's world-space position and rotation are preserved by adjusting the local transform:
129
+
130
+ ```ts
131
+ import { Behaviour } from "@relu-interactives/spatial-ecs";
132
+
133
+ export default class Attach extends Behaviour {
134
+ protected init() {
135
+ const oms = this.world.getObjectManagementSystem();
136
+ if (!oms) return;
137
+
138
+ const anchor = this.world.getEntityByName("Anchor");
139
+ if (!anchor) return;
140
+
141
+ // Attach this entity under Anchor, preserving world position
142
+ oms.setParent(this.world, this.entityId, anchor.entityId);
143
+ }
144
+ }
145
+ ```
146
+
147
+ Detach from parent (become a direct child of the scene root):
148
+
149
+ ```ts
150
+ oms.setParent(this.world, this.entityId, null);
151
+ ```
152
+
153
+ `setParent` guards against cycles and returns `false` if the operation would create a loop or if either entity does not exist.
154
+
155
+ ---
156
+
157
+ ## 5. Deleting entities and freeing GPU memory
158
+
159
+ `oms.deleteEntity(world, entityId, dispose?)` removes the entity from the ECS world and its `Object3D` from the Three.js scene graph:
160
+
161
+ - Pass `dispose = false` (default) when geometry or materials are shared — GPU resources stay allocated.
162
+ - Pass `dispose = true` for dynamically spawned, one-off objects to immediately free `BufferGeometry` and `Material` GPU memory.
163
+
164
+ ```ts
165
+ // Safe remove (shared geometry/materials stay on GPU)
166
+ oms.deleteEntity(this.world, entityId);
167
+
168
+ // Full cleanup (frees GPU memory — use for unique spawned objects)
169
+ oms.deleteEntity(this.world, entityId, true);
170
+ ```
171
+
172
+ Always delete spawned entities in `dispose()` to prevent leaks when the script is removed:
173
+
174
+ ```ts
175
+ dispose(world: any) {
176
+ if (this.spawnedId !== null) {
177
+ this.world.getObjectManagementSystem()?.deleteEntity(this.world, this.spawnedId, true);
178
+ }
179
+ }
180
+ ```
181
+
182
+ ---
183
+
184
+ ## 6. Async loading patterns
185
+
186
+ Models and audio load asynchronously. The entity is created immediately but the asset is not available until the loader fires. Use the `onCreated` callback for setup that doesn't need the asset, and poll component state in `onUpdate` for setup that does:
187
+
188
+ ```ts
189
+ import { Behaviour } from "@relu-interactives/spatial-ecs";
190
+ import { AnimationComponent } from "@relu-interactives/spatial-ecs/components";
191
+
192
+ export default class ModelSpawner extends Behaviour {
193
+ private modelId: number | null = null;
194
+ private animSetup = false;
195
+
196
+ protected init() {
197
+ const oms = this.world.getObjectManagementSystem();
198
+
199
+ oms?.requestCreate("model", {
200
+ name: "Character",
201
+ path: "localasset://assets/character.glb",
202
+ transform: { position: { x: 0, y: 0, z: 0 } },
203
+ }, (entityId) => {
204
+ this.modelId = entityId;
205
+
206
+ // Safe immediately — entity exists, physics factory is synchronous
207
+ oms.createComponentNow("physics", this.world, entityId, {
208
+ rigidbody: { type: "dynamic", mass: 1 },
209
+ collider: { shape: "capsule" },
210
+ });
211
+ });
212
+ }
213
+
214
+ protected onUpdate() {
215
+ if (this.modelId === null || this.animSetup) return;
216
+
217
+ // availableClips populates after the GLB finishes loading
218
+ const anim = this.world.getComponent(this.modelId, AnimationComponent);
219
+ if (anim && anim.availableClips.length > 0) {
220
+ anim.addState("Idle", anim.availableClips[0], true);
221
+ anim.switchState("Idle");
222
+ this.animSetup = true;
223
+ }
224
+ }
225
+
226
+ dispose(world: any) {
227
+ if (this.modelId !== null) {
228
+ this.world.getObjectManagementSystem()?.deleteEntity(this.world, this.modelId, true);
229
+ }
230
+ }
231
+ }
232
+ ```
233
+
234
+ ---
235
+
236
+ ## 7. Complete worked example
237
+
238
+ Spawn a model, add physics, parent it under an AR anchor, set up animation once clips are ready, and clean everything up on dispose:
239
+
240
+ ```ts
241
+ import { Behaviour } from "@relu-interactives/spatial-ecs";
242
+ import { AnimationComponent } from "@relu-interactives/spatial-ecs/components";
243
+
244
+ export default class ARCharacter extends Behaviour {
245
+ private characterId: number | null = null;
246
+ private animReady = false;
247
+
248
+ protected init() {
249
+ const oms = this.world.getObjectManagementSystem();
250
+ if (!oms) return;
251
+
252
+ const anchor = this.world.getEntityByName("ImageTargetAnchor");
253
+
254
+ oms.requestCreate("model", {
255
+ name: "ARCharacter",
256
+ path: "localasset://assets/character.glb",
257
+ transform: { position: { x: 0, y: 0, z: 0 } },
258
+ }, (entityId) => {
259
+ this.characterId = entityId;
260
+
261
+ // Parent under AR anchor so it tracks with the target
262
+ if (anchor) {
263
+ oms.setParent(this.world, entityId, anchor.entityId);
264
+ }
265
+
266
+ // Add physics (Rapier body wired immediately by the factory)
267
+ oms.createComponentNow("physics", this.world, entityId, {
268
+ rigidbody: { type: "kinematic-position" },
269
+ collider: { shape: "capsule" },
270
+ });
271
+ });
272
+ }
273
+
274
+ protected onUpdate() {
275
+ if (this.characterId === null || this.animReady) return;
276
+
277
+ const anim = this.world.getComponent(this.characterId, AnimationComponent);
278
+ if (anim && anim.availableClips.length > 0) {
279
+ if (anim.availableClips.includes("Idle")) anim.addState("Idle", "Idle", true);
280
+ if (anim.availableClips.includes("Walk")) anim.addState("Walk", "Walk", true);
281
+ anim.crossFadeDuration = 0.3;
282
+ anim.switchState("Idle");
283
+ this.animReady = true;
284
+ }
285
+ }
286
+
287
+ dispose(world: any) {
288
+ if (this.characterId !== null) {
289
+ this.world.getObjectManagementSystem()?.deleteEntity(this.world, this.characterId, true);
290
+ }
291
+ }
292
+ }
293
+ ```
294
+
295
+ ---
296
+
297
+ ## See also
298
+
299
+ - [Spawning reference](./spawning/) — per-kind payload schemas for `requestCreate`
300
+ - [ObjectManagementSystem](./systems/object-management-system) — system internals and API reference
301
+ - [Accessing Components](./accessing-components) — reading and writing component state
302
+ - [World API](./world-api) — entity queries, input, raycasting, scene access