@relu-interactives/spatial-ecs 0.3.14 → 0.3.17
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/{chunk-XHMOFI3Q.js → chunk-CE2CPZSP.js} +25 -11
- package/dist/chunk-CE2CPZSP.js.map +1 -0
- package/dist/docs/guide/vr-and-hand-poses.md +11 -0
- package/dist/{index-CbrjAm_8.d.cts → index-ekS4iypo.d.cts} +10 -3
- package/dist/{index-CvIXdHzx.d.ts → index-uKFxVCmr.d.ts} +10 -3
- package/dist/index.cjs +23 -9
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/systems/index.cjs +23 -9
- package/dist/systems/index.cjs.map +1 -1
- package/dist/systems/index.d.cts +1 -1
- package/dist/systems/index.d.ts +1 -1
- package/dist/systems/index.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-XHMOFI3Q.js.map +0 -1
|
@@ -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
|
|
504
|
+
* Whether this input's primary action is committed to interaction.
|
|
505
505
|
*
|
|
506
|
-
* True while the
|
|
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
|
-
|
|
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
|
/**
|
|
@@ -545,6 +550,8 @@ declare class XRLocomotionSystem implements EcsSystem {
|
|
|
545
550
|
private aimValid;
|
|
546
551
|
/** Sources currently aiming via thumbstick (hysteresis, so it doesn't chatter). */
|
|
547
552
|
private readonly stickAiming;
|
|
553
|
+
/** One-time warning when a committed teleport found nowhere to land. */
|
|
554
|
+
private warnedNoLanding;
|
|
548
555
|
/** Gates teleport commits on a prior aim. See {@link TeleportAimGate}. */
|
|
549
556
|
private readonly teleportAim;
|
|
550
557
|
/** True once we've tried to self-heal camera parenting for this session. */
|
|
@@ -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
|
|
504
|
+
* Whether this input's primary action is committed to interaction.
|
|
505
505
|
*
|
|
506
|
-
* True while the
|
|
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
|
-
|
|
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
|
/**
|
|
@@ -545,6 +550,8 @@ declare class XRLocomotionSystem implements EcsSystem {
|
|
|
545
550
|
private aimValid;
|
|
546
551
|
/** Sources currently aiming via thumbstick (hysteresis, so it doesn't chatter). */
|
|
547
552
|
private readonly stickAiming;
|
|
553
|
+
/** One-time warning when a committed teleport found nowhere to land. */
|
|
554
|
+
private warnedNoLanding;
|
|
548
555
|
/** Gates teleport commits on a prior aim. See {@link TeleportAimGate}. */
|
|
549
556
|
private readonly teleportAim;
|
|
550
557
|
/** True once we've tried to self-heal camera parenting for this session. */
|
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
|
|
11731
|
+
* Whether this input's primary action is committed to interaction.
|
|
11732
11732
|
*
|
|
11733
|
-
* True while the
|
|
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
|
-
|
|
11740
|
-
if (this.
|
|
11741
|
-
const hover = this.
|
|
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) {
|
|
@@ -11870,6 +11875,8 @@ var _XRLocomotionSystem = class _XRLocomotionSystem {
|
|
|
11870
11875
|
this.aimValid = false;
|
|
11871
11876
|
/** Sources currently aiming via thumbstick (hysteresis, so it doesn't chatter). */
|
|
11872
11877
|
this.stickAiming = /* @__PURE__ */ new WeakSet();
|
|
11878
|
+
/** One-time warning when a committed teleport found nowhere to land. */
|
|
11879
|
+
this.warnedNoLanding = false;
|
|
11873
11880
|
/** Gates teleport commits on a prior aim. See {@link TeleportAimGate}. */
|
|
11874
11881
|
this.teleportAim = new TeleportAimGate();
|
|
11875
11882
|
/** True once we've tried to self-heal camera parenting for this session. */
|
|
@@ -12006,7 +12013,7 @@ var _XRLocomotionSystem = class _XRLocomotionSystem {
|
|
|
12006
12013
|
const targets = this.collectTeleportTargets(world);
|
|
12007
12014
|
for (const input of inputs) {
|
|
12008
12015
|
const source = input;
|
|
12009
|
-
if (
|
|
12016
|
+
if (this.interactionSystem?.isSourceBusy(world, input)) {
|
|
12010
12017
|
if (this.teleportAim.cancel(source)) {
|
|
12011
12018
|
this.aimCurve = [];
|
|
12012
12019
|
this.landingPoint = null;
|
|
@@ -12015,8 +12022,9 @@ var _XRLocomotionSystem = class _XRLocomotionSystem {
|
|
|
12015
12022
|
continue;
|
|
12016
12023
|
}
|
|
12017
12024
|
const stick = this.readStickAim(input, source);
|
|
12018
|
-
const aiming = input.select || stick.aiming;
|
|
12019
|
-
const
|
|
12025
|
+
const aiming = input.select || input.selectStart || stick.aiming;
|
|
12026
|
+
const vanished = !input.connected && this.teleportAim.isAiming(source);
|
|
12027
|
+
const releasing = input.selectEnd || stick.released || vanished;
|
|
12020
12028
|
const commit = this.teleportAim.update(source, aiming, releasing);
|
|
12021
12029
|
if (!aiming && !commit) continue;
|
|
12022
12030
|
const points = sampleTeleportArc(
|
|
@@ -12036,6 +12044,12 @@ var _XRLocomotionSystem = class _XRLocomotionSystem {
|
|
|
12036
12044
|
this.landingPoint = null;
|
|
12037
12045
|
this.aimValid = false;
|
|
12038
12046
|
} else if (commit) {
|
|
12047
|
+
if (!this.warnedNoLanding) {
|
|
12048
|
+
this.warnedNoLanding = true;
|
|
12049
|
+
console.warn(
|
|
12050
|
+
"[XRLocomotionSystem] Teleport released but the arc did not land on a Teleport Area. Add a teleportArea entity where the player should be able to go."
|
|
12051
|
+
);
|
|
12052
|
+
}
|
|
12039
12053
|
this.aimCurve = [];
|
|
12040
12054
|
this.aimValid = false;
|
|
12041
12055
|
}
|