Make Options Timeout
Learn how to make options that timeout after a period of inactivity from the user.
Last updated
Was this helpful?
Learn how to make options that timeout after a period of inactivity from the user.
Last updated
Was this helpful?
This sample demonstrates how to create a custom dialogue presenter where dialogue options include a timeout bar. When options are presented, the timeout bar gradually shrinks. If the player doesn't select an option before the timer expires, the system automatically selects one for them.
The sample supports three variations of this functionality:
A hidden option that gets selected when time runs out
A visible option designated as the default that gets selected when time runs out
The currently highlighted option gets selected when time runs out
For simplicity, this guide focuses on implementing the first approach. To explore the other methods, check out the full sample.
Custom Dialogue Presenters
Using Metadata to control views
The nightmare hellscape that is Unity UI
If you want to see a finished version of this go to PATH TO SAMPLE.
If you haven't already install Yarn Spinner link to install guide. Once installed we will start by building out a basic scene.
Make a new scene in Unity.
From Samples/Shared Assets/Prefabs
drag the Basic Arena into the scene
From Samples/Shared Assets/Prefabs
add a Camera Rig into the scene
From Samples/Shared Assets/Prefabs
drag a Player prefab into the scene
From Samples/Shared Assets/Prefabs
drag an NPC prefab into the scene and rename it to be Alice
Add a default dialogue system to the scene, in the Hierarchy right click Yarn Spinner -> Dialogue Runner
Create a new Yarn Spinner project Assets -> Create -> Yarn Spinner -> Yarn Project
and name it Timeout Dialogues
Create a new Yarn script Assets -> Create -> Yarn Spinner -> Yarn Script
and name it Alice
Delete the previous camera called Main Camera
Replace the contents of the Alice Yarn script with the following:
This script contains a single node with three options. Only the first two will be visible to the player. The third option (marked with the #fallback
metadata) will be hidden but selected automatically if the timer expires before the player makes a choice.
Select the Camera Rig and in the inspector drag the Player into the target field
Select Alice and in the Inspector set the Dialogue field to use the new Timeout Dialogues
project
Select Alice and in the Inspector select the Alice node in the dropdown
Select Alice and in the Dialogue Runner field drag in the Dialogue System object from the hierarchy
Select the Dialogue System and in the Inspector set the Yarn Project to use the Timeout Dialogues
project
At this point, you can talk to Alice and go through the dialogue, but the timeout functionality isn't implemented yet. Now we'll create our custom presenter by building a UI bar that shrinks over time, then develop a custom dialogue presenter that uses it.
First, we'll create the UI elements for our timer by modifying the existing canvas from the Dialogue System prefab.
Right click on the Dialogue System in the hierarchy and choose Prefab -> Unpack Completely
This will disconnect this from being a prefab but will otherwise leave it in the state we want.
Expand the Dialogue System out in the hierarchy so we can see all the individual components we are interested in the children and grandchildren of the Option View gameobject.
Select the Last Line game object and delete it. While we could handle also showing the last line it would just add more logic to our view while not actually showing off the main feature of this sample. So instead we'll just drop it.
Add a new empty child gameobject to the Options View and name it timer container
, this will be what holds our timer bar. By default it will have inherited a slew of position and size properties from it's parent's vertical layout group, which is mostly what we want, but we do need it to have a specific height.
In the inspector add a new Layout Element component to the timer container.
Tick the Preferred Height field and set it to have a height of 30
. Now this element will tell it's parent layout group that it is going to be 30 units high which is perfect.
Add a new empty gameobject to the timer container and name it timer bar
.
Give the bar an image component.
Change the bars rectangle widget to be centre-aligned + vertical stretch.
Set the width to be whatever you like the look of, we found that 1480 units was a nice value.
This bar will shrink as the time to select an option runs out.
Our UI work is now complete. Next, we'll write the code to make the bar shrink over time.
Create a new monobehaviour script and name it TimeoutBar.cs
Open up the new file and replace the imports with the following:
Add the following fields to the class:
These fields represent the bar UI element and its original size, which we'll use to reset it between option sets.
Replace the Start
method with the following:
When Unity initializes the component, we store the default bar size in originalSize
for later reference.
Add the following new method:
This method resets the bar to its original size. There are several ways to accomplish this, but using SetSizeWithCurrentAnchors
is straightforward and lets Unity handle the rectangle's final dimensions.
Add the following new method:
This method gradually shrinks the bar over the specified duration. It first verifies that the bar exists, then enters a loop that continues until either the duration elapses or cancellation is requested. Each iteration updates the bar size using linear interpolation from its current size toward zero. After the loop, we ensure the bar is fully shrunk regardless of how it exited the loop.
Add that class as a component to our bar
Drag the bar gameobject into the Bar field in the TimeoutBar inspector and now the bar is done.
To see a complete version of this check out path to the file in the samples.
Now we'll create the custom dialogue presenter subclass. Since this involves substantial code, we'll approach it in sections.
Create a new presenter subclass Assets -> Create -> Yarn Spinner -> Create -> Dialogue View Script
and name it TimeoutOptionsView
. This creates a stubbed out subclass with all the necessary methods for a custom view.
Replace the imports with the following:
Add the following fields to the class:
These fields reference the UI elements we need to manage: the canvas group for fade effects, the option item prefab, the timer bar, and a list to cache option items. Other than timedBar
, these are similar to what you'd find in the default options presenter.
Add the following fields to the class:
These fields control the timing aspects: how long the player has to select an option before timeout, and the duration of fade-in and fade-out animations.
Add the following field to the class:
This constant identifies the option that should be selected when the timer expires. We'll use it later when examining option metadata.
Replace OnDialogueStartedAsync
with the following:
Replace OnDialogueCompleteAsync
with the following:
These methods are called by the dialogue runner at the beginning and end of dialogue. Both hide and disable the options UI, then return a completed task.
Replace RunLineAsync
method with the following:
Since this presenter only handles options, we can return immediately when asked to run a normal line.
Replace Start
with the following:
Similar to the dialogue event handlers, we initialize the UI as hidden and non-interactive.
Finally add a new internal enum to the class:
This enum identifies which timeout behavior we're using. The default is None
, meaning no timeout is needed for the current option set.
Now we'll implement the RunOptionsAsync
method, starting with determining which timeout option to use and validating the option group.
Inside the RunOptionsAsync
method add the following code:
These variables will track whether we need a timeout and which option should be selected if timeout occurs.
Add the following code:
This code examines all the options to find any with the fallback
metadata tag. If it finds one that's available, it marks that option as the hidden fallback to be selected when the timer expires. We also count how many tagged options we've seen for validation purposes.
Add the following code:
This validation ensures that if we're using a hidden fallback, we have exactly one option tagged as such, and we've successfully identified which option it is. If either condition fails, we log an error and tell the dialogue runner that no option was selected.
Next, we'll set up the infrastructure to handle completion and cancellation. These mechanisms will be provided to the option items later.
Add the following code to create our completion source:
Add the following code to create our cancellation source:
These sources work together to handle the two possible outcomes: the player selects an option, or the dialogue is cancelled. Now we'll implement the cancellation handling:
Add the following encapsulated method:
This task monitors for dialogue cancellation and ensures proper cleanup by setting a null result on the completion source if needed. This connects the two sources: if dialogue is cancelled, this function sets the completion value on selectedOptionCompletionSource
.
Now we'll create and configure the option items to display to the user:
Add the following code to instantiate new option items as needed:
This creates just enough option item prefab instances to handle the current option group, reusing any we've already created. The CreateNewOptionView
method will be implemented later.
Add the following code to configure the option items:
This code configures each option view with its corresponding option data, skipping any that are unavailable or marked as hidden fallbacks. It then selects the first valid option to be highlighted initially.
Add the following code to enable or disable the timer bar:
This activates the timer bar only if we're using a timeout option, resetting it to full size and ensuring it appears at the bottom of the option list.
Now we'll display the UI to the player:
Add the following code to fade in the UI:
This fades in the UI and enables user interaction once the fade is complete.
Add the following code to start the timeout bar if needed:
If we're using a hidden fallback option, this starts the timer that will eventually select it. The BeginDefaultSelectTimeout
method will be implemented later.
Add the following wait code:
This suspends execution until the player selects an option, the timer expires, or dialogue is cancelled.
After a selection is made, we need to clean up and exit:
Add the following code:
This cancels the completion source to prevent further selections and disables UI interaction.
Add the following code to fade out the UI:
This fades out the UI and hides all option views, though they remain cached for future use.
Add the following code to return the selected option:
This returns the selected option to the dialogue runner, or indicates that no option was selected if dialogue was cancelled.
Now we need to implement the methods we've referenced but haven't defined yet:
Add the following new method:
This creates a new option item instance, adds it to the canvas group, positions it at the end of the list, and disables it by default.
Add the BeginDefaultSelectTimeout
method:
This method initiates the timer bar shrinking animation. When complete (if not cancelled), it sets the designated fallback option as the selected choice.
Finally, let's hook everything up in Unity:
Move back over to Unity and select the Options View in the hierarchy.
Add the TimeoutOptionsView.cs
script as a new component to the Option View gameobject.
Delete the Options Presenter
component.
Select the Options View gameobject and in the Canvas Group field drag the Options View into this field.
From Packages/Yarn Spinner/Prefabs
folder drag the Option Item prefab into the Option View Prefab field.
Drag the timer gameobject into the Timed Bar field.
Pick a duration for the Auto Select, Fade Up, and Fade Down durations.
Now we need to tell the dialogue runner about our custom presenter:
Select the Dialogue System game object in the hierarchy.
Remove the old (now missing) Options Presenter from the Dialogue Views field.
Add the new timeout options view into it's place.
Take it for a spin!
Congratulations! You've created a custom options presenter that supports timed dialogue choices. When the timer expires, the system automatically selects a predefined fallback option, adding a sense of urgency to dialogue interactions.