I part 1 I mentioned that using plain text as a game dialogue format is insane and should not be attempted.
What I didn’t mention was that I know this because I attempted it.
I spent a few weeks flailing around trying to develop a Lifeline-like narrative game engine using plain text files and only after sinking a ton of time into it did I take a moment to see how everyone else handles this problem.
What I quickly learned is that no one uses plain text as a format. That would be mad and only an idiot would attempt it.
The most popular formats are structured data formats like JSON and XML. This makes a lot of sense. Each node has its own identifier which makes things like branching or looping dialogue a lot easier to manage.
Designing The Format
I found Yarn’s JSON output to be the easiest to get started with and used that as a starting point. There are a few key design decisions that I think will make this format easier to work with and develop for.
1. One File Per Conversation
A file will be a collection of nodes that represents this conversation. When you initiate a conversation, the parser will load up the relevant JSON file and begin delivering the lines.
2. One Node Per Character
Each node will represent one character speaking. This allows support for having the player talk with a group. It will also support player monologues.
Dialogue options are an exception. These will always be in the player character’s voice even if they appear in another character’s node.
3. Line Metadata
Each line of dialogue or instruction will be its own entry in an array within the JSON (see below), which will contain additional information about that line.
I wanted to do was move away from the Big Blob of Text approach that many of these dialogue editors seem to use, where all of the lines of dialogue, as well as any instructions like conditional statements, are stored in a single text field as a string.
I think a more structured approach would be useful. Each line should know a little something about itself, for example whether or not it is a regular line of dialogue, or an option for the player to choose from or a variable, etc.
This makes the dialogue easier to parse, makes errors less likely, and easily surfaces issues when typos do occur.
4. Repetition management
In adventure games, you might end up talking to the same character multiple times. It’s nice to have alternative phrasing (e.g. “Nice to meet you” the first time, “Good to see you again” in subsequent times.
It would be nice for this to be automatically supported without having to rely on variables within the text itself.
5. Branch management
Related to the above, sometimes you’ll want dialogue branches to disappear after you’ve exhausted them (e.g. if they’re throwaway jokes or inessential world-building details). Other times, you need those branches to stay until a certain condition is met.
Being able to flick a switch within the editor to say whether lines should appear in subsequent conversations makes this much easier than trying to manage variables within the body of the text file itself.
Having said that, variables are still important to reflect more complex state within the conversation, as well as the world outside the conversation.
However, variable management should be as easy as possible. The editor should be aware of all available variables and offer easy to use dropdown lists of currently set variables to prevent errors from typos.
It should also support built-in variables that give the writer access to world state—the name of the file and the number of times it has been run are two examples of the kind of information these built-in variables could provide.
There are some basic triggers that a game engine could respond to that would make a conversation more realistic or make a conversation feel more dynamic. For example, being able to have a character move away from another character that is saying something threatening. The editor should make these triggers easy to use.
The Technical Details
I found that it was possible to cover most of the above situations with three standard fields, two of which are entirely optional. At the moment I’m calling them
type being the only required field).
Here’s how the format looks so far:
...Other Node Properties...
"id" : "ADF790-GADSGSA-987DSAF-1238"
"items" : [
"type" : "text",
"body" : "A line of dialogue."
"type" : "text",
"body" : "A line of dialogue, which is shown the first time through the conversation.",
"value": "In this case, there is text in the value field. This text would be shown on the second and subsequent times through the conversation."
"type" : "if",
"body" : "$variableName",
"value" : "20"
"type" : "text",
"body" : "This line will be spoken if $variableName == 20"
"type" : "else"
"type" : "text",
"body" : "This line will be spoken if $variableName != 20"
"type" : "/if"
"type" : "trigger",
"body" : "Run Animation",
"value" : "animationName"
"type" : "link",
"body" : "If there are only one of these links, then the player character would speak it and the dialogue would move to the linked node (The value is the ID of the next node.). With multiple links in a row, these would become options that the player could choose from.",
"value" : "ADF790-GADSGSA-987DSAF-09899"
The Next Steps
I’m currently developing both an editor and parser based on this format, both written in Swift, and both coming along nicely.
Initially, the editor will be iOS only as I like writing on an iPad and I am much more comfortable developing on iOS although, in future, I would like to develop a Mac version as well. I’d also like to write a parser in C# for Unity support as well.
Over the coming weeks I’ll be detailing the development of the parser and editor and highlighting some of its major features—if you follow me on Instagram, you can see the editor’s testing screen with animated characters in action!