Skip to content

Commit ae8103d

Browse files
author
Rene Damm
authored
FIX: Hold interaction not staying performed (case 1195498, #1055).
1 parent e1582e5 commit ae8103d

File tree

6 files changed

+50
-89
lines changed

6 files changed

+50
-89
lines changed

Assets/Tests/InputSystem/CoreTests_Actions_Interactions.cs

Lines changed: 35 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -191,111 +191,62 @@ public void Actions_CanPerformPressInteraction()
191191
[Category("Actions")]
192192
public void Actions_CanPerformHoldInteraction()
193193
{
194-
const int timeOffset = 123;
195-
runtime.currentTimeOffsetToRealtimeSinceStartup = timeOffset;
196-
runtime.currentTime = 10 + timeOffset;
197194
var gamepad = InputSystem.AddDevice<Gamepad>();
198195

199-
var performedReceivedCalls = 0;
200-
InputAction performedAction = null;
201-
InputControl performedControl = null;
202-
203-
var startedReceivedCalls = 0;
204-
InputAction startedAction = null;
205-
InputControl startedControl = null;
206-
207-
var canceledReceivedCalls = 0;
208-
InputAction canceledAction = null;
209-
InputControl canceledControl = null;
210-
211196
var action = new InputAction(binding: "<Gamepad>/{primaryAction}", interactions: "hold(duration=0.4)");
212-
action.performed +=
213-
ctx =>
214-
{
215-
++performedReceivedCalls;
216-
performedAction = ctx.action;
217-
performedControl = ctx.control;
197+
action.Enable();
218198

219-
Assert.That(action.phase, Is.EqualTo(InputActionPhase.Performed));
220-
Assert.That(ctx.duration, Is.GreaterThanOrEqualTo(0.4));
221-
};
222-
action.started +=
223-
ctx =>
199+
using (var trace = new InputActionTrace(action))
224200
{
225-
++startedReceivedCalls;
226-
startedAction = ctx.action;
227-
startedControl = ctx.control;
201+
// Press.
202+
Press(gamepad.buttonSouth, time: 10);
228203

204+
Assert.That(trace, Started<HoldInteraction>(action, gamepad.buttonSouth, time: 10, value: 1.0));
205+
Assert.That(action.ReadValue<float>(), Is.EqualTo(1));
229206
Assert.That(action.phase, Is.EqualTo(InputActionPhase.Started));
230-
Assert.That(ctx.duration, Is.EqualTo(0.0));
231-
};
232-
action.canceled +=
233-
ctx =>
234-
{
235-
++canceledReceivedCalls;
236-
canceledAction = ctx.action;
237-
canceledControl = ctx.control;
238207

239-
Assert.That(action.phase, Is.EqualTo(InputActionPhase.Canceled));
240-
Assert.That(ctx.duration, Is.GreaterThan(0.0));
241-
Assert.That(ctx.duration, Is.LessThan(0.4));
242-
};
243-
action.Enable();
208+
trace.Clear();
244209

245-
InputSystem.QueueStateEvent(gamepad, new GamepadState().WithButton(GamepadButton.South), 10.0);
246-
InputSystem.Update();
210+
// Release in less than hold time.
211+
Release(gamepad.buttonSouth, time: 10.25);
247212

248-
Assert.That(startedReceivedCalls, Is.EqualTo(1));
249-
Assert.That(performedReceivedCalls, Is.Zero);
250-
Assert.That(canceledReceivedCalls, Is.Zero);
251-
Assert.That(startedAction, Is.SameAs(action));
252-
Assert.That(startedControl, Is.SameAs(gamepad.buttonSouth));
213+
Assert.That(trace, Canceled<HoldInteraction>(action, gamepad.buttonSouth, duration: 0.25, time: 10.25, value: 0.0));
214+
Assert.That(action.phase, Is.EqualTo(InputActionPhase.Waiting));
215+
Assert.That(action.ReadValue<float>(), Is.Zero);
253216

254-
startedReceivedCalls = 0;
217+
trace.Clear();
255218

256-
InputSystem.QueueStateEvent(gamepad, new GamepadState(), 10.25);
257-
InputSystem.Update();
219+
// Press again.
220+
Press(gamepad.buttonSouth, time: 10.5);
258221

259-
Assert.That(startedReceivedCalls, Is.Zero);
260-
Assert.That(performedReceivedCalls, Is.Zero);
261-
Assert.That(canceledReceivedCalls, Is.EqualTo(1));
262-
Assert.That(canceledAction, Is.SameAs(action));
263-
Assert.That(canceledControl, Is.SameAs(gamepad.buttonSouth));
264-
Assert.That(action.phase, Is.EqualTo(InputActionPhase.Waiting));
222+
Assert.That(trace, Started<HoldInteraction>(action, gamepad.buttonSouth, time: 10.5, value: 1.0));
223+
Assert.That(action.ReadValue<float>(), Is.EqualTo(1));
224+
Assert.That(action.phase, Is.EqualTo(InputActionPhase.Started));
265225

266-
canceledReceivedCalls = 0;
226+
trace.Clear();
267227

268-
InputSystem.QueueStateEvent(gamepad, new GamepadState().WithButton(GamepadButton.South), 10.5);
269-
InputSystem.Update();
228+
// Let time pass but stay under hold time.
229+
currentTime = 10.75;
230+
InputSystem.Update();
270231

271-
Assert.That(startedReceivedCalls, Is.EqualTo(1));
272-
Assert.That(performedReceivedCalls, Is.Zero);
273-
Assert.That(canceledReceivedCalls, Is.Zero);
274-
Assert.That(startedAction, Is.SameAs(action));
275-
Assert.That(startedControl, Is.SameAs(gamepad.buttonSouth));
276-
Assert.That(action.phase, Is.EqualTo(InputActionPhase.Started));
232+
Assert.That(trace, Is.Empty);
277233

278-
startedReceivedCalls = 0;
234+
// Now exceed hold time. Make sure action performs and *stays* performed.
235+
currentTime = 11;
236+
InputSystem.Update();
279237

280-
runtime.currentTime = 10.75 + timeOffset;
281-
InputSystem.Update();
238+
Assert.That(trace,
239+
Performed<HoldInteraction>(action, gamepad.buttonSouth, time: 11, duration: 0.5, value: 1.0));
240+
Assert.That(action.phase, Is.EqualTo(InputActionPhase.Performed));
241+
Assert.That(action.ReadValue<float>(), Is.EqualTo(1));
282242

283-
Assert.That(startedReceivedCalls, Is.Zero);
284-
Assert.That(performedReceivedCalls, Is.Zero);
285-
Assert.That(canceledReceivedCalls, Is.Zero);
286-
Assert.That(startedAction, Is.SameAs(action));
287-
Assert.That(startedControl, Is.SameAs(gamepad.buttonSouth));
288-
Assert.That(action.phase, Is.EqualTo(InputActionPhase.Started));
243+
trace.Clear();
289244

290-
runtime.currentTime = 11 + timeOffset;
291-
InputSystem.Update();
245+
// Release button.
246+
Release(gamepad.buttonSouth, time: 11.5);
292247

293-
Assert.That(startedReceivedCalls, Is.Zero);
294-
Assert.That(performedReceivedCalls, Is.EqualTo(1));
295-
Assert.That(canceledReceivedCalls, Is.Zero);
296-
Assert.That(performedAction, Is.SameAs(action));
297-
Assert.That(performedControl, Is.SameAs(gamepad.buttonSouth));
298-
Assert.That(action.phase, Is.EqualTo(InputActionPhase.Waiting));
248+
Assert.That(trace, Canceled<HoldInteraction>(action, gamepad.buttonSouth, time: 11.5, duration: 1, value: 0.0));
249+
}
299250
}
300251

301252
[Test]

Packages/com.unity.inputsystem/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ however, it has to be formatted properly to pass verification tests.
4646
- "Invalid user" `ArgumentException` when turning the same `PlayerInput` on and off ([case 1198889](https://issuetracker.unity3d.com/issues/input-system-package-argumentexception-invalid-user-error-is-thrown-when-the-callback-disables-game-object-with-playerinput)).
4747
- The list of device requirements for a control scheme in the action editor no longer displays devices with their internal layout name rather than their external display name.
4848
- `StackOverflowException` when `Invoke Unity Events` is selected in `PlayerInput` and it cannot find an action (#1033).
49+
- `HoldInteraction` now stays performed after timer has expired and cancels only on release of the control ([case 1195498](https://issuetracker.unity3d.com/issues/inputsystem-inputaction-dot-readvalue-returns-0-when-a-hold-action-is-performed-for-hold-time-amount-of-time)).
4950

5051
### Added
5152

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

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,16 @@ namespace UnityEngine.InputSystem.Interactions
1212
/// </summary>
1313
/// <remarks>
1414
/// The action is started when the control is pressed. If the control is released before the
15-
/// set <see cref="duration"/>, the action is canceled.
15+
/// set <see cref="duration"/>, the action is canceled. As soon as the hold time is reached,
16+
/// the action performs. The action then stays performed until the control is released, at
17+
/// which point the action cancels.
18+
///
19+
/// <example>
20+
/// <code>
21+
/// // Action that requires A button on gamepad to be held for half a second.
22+
/// var action = new InputAction(binding: "&lt;Gamepad&gt;/buttonSouth", interactions: "hold(duration=0.5)");
23+
/// </code>
24+
/// </example>
1625
/// </remarks>
1726
[Preserve]
1827
[DisplayName("Hold")]
@@ -49,7 +58,7 @@ public void Process(ref InputInteractionContext context)
4958
{
5059
if (context.timerHasExpired)
5160
{
52-
context.Performed();
61+
context.PerformedAndStayPerformed();
5362
return;
5463
}
5564

Packages/com.unity.inputsystem/InputSystem/Devices/InputDevice.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -649,7 +649,7 @@ internal void ClearDeviceUsages()
649649
m_UsageCount = default;
650650
}
651651

652-
internal bool RequestRequest()
652+
internal bool RequestReset()
653653
{
654654
var resetCommand = RequestResetCommand.Create();
655655
var result = device.ExecuteCommand(ref resetCommand);

Packages/com.unity.inputsystem/InputSystem/InputManager.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2410,7 +2410,7 @@ private unsafe void OnFocusChanged(bool focus)
24102410
new InputEventPtr((InputEvent*)stateEventPtr));
24112411

24122412
// Tell the backend to reset.
2413-
device.RequestRequest();
2413+
device.RequestReset();
24142414
}
24152415
}
24162416
}

Packages/com.unity.inputsystem/InputSystem/InputSystem.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1677,7 +1677,7 @@ public static bool TryResetDevice(InputDevice device)
16771677
{
16781678
if (device == null)
16791679
throw new ArgumentNullException(nameof(device));
1680-
return device.RequestRequest();
1680+
return device.RequestReset();
16811681
}
16821682

16831683
////REVIEW: should there be a global pause state? what about haptics that are issued *while* paused?

0 commit comments

Comments
 (0)