Exploring Tap & Hold Interactions, Hierarchical Precedence and Errors — Unity’s New Input System
Objective: To understand how ‘Tap’ & ‘Hold’ Interaction buttons work in Unity’s New Input System.
We have all played those games where if you hold a button a game object moves a small distance but if you held down the same button for longer it would move further.
Let’s build that feature using Unity’s New Input System!
But first we need to discuss the differences between a ‘Tap’ and a ‘Hold’ Interaction and also some important topics — Hierarchical Precedence and Errors!
Tap
For a button event to be considered a ‘Tap’, it has to have been pressed and released before a certain time duration — triggering the Action.
This can be manually configured in the Input Action Asset.
Hold
For a button event to be considered a ‘Hold’, it has to meet a threshold of waiting a certain time duration after pressing and holding — triggering the Action.
Similarly to the ‘Tap’ feature, this can be manually adjusted.
Hierarchical Precedence
It matters which order you stack the Interactions when dealing with event triggers! It starts with the top and works its way down.
Let’s test this!
Test 1 — The ‘Tap’
Test — I am going to simply ‘Tap’ a button as quick as possible and test the ‘Performed’ and ‘Canceled’ events to show the outcomes of the Hierarchical Precedence.
Let’s register the events and add some Callback context code that we can see in the Console Window:
We also need some reference material so we know the conditions we are testing:
Hold conditions for events to be triggered:
Tap conditions for events to be triggered:
The important events to look at here are ‘Performed’ and ‘Canceled’, as there is no difference between them for ‘Started’.
Let’s start with the ‘Tap’ Interaction at the top of the hierarchy:
Outcomes:
The Button was ‘Tapped’ for a total time of 0.0619 seconds, at which stage:1. The ‘Tap’ Interactions ‘Performed’ event triggered (holds true as I released the button before the ‘Max Tap Duration’ threshold).
2. The ‘Tap’ Interactions ‘Canceled’ event never triggered (holds true as this only gets called when the button is held down beyond the ‘Max Tap Duration’ threshold).
3. The ‘Hold’ Interactions ‘Performed’ event never triggered (holds true as we never made it beyond the ‘Hold Time’ threshold)
4. The ‘Hold’ Interactions ‘Canceled’ event NEVER TRIGGERED (holds FALSE as this should have triggered — the button was not held for long enough before reaching the ‘Hold Time’ threshold).
Now let’s switch them around and have the ‘Hold’ Interaction first.
You can use the arrows indicated in the image below to swap them:
Outcomes:
The Button was ‘Tapped’ for a total time of 0.0470 seconds, at which stage:1. The ‘Tap’ Interactions ‘Performed’ event triggered (holds true as I released the button before the ‘Hold Time’ threshold).
2. The ‘Tap’ Interactions ‘Canceled’ event never triggered (holds true as this only gets called when the button is held down beyond the ‘Max Tap Duration’ threshold).
3. The ‘Hold’ Interactions ‘Performed’ event never triggered (holds true as we never made it past the ‘Hold Time’ threshold).
4. The ‘Hold’ Interactions ‘Canceled’ event TRIGGERED (holds TRUE as we never made it past the ‘Hold Time’ threshold).
Test 2 — The ‘Hold’
Test — Similar to test 1 except we are going to ‘Hold’ the button and compare the difference between events.
Let’s start with the ‘Tap’ Interaction at the top:
Outcomes:
The Button was ‘Held’ down for a total time of 2.0469 seconds, at which stage:1. The ‘Tap’ Interactions ‘Performed’ event never triggered (holds true as we held the button down beyond it’s ‘Max Tap Duration’).
2. The ‘Tap’ Interactions ‘Canceled’ event triggered (holds true as this event triggers automatically when reaching ‘Max Tap Duration’ threshold).
3. The ‘Hold’ Interactions ‘Performed’ event triggered (holds true as this event triggers automatically when reaching the ‘Hold Time’ threshold).
4. The ‘Hold’ Interactions ‘Canceled’ event triggered (holds true as this event triggers when releasing the button after reaching it’s ‘Hold Time’ threshold).
Now let’s switch them around and have the ‘Hold’ Interaction first.
Outcomes:
The Button was held down for a total time of 2.2179 seconds, at which stage:1. The ‘Tap’ Interactions ‘Performed’ event never triggered (holds true as we held the button down beyond it’s ‘Max Tap Duration’).
2. The ‘Tap’ Interactions ‘Canceled’ event NEVER TRIGGERED (holds FALSE as this event should trigger automatically when reaching the ‘Max Tap Duration’ threshold).
3. The ‘Hold’ Interactions ‘Performed’ event triggered (holds true as this event triggers automatically when reaching the ‘Hold Time’ threshold).
4. The ‘Hold’ Interactions ‘Canceled’ event triggered (holds true as this event triggers when releasing the button after reaching it’s ‘Hold Time’ threshold).
This is the desired behaviour according to Unity’s documentation — Multiple Interactions On a Binding.
Should I Add an Interaction to the Binding or Action?
Great question! And this all comes down to the desired behaviour.
If you add an Interaction directly to an Action, then it will apply to ALL the bindings that are associated with this Action.
If you add an Interaction directly to a Binding, then it will ONLY apply to that binding.
Errors When Adding 2 Interactions to 1 Binding/Action and Pressing the Button During Runtime
This is an interesting error that I came across and has actually been known for a while but I am unsure as to when a fix will be applied:
Unity Forums — Index on trigger does not correspond to map index of trigger state
Problem: When adding 2 Interactions to 1 binding/action, and pressing ‘Play’, then pressing said button during runtime, you will be met with these errors:
Solution(so far): Move the Action Map to the first spot on the list (so above ‘Player’) and this should resolve your issues.
Albeit, this isn’t the best solution as what happens if we have multiple Actions Maps with the same issue — They can’t all be number 1!
If someone has a better workaround/solution for this, I would love to hear from you so I can update this article and help others.
Setting Up the Tap & Hold Buttons
To get started let’s setup the new Action Map, Action and Binding.
Then let’s add the ‘Tap’ and ‘Hold’ buttons in the ‘Interactions’ panel:
I have gone ahead and left these values as default, but this is where you are able to ‘Open Input Settings’, create a new asset and build your own default settings.
Scene Setup
This is a super simple scene that uses a plane for a ground, a sphere for the ball and cylinders for the targets.
All the objects have a collider on them — However, I did need to change the cylinders collider from a capsule to a box as it falls over when the scene starts because the collider isn’t flat.
All objects also have a Rigidbody attached so we can use the physics system.
Now we just need to project our ball into the targets!
Adding Force to a Game Object
Let’s now use Unity’s physics system and apply some Force to the ball dependant on how long the button was held down for:
Most of this is pretty self explanatory, so we will jump straight to the 2 most important concepts which are the — context.duration which returns a double (let’s cast to a float) and stores the result in _forceEffect. This context.duration is going to tell us how long we held down the button for. We want to take this value and use it to calculate how much force we should add to the ball (the longer we hold, the more force).
Secondly, we are using the ‘Canceled’ event to calculate when the button is released. If we tried to use ‘Started’ or ‘Performed’ events this wouldn’t work as we would always get the same results — 0 and 1 respectively (if we held down the button for longer than 1 second).
Let’s test 2 different scenarios — A light hold and a longer hold:
Light hold (1.5 seconds):
Longer Hold (3.3 seconds):
SUCCESS!!!
Article Links
Next Article
“Rapid Prototyping With Unity’s new Input System”.