Exploring Tap & Hold Interactions, Hierarchical Precedence and Errors — Unity’s New Input System

Chris Hilton
8 min readNov 4, 2022

--

Objective: To understand how ‘Tap’ & ‘HoldInteraction buttons work in Unity’s New Input System.

Building a game object slingshot

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 ‘HoldInteraction 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.

Tap Interaction settings

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.

Hold Interaction settings

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:

Callback context code to test event triggers with Interactions

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 ‘TapInteraction at the top of the hierarchy:

Tap Interaction first
Callback context output

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 ‘HoldInteraction first.

You can use the arrows indicated in the image below to swap them:

Hold Interaction first
Callback context output

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 ‘TapInteraction at the top:

Tap Interaction first
Callback context output

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 ‘HoldInteraction first.

Hold Interaction first
Callback context output

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 ‘Holdbuttons in the ‘Interactionspanel:

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 ‘Canceledevent to calculate when the button is released. If we tried to use ‘Started’ or ‘Performedevents 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):

Light hold
Callback context output for lighter hold

Longer Hold (3.3 seconds):

Long hold
Callback context output for longer hold

SUCCESS!!!

Next Article

“Rapid Prototyping With Unity’s new Input System”.

--

--