NokiMo
Megan Fox
Megan Fox

patreon


Controlling my dialog with Timeline

Ok! I'm going to brush over the basics of Timeline, or the full process of how to make custom Timeline nodes, because hey there's a lot of material on that. What I want to dig into is something specific: how can you use Timeline to play dialog during your cutscenes?

At first blush, it's easy, right? You make a simple node that throws up a dialog line. You find walkthroughs of exactly that. Except oh dear, not everyone reads at the same speed, so somehow you have to keep the timeline going for however long it takes a player to read? Also, you want you architect your conversations in a dialog system with better tooling for it, not entirely in the timeline? It just spirals.

So here's how I did it!

(video here, Patreon can't embed these, it's hilarious: https://twitter.com/glassbottommeg/status/1262866597946908672)

What Do I Use For Dialog

I use Dialog System For Unity, but Fungus is free and turns out to do the same basic thing. Dialog System For Unity is nice, but it turns out to be one of those systems that wants to control more of your game than makes sense (it wants to control quest state, and persistence in general, and a whole bunch of other things), so I can't say that either system is really the best here. They each have their annoyances.

How To Do

It turns out most of the magic for this is in the Mixer. Your PlayableAsset and PlayableBehaviour are mostly going to be empty data-carrying shells, but your Mixer, hoo boy.

This is the meat of it. The trick to remember here is that Timeline is basically just Unity's Animation system. These aren't anything new, these are AnimClip's in a funny hat, and that means they do not have state. I know that node in the timeline looks like it should know when it starts and stops, but no, AnimClips don't work that way (and me screaming about AnimClips is a whole other Patreon post). The important part is, just remember that the only state in a Timeline exists in the Mixer itself. Calling even what you have in the Mixer "stateful" is generous, since you're working with a ProcessFrame and that's it, but... yeah ok screaming over moving on.

Starting your dialog is easy. Just start the conversation whenever the timeline finds a Dialog node. Remember, they don't have "on" or "off", these are AnimClip. Your nodes have a weight between 0 and 1, and technically they process every frame even at weight 0 (which is why the logic lives in the Mixer). So identify the first node with a weight of 1, and then play the conversation.

Now comes the trickier part. Your conversation is now untethered from your cutscene. There's a secondary conversation UI showing now, and you have no idea how far into the conversation the player is, or when they might be gone, so, what happens to your cutscene?

Wait For It

No really, make the cutscene wait after a while. Here's how my Timeline looks for the conversation I linked at the very top of this. Specifically, this is the second conversation after you finish the mission (the one where the camera sweeps to the window after the dialog ends).

What happens is that at the leading edge of the dialog node, the conversation starts. The timeline then continues in the background while the player reads the dialog. If the timeline gets to the end of that node and the conversation is still active? It pauses the Timeline. Then it waits in a paused state until the conversation ends, and that conversation-ending is what resumes the timeline.

This is how that logic looks in Dialog System, but it'll vary depending on your conversation system.

Now that OnConversationEnd is called automatically by DialogSystemForUnity on all actors that were in a cutscene (I have a DialogActor on that object, etc etc Dialog System-specific stuff). CutscenePlayed, on the other hand, is called this way:

... which I can do because this is the MissionGiver, and my MissionGiver is aware of all cutscenes they can play. They need to be, since they're who all the other cutscene-playing functions query, so stuffing that extra bit of cutscene-continuing logic in there makes perfect sense.

Anyways, that's about it! The major trick is just start the dialog on the leading-edge of your dialog node, and then on the trailing edge, query if there's still a conversation going. If there is, pause your timeline, if not, everything continues on smooth.

If you don't balance the length of your nodes right, you'll get this thing where if a player clicks through the dialog fast, it feels like the game froze, because the timeline still has to play through all of that time you'd assumed they'd be reading dialog for. Okami (like the console game) has a bunch of cutscenes like this if you're curious. On the other side, though, if you make that overlap zone too short? Your timeline really does have to freeze. That means you can't bake much in the way of animation into the timeline itself, because it'll pause along with everything else.

To get around this, bake your active elements into stuff like the camera itself. Use the camera noise sytem to make your camera seem alive, instead of trying to bake that animation into the cutscene. Make sure your cutscene actors have animations that are being driven by their own state machines, with the cutscene only telling them where to stand and what animation they should be playing at any one time. That way, they might stand there looking a bit silly a second too long, but at least their idle animation will be looping, and not paused.

That's the gist! Code folks, you're just going to get a major dump of the files involved, and I'll see y'all later this month for a SECOND post that I owe y'all.


Related Creators