diff --git a/Assets/Tests/InputSystem/Plugins/XRTests.cs b/Assets/Tests/InputSystem/Plugins/XRTests.cs index d222dd26b2..6f0e9cd1fd 100644 --- a/Assets/Tests/InputSystem/Plugins/XRTests.cs +++ b/Assets/Tests/InputSystem/Plugins/XRTests.cs @@ -1,6 +1,7 @@ // ENABLE_VR is not defined on Game Core but the assembly is available with limited features when the XR module is enabled. #if ENABLE_VR || UNITY_GAMECORE using System; +using System.Collections; using System.Collections.Generic; using System.Runtime.InteropServices; using NUnit.Framework; @@ -11,6 +12,7 @@ using UnityEngine.InputSystem.LowLevel; using UnityEngine.InputSystem.Utilities; using UnityEngine.InputSystem.XR; +using UnityEngine.TestTools; using UnityEngine.XR; using Usages = UnityEngine.InputSystem.CommonUsages; @@ -629,6 +631,54 @@ public void Components_TrackedPoseDriver_DoesNotEnableOrDisableReferenceActions( Assert.That(trackingStateInput.action.enabled, Is.False); } + [UnityTest] + [Category("Components")] + public IEnumerator CanUseTrackedPoseDriverWithoutTrackingAction() + { + var go = new GameObject(); + var tpd = go.AddComponent(); + tpd.updateType = TrackedPoseDriver.UpdateType.UpdateAndBeforeRender; + tpd.trackingType = TrackedPoseDriver.TrackingType.RotationAndPosition; + tpd.ignoreTrackingState = false; + var transform = tpd.transform; + + var positionAction = new InputAction(binding: "/devicePosition"); + var rotationAction = new InputAction(binding: "/deviceRotation"); + tpd.positionInput = new InputActionProperty(positionAction); + tpd.rotationInput = new InputActionProperty(rotationAction); + + yield return null; + + Assert.That(positionAction.controls.Count, Is.EqualTo(0)); + Assert.That(rotationAction.controls.Count, Is.EqualTo(0)); + + var device = InputSystem.AddDevice(); + InputSystem.AddDeviceUsage(device, "RightHand"); + + yield return null; + + Assert.That(positionAction.controls.Count, Is.EqualTo(1)); + Assert.That(rotationAction.controls.Count, Is.EqualTo(1)); + + var position = new Vector3(1f, 2f, 3f); + var rotation = new Quaternion(0.09853293f, 0.09853293f, 0.09853293f, 0.9853293f); + using (StateEvent.From(device, out var stateEvent)) + { + device.devicePosition.WriteValueIntoEvent(position, stateEvent); + device.deviceRotation.WriteValueIntoEvent(rotation, stateEvent); + + transform.position = Vector3.zero; + transform.rotation = Quaternion.identity; + InputSystem.QueueEvent(stateEvent); + InputSystem.Update(InputUpdateType.Dynamic); + } + + yield return null; + + Assert.That(transform.position, Is.EqualTo(position)); + Assert.That(transform.rotation, Is.EqualTo(rotation)); + } + [Test] [Category("Components")] public void Components_TrackedPoseDriver_RequiresResolvedTrackingStateBindings() diff --git a/Packages/com.unity.inputsystem/CHANGELOG.md b/Packages/com.unity.inputsystem/CHANGELOG.md index 280718a4f7..a7a8426a40 100644 --- a/Packages/com.unity.inputsystem/CHANGELOG.md +++ b/Packages/com.unity.inputsystem/CHANGELOG.md @@ -31,6 +31,7 @@ however, it has to be formatted properly to pass verification tests. - Fixed PlayerInput component automatically switching away from the default ActionMap set to 'None'. - Fixed a console error being shown when targeting visionOS builds in 2022.3. - Fixed a Tap Interaction issue with analog controls. The Tap interaction would keep re-starting after timeout. [ISXB-627](https://issuetracker.unity3d.com/product/unity/issues/guid/ISXB-627) +- Fixed TrackedPoseDriver stops updating position and rotation when device is added after its initialization. [ISXB-1555](https://issuetracker.unity3d.com/product/unity/issues/guid/ISXB-1555) - Fixed PlayerInput component not working with C# Wrappers (ISXB-1535). This reverted changes done to fix [ISXB-920](https://issuetracker.unity3d.com/product/unity/issues/guid/ISXB-920) but users can now fix it themselves. ## [1.14.0] - 2025-03-20 diff --git a/Packages/com.unity.inputsystem/InputSystem/Plugins/XR/TrackedPoseDriver.cs b/Packages/com.unity.inputsystem/InputSystem/Plugins/XR/TrackedPoseDriver.cs index 56e914af04..f4b9e1decc 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Plugins/XR/TrackedPoseDriver.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Plugins/XR/TrackedPoseDriver.cs @@ -432,6 +432,7 @@ protected virtual void Awake() protected void OnEnable() { InputSystem.onAfterUpdate += UpdateCallback; + InputSystem.onDeviceChange += OnDeviceChanged; BindActions(); // Read current input values when becoming enabled, @@ -446,6 +447,7 @@ protected void OnDisable() { UnbindActions(); InputSystem.onAfterUpdate -= UpdateCallback; + InputSystem.onDeviceChange -= OnDeviceChanged; } /// @@ -484,7 +486,7 @@ protected void UpdateCallback() else m_CurrentRotation = transform.localRotation; - ReadTrackingState(hasResolvedPositionInputControl, hasResolvedRotationInputControl); + ReadTrackingState(); m_IsFirstUpdate = false; } @@ -495,7 +497,40 @@ protected void UpdateCallback() OnUpdate(); } - void ReadTrackingState(bool hasResolvedPositionInputControl, bool hasResolvedRotationInputControl) + void OnDeviceChanged(InputDevice inputDevice, InputDeviceChange inputDeviceChange) + { + if (m_IsFirstUpdate) + return; + ReadTrackingStateWithoutTrackingAction(); + } + + /// + /// React to changes of devices to stop the tracking of position / rotation or both if a device is removed, starts the tracking if + /// a device is added. + /// + void ReadTrackingStateWithoutTrackingAction() + { + var trackingStateAction = m_TrackingStateInput.action; + if (trackingStateAction != null && trackingStateAction.m_BindingsCount != 0) + return; + + var hasResolvedPositionInputControl = HasResolvedControl(m_PositionInput.action); + var hasResolvedRotationInputControl = HasResolvedControl(m_RotationInput.action); + + // Treat an Input Action Reference with no reference the same as + // an enabled Input Action with no authored bindings, and allow driving the Transform pose. + // Check if we have transform and rotation controls to drive the pose. + if (hasResolvedPositionInputControl && hasResolvedRotationInputControl) + m_CurrentTrackingState = TrackingStates.Position | TrackingStates.Rotation; + else if (hasResolvedPositionInputControl) + m_CurrentTrackingState = TrackingStates.Position; + else if (hasResolvedRotationInputControl) + m_CurrentTrackingState = TrackingStates.Rotation; + else + m_CurrentTrackingState = TrackingStates.None; + } + + void ReadTrackingState() { var trackingStateAction = m_TrackingStateInput.action; if (trackingStateAction != null && !trackingStateAction.enabled) @@ -504,29 +539,16 @@ void ReadTrackingState(bool hasResolvedPositionInputControl, bool hasResolvedRot m_CurrentTrackingState = TrackingStates.None; return; } - - if (trackingStateAction == null || trackingStateAction.m_BindingsCount == 0) - { - // Treat an Input Action Reference with no reference the same as - // an enabled Input Action with no authored bindings, and allow driving the Transform pose. - // Check if we have transform and rotation controls to drive the pose. - if (hasResolvedPositionInputControl && hasResolvedRotationInputControl) - m_CurrentTrackingState = TrackingStates.Position | TrackingStates.Rotation; - else if (hasResolvedPositionInputControl) - m_CurrentTrackingState = TrackingStates.Position; - else if (hasResolvedRotationInputControl) - m_CurrentTrackingState = TrackingStates.Rotation; - else - m_CurrentTrackingState = TrackingStates.None; - } - else if (HasResolvedControl(trackingStateAction)) + if (HasResolvedControl(trackingStateAction)) { // Retain the current value if there is no resolved binding. // Since the field initializes to allowing position and rotation, // this allows for driving the Transform pose always when the device // doesn't support reporting the tracking state. m_CurrentTrackingState = (TrackingStates)trackingStateAction.ReadValue(); + return; } + ReadTrackingStateWithoutTrackingAction(); } ///