Skip to content
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
063ddb9
reduce complexity for checking UI interactions in update - frame base…
ritamerkl Jan 22, 2025
5067af8
added changelog
ritamerkl Jan 22, 2025
29c0eda
changelog formatting
ritamerkl Jan 22, 2025
7beeb2b
switch to additional InFrame and InputSystem update logic
ritamerkl Jan 23, 2025
b6e48dc
fixed wrong reference to update instead of frame
ritamerkl Jan 23, 2025
ea856c3
temporary workaround for rebinding test
ritamerkl Jan 24, 2025
a814fa2
save frame count when status changed
ritamerkl Jan 24, 2025
e581aad
fixed frame state saving
ritamerkl Jan 24, 2025
94b59a1
switched to coroutine with rebinding test to fix UI submit framebased…
ritamerkl Jan 24, 2025
65b20c9
removed redundant frame from InputActionState and InputAction
ritamerkl Jan 24, 2025
18dedda
Removed m_Frame
ritamerkl Jan 24, 2025
6e01bb1
Merge remote-tracking branch 'origin/develop' into fix-ui-behavior-fo…
ritamerkl Feb 3, 2025
b486ebd
changed to proposal 2
ritamerkl Feb 4, 2025
f49ccf0
fix comments in InputAction tests
ritamerkl Feb 4, 2025
39e057c
Fixed spelling
ritamerkl Feb 4, 2025
cb8cc82
updated documentation
ritamerkl Feb 7, 2025
9d8b5b4
Merge branch 'develop' into fix-ui-behavior-for-update-non-dynamic
ritamerkl Feb 7, 2025
e9367a4
Merge branch 'develop' into fix-ui-behavior-for-update-non-dynamic
Pauliusd01 Feb 10, 2025
adeddc9
Merge branch 'develop' into fix-ui-behavior-for-update-non-dynamic
ritamerkl Feb 12, 2025
e3b1a38
Update CHANGELOG.md
ritamerkl Feb 12, 2025
7114e6f
skip properties
ritamerkl Feb 12, 2025
8332195
added tests
ritamerkl Feb 12, 2025
95e4bfc
update version to 1.14.0
stefanunity Feb 13, 2025
2b4d758
fixed doc
ritamerkl Feb 14, 2025
b826dc2
Merge branch 'fix-ui-behavior-for-update-non-dynamic' of https://gith…
ritamerkl Feb 14, 2025
49fe0dc
Merge branch 'develop' into fix-ui-behavior-for-update-non-dynamic
ritamerkl Feb 14, 2025
998fcda
Update CHANGELOG.md
ritamerkl Feb 14, 2025
8eee052
fix formatting for changelog
ritamerkl Feb 14, 2025
e6cc47a
fix formatting
ritamerkl Feb 14, 2025
c37b912
Merge branch 'develop' into fix-ui-behavior-for-update-non-dynamic
ritamerkl Feb 14, 2025
51b9643
update comment
ritamerkl Feb 19, 2025
676ad0f
Changed naming from RenderingFrame to DynamicUpdate
ritamerkl Feb 21, 2025
fc0bfed
Merge remote-tracking branch 'origin/develop' into fix-ui-behavior-fo…
ritamerkl Feb 21, 2025
92d8773
Fixed comments for rename of method done earlier in 676ad0f
chris-massie Feb 27, 2025
f1af620
update comment
ritamerkl Feb 28, 2025
1deb9ac
Update Packages/com.unity.inputsystem/InputSystem/Actions/InputAction.cs
ritamerkl Feb 28, 2025
6abb34e
update comment
ritamerkl Feb 28, 2025
daa9dc4
update comment
ritamerkl Feb 28, 2025
267954a
Merge branch 'develop' into fix-ui-behavior-for-update-non-dynamic
ritamerkl Mar 3, 2025
40b8910
added line of documentation to explain frame delay
ritamerkl Mar 10, 2025
440af88
added help box to indicate the InputSystem update mode is not recomme…
ritamerkl Mar 10, 2025
3a12620
Merge branch 'develop' into fix-ui-behavior-for-update-non-dynamic
ritamerkl Mar 10, 2025
73725ef
update text
ritamerkl Mar 12, 2025
73aec85
replace link with static to make it easier upgradeable
ritamerkl Mar 12, 2025
d422bf4
Merge branch 'fix-ui-behavior-for-update-non-dynamic' of https://gith…
ritamerkl Mar 12, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions Assets/Samples/UIvsGameInput/UIvsGameInputHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ public void Update()
transform.rotation = default;

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

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

if (m_CancelAction.WasPressedThisFrame() || !m_LookEngageAction.IsPressed())
if (m_CancelAction.WasPressedThisRenderingFrame() || !m_LookEngageAction.IsPressed())
DisengageCameraControl();

break;

case State.InMenu:

if (m_CancelAction.WasPressedThisFrame())
if (m_CancelAction.WasPressedThisRenderingFrame())
OnContinueClicked();

break;
Expand Down
10 changes: 8 additions & 2 deletions Assets/Tests/Samples/RebindingUITests.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
using System.Collections;
using System.IO;
using NUnit.Framework;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Samples.RebindUI;
using UnityEngine.InputSystem.UI;
using UnityEngine.TestTools;
using UnityEngine.UI;

public class RebindingUITests : CoreTestsFixture
Expand Down Expand Up @@ -84,9 +86,9 @@ public void Samples_RebindingUI_UpdatesWhenKeyboardLayoutChanges()
}

// https://fogbugz.unity3d.com/f/cases/1271591/
[Test]
[UnityTest]
[Category("Samples")]
public void Samples_RebindingUI_SuppressingEventsDoesNotInterfereWithUIInput()
public IEnumerator Samples_RebindingUI_SuppressingEventsDoesNotInterfereWithUIInput()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ritamerkl Do we have a test that verifies behavior when configured to run input processing on fixed update?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will add tests now

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added 8332195

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

especially tested the update and frame based comparing in co-dependency to another

{
var keyboard = InputSystem.AddDevice<Keyboard>();

Expand Down Expand Up @@ -135,6 +137,8 @@ public void Samples_RebindingUI_SuppressingEventsDoesNotInterfereWithUIInput()
// UI should be fine with that.
PressAndRelease(keyboard.enterKey);
eventSystem.InvokeUpdate();
yield return null;


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

Press(keyboard.bKey);
eventSystem.InvokeUpdate();
yield return null;

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

Assert.That(rebind.ongoingRebind, Is.Not.Null);
Assert.That(rebind.ongoingRebind.started, Is.True);
Expand Down
1 change: 1 addition & 0 deletions Packages/com.unity.inputsystem/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ however, it has to be formatted properly to pass verification tests.
- Fixed an issue where compiling Addressables with Input System package present would result in failed compilation due to `IInputAnalytic.TryGatherData` not being defined [ISXB-1203](https://issuetracker.unity3d.com/product/unity/issues/guid/ISXB-1203).
- Pinned Touch Samples sample package dependencies to avoid errors with Cinemachine 3.x and Probuilder 6.x. [ISXB-1245](https://issuetracker.unity3d.com/product/unity/issues/guid/ISXB-1245)
- Fixed an issue where dropdown menu for Path in Input Actions Editor could not be selected from any button position. [ISXB-1309](https://issuetracker.unity3d.com/product/unity/issues/guid/ISXB-1309)
- Fixed an issue where updating the InputSystem outside of the dynamic Update would lead to UI input and navigation events get lost.

## [1.12.0] - 2025-01-15

Expand Down
173 changes: 159 additions & 14 deletions Packages/com.unity.inputsystem/InputSystem/Actions/InputAction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1248,10 +1248,11 @@ private int ExpectedFrame()
/// This method will disregard whether the action is currently enabled or disabled. It will keep returning
/// true for the duration of the frame even if the action was subsequently disabled in the frame.
///
/// The meaning of "frame" is either the current "dynamic" update (<c>MonoBehaviour.Update</c>) or the current
/// fixed update (<c>MonoBehaviour.FixedUpdate</c>) depending on the value of the <see cref="InputSettings.updateMode"/> setting.
/// NOTE: If the <see cref="InputSettings.updateMode"/> is set to <see cref="InputSettings.UpdateMode.ProcessEventsInFixedUpdate"/> or <see cref="InputSettings.UpdateMode.ProcessEventsManually"/> and InputSystem.Update() is not called in
/// the dynamic Update, use <see cref="WasPressedThisRenderingFrame"/> during dynamic Update instead.
/// </remarks>
/// <seealso cref="IsPressed"/>
/// <seealso cref="WasPressedThisRenderingFrame"/>
/// <seealso cref="WasReleasedThisFrame"/>
/// <seealso cref="CallbackContext.ReadValueAsButton"/>
/// <seealso cref="WasPerformedThisFrame"/>
Expand All @@ -1262,7 +1263,43 @@ public unsafe bool WasPressedThisFrame()
{
var actionStatePtr = &state.actionStates[m_ActionIndexInState];
var currentUpdateStep = InputUpdate.s_UpdateStepCount;
return actionStatePtr->pressedInUpdate == currentUpdateStep && currentUpdateStep != default && actionStatePtr->frame == ExpectedFrame();
return actionStatePtr->pressedInUpdate == currentUpdateStep && currentUpdateStep != default;
}

return false;
}

/// <summary>
/// Returns true if the action's value crossed the press threshold (see <see cref="InputSettings.defaultButtonPressPoint"/>)
/// in the MonoBehaviour Update cycle (rendering frame).
/// </summary>
/// <returns>True if the action was pressed in the MonoBehaviour Update cycle (rendering frame).</returns>
/// <remarks>
/// Unlike <see cref="WasPressedThisFrame"/>, this method will return true only if the InputSystem was updated and the action was pressed in the current dynamic Update cycle (in between the previous and the current frame).
/// This can be used in dynamic update if the <see cref="InputSettings.updateMode"/> is set to <see cref="InputSettings.UpdateMode.ProcessEventsInFixedUpdate"/> or <see cref="InputSettings.UpdateMode.ProcessEventsManually"/>.
/// If the update mode is set to <see cref="InputSettings.UpdateMode.ProcessEventsInDynamicUpdate"/>, this method will behave exactly like <see cref="WasPressedThisFrame"/>.
/// </remarks>
/// <example>
/// <code>
/// var fire = playerInput.actions["fire"];
/// if (fire.WasPressed() &amp;&amp; fire.IsPressed())
/// StartFiring();
/// else if (fire.WasReleasedThisFrame())
/// StopFiring();
/// </code>
/// </example>
/// <seealso cref="IsPressed"/>
/// <seealso cref="WasPressedThisFrame"/>
/// <seealso cref="WasReleasedThisFrame"/>
/// <seealso cref="WasPerformedThisFrame"/>
/// <seealso cref="InputSettings.updateMode"/>
public unsafe bool WasPressedThisRenderingFrame()
{
var state = GetOrCreateActionMap().m_State;
if (state != null)
{
var actionStatePtr = &state.actionStates[m_ActionIndexInState];
return actionStatePtr->framePressed == ExpectedFrame();
}

return false;
Expand Down Expand Up @@ -1297,10 +1334,11 @@ public unsafe bool WasPressedThisFrame()
/// This method will disregard whether the action is currently enabled or disabled. It will keep returning
/// true for the duration of the frame even if the action was subsequently disabled in the frame.
///
/// The meaning of "frame" is either the current "dynamic" update (<c>MonoBehaviour.Update</c>) or the current
/// fixed update (<c>MonoBehaviour.FixedUpdate</c>) depending on the value of the <see cref="InputSettings.updateMode"/> setting.
/// NOTE: If the <see cref="InputSettings.updateMode"/> is set to <see cref="InputSettings.UpdateMode.ProcessEventsInFixedUpdate"/> or <see cref="InputSettings.UpdateMode.ProcessEventsManually"/> and InputSystem.Update() is not called in
/// the dynamic Update, use <see cref="WasReleasedThisRenderingFrame"/> during dynamic Update instead.
/// </remarks>
/// <seealso cref="IsPressed"/>
/// <seealso cref="WasReleasedThisRenderingFrame"/>
/// <seealso cref="WasPressedThisFrame"/>
/// <seealso cref="CallbackContext.ReadValueAsButton"/>
/// <seealso cref="WasCompletedThisFrame"/>
Expand All @@ -1311,7 +1349,44 @@ public unsafe bool WasReleasedThisFrame()
{
var actionStatePtr = &state.actionStates[m_ActionIndexInState];
var currentUpdateStep = InputUpdate.s_UpdateStepCount;
return actionStatePtr->releasedInUpdate == currentUpdateStep && currentUpdateStep != default && actionStatePtr->frame == ExpectedFrame();
return actionStatePtr->releasedInUpdate == currentUpdateStep && currentUpdateStep != default;
}

return false;
}

/// <summary>
/// Returns true if the action's value crossed the release threshold (see <see cref="InputSettings.buttonReleaseThreshold"/>)
/// at any point in the MonoBehaviour Update cycle (rendering frame).
/// </summary>
/// <returns>True if the action was released in the MonoBehaviour Update cycle (rendering frame).</returns>
/// <remarks>
/// Unlike <see cref="WasReleasedThisFrame"/>, this method will return true only if the InputSystem was updated and the action was released in the current dynamic Update cycle (in between the previous and the current frame).
/// This can be used in dynamic update if the <see cref="InputSettings.updateMode"/> is set to <see cref="InputSettings.UpdateMode.ProcessEventsInFixedUpdate"/> or <see cref="InputSettings.UpdateMode.ProcessEventsManually"/>.
/// If the update mode is set to <see cref="InputSettings.UpdateMode.ProcessEventsInDynamicUpdate"/>, this method will behave exactly like <see cref="WasReleasedThisFrame"/>.
/// </remarks>
/// <example>
/// <code>
/// var fire = playerInput.actions["fire"];
/// if (fire.WasPressed() &amp;&amp; fire.IsPressed())
/// StartFiring();
/// else if (fire.WasReleased())
/// StopFiring();
/// </code>
/// </example>
/// <seealso cref="IsPressed"/>
/// <seealso cref="WasPressedThisFrame"/>
/// <seealso cref="WasReleasedThisFrame"/>
/// <seealso cref="CallbackContext.ReadValueAsButton"/>
/// <seealso cref="WasCompletedThisFrame"/>
/// <seealso cref="InputSettings.updateMode"/>
public unsafe bool WasReleasedThisRenderingFrame()
{
var state = GetOrCreateActionMap().m_State;
if (state != null)
{
var actionStatePtr = &state.actionStates[m_ActionIndexInState];
return actionStatePtr->frameReleased == ExpectedFrame();
}

return false;
Expand Down Expand Up @@ -1356,9 +1431,10 @@ public unsafe bool WasReleasedThisFrame()
/// This method will disregard whether the action is currently enabled or disabled. It will keep returning
/// true for the duration of the frame even if the action was subsequently disabled in the frame.
///
/// The meaning of "frame" is either the current "dynamic" update (<c>MonoBehaviour.Update</c>) or the current
/// fixed update (<c>MonoBehaviour.FixedUpdate</c>) depending on the value of the <see cref="InputSettings.updateMode"/> setting.
/// NOTE: If the <see cref="InputSettings.updateMode"/> is set to <see cref="InputSettings.UpdateMode.ProcessEventsInFixedUpdate"/> or <see cref="InputSettings.UpdateMode.ProcessEventsManually"/> and InputSystem.Update() is not called in
/// the dynamic Update, use <see cref="WasPerformedThisRenderingFrame"/> when trying to access in dynamic Update instead.
/// </remarks>
/// <seealso cref="WasPerformedThisRenderingFrame"/>
/// <seealso cref="WasCompletedThisFrame"/>
/// <seealso cref="WasPressedThisFrame"/>
/// <seealso cref="phase"/>
Expand All @@ -1370,7 +1446,42 @@ public unsafe bool WasPerformedThisFrame()
{
var actionStatePtr = &state.actionStates[m_ActionIndexInState];
var currentUpdateStep = InputUpdate.s_UpdateStepCount;
return actionStatePtr->lastPerformedInUpdate == currentUpdateStep && currentUpdateStep != default && actionStatePtr->frame == ExpectedFrame();
return actionStatePtr->lastPerformedInUpdate == currentUpdateStep && currentUpdateStep != default;
}

return false;
}

/// <summary>
/// Check whether <see cref="phase"/> was <see cref="InputActionPhase.Performed"/> at any point
/// in the MonoBehaviour Update cycle (rendering frame).
/// </summary>
/// <returns>True if the action performed in the MonoBehaviour Update cycle (rendering frame).</returns>
/// <remarks>
/// Unlike <see cref="WasPerformedThisFrame"/>, this method will return true only if the InputSystem was updated and the action was performed in the current dynamic Update cycle (in between the previous and the current frame).
/// This can be used in dynamic update if the <see cref="InputSettings.updateMode"/> is set to <see cref="InputSettings.UpdateMode.ProcessEventsInFixedUpdate"/> or <see cref="InputSettings.UpdateMode.ProcessEventsManually"/>.
/// If the update mode is set to <see cref="InputSettings.UpdateMode.ProcessEventsInDynamicUpdate"/>, this method will behave exactly like <see cref="WasPerformedThisFrame"/>.
/// </remarks>
/// <example>
/// <code>
/// var warp = playerInput.actions["Warp"];
/// if (warp.WasPerformed())
/// InitiateWarp();
/// </code>
/// </example>
/// <seealso cref="WasPerformedThisFrame"/>
/// <seealso cref="WasCompletedThisFrame"/>
/// <seealso cref="WasPressedThisFrame"/>
/// <seealso cref="phase"/>
/// <seealso cref="InputSettings.updateMode"/>
public unsafe bool WasPerformedThisRenderingFrame()
{
var state = GetOrCreateActionMap().m_State;

if (state != null)
{
var actionStatePtr = &state.actionStates[m_ActionIndexInState];
return actionStatePtr->framePerformed == ExpectedFrame();
}

return false;
Expand Down Expand Up @@ -1425,10 +1536,8 @@ public unsafe bool WasPerformedThisFrame()
/// This method will disregard whether the action is currently enabled or disabled. It will keep returning
/// true for the duration of the frame even if the action was subsequently disabled in the frame.
/// </para>
/// <para>
/// The meaning of "frame" is either the current "dynamic" update (<c>MonoBehaviour.Update</c>) or the current
/// fixed update (<c>MonoBehaviour.FixedUpdate</c>) depending on the value of the <see cref="InputSettings.updateMode"/> setting.
/// </para>
/// NOTE: If the <see cref="InputSettings.updateMode"/> is set to <see cref="InputSettings.UpdateMode.ProcessEventsInFixedUpdate"/> or <see cref="InputSettings.UpdateMode.ProcessEventsManually"/> and InputSystem.Update() is not called in
/// the dynamic Update, use <see cref="WasCompletedThisRenderingFrame"/> to access this during dynamic Update instead.
/// </remarks>
/// <example>
/// <code>
Expand All @@ -1439,6 +1548,7 @@ public unsafe bool WasPerformedThisFrame()
/// StopTeleport();
/// </code>
/// </example>
/// <seealso cref="WasCompletedThisRenderingFrame"/>
/// <seealso cref="WasPerformedThisFrame"/>
/// <seealso cref="WasReleasedThisFrame"/>
/// <seealso cref="phase"/>
Expand All @@ -1450,7 +1560,42 @@ public unsafe bool WasCompletedThisFrame()
{
var actionStatePtr = &state.actionStates[m_ActionIndexInState];
var currentUpdateStep = InputUpdate.s_UpdateStepCount;
return actionStatePtr->lastCompletedInUpdate == currentUpdateStep && currentUpdateStep != default && actionStatePtr->frame == ExpectedFrame();
return actionStatePtr->lastCompletedInUpdate == currentUpdateStep && currentUpdateStep != default;
}

return false;
}

/// <summary>
/// Check whether <see cref="phase"/> transitioned from <see cref="InputActionPhase.Performed"/> to any other phase
/// value at least once in the MonoBehaviour Update cycle (rendering frame).
/// </summary>
/// <returns>True if the action completed in this MonoBehaviour Update cycle (rendering frame).</returns>
/// <remarks>
/// Unlike <see cref="WasCompletedThisFrame"/>, this method will return true only if the InputSystem was updated and the action was completed in the current dynamic Update cycle (in between the previous and the current frame).
/// This can be used in dynamic update if the <see cref="InputSettings.updateMode"/> is set to <see cref="InputSettings.UpdateMode.ProcessEventsInFixedUpdate"/> or <see cref="InputSettings.UpdateMode.ProcessEventsManually"/>.
/// If the update mode is set to <see cref="InputSettings.UpdateMode.ProcessEventsInDynamicUpdate"/>, this method will behave exactly like <see cref="WasCompletedThisFrame"/>.
/// </remarks>
/// <example>
/// <code>
/// var warp = playerInput.actions["Warp"];
/// if (warp.WasPerformed())
/// InitiateWarp();
/// </code>
/// </example>
/// <seealso cref="WasPerformedThisFrame"/>
/// <seealso cref="WasCompletedThisFrame"/>
/// <seealso cref="WasPressedThisFrame"/>
/// <seealso cref="phase"/>
/// <seealso cref="InputSettings.updateMode"/>
public unsafe bool WasCompletedThisRenderingFrame()
{
var state = GetOrCreateActionMap().m_State;

if (state != null)
{
var actionStatePtr = &state.actionStates[m_ActionIndexInState];
return actionStatePtr->frameCompleted == ExpectedFrame();
}

return false;
Expand Down
Loading