Scripted Dialogue Sequences

I've been working on a prototype for a game in Unreal as a personal endeavor for around 8-9 months now. One of the systems I setup pretty early on was a way to very easily script cutscenes through blueprint. It's primarily based around writing textual dialogue, and is called "TalkMoment" (the term "Dialogue" is, annoyingly, taken in Unreal and naming is hard, but it gets the idea across).

Here's a very short bit of dialogue just to show the basics:

Excuse the placeholder, uh, everything.

This short exchange is driven by a basic blueprint (extending a C++ base class) with actions for character and level interaction (in this case, making the characters face each other) and for displaying dialogue.

The larger node at the upper-right is an "expanded" version of the "Speak" node that lets me set additional options like portraits for characters.

This is very simple, and works very well. It even supports branching dialogue trees:

Would you rather have cake or pie?

There's just one small problem... Take this short exchange:

Chibi placeholder art courtesy of Shannon Walsh.

This is just 10 lines of dialogue, with a few extra actions to control timing. If written into a word document it'd take up practically no space on screen, but in blueprint...

This is actually a slightly earlier version of the dialogue above, but the number of actions is pretty much the same length.

Even on a 27" monitor it is impossible to read anything while keeping this short set of dialogue on screen. And that isn't even the entire scene - only about a third of it!

Even in dedicated branching dialogue software like Chat Mapper or articy:draft I've always felt like viewing large sets of dialogue is clunky, especially when actually writing said dialogue since you are constantly context-switching to add and move nodes around.

My solution to this isn't particularly novel and took only a few days of real work. I had looked at Yarn Spinner previously but unfortunately it only has a runtime for Unity. I decided to write my own scripting system inspired by it, but designed with the explicit goal of integrating into my existing "TalkMoment" system and blueprint.

Player = /Game/Flame/Characters/Player/Sp_Player
Uncle = /Game/Flame/Characters/Tower/Sp_Tower_Uncle

Uncle [Left, Default]: Is something on your mind? {Player}?
Player [Right, Default]: Huh? Oh, did you say something?
Uncle: You've got your mind stuck on something. What's going on?
Player: Just a weird dream. Some people talking and then a loud noise that scared me.
Uncle: ...
Uncle: Are you making mischief even in your dreams now?
Player: Hey!
Player: I mean...
Player: No...
Player: I can't even really remember what was being said. And I don't think I could see anything.
> ClearLine

This is the script powering the same short cutscene as before, but now it takes up a tiny portion of the screen. It allows me to set portraits in [brackets] and let's me call into both C++ and Blueprint with > commands. I can reference /assets/on/disk which let me share properties between scripts (such as the name of a character and the portraits they use). I can also use standard Unreal {text formatting} to intersperse variables with handwritten text. Since the original system is called "Talk", I call this "Supertalk".

It supports branching dialogue:

Person1: Foo or bar?
  * Foo
    Person1: You chose foo!
  * Bar
    Person1: You chose bar!
Person2: Cake or pie?
  * Cake
    -> ChooseCake
  * Pie
    -> ChoosePie
    
# ChooseCake

Person2: You chose... poorly.

# ChoosePie

Person2: You chose pie!
Person2: This was the right choice.

And it can even be used in a mixed blueprint/supertalk environment:

The highlighted node will play a supertalk script to completion before continuing with blueprint execution. The script can also call back into custom Blueprint events!

This scripting language is very simple and I want to keep it that way - commands aren't really handled by the scripting runtime, but instead defer to Unreal to parse arguments and call functions. The "compiler" just takes the text script and serializes the syntax tree as a normal Unreal asset that can then be loaded when the game is running. There's no such thing as an if statement or any other flow control beyond jumping between sections of dialogue after a choice. All of that is better left to blueprint (or C++) which can handle more complex interactions, while leaving the actual text very simple.

Conclusion

I'm slowly going back through any prototype cutscenes I've written and finding any large masses of dialogue that can be replaced with Supertalk. It works pretty well so far - I'm sure I'll keep improving it but the simplicity is nice. The only thing I'd really like to add at this point is some custom syntax highlighting for an editor like VS Code. Maybe some day once I've polished things up a bit I'll release it.

Show Comments