Skip to content

Commit 5374a59

Browse files
Fix attempt for ISXB-1066 where we still process a hold interaction despite having a more dominant interaction. Test case included.
1 parent 343e8bf commit 5374a59

File tree

2 files changed

+141
-0
lines changed

2 files changed

+141
-0
lines changed

Assets/Tests/InputSystem/CoreTests_Actions.cs

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

61+
[Test]
62+
[Category("Actions")]
63+
public void Actions_WithMultipleBindingsAndMultipleInteractions_Works()
64+
{
65+
InputSystem.settings.defaultButtonPressPoint = 0.5f;
66+
67+
var keyboard = InputSystem.AddDevice<Keyboard>();
68+
69+
var action = new InputAction(interactions: "tap,hold(duration=2)");
70+
action.AddBinding("<Keyboard>/w");
71+
action.AddBinding("<Keyboard>/a");
72+
action.Enable();
73+
74+
IInputInteraction performedInteraction = null;
75+
IInputInteraction canceledInteraction = null;
76+
action.performed += ctx =>
77+
{
78+
performedInteraction = ctx.interaction;
79+
};
80+
action.canceled += ctx =>
81+
{
82+
canceledInteraction = ctx.interaction;
83+
};
84+
85+
// PressRelease AW trigger a tap
86+
currentTime = 0;
87+
InputSystem.QueueStateEvent(keyboard, new KeyboardState(Key.A, Key.W));
88+
InputSystem.Update();
89+
90+
// nothing triggered
91+
Assert.That(canceledInteraction, Is.Null);
92+
Assert.That(performedInteraction, Is.Null);
93+
94+
currentTime = 0.01;
95+
InputSystem.QueueStateEvent(keyboard, new KeyboardState());
96+
InputSystem.Update();
97+
98+
// tap should be triggered
99+
Assert.That(canceledInteraction, Is.Null);
100+
Assert.That(performedInteraction, Is.TypeOf(typeof(TapInteraction)));
101+
performedInteraction = null;
102+
103+
// Should be no other remaining events
104+
currentTime = 10;
105+
InputSystem.Update();
106+
Assert.That(canceledInteraction, Is.Null);
107+
Assert.That(performedInteraction, Is.Null);
108+
109+
110+
111+
// PressRelease AW trigger a tap
112+
currentTime = 11;
113+
InputSystem.QueueStateEvent(keyboard, new KeyboardState(Key.A, Key.W));
114+
InputSystem.Update();
115+
116+
// nothing triggered
117+
Assert.That(canceledInteraction, Is.Null);
118+
Assert.That(performedInteraction, Is.Null);
119+
120+
currentTime = 11.01;
121+
InputSystem.QueueStateEvent(keyboard, new KeyboardState(Key.A));
122+
InputSystem.Update();
123+
124+
currentTime = 11.02;
125+
InputSystem.QueueStateEvent(keyboard, new KeyboardState());
126+
InputSystem.Update();
127+
128+
// tap should be triggered
129+
Assert.That(canceledInteraction, Is.Null);
130+
Assert.That(performedInteraction, Is.TypeOf(typeof(TapInteraction)));
131+
performedInteraction = null;
132+
133+
// Should be no other remaining events
134+
currentTime = 20;
135+
InputSystem.Update();
136+
Assert.That(canceledInteraction, Is.Null);
137+
Assert.That(performedInteraction, Is.Null);
138+
139+
// PressRelease AW trigger a tap
140+
currentTime = 21;
141+
InputSystem.QueueStateEvent(keyboard, new KeyboardState(Key.A, Key.W));
142+
InputSystem.Update();
143+
144+
// nothing triggered
145+
Assert.That(canceledInteraction, Is.Null);
146+
Assert.That(performedInteraction, Is.Null);
147+
148+
currentTime = 21.01;
149+
InputSystem.QueueStateEvent(keyboard, new KeyboardState(Key.W));
150+
InputSystem.Update();
151+
152+
currentTime = 21.02;
153+
InputSystem.QueueStateEvent(keyboard, new KeyboardState());
154+
InputSystem.Update();
155+
156+
// tap should be triggered
157+
Assert.That(canceledInteraction, Is.Null);
158+
Assert.That(performedInteraction, Is.TypeOf(typeof(TapInteraction)));
159+
performedInteraction = null;
160+
161+
// Should be no other remaining events
162+
currentTime = 30;
163+
InputSystem.Update();
164+
Assert.That(canceledInteraction, Is.Null);
165+
Assert.That(performedInteraction, Is.Null);
166+
}
167+
61168
[Test]
62169
[Category("Actions")]
63170
public void Actions_WhenShortcutsDisabled_AllConflictingActionsTrigger()

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

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
using UnityEngine.InputSystem.Utilities;
1111

1212
using ProfilerMarker = Unity.Profiling.ProfilerMarker;
13+
using UnityEngine.InputSystem.Interactions;
1314

1415
////TODO: now that we can bind to controls by display name, we need to re-resolve controls when those change (e.g. when the keyboard layout changes)
1516

@@ -1510,6 +1511,8 @@ private void ProcessControlStateChange(int mapIndex, int controlIndex, int bindi
15101511
}
15111512
}
15121513

1514+
var previousTrigger = trigger;
1515+
var previousBindingStatePtr = bindingStatePtr;
15131516
// Check if we have multiple concurrent actuations on the same action. This may lead us
15141517
// to ignore certain inputs (e.g. when we get an input of lesser magnitude while already having
15151518
// one of higher magnitude) or may even lead us to switch to processing a different binding
@@ -1528,6 +1531,11 @@ private void ProcessControlStateChange(int mapIndex, int controlIndex, int bindi
15281531
if (interactionCount > 0 && !bindingStatePtr->isPartOfComposite)
15291532
{
15301533
ProcessInteractions(ref trigger, bindingStatePtr->interactionStartIndex, interactionCount);
1534+
// We still need to process hold interactions. If we don't, then we risk having a danling hold which triggers after all buttons are unpressed.
1535+
if (previousBindingStatePtr != bindingStatePtr)
1536+
{
1537+
ProcessHoldInteractions(ref previousTrigger, previousBindingStatePtr->interactionStartIndex, previousBindingStatePtr->interactionCount);
1538+
}
15311539
}
15321540
else if (!haveInteractionsOnComposite && !isConflictingInput)
15331541
{
@@ -2048,6 +2056,32 @@ private void ProcessInteractions(ref TriggerState trigger, int interactionStartI
20482056
}
20492057
}
20502058

2059+
private void ProcessHoldInteractions(ref TriggerState trigger, int interactionStartIndex, int interactionCount)
2060+
{
2061+
var context = new InputInteractionContext
2062+
{
2063+
m_State = this,
2064+
m_TriggerState = trigger
2065+
};
2066+
2067+
for (var i = 0; i < interactionCount; ++i)
2068+
{
2069+
var index = interactionStartIndex + i;
2070+
var state = interactionStates[index];
2071+
var interaction = interactions[index];
2072+
if (interaction is not HoldInteraction)
2073+
{
2074+
continue;
2075+
}
2076+
2077+
context.m_TriggerState.phase = state.phase;
2078+
context.m_TriggerState.startTime = state.startTime;
2079+
context.m_TriggerState.interactionIndex = index;
2080+
2081+
interaction.Process(ref context);
2082+
}
2083+
}
2084+
20512085
private void ProcessTimeout(double time, int mapIndex, int controlIndex, int bindingIndex, int interactionIndex)
20522086
{
20532087
Debug.Assert(controlIndex >= 0 && controlIndex < totalControlCount, "Control index out of range");

0 commit comments

Comments
 (0)