Skip to content

Commit f52ba87

Browse files
FIX: Tap Interaction is performed after it times out (ISXB-627) (#2172)
1 parent 8b1f3a8 commit f52ba87

File tree

3 files changed

+86
-1
lines changed

3 files changed

+86
-1
lines changed

Assets/Tests/InputSystem/CoreTests_Actions_Interactions.cs

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -669,6 +669,78 @@ public void Actions_CanPerformTapInteraction()
669669
Assert.That(action.phase, Is.EqualTo(InputActionPhase.Waiting));
670670
}
671671

672+
[Test]
673+
[Category("Actions")]
674+
public void Actions_CanPerformTapInteractionWithAnalogControls()
675+
{
676+
ResetTime();
677+
678+
var gamepad = InputSystem.AddDevice<Gamepad>();
679+
680+
var action = new InputAction(binding: "<Gamepad>/leftTrigger", type: InputActionType.Button,
681+
interactions: "tap(duration=0.2)");
682+
683+
// This is the default value, which makes the release point to be 0.75 * 0.5 = 0.375.
684+
InputSystem.settings.defaultButtonPressPoint = 0.5f;
685+
686+
action.Enable();
687+
688+
currentTime = 0f;
689+
690+
using (var trace = new InputActionTrace())
691+
{
692+
trace.SubscribeTo(action);
693+
694+
currentTime = 0.1f;
695+
Set(gamepad.leftTrigger, 0.3f);
696+
currentTime = 0.2f;
697+
Set(gamepad.leftTrigger, 0.54f);
698+
699+
Assert.That(trace,
700+
Started<TapInteraction>(action, value: 0.54f, time: 0.2f));
701+
trace.Clear();
702+
703+
// Assert that a timeout will ocurr and a canceled event will be triggered.
704+
currentTime = 0.5f;
705+
Set(gamepad.leftTrigger, 0.9f);
706+
Assert.That(trace,
707+
Canceled<TapInteraction>(action));
708+
trace.Clear();
709+
710+
// Maintain a value above the press point for a while to assess that a start event is not triggered.
711+
// This was the case where the tap interaction was previously re-starting.
712+
currentTime = 1.2f;
713+
Set(gamepad.leftTrigger, 0.52f);
714+
715+
Assert.That(trace, Is.Empty);
716+
717+
// Go below the release point so check that no cancel event is triggered, since it didn't start.
718+
// This was the case where the tap interaction was previously re-starting and then would cancel after
719+
// timeout.
720+
currentTime = 1.5f;
721+
Set(gamepad.leftTrigger, 0.2f);
722+
723+
Assert.That(trace, Is.Empty);
724+
725+
// Go above the press point again and check that a start event is triggered.
726+
currentTime = 2.0f;
727+
Set(gamepad.leftTrigger, 0.6f);
728+
729+
Assert.That(trace,
730+
Started<TapInteraction>(action));
731+
trace.Clear();
732+
733+
currentTime = 2.10f;
734+
Set(gamepad.leftTrigger, 0.4f);
735+
736+
// Check that the tap is performed.
737+
currentTime = 2.15f;
738+
Set(gamepad.leftTrigger, 0.2f);
739+
Assert.That(trace,
740+
Performed<TapInteraction>(action));
741+
}
742+
}
743+
672744
[Test]
673745
[Category("Actions")]
674746
public void Actions_CanPerformDoubleTapInteraction()

Packages/com.unity.inputsystem/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ however, it has to be formatted properly to pass verification tests.
2424
- Fixed Gamepad stick up/down inputs that were not recognized in WebGL. [ISXB-1090](https://issuetracker.unity3d.com/product/unity/issues/guid/ISXB-1090)
2525
- Fixed PlayerInput component automatically switching away from the default ActionMap set to 'None'.
2626
- Fixed a console error being shown when targeting visionOS builds in 2022.3.
27+
- 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)
2728

2829
## [1.14.0] - 2025-03-20
2930

Packages/com.unity.inputsystem/InputSystem/Actions/Interactions/TapInteraction.cs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ public class TapInteraction : IInputInteraction
4141
private float releasePointOrDefault => pressPointOrDefault * ButtonControl.s_GlobalDefaultButtonReleaseThreshold;
4242

4343
private double m_TapStartTime;
44+
bool canceledFromTimerExpired;
4445

4546
////TODO: make sure 2d doesn't move too far
4647

@@ -49,10 +50,15 @@ public void Process(ref InputInteractionContext context)
4950
if (context.timerHasExpired)
5051
{
5152
context.Canceled();
53+
// Cache the fact that we canceled the interaction due to a timer expiration.
54+
canceledFromTimerExpired = true;
5255
return;
5356
}
5457

55-
if (context.isWaiting && context.ControlIsActuated(pressPointOrDefault))
58+
// Check if the control is actuated but avoid starting the interaction if it was canceled due to a timeout.
59+
// Otherwise, we would start the interaction again immediately after it is canceled due to timeout,
60+
// particularly in analog controls such as Gamepad stick or triggers. (ISXB-627)
61+
if (context.isWaiting && context.ControlIsActuated(pressPointOrDefault) && !canceledFromTimerExpired)
5662
{
5763
m_TapStartTime = context.time;
5864
// Set timeout slightly after duration so that if tap comes in exactly at the expiration
@@ -74,6 +80,12 @@ public void Process(ref InputInteractionContext context)
7480
context.Canceled();
7581
}
7682
}
83+
84+
// Once the control is released, we allow the interaction to be started again.
85+
if (!context.ControlIsActuated(releasePointOrDefault))
86+
{
87+
canceledFromTimerExpired = false;
88+
}
7789
}
7890

7991
public void Reset()

0 commit comments

Comments
 (0)