From d209cbea220b1c1d6cc0f8ed2a278c520d2c71b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Malrat?= Date: Mon, 16 Dec 2024 14:42:16 -0500 Subject: [PATCH 1/2] Fixed multiple `OnScreenStick` Components that does not work together when using them simultaneously in isolation mode. --- .../InputSystem/Plugins/OnScreenTests.cs | 78 +++++++++++++++---- Packages/com.unity.inputsystem/CHANGELOG.md | 1 + .../Plugins/OnScreen/OnScreenStick.cs | 73 ++++++++++++++--- 3 files changed, 129 insertions(+), 23 deletions(-) diff --git a/Assets/Tests/InputSystem/Plugins/OnScreenTests.cs b/Assets/Tests/InputSystem/Plugins/OnScreenTests.cs index fd24a691b9..c894865470 100644 --- a/Assets/Tests/InputSystem/Plugins/OnScreenTests.cs +++ b/Assets/Tests/InputSystem/Plugins/OnScreenTests.cs @@ -395,7 +395,7 @@ public void Devices_DisablingLastOnScreenControlDoesReportActiveControl() // https://fogbugz.unity3d.com/f/cases/1271942 [UnityTest] [Category("Devices")] - public IEnumerator Devices_CanHaveOnScreenJoystickControls() + public IEnumerator Devices_CanHaveOnScreenJoystickControls([Values(false, true)] bool useInIsolation) { foreach (var c in Camera.allCameras) Object.Destroy(c.gameObject); @@ -422,18 +422,33 @@ public IEnumerator Devices_CanHaveOnScreenJoystickControls() canvasGO.AddComponent(); canvas.renderMode = RenderMode.ScreenSpaceOverlay; - var stickGO = new GameObject("Stick"); - stickGO.SetActive(false); - var stickTransform = stickGO.AddComponent(); - var stick = stickGO.AddComponent(); - stickGO.AddComponent(); - stickTransform.SetParent(canvasTransform); - stickTransform.anchorMin = new Vector2(0, 0); - stickTransform.anchorMax = new Vector2(0, 0); - stickTransform.anchoredPosition = new Vector2(100, 100); - stickTransform.sizeDelta = new Vector2(100, 100); - stick.controlPath = "/leftStick"; - stickGO.SetActive(true); + var stickLeftGO = new GameObject("StickLeft"); + stickLeftGO.SetActive(false); + var stickLeftTransform = stickLeftGO.AddComponent(); + var stickLeft = stickLeftGO.AddComponent(); + stickLeft.useIsolatedInputActions = useInIsolation; + stickLeftGO.AddComponent(); + stickLeftTransform.SetParent(canvasTransform); + stickLeftTransform.anchorMin = new Vector2(0, 0); + stickLeftTransform.anchorMax = new Vector2(0, 0); + stickLeftTransform.anchoredPosition = new Vector2(100, 100); + stickLeftTransform.sizeDelta = new Vector2(100, 100); + stickLeft.controlPath = "/leftStick"; + stickLeftGO.SetActive(true); + + var stickRightGO = new GameObject("StickRight"); + stickRightGO.SetActive(false); + var stickRightTransform = stickRightGO.AddComponent(); + var stickRight = stickRightGO.AddComponent(); + stickRight.useIsolatedInputActions = useInIsolation; + stickRightGO.AddComponent(); + stickRightTransform.SetParent(canvasTransform); + stickRightTransform.anchorMin = new Vector2(0, 0); + stickRightTransform.anchorMax = new Vector2(0, 0); + stickRightTransform.anchoredPosition = new Vector2(500, 100); + stickRightTransform.sizeDelta = new Vector2(100, 100); + stickRight.controlPath = "/rightStick"; + stickRightGO.SetActive(true); var buttonGO = new GameObject("Button"); buttonGO.SetActive(false); @@ -464,7 +479,7 @@ public IEnumerator Devices_CanHaveOnScreenJoystickControls() Assert.That(player.devices, Is.EquivalentTo(new[] { Gamepad.all[0] })); - // Touch the stick and drag it upwards. + // Touch the Left stick and drag it upwards. BeginTouch(1, new Vector2(150, 150)); yield return null; eventSystem.Update(); @@ -491,6 +506,38 @@ public IEnumerator Devices_CanHaveOnScreenJoystickControls() InputSystem.Update(); // Button is feeding events when responding to UI events. Assert.That(Gamepad.all[0].buttonSouth.isPressed, Is.False); + + // Touch the right stick and drag it downwards + BeginTouch(2, new Vector2(550, 150)); + yield return null; + eventSystem.Update(); + Assert.That(eventSystem.IsPointerOverGameObject(), Is.True); + MoveTouch(2, new Vector2(550, 50)); + yield return null; + eventSystem.Update(); + InputSystem.Update(); // Stick is feeding events when responding to UI events. + + Assert.That(Gamepad.all[0].leftStick.ReadValue(), Is.EqualTo(new Vector2(0, 1)).Using(Vector2EqualityComparer.Instance)); + Assert.That(Gamepad.all[0].rightStick.ReadValue(), Is.EqualTo(new Vector2(0, -1)).Using(Vector2EqualityComparer.Instance)); + + // Release finger one and move second and ensure that it still works + EndTouch(1, new Vector2(550, 200)); + MoveTouch(2, new Vector2(600, 150)); + yield return null; + eventSystem.Update(); + InputSystem.Update(); // Stick is feeding events when responding to UI events. + + Assert.That(Gamepad.all[0].leftStick.ReadValue(), Is.EqualTo(new Vector2(0, 0)).Using(Vector2EqualityComparer.Instance)); + Assert.That(Gamepad.all[0].rightStick.ReadValue(), Is.EqualTo(new Vector2(1, 0)).Using(Vector2EqualityComparer.Instance)); + + // Release finger two + EndTouch(2, new Vector2(600, 150)); + yield return null; + eventSystem.Update(); + InputSystem.Update(); // Stick is feeding events when responding to UI events. + + Assert.That(Gamepad.all[0].leftStick.ReadValue(), Is.EqualTo(new Vector2(0, 0)).Using(Vector2EqualityComparer.Instance)); + Assert.That(Gamepad.all[0].rightStick.ReadValue(), Is.EqualTo(new Vector2(0, 0)).Using(Vector2EqualityComparer.Instance)); } [UnityTest] @@ -518,6 +565,9 @@ public IEnumerator Devices_OnScreenStickDoesNotReceivePointerUpEventsInIsolatedM { uiTestScene.uiInputModule.actionsAsset.actionMaps[0].LazyResolveBindings(true); }; + + // Ensure that the OnScreenStick component has been started + yield return null; yield return uiTestScene.PressAndDrag(image, new Vector2(50, 50)); diff --git a/Packages/com.unity.inputsystem/CHANGELOG.md b/Packages/com.unity.inputsystem/CHANGELOG.md index 2a36143974..4c254c5ae7 100644 --- a/Packages/com.unity.inputsystem/CHANGELOG.md +++ b/Packages/com.unity.inputsystem/CHANGELOG.md @@ -34,6 +34,7 @@ however, it has to be formatted properly to pass verification tests. - Fixed an issue with default device selection when adding new Control Scheme. - Fixed an issue where action map delegates were not updated when the asset already assigned to the PlayerInput component were changed [ISXB-711](https://issuetracker.unity3d.com/product/unity/issues/guid/ISXB-711). - Fixed Action properties edition in the UI Toolkit version of the Input Actions Asset editor. [ISXB-1277](https://issuetracker.unity3d.com/product/unity/issues/guid/ISXB-1277) +- Fixed multiple `OnScreenStick` Components that does not work together when using them simultaneously in isolation mode. [ISXB-813](https://issuetracker.unity3d.com/product/unity/issues/guid/ISXB-813) ### Changed - Added back the InputManager to InputSystem project-wide asset migration code with performance improvement (ISX-2086). diff --git a/Packages/com.unity.inputsystem/InputSystem/Plugins/OnScreen/OnScreenStick.cs b/Packages/com.unity.inputsystem/InputSystem/Plugins/OnScreen/OnScreenStick.cs index c226926a02..9092c98cc3 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Plugins/OnScreen/OnScreenStick.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Plugins/OnScreen/OnScreenStick.cs @@ -6,6 +6,7 @@ using UnityEngine.InputSystem.Layouts; using UnityEngine.InputSystem.Utilities; using UnityEngine.UI; +using UnityEngine.InputSystem.Controls; #if UNITY_EDITOR using UnityEditor; @@ -91,7 +92,10 @@ private void Start() if (m_PointerDownAction == null || m_PointerDownAction.bindings.Count == 0) { if (m_PointerDownAction == null) - m_PointerDownAction = new InputAction(); + m_PointerDownAction = new InputAction(type: InputActionType.PassThrough); + // ensure PassThrough mode + else if (m_PointerDownAction.m_Type != InputActionType.PassThrough) + m_PointerDownAction.m_Type = InputActionType.PassThrough; #if UNITY_EDITOR InputExitPlayModeAnalytic.suppress = true; @@ -121,8 +125,7 @@ private void Start() #endif } - m_PointerDownAction.started += OnPointerDown; - m_PointerDownAction.canceled += OnPointerUp; + m_PointerDownAction.performed += OnPointerChanged; m_PointerDownAction.Enable(); m_PointerMoveAction.Enable(); } @@ -154,8 +157,7 @@ private void OnDestroy() { if (m_UseIsolatedInputActions) { - m_PointerDownAction.started -= OnPointerDown; - m_PointerDownAction.canceled -= OnPointerUp; + m_PointerDownAction.performed -= OnPointerChanged; } } @@ -227,12 +229,21 @@ private void EndInteraction() private void OnPointerDown(InputAction.CallbackContext ctx) { + if (m_IsIsolationActive) { return; } Debug.Assert(EventSystem.current != null); var screenPosition = Vector2.zero; - if (ctx.control?.device is Pointer pointer) + TouchControl touchControl = null; + if (ctx.control?.parent is TouchControl touch) + { + touchControl = touch; + screenPosition = touch.position.ReadValue(); + } + else if (ctx.control?.device is Pointer pointer) + { screenPosition = pointer.position.ReadValue(); - + } + m_PointerEventData.position = screenPosition; EventSystem.current.RaycastAll(m_PointerEventData, m_RaycastResults); if (m_RaycastResults.Count == 0) @@ -251,23 +262,63 @@ private void OnPointerDown(InputAction.CallbackContext ctx) return; BeginInteraction(screenPosition, GetCameraFromCanvas()); + if (touchControl != null) + { + m_TouchControl = touchControl; + m_PointerMoveAction.ApplyBindingOverride($"{touchControl.path}/position", path: "/touch*/position"); + } + m_PointerMoveAction.performed += OnPointerMove; + m_IsIsolationActive = true; + } + + private void OnPointerChanged(InputAction.CallbackContext ctx) + { + if (ctx.control.IsPressed()) + OnPointerDown(ctx); + else + OnPointerUp(ctx); } private void OnPointerMove(InputAction.CallbackContext ctx) { // only pointer devices are allowed Debug.Assert(ctx.control?.device is Pointer); - - var screenPosition = ((Pointer)ctx.control.device).position.ReadValue(); + Vector2 screenPosition; + + // If it's a finger take the value from the finger that initiated the change + if (m_TouchControl != null) + { + // if the finger is up ignore the move + if (m_TouchControl.isInProgress == false) + { + return; + } + screenPosition = m_TouchControl.position.ReadValue(); + } + else + { + screenPosition = ((Pointer)ctx.control.device).position.ReadValue(); + } MoveStick(screenPosition, GetCameraFromCanvas()); } private void OnPointerUp(InputAction.CallbackContext ctx) { + if (!m_IsIsolationActive) return; + + // if it's a finger ensure that is the one that get released + if (m_TouchControl != null) + { + if (m_TouchControl.isInProgress) return; + m_PointerMoveAction.ApplyBindingOverride(null, path: "/touch*/position"); + m_TouchControl = null; + } + EndInteraction(); m_PointerMoveAction.performed -= OnPointerMove; + m_IsIsolationActive = false; } private Camera GetCameraFromCanvas() @@ -430,6 +481,10 @@ public bool useIsolatedInputActions private List m_RaycastResults; [NonSerialized] private PointerEventData m_PointerEventData; + [NonSerialized] + private TouchControl m_TouchControl; + [NonSerialized] + private bool m_IsIsolationActive; protected override string controlPathInternal { From a7ca2e9c1b78025c539cae79f60d01a355728640 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Malrat?= Date: Mon, 16 Dec 2024 16:19:37 -0500 Subject: [PATCH 2/2] fixed format --- .../Tests/InputSystem/Plugins/OnScreenTests.cs | 10 +++++----- .../Plugins/OnScreen/OnScreenStick.cs | 16 ++++++++-------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/Assets/Tests/InputSystem/Plugins/OnScreenTests.cs b/Assets/Tests/InputSystem/Plugins/OnScreenTests.cs index c894865470..c25675a958 100644 --- a/Assets/Tests/InputSystem/Plugins/OnScreenTests.cs +++ b/Assets/Tests/InputSystem/Plugins/OnScreenTests.cs @@ -435,7 +435,7 @@ public IEnumerator Devices_CanHaveOnScreenJoystickControls([Values(false, true)] stickLeftTransform.sizeDelta = new Vector2(100, 100); stickLeft.controlPath = "/leftStick"; stickLeftGO.SetActive(true); - + var stickRightGO = new GameObject("StickRight"); stickRightGO.SetActive(false); var stickRightTransform = stickRightGO.AddComponent(); @@ -506,7 +506,7 @@ public IEnumerator Devices_CanHaveOnScreenJoystickControls([Values(false, true)] InputSystem.Update(); // Button is feeding events when responding to UI events. Assert.That(Gamepad.all[0].buttonSouth.isPressed, Is.False); - + // Touch the right stick and drag it downwards BeginTouch(2, new Vector2(550, 150)); yield return null; @@ -519,7 +519,7 @@ public IEnumerator Devices_CanHaveOnScreenJoystickControls([Values(false, true)] Assert.That(Gamepad.all[0].leftStick.ReadValue(), Is.EqualTo(new Vector2(0, 1)).Using(Vector2EqualityComparer.Instance)); Assert.That(Gamepad.all[0].rightStick.ReadValue(), Is.EqualTo(new Vector2(0, -1)).Using(Vector2EqualityComparer.Instance)); - + // Release finger one and move second and ensure that it still works EndTouch(1, new Vector2(550, 200)); MoveTouch(2, new Vector2(600, 150)); @@ -529,7 +529,7 @@ public IEnumerator Devices_CanHaveOnScreenJoystickControls([Values(false, true)] Assert.That(Gamepad.all[0].leftStick.ReadValue(), Is.EqualTo(new Vector2(0, 0)).Using(Vector2EqualityComparer.Instance)); Assert.That(Gamepad.all[0].rightStick.ReadValue(), Is.EqualTo(new Vector2(1, 0)).Using(Vector2EqualityComparer.Instance)); - + // Release finger two EndTouch(2, new Vector2(600, 150)); yield return null; @@ -565,7 +565,7 @@ public IEnumerator Devices_OnScreenStickDoesNotReceivePointerUpEventsInIsolatedM { uiTestScene.uiInputModule.actionsAsset.actionMaps[0].LazyResolveBindings(true); }; - + // Ensure that the OnScreenStick component has been started yield return null; diff --git a/Packages/com.unity.inputsystem/InputSystem/Plugins/OnScreen/OnScreenStick.cs b/Packages/com.unity.inputsystem/InputSystem/Plugins/OnScreen/OnScreenStick.cs index 9092c98cc3..25920c27c2 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Plugins/OnScreen/OnScreenStick.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Plugins/OnScreen/OnScreenStick.cs @@ -243,7 +243,7 @@ private void OnPointerDown(InputAction.CallbackContext ctx) { screenPosition = pointer.position.ReadValue(); } - + m_PointerEventData.position = screenPosition; EventSystem.current.RaycastAll(m_PointerEventData, m_RaycastResults); if (m_RaycastResults.Count == 0) @@ -267,11 +267,11 @@ private void OnPointerDown(InputAction.CallbackContext ctx) m_TouchControl = touchControl; m_PointerMoveAction.ApplyBindingOverride($"{touchControl.path}/position", path: "/touch*/position"); } - + m_PointerMoveAction.performed += OnPointerMove; m_IsIsolationActive = true; } - + private void OnPointerChanged(InputAction.CallbackContext ctx) { if (ctx.control.IsPressed()) @@ -285,8 +285,8 @@ private void OnPointerMove(InputAction.CallbackContext ctx) // only pointer devices are allowed Debug.Assert(ctx.control?.device is Pointer); Vector2 screenPosition; - - // If it's a finger take the value from the finger that initiated the change + + // If it's a finger take the value from the finger that initiated the change if (m_TouchControl != null) { // if the finger is up ignore the move @@ -307,15 +307,15 @@ private void OnPointerMove(InputAction.CallbackContext ctx) private void OnPointerUp(InputAction.CallbackContext ctx) { if (!m_IsIsolationActive) return; - + // if it's a finger ensure that is the one that get released if (m_TouchControl != null) { if (m_TouchControl.isInProgress) return; - m_PointerMoveAction.ApplyBindingOverride(null, path: "/touch*/position"); + m_PointerMoveAction.ApplyBindingOverride(null, path: "/touch*/position"); m_TouchControl = null; } - + EndInteraction(); m_PointerMoveAction.performed -= OnPointerMove; m_IsIsolationActive = false;