Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
72 changes: 72 additions & 0 deletions Assets/Tests/InputSystem/CoreTests_Actions_Interactions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -669,6 +669,78 @@ public void Actions_CanPerformTapInteraction()
Assert.That(action.phase, Is.EqualTo(InputActionPhase.Waiting));
}

[Test]
[Category("Actions")]
public void Actions_CanPerformTapInteractionWithAnalogControls()
{
ResetTime();

var gamepad = InputSystem.AddDevice<Gamepad>();

var action = new InputAction(binding: "<Gamepad>/leftTrigger", type: InputActionType.Button,
interactions: "tap(duration=0.2)");

// This is the default value, which makes the release point to be 0.75 * 0.5 = 0.375.
InputSystem.settings.defaultButtonPressPoint = 0.5f;

action.Enable();

currentTime = 0f;

using (var trace = new InputActionTrace())
{
trace.SubscribeTo(action);

currentTime = 0.1f;
Set(gamepad.leftTrigger, 0.3f);
currentTime = 0.2f;
Set(gamepad.leftTrigger, 0.54f);

Assert.That(trace,
Started<TapInteraction>(action, value: 0.54f, time: 0.2f));
trace.Clear();

// Assert that a timeout will ocurr and a canceled event will be triggered.
currentTime = 0.5f;
Set(gamepad.leftTrigger, 0.9f);
Assert.That(trace,
Canceled<TapInteraction>(action));
trace.Clear();

// Maintain a value above the press point for a while to assess that a start event is not triggered.
// This was the case where the tap interaction was previously re-starting.
currentTime = 1.2f;
Set(gamepad.leftTrigger, 0.52f);

Assert.That(trace, Is.Empty);

// Go below the release point so check that no cancel event is triggered, since it didn't start.
// This was the case where the tap interaction was previously re-starting and then would cancel after
// timeout.
currentTime = 1.5f;
Set(gamepad.leftTrigger, 0.2f);

Assert.That(trace, Is.Empty);

// Go above the press point again and check that a start event is triggered.
currentTime = 2.0f;
Set(gamepad.leftTrigger, 0.6f);

Assert.That(trace,
Started<TapInteraction>(action));
trace.Clear();

currentTime = 2.10f;
Set(gamepad.leftTrigger, 0.4f);

// Check that the tap is performed.
currentTime = 2.15f;
Set(gamepad.leftTrigger, 0.2f);
Assert.That(trace,
Performed<TapInteraction>(action));
}
}

[Test]
[Category("Actions")]
public void Actions_CanPerformDoubleTapInteraction()
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 @@ -24,6 +24,7 @@ however, it has to be formatted properly to pass verification tests.
- Fixed Gamepad stick up/down inputs that were not recognized in WebGL. [ISXB-1090](https://issuetracker.unity3d.com/product/unity/issues/guid/ISXB-1090)
- 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)

## [1.14.0] - 2025-03-20

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ public class TapInteraction : IInputInteraction
private float releasePointOrDefault => pressPointOrDefault * ButtonControl.s_GlobalDefaultButtonReleaseThreshold;

private double m_TapStartTime;
bool canceledFromTimerExpired;

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

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

if (context.isWaiting && context.ControlIsActuated(pressPointOrDefault))
// Check if the control is actuated but avoid starting the interaction if it was canceled due to a timeout.
// Otherwise, we would start the interaction again immediately after it is canceled due to timeout,
// particularly in analog controls such as Gamepad stick or triggers. (ISXB-627)
if (context.isWaiting && context.ControlIsActuated(pressPointOrDefault) && !canceledFromTimerExpired)
{
m_TapStartTime = context.time;
// Set timeout slightly after duration so that if tap comes in exactly at the expiration
Expand All @@ -74,6 +80,12 @@ public void Process(ref InputInteractionContext context)
context.Canceled();
}
}

// Once the control is released, we allow the interaction to be started again.
if (!context.ControlIsActuated(releasePointOrDefault))
{
canceledFromTimerExpired = false;
}
}

public void Reset()
Expand Down