@relu-interactives/spatial-ecs 0.4.5 → 0.5.1
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/dist/{System-ln4Q7Eot.d.cts → System-V_mv07le.d.cts} +2 -2
- package/dist/{System-ln4Q7Eot.d.ts → System-V_mv07le.d.ts} +2 -2
- package/dist/{XRHandPose-CxPv-LBo.d.cts → XRHandPose-CQTLGxFC.d.cts} +218 -2
- package/dist/{XRHandPose-BImIa4ZS.d.ts → XRHandPose-CU3vNFuS.d.ts} +218 -2
- package/dist/{chunk-4AK4I3QF.js → chunk-BUEKOSR5.js} +102 -3
- package/dist/chunk-BUEKOSR5.js.map +1 -0
- package/dist/{chunk-QIIWFP3V.js → chunk-Q52ADLF7.js} +1255 -18
- package/dist/chunk-Q52ADLF7.js.map +1 -0
- package/dist/{chunk-SGADE55G.js → chunk-RXBLOO5B.js} +249 -90
- package/dist/chunk-RXBLOO5B.js.map +1 -0
- package/dist/{chunk-4DWZK4X4.js → chunk-WJD2VKEH.js} +3 -3
- package/dist/chunk-WJD2VKEH.js.map +1 -0
- package/dist/components/index.cjs +99 -0
- package/dist/components/index.cjs.map +1 -1
- package/dist/components/index.d.cts +3 -3
- package/dist/components/index.d.ts +3 -3
- package/dist/components/index.js +1 -1
- package/dist/docs/guide/spatial-ui.md +165 -0
- package/dist/factories/index.cjs +372 -114
- package/dist/factories/index.cjs.map +1 -1
- package/dist/factories/index.d.cts +13 -3
- package/dist/factories/index.d.ts +13 -3
- package/dist/factories/index.js +3 -3
- package/dist/{index-CuGqxuk-.d.ts → index-C3ik3sA1.d.ts} +16 -3
- package/dist/{index-BphKHaV-.d.ts → index-D2Jt_JzH.d.ts} +156 -3
- package/dist/{index-CH7qMsQT.d.cts → index-DaG03vyN.d.cts} +156 -3
- package/dist/{index-CQsHUez3.d.cts → index-Dlr-7-4m.d.cts} +16 -3
- package/dist/index.cjs +1864 -360
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +42 -9
- package/dist/index.d.ts +42 -9
- package/dist/index.js +10 -8
- package/dist/index.js.map +1 -1
- package/dist/systems/index.cjs +1346 -13
- package/dist/systems/index.cjs.map +1 -1
- package/dist/systems/index.d.cts +2 -2
- package/dist/systems/index.d.ts +2 -2
- package/dist/systems/index.js +3 -3
- package/dist/types.d.cts +1 -1
- package/dist/types.d.ts +1 -1
- package/dist/worldSnapshotUtils.d.cts +1 -1
- package/dist/worldSnapshotUtils.d.ts +1 -1
- package/package.json +1 -1
- package/dist/chunk-4AK4I3QF.js.map +0 -1
- package/dist/chunk-4DWZK4X4.js.map +0 -1
- package/dist/chunk-QIIWFP3V.js.map +0 -1
- package/dist/chunk-SGADE55G.js.map +0 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
export { A as AnimationComponent, a as AnimationComponentOptions, b as AudioComponent, C as CameraComponent, c as CameraComponentOptions, d as CameraProjection, e as CameraRuntimeType, f as ColliderShape, g as ColliderState, h as CubeMeshParameters, D as DEFAULT_COLLIDER_STATE, i as DEFAULT_INSTANCED_MESH_PARAMETERS, j as DEFAULT_INSTANCE_TRANSFORM, k as DEFAULT_MESH_GEOMETRY_PARAMETERS, l as DEFAULT_PHYSICS_STATE, m as DEFAULT_POSE_CURLS, n as DEFAULT_POSE_DIRECTIONS, o as DEFAULT_RIGIDBODY_STATE, E as EditableMeshObjectKind, p as EntityType, I as ImageComponent, q as ImageTarget, r as ImageTargetAnchor, s as ImageTargetAnchorOptions, t as ImageTargetReference, u as InstanceSlotComponent, v as InstanceTransform, w as InstancedMeshComponent, x as InstancedMeshKind, L as LightComponent, y as LightValueState, M as MaterialComponent, z as MaterialTypeValue, B as MaterialValueState, F as MeshComponent, G as MeshGeometryComponent, H as MeshGeometryParameters, J as MeshGeometryParametersByKind, K as ModelComponent, N as Name, O as Object3DRef, P as PhysicsComponent, Q as PhysicsState, R as PlaneMeshParameters, S as RadiusHeightMeshParameters, T as RadiusMeshParameters, U as RigidbodyState, V as RigidbodyType, W as ScriptComponent, X as ScriptEntry, Y as ScriptObjectComponent, Z as ScriptObjectComponentInput, _ as ScriptObjectRuntimeState, $ as Selectable, a0 as SelectionTransformState, a1 as SpriteComponent, a2 as TextureFilteringValue, a3 as TransformModeValue, a4 as TransformSpaceValue, a5 as
|
|
2
|
-
export { D as DEFAULT_ENVIRONMENT_STATE, a as DEFAULT_POSTPROCESSING_STATE, E as Environment, b as EnvironmentFogState, c as EnvironmentFogType, d as EnvironmentGradientEffect, e as EnvironmentSkyboxState, f as EnvironmentSkyboxType, g as EnvironmentState, P as ParentId, h as Postprocessing, i as PostprocessingState, T as TransformComponent, j as TransformData, V as Vec3 } from '../System-
|
|
3
|
-
export { P as PrefabInstance, a as PrefabNode, X as XREnvironmentComponent, b as XrEnvironmentKind, c as XrEnvironmentOrientation } from '../index-
|
|
1
|
+
export { A as AnimationComponent, a as AnimationComponentOptions, b as AudioComponent, C as CameraComponent, c as CameraComponentOptions, d as CameraProjection, e as CameraRuntimeType, f as ColliderShape, g as ColliderState, h as CubeMeshParameters, D as DEFAULT_COLLIDER_STATE, i as DEFAULT_INSTANCED_MESH_PARAMETERS, j as DEFAULT_INSTANCE_TRANSFORM, k as DEFAULT_MESH_GEOMETRY_PARAMETERS, l as DEFAULT_PHYSICS_STATE, m as DEFAULT_POSE_CURLS, n as DEFAULT_POSE_DIRECTIONS, o as DEFAULT_RIGIDBODY_STATE, E as EditableMeshObjectKind, p as EntityType, I as ImageComponent, q as ImageTarget, r as ImageTargetAnchor, s as ImageTargetAnchorOptions, t as ImageTargetReference, u as InstanceSlotComponent, v as InstanceTransform, w as InstancedMeshComponent, x as InstancedMeshKind, L as LightComponent, y as LightValueState, M as MaterialComponent, z as MaterialTypeValue, B as MaterialValueState, F as MeshComponent, G as MeshGeometryComponent, H as MeshGeometryParameters, J as MeshGeometryParametersByKind, K as ModelComponent, N as Name, O as Object3DRef, P as PhysicsComponent, Q as PhysicsState, R as PlaneMeshParameters, S as RadiusHeightMeshParameters, T as RadiusMeshParameters, U as RigidbodyState, V as RigidbodyType, W as ScriptComponent, X as ScriptEntry, Y as ScriptObjectComponent, Z as ScriptObjectComponentInput, _ as ScriptObjectRuntimeState, $ as Selectable, a0 as SelectionTransformState, a1 as SpriteComponent, a2 as TextureFilteringValue, a3 as TransformModeValue, a4 as TransformSpaceValue, a5 as UIButtonComponent, a6 as UIButtonOptions, a7 as UIPanelComponent, a8 as UIPanelOptions, a9 as UISliderComponent, aa as UISliderOptions, ab as UITextComponent, ac as UITextOptions, ad as UIToggleComponent, ae as UIToggleOptions, af as UiFollowMode, ag as UiTextAlign, ah as UiTextVAlign, ai as VRTeleportAreaComponent, aj as VRTeleportAreaOptions, ak as VideoComponent, al as VrTeleportShape, am as XRAnchorComponent, an as XRAnchorOptions, ao as XRHandPoseComponent, ap as XRHandPoseOptions, aq as XRInteractableComponent, ar as XRInteractableOptions, as as XRInteractorComponent, at as XRInteractorOptions, au as XRInteractorSideComponent, av as XRRigComponent, aw as XRRigOptions, ax as XrAttachPoint, ay as XrDistanceGrabBehavior, az as XrEnvironmentPhysics, aA as XrGrabGesture, aB as XrGrabHands, aC as XrGrabReach, aD as XrHandPoseEntry, aE as XrHandPreset, aF as XrInteractorSide, aG as XrInteractorType, aH as XrLocomotionMode, aI as XrTrackingOrigin, aJ as XrTwoHandRotation, aK as createAttachPoint, aL as createHandPoseEntry, aM as defaultMaterialValue, aN as handPoseDefinitionFor, aO as normalizePhysicsState, aP as setPhysicsLiveRef } from '../XRHandPose-CQTLGxFC.cjs';
|
|
2
|
+
export { D as DEFAULT_ENVIRONMENT_STATE, a as DEFAULT_POSTPROCESSING_STATE, E as Environment, b as EnvironmentFogState, c as EnvironmentFogType, d as EnvironmentGradientEffect, e as EnvironmentSkyboxState, f as EnvironmentSkyboxType, g as EnvironmentState, P as ParentId, h as Postprocessing, i as PostprocessingState, T as TransformComponent, j as TransformData, V as Vec3 } from '../System-V_mv07le.cjs';
|
|
3
|
+
export { P as PrefabInstance, a as PrefabNode, X as XREnvironmentComponent, b as XrEnvironmentKind, c as XrEnvironmentOrientation } from '../index-Dlr-7-4m.cjs';
|
|
4
4
|
import 'three';
|
|
5
5
|
import '@dimforge/rapier3d-compat';
|
|
6
6
|
import '../handPose-DlcTtqgx.cjs';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
export { A as AnimationComponent, a as AnimationComponentOptions, b as AudioComponent, C as CameraComponent, c as CameraComponentOptions, d as CameraProjection, e as CameraRuntimeType, f as ColliderShape, g as ColliderState, h as CubeMeshParameters, D as DEFAULT_COLLIDER_STATE, i as DEFAULT_INSTANCED_MESH_PARAMETERS, j as DEFAULT_INSTANCE_TRANSFORM, k as DEFAULT_MESH_GEOMETRY_PARAMETERS, l as DEFAULT_PHYSICS_STATE, m as DEFAULT_POSE_CURLS, n as DEFAULT_POSE_DIRECTIONS, o as DEFAULT_RIGIDBODY_STATE, E as EditableMeshObjectKind, p as EntityType, I as ImageComponent, q as ImageTarget, r as ImageTargetAnchor, s as ImageTargetAnchorOptions, t as ImageTargetReference, u as InstanceSlotComponent, v as InstanceTransform, w as InstancedMeshComponent, x as InstancedMeshKind, L as LightComponent, y as LightValueState, M as MaterialComponent, z as MaterialTypeValue, B as MaterialValueState, F as MeshComponent, G as MeshGeometryComponent, H as MeshGeometryParameters, J as MeshGeometryParametersByKind, K as ModelComponent, N as Name, O as Object3DRef, P as PhysicsComponent, Q as PhysicsState, R as PlaneMeshParameters, S as RadiusHeightMeshParameters, T as RadiusMeshParameters, U as RigidbodyState, V as RigidbodyType, W as ScriptComponent, X as ScriptEntry, Y as ScriptObjectComponent, Z as ScriptObjectComponentInput, _ as ScriptObjectRuntimeState, $ as Selectable, a0 as SelectionTransformState, a1 as SpriteComponent, a2 as TextureFilteringValue, a3 as TransformModeValue, a4 as TransformSpaceValue, a5 as
|
|
2
|
-
export { D as DEFAULT_ENVIRONMENT_STATE, a as DEFAULT_POSTPROCESSING_STATE, E as Environment, b as EnvironmentFogState, c as EnvironmentFogType, d as EnvironmentGradientEffect, e as EnvironmentSkyboxState, f as EnvironmentSkyboxType, g as EnvironmentState, P as ParentId, h as Postprocessing, i as PostprocessingState, T as TransformComponent, j as TransformData, V as Vec3 } from '../System-
|
|
3
|
-
export { P as PrefabInstance, a as PrefabNode, X as XREnvironmentComponent, b as XrEnvironmentKind, c as XrEnvironmentOrientation } from '../index-
|
|
1
|
+
export { A as AnimationComponent, a as AnimationComponentOptions, b as AudioComponent, C as CameraComponent, c as CameraComponentOptions, d as CameraProjection, e as CameraRuntimeType, f as ColliderShape, g as ColliderState, h as CubeMeshParameters, D as DEFAULT_COLLIDER_STATE, i as DEFAULT_INSTANCED_MESH_PARAMETERS, j as DEFAULT_INSTANCE_TRANSFORM, k as DEFAULT_MESH_GEOMETRY_PARAMETERS, l as DEFAULT_PHYSICS_STATE, m as DEFAULT_POSE_CURLS, n as DEFAULT_POSE_DIRECTIONS, o as DEFAULT_RIGIDBODY_STATE, E as EditableMeshObjectKind, p as EntityType, I as ImageComponent, q as ImageTarget, r as ImageTargetAnchor, s as ImageTargetAnchorOptions, t as ImageTargetReference, u as InstanceSlotComponent, v as InstanceTransform, w as InstancedMeshComponent, x as InstancedMeshKind, L as LightComponent, y as LightValueState, M as MaterialComponent, z as MaterialTypeValue, B as MaterialValueState, F as MeshComponent, G as MeshGeometryComponent, H as MeshGeometryParameters, J as MeshGeometryParametersByKind, K as ModelComponent, N as Name, O as Object3DRef, P as PhysicsComponent, Q as PhysicsState, R as PlaneMeshParameters, S as RadiusHeightMeshParameters, T as RadiusMeshParameters, U as RigidbodyState, V as RigidbodyType, W as ScriptComponent, X as ScriptEntry, Y as ScriptObjectComponent, Z as ScriptObjectComponentInput, _ as ScriptObjectRuntimeState, $ as Selectable, a0 as SelectionTransformState, a1 as SpriteComponent, a2 as TextureFilteringValue, a3 as TransformModeValue, a4 as TransformSpaceValue, a5 as UIButtonComponent, a6 as UIButtonOptions, a7 as UIPanelComponent, a8 as UIPanelOptions, a9 as UISliderComponent, aa as UISliderOptions, ab as UITextComponent, ac as UITextOptions, ad as UIToggleComponent, ae as UIToggleOptions, af as UiFollowMode, ag as UiTextAlign, ah as UiTextVAlign, ai as VRTeleportAreaComponent, aj as VRTeleportAreaOptions, ak as VideoComponent, al as VrTeleportShape, am as XRAnchorComponent, an as XRAnchorOptions, ao as XRHandPoseComponent, ap as XRHandPoseOptions, aq as XRInteractableComponent, ar as XRInteractableOptions, as as XRInteractorComponent, at as XRInteractorOptions, au as XRInteractorSideComponent, av as XRRigComponent, aw as XRRigOptions, ax as XrAttachPoint, ay as XrDistanceGrabBehavior, az as XrEnvironmentPhysics, aA as XrGrabGesture, aB as XrGrabHands, aC as XrGrabReach, aD as XrHandPoseEntry, aE as XrHandPreset, aF as XrInteractorSide, aG as XrInteractorType, aH as XrLocomotionMode, aI as XrTrackingOrigin, aJ as XrTwoHandRotation, aK as createAttachPoint, aL as createHandPoseEntry, aM as defaultMaterialValue, aN as handPoseDefinitionFor, aO as normalizePhysicsState, aP as setPhysicsLiveRef } from '../XRHandPose-CU3vNFuS.js';
|
|
2
|
+
export { D as DEFAULT_ENVIRONMENT_STATE, a as DEFAULT_POSTPROCESSING_STATE, E as Environment, b as EnvironmentFogState, c as EnvironmentFogType, d as EnvironmentGradientEffect, e as EnvironmentSkyboxState, f as EnvironmentSkyboxType, g as EnvironmentState, P as ParentId, h as Postprocessing, i as PostprocessingState, T as TransformComponent, j as TransformData, V as Vec3 } from '../System-V_mv07le.js';
|
|
3
|
+
export { P as PrefabInstance, a as PrefabNode, X as XREnvironmentComponent, b as XrEnvironmentKind, c as XrEnvironmentOrientation } from '../index-C3ik3sA1.js';
|
|
4
4
|
import 'three';
|
|
5
5
|
import '@dimforge/rapier3d-compat';
|
|
6
6
|
import '../handPose-DlcTtqgx.js';
|
package/dist/components/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { AnimationComponent, AudioComponent, CameraComponent, DEFAULT_COLLIDER_STATE, DEFAULT_ENVIRONMENT_STATE, DEFAULT_INSTANCED_MESH_PARAMETERS, DEFAULT_INSTANCE_TRANSFORM, DEFAULT_MESH_GEOMETRY_PARAMETERS, DEFAULT_PHYSICS_STATE, DEFAULT_POSE_CURLS, DEFAULT_POSE_DIRECTIONS, DEFAULT_POSTPROCESSING_STATE, DEFAULT_RIGIDBODY_STATE, EntityType, Environment, ImageComponent, ImageTarget, ImageTargetAnchor, InstanceSlotComponent, InstancedMeshComponent, LightComponent, MaterialComponent, MeshComponent, MeshGeometryComponent, ModelComponent, Name, Object3DRef, ParentId, PhysicsComponent, Postprocessing, PrefabInstance, PrefabNode, ScriptComponent, ScriptObjectComponent, Selectable, SelectionTransformState, SpriteComponent, TransformComponent, VRTeleportAreaComponent, VideoComponent, XRAnchorComponent, XREnvironmentComponent, XRHandPoseComponent, XRInteractableComponent, XRInteractorComponent, XRInteractorSideComponent, XRRigComponent, createAttachPoint, createHandPoseEntry, defaultMaterialValue, handPoseDefinitionFor, normalizePhysicsState, setPhysicsLiveRef } from '../chunk-
|
|
1
|
+
export { AnimationComponent, AudioComponent, CameraComponent, DEFAULT_COLLIDER_STATE, DEFAULT_ENVIRONMENT_STATE, DEFAULT_INSTANCED_MESH_PARAMETERS, DEFAULT_INSTANCE_TRANSFORM, DEFAULT_MESH_GEOMETRY_PARAMETERS, DEFAULT_PHYSICS_STATE, DEFAULT_POSE_CURLS, DEFAULT_POSE_DIRECTIONS, DEFAULT_POSTPROCESSING_STATE, DEFAULT_RIGIDBODY_STATE, EntityType, Environment, ImageComponent, ImageTarget, ImageTargetAnchor, InstanceSlotComponent, InstancedMeshComponent, LightComponent, MaterialComponent, MeshComponent, MeshGeometryComponent, ModelComponent, Name, Object3DRef, ParentId, PhysicsComponent, Postprocessing, PrefabInstance, PrefabNode, ScriptComponent, ScriptObjectComponent, Selectable, SelectionTransformState, SpriteComponent, TransformComponent, UIButtonComponent, UIPanelComponent, UISliderComponent, UITextComponent, UIToggleComponent, VRTeleportAreaComponent, VideoComponent, XRAnchorComponent, XREnvironmentComponent, XRHandPoseComponent, XRInteractableComponent, XRInteractorComponent, XRInteractorSideComponent, XRRigComponent, createAttachPoint, createHandPoseEntry, defaultMaterialValue, handPoseDefinitionFor, normalizePhysicsState, setPhysicsLiveRef } from '../chunk-BUEKOSR5.js';
|
|
2
2
|
import '../chunk-OY7ZODZP.js';
|
|
3
3
|
//# sourceMappingURL=index.js.map
|
|
4
4
|
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
# Spatial UI
|
|
2
|
+
|
|
3
|
+
Menus, HUDs, labels and settings panels that live **in the world**. UI is built
|
|
4
|
+
from five widget entities — **panel**, **text**, **button**, **toggle**,
|
|
5
|
+
**slider** — each a real entity with its own transform: move it with the gizmo,
|
|
6
|
+
parent it like anything else, save it with the scene.
|
|
7
|
+
|
|
8
|
+
Widgets render on both XR headsets (point with the ray or gaze, click with
|
|
9
|
+
trigger or pinch) and flat 3D experiences (hover and click with the mouse).
|
|
10
|
+
Hover and press feedback is built in — you never script your own highlight.
|
|
11
|
+
|
|
12
|
+
## The one sizing rule
|
|
13
|
+
|
|
14
|
+
**Widget size is authored in metres on the component — never with transform
|
|
15
|
+
scale.** The widget's face is a texture rasterised for the authored size;
|
|
16
|
+
scaling the entity stretches those texels into blur. To make a panel bigger,
|
|
17
|
+
raise its `width`/`height` fields and it re-renders crisply.
|
|
18
|
+
|
|
19
|
+
## Building a menu
|
|
20
|
+
|
|
21
|
+
1. Create a **UI Panel** — the backdrop. A comfortable read is 1–2 m from the
|
|
22
|
+
player, centred around eye height (y ≈ 1.2–1.6).
|
|
23
|
+
2. Create **Text**, **Button**, **Toggle**, **Slider** entities and parent
|
|
24
|
+
them under the panel. Place them with local transforms, a hair in front of
|
|
25
|
+
the backdrop (local z ≈ 0.01) so they draw over it.
|
|
26
|
+
3. Give each interactive widget an `actionId` (empty means "use the entity's
|
|
27
|
+
name").
|
|
28
|
+
4. Attach **one Behaviour to the panel**. UI events fire on the widget's own
|
|
29
|
+
entity first, then **bubble up the parent chain** — so the panel script
|
|
30
|
+
hears every child widget and can switch on `actionId`:
|
|
31
|
+
|
|
32
|
+
```ts
|
|
33
|
+
import { Behaviour, type UiInteractionEvent } from "@relu-interactives/spatial-ecs";
|
|
34
|
+
|
|
35
|
+
export class MenuController extends Behaviour {
|
|
36
|
+
onUiPress(event: UiInteractionEvent) {
|
|
37
|
+
switch (event.actionId) {
|
|
38
|
+
case "start-game":
|
|
39
|
+
// A button under this panel was clicked.
|
|
40
|
+
break;
|
|
41
|
+
case "quit":
|
|
42
|
+
break;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
onUiToggle(event: UiInteractionEvent) {
|
|
47
|
+
if (event.actionId === "music") {
|
|
48
|
+
const on = event.value === true;
|
|
49
|
+
console.log("music enabled:", on);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
onUiChange(event: UiInteractionEvent) {
|
|
54
|
+
if (event.actionId === "volume" && typeof event.value === "number") {
|
|
55
|
+
console.log("volume:", event.value);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Bubbling — not broadcast — is what keeps two copies of the same menu prefab
|
|
62
|
+
independent: each panel script hears only its own children.
|
|
63
|
+
|
|
64
|
+
## The widgets
|
|
65
|
+
|
|
66
|
+
| Widget | Component | What it is |
|
|
67
|
+
| --- | --- | --- |
|
|
68
|
+
| Panel | `UIPanelComponent` | The flat backdrop. Rounded corners, border, translucency, follow mode. |
|
|
69
|
+
| Text | `UITextComponent` | Wrapped, aligned text. `fontSize` is metres of glyph height. |
|
|
70
|
+
| Button | `UIButtonComponent` | Pressable. Fires `onUiPress` on release. |
|
|
71
|
+
| Toggle | `UIToggleComponent` | On/off switch. Fires `onUiToggle`; `value` persists with the scene. |
|
|
72
|
+
| Slider | `UISliderComponent` | Draggable value in `[min, max]`. Fires `onUiChange`. |
|
|
73
|
+
|
|
74
|
+
Images need no widget: parent an ordinary `image` entity under the panel.
|
|
75
|
+
|
|
76
|
+
### Press semantics
|
|
77
|
+
|
|
78
|
+
A click is **press, then release while still pointing at the same widget** —
|
|
79
|
+
sliding off before releasing cancels, exactly like every platform's buttons.
|
|
80
|
+
While a press or slider drag is held, that hand cannot accidentally teleport
|
|
81
|
+
(suppressed the same way grabbing suppresses it).
|
|
82
|
+
|
|
83
|
+
`onUiChange` streams while a slider is dragged (only on real value changes)
|
|
84
|
+
and always fires once more with the final value on release.
|
|
85
|
+
|
|
86
|
+
### Text sizing
|
|
87
|
+
|
|
88
|
+
`fontSize` is **how tall the glyphs stand in the world**, in metres —
|
|
89
|
+
`0.035` reads like comfortable body text at arm's length, independent of the
|
|
90
|
+
panel's texture density. Lines wrap automatically to the component's `width`;
|
|
91
|
+
overflow past `height` is clamped with an ellipsis.
|
|
92
|
+
|
|
93
|
+
## Follow modes
|
|
94
|
+
|
|
95
|
+
- `follow: "world"` (default) — the panel stays where you put it.
|
|
96
|
+
- `follow: "billboard"` — the panel yaw-rotates to face the player every frame
|
|
97
|
+
at runtime (in the editor it holds still so you can work on it).
|
|
98
|
+
|
|
99
|
+
For a **wrist menu**, don't billboard — parent the panel under an XR
|
|
100
|
+
Interactor's **Left** or **Right** slot entity (the same slots used for custom
|
|
101
|
+
hand meshes). It then rides the hand like a watch.
|
|
102
|
+
|
|
103
|
+
## Reading and driving widgets from scripts
|
|
104
|
+
|
|
105
|
+
Widget state is ordinary component data:
|
|
106
|
+
|
|
107
|
+
```ts
|
|
108
|
+
import { Behaviour } from "@relu-interactives/spatial-ecs";
|
|
109
|
+
import {
|
|
110
|
+
UISliderComponent,
|
|
111
|
+
UITextComponent,
|
|
112
|
+
} from "@relu-interactives/spatial-ecs/components";
|
|
113
|
+
|
|
114
|
+
export class ScoreHud extends Behaviour {
|
|
115
|
+
private score = 0;
|
|
116
|
+
|
|
117
|
+
addPoint() {
|
|
118
|
+
this.score += 1;
|
|
119
|
+
// This entity is the HUD text; editing the component re-renders it.
|
|
120
|
+
const text = this.world.getComponent(this.entityId, UITextComponent);
|
|
121
|
+
if (text) text.text = `Score: ${this.score}`;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
readVolume(sliderEntityId: number): number {
|
|
125
|
+
const slider = this.world.getComponent(sliderEntityId, UISliderComponent);
|
|
126
|
+
return slider ? slider.value : 0;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Style/text edits re-render the widget's texture on the next frame. Toggle and
|
|
132
|
+
slider `value` are authored state — what you set in the editor is the state
|
|
133
|
+
the experience starts with.
|
|
134
|
+
|
|
135
|
+
## Event payload
|
|
136
|
+
|
|
137
|
+
Every UI hook receives the same shape:
|
|
138
|
+
|
|
139
|
+
```ts
|
|
140
|
+
import { Behaviour, type UiInteractionEvent } from "@relu-interactives/spatial-ecs";
|
|
141
|
+
|
|
142
|
+
export class Inspect extends Behaviour {
|
|
143
|
+
onUiPress(event: UiInteractionEvent) {
|
|
144
|
+
event.targetEntityId; // the widget that was activated
|
|
145
|
+
event.actionId; // its actionId, or its entity name
|
|
146
|
+
event.input; // the XR input source, or null for mouse clicks
|
|
147
|
+
event.value; // boolean (toggle) / number (slider); absent for presses
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
onUiHoverStart(event: UiInteractionEvent) {
|
|
151
|
+
// The pointer moved onto the widget (also fires for mouse hover).
|
|
152
|
+
console.log("hovering", event.targetEntityId);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
## Performance notes
|
|
158
|
+
|
|
159
|
+
- A widget's face redraws only when its **style or content** changes — an
|
|
160
|
+
idle menu costs nothing per frame.
|
|
161
|
+
- Slider drags and toggle flips move **geometry** (the knob and fill quads),
|
|
162
|
+
not pixels, so dragging never re-renders the texture.
|
|
163
|
+
- `pixelDensity` (panel) trades texture memory for sharpness: the default
|
|
164
|
+
1024 px/m is crisp at arm's length; raise it only for panels the player
|
|
165
|
+
inspects up close.
|