Skip to content

Commit a532aa7

Browse files
ritamerklPauliusd01stefanunitychris-massie
authored
FIX: Changed to frame based comparison for tracking UI click/navigation events (#2112)
Co-authored-by: Paulius Dervinis <54306142+Pauliusd01@users.noreply.github.com> Co-authored-by: Stefan Schubert <stefan@unity3d.com> Co-authored-by: Chris Massie <chris.massie@unity3d.com> Co-authored-by: Chris Massie <67029035+chris-massie@users.noreply.github.com>
1 parent 01f5550 commit a532aa7

File tree

8 files changed

+351
-33
lines changed

8 files changed

+351
-33
lines changed

Assets/Samples/UIvsGameInput/UIvsGameInputHandler.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ public void Update()
137137
transform.rotation = default;
138138

139139
// When using a pointer-based control scheme, we engage camera look explicitly.
140-
if (m_ControlStyle != ControlStyle.GamepadJoystick && m_LookEngageAction.WasPressedThisFrame() && IsPointerInsideScreen())
140+
if (m_ControlStyle != ControlStyle.GamepadJoystick && m_LookEngageAction.WasPressedThisDynamicUpdate() && IsPointerInsideScreen())
141141
EngageCameraControl();
142142

143143
// With gamepad/joystick, we can freely rotate the camera at any time.
@@ -166,14 +166,14 @@ public void Update()
166166
if (m_Mouse != null)
167167
m_MousePositionToWarpToAfterCursorUnlock = m_MousePositionToWarpToAfterCursorUnlock.Value + m_Mouse.delta.ReadValue();
168168

169-
if (m_CancelAction.WasPressedThisFrame() || !m_LookEngageAction.IsPressed())
169+
if (m_CancelAction.WasPressedThisDynamicUpdate() || !m_LookEngageAction.IsPressed())
170170
DisengageCameraControl();
171171

172172
break;
173173

174174
case State.InMenu:
175175

176-
if (m_CancelAction.WasPressedThisFrame())
176+
if (m_CancelAction.WasPressedThisDynamicUpdate())
177177
OnContinueClicked();
178178

179179
break;

Assets/Tests/InputSystem/CoreTests_Actions.cs

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,120 @@ public void Settings_ShouldStoreSettingsAndFeatureFlags(string featureName)
5858
}
5959
}
6060

61+
[UnityTest]
62+
[Category("Actions")]
63+
public IEnumerator Actions_WasStateReachedThisRenderingFrameOperatesIndependentlyFromInputUpdateStep()
64+
{
65+
var updateMode = InputSystem.settings.updateMode;
66+
InputSystem.settings.updateMode = InputSettings.UpdateMode.ProcessEventsInDynamicUpdate;
67+
68+
var gamepad = InputSystem.AddDevice<Gamepad>();
69+
var simpleAction = new InputAction(binding: "<Gamepad>/buttonSouth");
70+
simpleAction.Enable();
71+
72+
Assert.That(simpleAction.WasPerformedThisDynamicUpdate(), Is.False);
73+
Assert.That(simpleAction.WasPressedThisDynamicUpdate(), Is.False);
74+
Assert.That(simpleAction.WasReleasedThisDynamicUpdate(), Is.False);
75+
Assert.That(simpleAction.WasCompletedThisDynamicUpdate(), Is.False);
76+
77+
PressAndRelease(gamepad.buttonSouth);
78+
79+
yield return null; // InputSystem.Update is called and the action state chenges
80+
81+
Assert.That(simpleAction.WasPerformedThisDynamicUpdate(), Is.True);
82+
Assert.That(simpleAction.WasPressedThisDynamicUpdate(), Is.True);
83+
Assert.That(simpleAction.WasReleasedThisDynamicUpdate(), Is.True);
84+
85+
InputSystem.Update(); // a manual update happens between two frames, that does not affect the output of the WasPerformedThisDynamicUpdate
86+
87+
Assert.That(simpleAction.WasPerformedThisDynamicUpdate(), Is.True);
88+
Assert.That(simpleAction.WasPressedThisDynamicUpdate(), Is.True);
89+
Assert.That(simpleAction.WasReleasedThisDynamicUpdate(), Is.True);
90+
91+
//Reset State
92+
InputSystem.settings.updateMode = updateMode;
93+
}
94+
95+
[UnityTest]
96+
[Category("Actions")]
97+
public IEnumerator Actions_WasStateReachedThisFrameOperatesIndependentlyFromRenderingFrame()
98+
{
99+
var updateMode = InputSystem.settings.updateMode;
100+
InputSystem.settings.updateMode = InputSettings.UpdateMode.ProcessEventsManually;
101+
102+
var gamepad = InputSystem.AddDevice<Gamepad>();
103+
var simpleAction = new InputAction(binding: "<Gamepad>/buttonSouth");
104+
simpleAction.Enable();
105+
106+
Assert.That(simpleAction.WasPerformedThisFrame(), Is.False);
107+
Assert.That(simpleAction.WasPressedThisFrame(), Is.False);
108+
Assert.That(simpleAction.WasReleasedThisFrame(), Is.False);
109+
Assert.That(simpleAction.WasCompletedThisFrame(), Is.False);
110+
111+
PressAndRelease(gamepad.buttonSouth);
112+
113+
yield return null; // InputSystem.Update is not called and the action state does not change
114+
yield return null;
115+
yield return null;
116+
117+
Assert.That(simpleAction.WasPerformedThisFrame(), Is.False);
118+
Assert.That(simpleAction.WasPressedThisFrame(), Is.False);
119+
Assert.That(simpleAction.WasReleasedThisFrame(), Is.False);
120+
121+
InputSystem.Update(); // a manual update happens between two frames, that does not affect the output of the WasXYZThisRenderingFrame but does affect the WasXYZThisFrame
122+
123+
Assert.That(simpleAction.WasPerformedThisFrame(), Is.True);
124+
Assert.That(simpleAction.WasPressedThisFrame(), Is.True);
125+
Assert.That(simpleAction.WasReleasedThisFrame(), Is.True);
126+
127+
//Reset State
128+
InputSystem.settings.updateMode = updateMode;
129+
}
130+
131+
[UnityTest]
132+
[Category("Actions")]
133+
public IEnumerator Actions_WasStateReachedThisFrameAndWasStateReachedThisRenderingFrameCanOperateSimultanously()
134+
{
135+
var updateMode = InputSystem.settings.updateMode;
136+
InputSystem.settings.updateMode = InputSettings.UpdateMode.ProcessEventsManually;
137+
138+
var gamepad = InputSystem.AddDevice<Gamepad>();
139+
var simpleAction = new InputAction(binding: "<Gamepad>/buttonSouth");
140+
simpleAction.Enable();
141+
142+
Assert.That(simpleAction.WasPerformedThisFrame(), Is.False);
143+
Assert.That(simpleAction.WasPressedThisFrame(), Is.False);
144+
Assert.That(simpleAction.WasReleasedThisFrame(), Is.False);
145+
Assert.That(simpleAction.WasCompletedThisFrame(), Is.False);
146+
147+
PressAndRelease(gamepad.buttonSouth);
148+
149+
yield return null; // InputSystem.Update is not called and the action state does not change
150+
151+
Assert.That(simpleAction.WasPerformedThisFrame(), Is.False);
152+
Assert.That(simpleAction.WasPressedThisFrame(), Is.False);
153+
Assert.That(simpleAction.WasReleasedThisFrame(), Is.False);
154+
155+
InputSystem.Update(); // a manual update happens between two frames, that does not affect the output of the WasXYZThisRenderingFrame but does affect the WasXYZThisFrame
156+
157+
Assert.That(simpleAction.WasPerformedThisFrame(), Is.True);
158+
Assert.That(simpleAction.WasPressedThisFrame(), Is.True);
159+
Assert.That(simpleAction.WasReleasedThisFrame(), Is.True);
160+
161+
yield return null;
162+
163+
Assert.That(simpleAction.WasPerformedThisDynamicUpdate(), Is.True);
164+
Assert.That(simpleAction.WasPressedThisDynamicUpdate(), Is.True);
165+
Assert.That(simpleAction.WasReleasedThisDynamicUpdate(), Is.True);
166+
167+
yield return null;
168+
169+
Assert.That(simpleAction.WasCompletedThisDynamicUpdate(), Is.False);
170+
171+
//Reset State
172+
InputSystem.settings.updateMode = updateMode;
173+
}
174+
61175
[Test]
62176
[Category("Actions")]
63177
public void Actions_WhenShortcutsDisabled_AllConflictingActionsTrigger()

Assets/Tests/Samples/RebindingUITests.cs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1+
using System.Collections;
12
using System.IO;
23
using NUnit.Framework;
34
using UnityEngine;
45
using UnityEngine.EventSystems;
56
using UnityEngine.InputSystem;
67
using UnityEngine.InputSystem.Samples.RebindUI;
78
using UnityEngine.InputSystem.UI;
9+
using UnityEngine.TestTools;
810
using UnityEngine.UI;
911

1012
public class RebindingUITests : CoreTestsFixture
@@ -84,9 +86,9 @@ public void Samples_RebindingUI_UpdatesWhenKeyboardLayoutChanges()
8486
}
8587

8688
// https://fogbugz.unity3d.com/f/cases/1271591/
87-
[Test]
89+
[UnityTest]
8890
[Category("Samples")]
89-
public void Samples_RebindingUI_SuppressingEventsDoesNotInterfereWithUIInput()
91+
public IEnumerator Samples_RebindingUI_SuppressingEventsDoesNotInterfereWithUIInput()
9092
{
9193
var keyboard = InputSystem.AddDevice<Keyboard>();
9294

@@ -135,6 +137,8 @@ public void Samples_RebindingUI_SuppressingEventsDoesNotInterfereWithUIInput()
135137
// UI should be fine with that.
136138
PressAndRelease(keyboard.enterKey);
137139
eventSystem.InvokeUpdate();
140+
yield return null;
141+
138142

139143
Assert.That(rebind.ongoingRebind, Is.Not.Null);
140144
Assert.That(rebind.ongoingRebind.started, Is.True);
@@ -144,6 +148,7 @@ public void Samples_RebindingUI_SuppressingEventsDoesNotInterfereWithUIInput()
144148

145149
Press(keyboard.bKey);
146150
eventSystem.InvokeUpdate();
151+
yield return null;
147152

148153
Assert.That(rebind.ongoingRebind, Is.Not.Null);
149154
Assert.That(rebind.ongoingRebind.started, Is.True);
@@ -162,6 +167,7 @@ public void Samples_RebindingUI_SuppressingEventsDoesNotInterfereWithUIInput()
162167
// Start another rebind via "Submit".
163168
PressAndRelease(keyboard.enterKey);
164169
eventSystem.InvokeUpdate();
170+
yield return null;
165171

166172
Assert.That(rebind.ongoingRebind, Is.Not.Null);
167173
Assert.That(rebind.ongoingRebind.started, Is.True);

Packages/com.unity.inputsystem/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,16 @@ however, it has to be formatted properly to pass verification tests.
2828
- Fixed arrow key navigation of Input Actions after Action rename. [ISXB-1024](https://issuetracker.unity3d.com/product/unity/issues/guid/ISXB-1024)
2929
- Fixed gamepad navigation in UI Toolkit TextField when using InputSystemUIInputModule. [UUM-77364](https://issuetracker.unity3d.com/product/unity/issues/guid/UUM-77364)
3030
- Fixed issue where asset editor window splitter positions were not persisted [ISXB-1316]
31+
- Fixed an issue where updating the InputSystem outside of the dynamic Update would lead to UI input and navigation events get lost. [ISXB-1313](https://issuetracker.unity3d.com/issues/ui-onclick-events-sometimes-do-not-trigger-when-manual-update-is-utilized-with-input-system)
3132
- Fixed a bug that would cause `TrackedPoseDriver` to update position and rotation when no HMD device is connected [ISXB-699](https://issuetracker.unity3d.com/product/unity/issues/guid/ISXB-699) instead of keeping it unchanged.
3233

3334
### Changed
3435
- Changed default input action asset name from New Controls to New Actions.
3536
- Added disabling of action maps in rebinding UI sample.
3637

38+
### Added
39+
- An alternative way to access if an action state reached a certain phase during this rendering frame (Update()). This can be utilized even if the InputSystem update mode is set to manual or FixedUpdate. It can be used to access the action phase during rendering, eg for perform updates to the UI.
40+
3741
## [1.13.0] - 2025-02-05
3842

3943
### Fixed

0 commit comments

Comments
 (0)