How to Tell a Joke With Unity Timeline
Writing jokes is easy. Telling jokes is hard. In this tutorial, you’ll learn about Unity’s Timeline and how to create and deliver your very own punchlines! By JJ Richards.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
How to Tell a Joke With Unity Timeline
25 mins
- Getting Started
- Setting the Scene
- Adding the Text Bubble
- Adding the Comedian
- Revealing Text Over Time
- Understanding Comic Timing
- Understanding Timeline
- Prepping Assets
- Activating GameObjects
- Sending Signals
- Receiving Signals
- Reacting to Signals
- Creating Custom Tracks
- Telling a Joke
- Where to Go From Here?
Understanding Comic Timing
The world is full of jokes in many forms. But ask yourself this: Do you laugh more reading a joke, or when you hear a professional tell it?
Try this at home with your favorite comedian on a streaming service. Watch with subtitles on and the sound off, and then watch with the sound on and the subtitles off. Which is funnier?
Spoken jokes are more popular than written jokes because timing is critical to getting a laugh. Rush it and you ruin it. Wait too long, and the audience fills in the gap. Even cartoon strips use the three-box format to control the delivery of the joke.
Those three parts are: the Setup, the Turn, and the Punchline. The length of each is important, the pace of each is important and the pause between each is important.
Professional stand-up comedians rehearse those timings over and over until they get it just right. But how do you control all that timing in Unity? The answer is: Timeline!
Understanding Timeline
Unity's Timeline infrastructure is powerful and flexible. It allows you to create cutscenes, cinematics and gameplay sequences by visually arranging tracks and clips linked to GameObjects in your scene.
In this tutorial, you'll use it to make a dialog system. The infrastructure is made of several parts that all need to work in harmony:
- Timeline Asset: An arrangement of tracks you manage in the Timeline Window.
- Timeline Track: A sequence of animation clips of a similar type.
- Playable Director GameObject: Associates a Timeline Asset with a GameObject in the scene.
- Signal Emitter: Contains a reference to a Signal Asset, which it visually represents visually with a marker on the Timeline.
- Signal Receiver: A component with a list of reactions linked to a Signal Asset.
- Signal Asset: The association between a Signal Emitter and a Signal Receiver.
This puzzle has a lot of pieces, but don't worry, they all fit together quite easily.
Prepping Assets
Create an empty GameObject called Timeline. The Hierarchy should now look like this:
Navigate to Window ▸ Sequencing ▸ Timeline to add an empty Timeline Window to the Editor workspace. Dock this tab to the bottom so you can easily see it and the Game view at the same time. The editor layout should now look like this:
With the Timeline GameObject selected in the Hierarchy, click the Create button in the Timeline Window. This starts the process of creating and setting up the Timeline Asset.
When prompted, save a new Timeline Asset to the Timeline folder and name it JokeTimeline. This step automatically adds a Playable Director component to the Timeline GameObject and assigns the newly-created JokeTimeline asset as the Playable.
Finally, toggle Play On Awake to True
in the Inspector.
Now that all the pieces are in place, it's time to connect them.
Activating GameObjects
Timeline can control many things, but one of its simplest uses is to enable and disable GameObjects.
First, select the Timeline GameObject in the Hierarchy and make sure the Timeline Window is visible. Then, drag the Comedian GameObject to the left panel of the Timeline Window.
At this point, you'll see a prompt for the type of track you want. Choose Add Activation Track. This populates a clip in the Timeline and associates the Comedian GameObject with that clip.
When the Timeline Marker is over the clip, the Comedian is active; when the marker is not over the clip, the Comedian is inactive.
You can change the duration of the clip and move it anywhere you like on the Timeline. You can view the Timeline either in seconds or frames.
Click the Gear icon in the Timeline Window to set your preference to Seconds. For the Comedian clip, start the clip at 0:00 and stretch it to 20:00.
Do this again for the JokeWindow GameObject. Drag it to the Timeline Window to create an Activation Clip. Start the clip at 2:00 and stretch it to 18:00. This makes the JokeWindow appear after the Comedian and disappear before the Comedian.
In the Game View, make sure you haven't selected Maximize On Play so you can see both the Game View and the Timeline Window simultaneously.
Click Play and watch the sequence play both in the Game View and the Timeline Window. In Edit Mode, scroll the mouse wheel while over the Timeline Window to see more or less of the overall Timeline. Then, scrub the Timeline Marker back and forth to watch the clips activate and deactivate their associated GameObjects.
Sending Signals
Timeline tells GameObjects what to do using Signals. Signals consist of three parts: the Signal Emitter, the Signal Receiver, and the Signal Asset. The Signal Emitters are placed on the Timeline and Signal Receivers are placed on GameObjects in the scene that you want to control. During playback, whenever Timeline passes a Signal Emitter, it sends the Signal Asset to the Signal Receiver.
Before writing code to send or receive a signal, it's best to define the elements that should be in the signal. There are many things you can include, but for this app, you'll pass four pieces of data with the signal.
To help manage that data, return to the Typewriter script and, at the very bottom, add a simple class with the following code:
public class Dialog
{
//1
public string Quote;
//2
public float PausePerLetter;
//3
public bool NewPage;
//4
public bool NewLine;
}
With this code, you're declaring four variables:
- Quote: The text that you'll send — in this case, the joke itself.
- PausePerLetter: Controls the speed of the text reveal.
- NewPage: Tells the Typewriter whether or not to start a new page.
- NewLine: Instructs the Typewriter whether or not to start a new line.
Now that you've created a form for the signal, create a new script called JokeMarker to send it. Add these declarations to the script, above the class declaration:
using UnityEngine.Playables;
using UnityEngine.Timeline;
These are necessary to access the Timeline API.
Instead of the usual Monobehaviour
, this script needs to derive from Marker
and implement INotification
. So replace the default class definition with:
public class JokeMarker : Marker, INotification
You need to send four pieces of information in the signal, which can be private
and visible in the Inspector via the SerializeField
attribute:
//1
[SerializeField]
private string quote = "";
//2
[SerializeField]
private float pausePerLetter = 0.1f;
//3
[SerializeField]
private bool startNewPage;
//4
[SerializeField]
private bool startNewLine;
Here, you declare:
- quote and set it to be empty.
-
pausePerLetter and set it to
0.1f
. -
startNewPage, which defaults to
false
. -
startNewLine, which also defaults to
false
.
Next, add the following code, which uses the variables you just declared:
public string Quote => quote;
public float PausePerLetter => pausePerLetter;
public bool StartNewPage => startNewPage;
public bool StartNewLine => startNewLine;
This exposes read-only access for your four private variables. Using [SerializeField]
, private
and read-only properties together is a good safety pattern. It ensures that other parts of your code can't randomly change the values you set in the Inspector.
Finally, implement the INotification
interface by adding the following line:
public PropertyName id => new PropertyName();
This is a necessary identifier for notifications. As your use of notifications grows, you can use this id
to identify notification channels or even individual notifications.
Save the script and return to the editor.
Now that you've created the JokeMarker, you need to create a script that listens for it. You'll do that next.