Custom Timeline Asset: Pause and wait for Player Input (Part 2/3)

06/17/23



Part 2

Part 1 went over how to set up a basic Timeline cutscene. Part 2 will show how to make a custom "pause clip" that will make the cutscene pause when playback reaches that clip.


2.1 PlayableAsset, PlayableBehaviour, and TrackAsset


I will be extending 3 classes from the Timeline API to build this custom cutscene input-pauser. Think of this as a set of 3 scripts that work together to define a single cutscene behavior.


PlayableAsset - this is a low-level class that can be extended to make many useful 'things' in the Timeline system (tracks, clips, etc.) In this demo we're going to extend it to make a custom clip. When added in Unity the clip asset will look like this:


TrackAsset - Our custom clip can only be added to a custom track designed to hold exactly this type of clip.  TrackAsset is the basic track class that can be extended to make this custom track type. When added in Unity, it will look like this:


PlayableBehaviour - clips are just empty containers that are meant to hold a behavior. Until we put a behavior inside of a clip, it will do nothing. Said another way, clips are time markers that tell us WHEN to do a thing, but PlayableBehaviours tell us WHAT exactly to do for the duration of the clip.

PlayableBehaviour is the basic behaviour class that can be extended to define our custom "pause until the player presses a button" behavior. 

Of course, unlike clips and tracks, playableBehaviors don't "look" like anything in Unity as they are non-editor-accessible objects instantiated at runtime by the clip. 

 


2.2 Creating the Custom Clip Script


To get started, make a Scripts folder. Create a new script called "PauseClip".

PauseClip should derive from the basic clip class, PlayableAsset.

Delete the Start and Update functions.

Add the 'using UnityEngine.Playables' and 'using System' statements up top.

PlayableAsset is an abstract class, which means we must override the abstract method CreatePlayable and put some actual code in it.

…However, we need to do a little more setup before we're ready to add this code, so for now, I'm just putting a TODO comment. We'll come back to this a few steps later.


Finally, add the Serializable attribute.



2.3 Creating the Custom Track Script

In Unity create a new script called "PauseTrack".

PauseTrack should derive from TrackAsset.

Delete the Start and Update functions.

Add "using UnityEngine.Timeline" up top.

Then add a line of code that will tell this track what kind of clips it is meant to be paired with. This is a pause track, so it should only accept pause clips:


That's it! Now let's get into the meat of it and define the clip behavior.


2.4 Creating the Custom Behaviour Script

Create a script called PauseBehaviour.

PauseBehaviour should derive from PlayableBehaviour:

Delete the Start and Update functions.

Add "using UnityEngine.Playables" up top.


Add this variable:


When our pause clip is reached during playback at runtime, it will create a new PauseBehaviour. The PauseBehaviour will then fire off a startup method called "OnPlayableCreate".


When this startup method fires off, we want to use it to give our PauseBehaviour a reference to the timeline. The timeline is the object that  has the power to pause and resume playback, so it is useful for our PauseBehavior to have a hotline to the it. It essentially gives PauseBehaviour the power to pause and resume the cutscene, through the timeline.

To open up that line of communication from our pause behaviour to the all-powerful timeline, we will override the behaviour’s OnPlayableCreate method:

TLDR Note about Resolvers:
If the "getResolver" stuff looks weird and unfamiliar, just know that Timelines deal a lot with things called ExposedReferences. An ExposedReference is kinda like a stand-in for a normal variable, that must be "Resolved" at runtime to get a hold of the actual variable and its values. To "resolve" something a "resolver" is needed (kinda like a key is needed to open a lock.)
They're a whole topic of their own... more info HERE.
Now all that said, we aren't really dealing with ExposedReferences or Resolving things in this demo -- we are just using some code that is part of that whole weird system, that ALSO just so happens to get us a nice reference to our all-powerful timeline object.


Next, we need to override the OnBehaviourPlay method. 

Since we have a nice reference to the timeline, we can ask the timeline to pause playback!

OnBehaviourPlay is sort of like our clip's "update" method -- it's a function that is called once every frame that the clip is actively being played. 

(You may see the logic problem of “pausing every frame” here already. Good catch if so! We will fix that in the next part.)


2.5 Adding the correct code to the TODO in PauseClip

Now that we've made all 3 of our scripts, one last thing we have to do is go back to that TODO and replace it with actual code:

This code takes our clip, which up till now was nothing but an empty box that could be placed at any point along the track, and fills it with some specific behavior to actually perform -- the PauseBehaviour.



Phew. Now we have 3 shiny new scripts all ready to go.

Let's put these scripts to work!


In Unity, go to your timeline.

Right click anywhere in the Timeline panel. Notice that you can now create a "Pause Track" -- our custom track we made!

Right click on the pause track's dark blue track strip. Notice that you can create a new Pause Clip!

The pause clip can be as long or short as you like. I make it shorter because I think it looks cleaner. Place the clip so that the start of the clip corresponds to where you want your timeline to pause.  In my case, I’m going to place it to pause right before the green guy waves.


Time to test it out. Let's hit play....



And it pauses! Super cool!


Now we have a custom timeline clip that pauses playback. The next section will go over how to resume playback when the player presses a button.


Part 3