@relu-interactives/spatial-ecs 0.3.14 → 0.3.15

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.
@@ -114,6 +114,17 @@ export default class Rifle extends Behaviour {
114
114
 
115
115
  Every grab setting is a plain field on the component, so a script can change it mid-session — switch on resizing, retune throw strength, or move an attach point in response to what the player is doing.
116
116
 
117
+ ## Vision Pro
118
+
119
+ Vision Pro drives WebXR entirely through **gaze and pinch**. There are no controllers, and the input source for a pinch is created when the pinch starts and destroyed when it ends. That has a few consequences worth designing around:
120
+
121
+ - **Look at a thing and pinch** to select or grab it. The pick ray starts between the user's eyes and is then carried by the hand, so aiming is "pinch, then move your hand", not "point, then press".
122
+ - **There is no hover before a pinch** — the input source does not exist until then. Pointer rays and hover feedback therefore only appear *during* a pinch, not while the user looks around.
123
+ - **Grip/squeeze is never reported.** Set an interactable's **Gesture** to `Pinch` or `Any`; `Full grip` can never trigger on Vision Pro.
124
+ - Grabbing does not teleport the user, the same as on hand tracking elsewhere.
125
+ - Hand tracking (and therefore hand poses) is available on visionOS 2 and later, but asks the user for a second permission. The OS draws the user's real hands.
126
+ - `immersive-ar` is not supported by Safari on visionOS, so VR projects are the only kind that run there.
127
+
117
128
  ## Hand poses
118
129
 
119
130
  Add an **XR Hand Pose** component and list the poses to watch for. Each is either a built-in preset or a custom set of finger curls.
@@ -501,15 +501,20 @@ declare class XRInteractionSystem implements EcsSystem {
501
501
  /** True when any source is holding something. */
502
502
  isGrabbing(): boolean;
503
503
  /**
504
- * Whether this hand's primary action is committed to interaction.
504
+ * Whether this input's primary action is committed to interaction.
505
505
  *
506
- * True while the hand holds something, and also while it merely HOVERS a
506
+ * True while the source holds something, and also while it merely HOVERS a
507
507
  * grabbable — because the pinch that is about to grab must not be read as a
508
508
  * teleport by the locomotion system. Without the hover half, the first pinch
509
509
  * of a grab teleports the player, and so does the pinch that takes an object
510
510
  * from the other hand.
511
+ *
512
+ * Keyed on the INPUT SOURCE rather than handedness. Vision Pro reports
513
+ * `handedness: "none"` for every gaze-pinch (it is hard-coded in WebKit), so a
514
+ * handedness-keyed check silently never matched there and each pinch both
515
+ * grabbed and teleported.
511
516
  */
512
- isHandBusy(world: World, handedness: XrInputState["handedness"]): boolean;
517
+ isSourceBusy(world: World, input: XrInputState): boolean;
513
518
  /** True when THIS hand is holding something. */
514
519
  isGrabbingWith(handedness: XrInputState["handedness"]): boolean;
515
520
  /**
@@ -501,15 +501,20 @@ declare class XRInteractionSystem implements EcsSystem {
501
501
  /** True when any source is holding something. */
502
502
  isGrabbing(): boolean;
503
503
  /**
504
- * Whether this hand's primary action is committed to interaction.
504
+ * Whether this input's primary action is committed to interaction.
505
505
  *
506
- * True while the hand holds something, and also while it merely HOVERS a
506
+ * True while the source holds something, and also while it merely HOVERS a
507
507
  * grabbable — because the pinch that is about to grab must not be read as a
508
508
  * teleport by the locomotion system. Without the hover half, the first pinch
509
509
  * of a grab teleports the player, and so does the pinch that takes an object
510
510
  * from the other hand.
511
+ *
512
+ * Keyed on the INPUT SOURCE rather than handedness. Vision Pro reports
513
+ * `handedness: "none"` for every gaze-pinch (it is hard-coded in WebKit), so a
514
+ * handedness-keyed check silently never matched there and each pinch both
515
+ * grabbed and teleported.
511
516
  */
512
- isHandBusy(world: World, handedness: XrInputState["handedness"]): boolean;
517
+ isSourceBusy(world: World, input: XrInputState): boolean;
513
518
  /** True when THIS hand is holding something. */
514
519
  isGrabbingWith(handedness: XrInputState["handedness"]): boolean;
515
520
  /**
package/dist/index.cjs CHANGED
@@ -11728,17 +11728,22 @@ var _XRInteractionSystem = class _XRInteractionSystem {
11728
11728
  return this.grabs.size > 0;
11729
11729
  }
11730
11730
  /**
11731
- * Whether this hand's primary action is committed to interaction.
11731
+ * Whether this input's primary action is committed to interaction.
11732
11732
  *
11733
- * True while the hand holds something, and also while it merely HOVERS a
11733
+ * True while the source holds something, and also while it merely HOVERS a
11734
11734
  * grabbable — because the pinch that is about to grab must not be read as a
11735
11735
  * teleport by the locomotion system. Without the hover half, the first pinch
11736
11736
  * of a grab teleports the player, and so does the pinch that takes an object
11737
11737
  * from the other hand.
11738
+ *
11739
+ * Keyed on the INPUT SOURCE rather than handedness. Vision Pro reports
11740
+ * `handedness: "none"` for every gaze-pinch (it is hard-coded in WebKit), so a
11741
+ * handedness-keyed check silently never matched there and each pinch both
11742
+ * grabbed and teleported.
11738
11743
  */
11739
- isHandBusy(world, handedness) {
11740
- if (this.isGrabbingWith(handedness)) return true;
11741
- const hover = this.getHoverFor(handedness);
11744
+ isSourceBusy(world, input) {
11745
+ if (this.grabs.has(input)) return true;
11746
+ const hover = this.hovers.get(input);
11742
11747
  if (!hover) return false;
11743
11748
  const interactable = world.getComponent(
11744
11749
  hover.entityId,
@@ -11832,7 +11837,7 @@ var _TeleportAimGate = class _TeleportAimGate {
11832
11837
  if (aiming) this.aiming.add(source);
11833
11838
  if (!releasing) return false;
11834
11839
  this.aiming.delete(source);
11835
- return hadAim;
11840
+ return hadAim || aiming;
11836
11841
  }
11837
11842
  /** True if this source currently has an aim engaged. */
11838
11843
  isAiming(source) {
@@ -12006,7 +12011,7 @@ var _XRLocomotionSystem = class _XRLocomotionSystem {
12006
12011
  const targets = this.collectTeleportTargets(world);
12007
12012
  for (const input of inputs) {
12008
12013
  const source = input;
12009
- if (input.handedness !== "none" && this.interactionSystem?.isHandBusy(world, input.handedness)) {
12014
+ if (this.interactionSystem?.isSourceBusy(world, input)) {
12010
12015
  if (this.teleportAim.cancel(source)) {
12011
12016
  this.aimCurve = [];
12012
12017
  this.landingPoint = null;
@@ -12015,7 +12020,7 @@ var _XRLocomotionSystem = class _XRLocomotionSystem {
12015
12020
  continue;
12016
12021
  }
12017
12022
  const stick = this.readStickAim(input, source);
12018
- const aiming = input.select || stick.aiming;
12023
+ const aiming = input.select || input.selectStart || stick.aiming;
12019
12024
  const releasing = input.selectEnd || stick.released;
12020
12025
  const commit = this.teleportAim.update(source, aiming, releasing);
12021
12026
  if (!aiming && !commit) continue;