Chatterbox: Branching Dialogue for Game Maker Studio 2

Spider Lily Studios
11 min readOct 2, 2022

Hello! About a year ago I made a tutorial for Scribble and Chatterbox, two FOSS libraries for Game Maker Studio 2 that allow for powerful and flexible dialogue systems. I promised I would update them, but a lot of things happened and I didn’t, and then Scribble updated to v8 anyway. So! I’m going to start over with just Chatterbox, and maybe bring in Scribble later. We’re going to keep it simple.

What Is Chatterbox And Why Should I Use It?

Chatterbox is a Game Maker implementation of Yarn Spinner, which is the narrative engine used by Night in the Woods, among other games. The appeal of Yarn, and thus Chatterbox, is that it automatically handles many useful functions needed for game dialogue. It’s also very customisable if you’re an advanced user.

What You’ll Need

GameMaker Studio 2.3.2+

Chatterbox v2.7

Optional but recommended: Crochet editor for Chatterbox

This tutorial will assume you understand the basics of GameMaker, such as creating objects and scripts and adding Included Files. I’ll try to use clear terminology so that you can look up items in the GameMaker documentation. If you get stuck, you can always join the Discord and ask there.

Getting Started

We’re going to start with a fresh GML project. Import the Chatterbox .yymps file you downloaded.

Next, we’re going to open Crochet. A fresh Crochet file will have one “node” box with the title Start.

Nodes are an important concept in Chatterbox! It can be useful to think of them as paragraphs, or “chunks” of dialogue.

Double click on your Start node to bring up the editor. There’s a few things going on here, but we’ll start with two lines of dialogue for now.

Now, Ctrl+S to save, and …

I actually took these out of order. Whoops.

Whoops, yes. We want to use the Export menu in Crochet to export this as a .yarn file, and specifically, we want to save it to the “datafiles” folder of our Game Maker project. Your file path will vary, but it’s inside wherever you saved your project.

Once you’ve done that, reopen that file in Crochet. You should notice that the file name in the header has changed to show the file path. This is very important! If it’s not correct, your changes won’t show up in Game Maker!

Like this!

Also note the asterisk at the end — that means you (well, I) have unsaved changes. Be sure to save often!

Okay, now we have a .yarn file with some dialogue. How do we make GameMaker show it to us?

Go back to GameMaker and create a new object. I’m calling mine oText, but you do you. Put it in Room1.

First we need to tell GameMaker where our file is and what it’s called. In the Create event, we use ChatterboxLoadFromFile(“filename.yarn”). (Replace “filename” with your filename, of course!)

Then, we’re going to create “a Chatterbox”. A Chatterbox is a variable — specifically a struct — that turns your Yarn file into something GameMaker can understand and display. To do that, we use ChatterboxCreate(“filename.yarn”).

A Chatterbox has a lot of information in it. When we want to display our text, we need to tell Chatterbox exactly where to find it, kind of like looking up a specific chapter in a book. In this case, the chapter is the node title “Start”. To get Chatterbox to go to that node, it’s ChatterboxJump(chatterbox,”Start”).

By default, each line in a Yarn file is treated separately by Chatterbox. To get that line, you call ChatterboxGetContent and specify the line number, starting at 0. So, for the first line, it’s ChatterboxGetContent(chatterbox,0). We also want to know what the current node we’re in is called — we only have one for now, but it’ll be useful later! So, we create two new variables called text and nodeTitle and use ChatterboxGetContent and ChatterboxGetCurrent to fill them in.

By now, your create event should look something like this:

//Load your file.
ChatterboxLoadFromFile("chatterboxtut.yarn"); //or whatever you called yours
// Create Chatterbox
chatterbox = ChatterboxCreate("chatterboxtut.yarn");
// Initialise Chatterbox by jumping to a node ("Start")
ChatterboxJump(chatterbox,"Start");
// Get Content from Chatterbox
text = ChatterboxGetContent(chatterbox,0);
nodeTitle = ChatterboxGetCurrent(chatterbox);

The variable “text” should now hold the line of dialogue you wrote earlier in Yarn. Exciting! But how do we get GameMaker to show it?

In your oText object, create a Draw event. We’ll start with the easy part.

draw_text(100,100,text)

If you run Game Maker now (you remembered to put oText in Room1, right?), your first line of dialogue should appear!

Video games!

… But we wrote two lines. That’s where things get trickier. Right now, Chatterbox has reached the end of this line and is waiting before it shows us the next line of dialogue, so we need to tell it to keep going, or continue. We do that by taking player input. I’m going to use the space bar.

Let’s open up our Step event and add this:

if ChatterboxIsWaiting(chatterbox){
if keyboard_check_pressed(vk_space){
ChatterboxContinue(chatterbox);
text = ChatterboxGetContent(chatterbox,0);
nodeTitle = ChatterboxGetCurrent(chatterbox); }
}

That is, if Chatterbox is waiting, check to see if the player has pressed the space bar. If they have, then continue. That means the Chatterbox will refresh itself and load up the next line of dialogue. Once it’s done that, we can run ChatterboxGetContent again to see what text is on the next line. (If we don’t refresh it, then even though the Chatterbox has changed, our “text” variable will still be the old line!)

If you run GameMaker now, you should be able to press space and advance through your dialogue…except, when you run out of dialogue to show, it’ll just say “undefined.” Now, what you want to happen when dialogue finishes depends a lot on what kind of game you’re making, but for now, we’re actually going to tell GameMaker that once it finishes a Chatterbox, it should destroy oText. And, specifically, we’re going to go back and put that in the Draw event, so that it doesn’t try to draw the undefined text. Your Draw event should now be:

if ChatterboxIsStopped(chatterbox){
instance_destroy(self);
} else draw_text(100,100,text);

Well, that’s great, but what if we want to see the dialogue again? Or what if we have a different node we want to start with? That’s when it’s time for …

Dynamic Entry

Not that kind. Unless…?

We’re going to take a step back here. First off, we’re going to create a second object, called oInit, and put that in Room1. Then, importantly, we’re going to remove oText from Room1.

Now, we take the ChatterboxLoadFromFile from oText’s Create event and put it in oInit’s Create event instead. In other words, oInit is now handling the loading part for us. That frees up oText to do other things. It also means we can learn how to load different nodes. Which means we’ll need to make some! Go back to Crochet and make another node.

I’m the best at names.

For this example, I’m going to tell GameMaker that, if no other dialogue is running (i.e. if oText doesn’t exist), when I press the 1 key, I want it to run the “Start” node, and if I press 2, I want it to run “Node2”. We do this in oInit’s Step event (because it’s the only object in the room right now). (If you don’t recognise some of these scripts, it might be a good idea to look them up in the GameMaker documentation. They’re very useful!)

if !instance_exists(oText){
if keyboard_check_pressed(ord("1")){
with instance_create_depth(0,0,0,oText){
chatterbox = ChatterboxCreate("chatterboxtut.yarn");
// Initialise Chatterbox by jumping to a node ("Start")
ChatterboxJump(chatterbox,"Start");
// Get Content from Chatterbox
text = ChatterboxGetContent(chatterbox,0);
nodeTitle = ChatterboxGetCurrent(chatterbox);
}
}
if keyboard_check_pressed(ord("2")){
with instance_create_depth(0,0,0,oText){
chatterbox = ChatterboxCreate("chatterboxtut.yarn");
// Initialise Chatterbox by jumping to a node
ChatterboxJump(chatterbox,"Node2");
// Get Content from Chatterbox
text = ChatterboxGetContent(chatterbox,0);
nodeTitle = ChatterboxGetCurrent(chatterbox);
}
}

}

Does some of that look familiar? It should, because it’s a straight cut-and-paste from the Create event of oText. (Speaking of which, go ahead and clear out everything from oText’s Create event — it should now be empty.)

Basically, when we press the given key, we make a fresh version of oText that loads up the correct node and plays it, and then destroys itself when it’s done. That means we don’t have to manually make a new Chatterbox for every single node we want to run. And in fact, we can make it even easier. Create a new Script and call it create_text. It’s going to look like this:

function create_text(_node,_file){


with instance_create_depth(0,0,0,oText){
chatterbox = ChatterboxCreate(_file);
// Initialise Chatterbox by jumping to a node
ChatterboxJump(chatterbox,_node);
// Get Content from Chatterbox
text = ChatterboxGetContent(chatterbox,0);
nodeTitle = ChatterboxGetCurrent(chatterbox);
}
}

And then — stay with me here, we’re almost done — go back to oInit and replace the Step event, like so:

if !instance_exists(oText){
if keyboard_check_pressed(ord("1")){
create_text("Start","chatterboxtut.yarn");
}
if keyboard_check_pressed(ord("2")){
create_text("Node2","chatterboxtut.yarn");
}

}

Now if you run GameMaker, the same thing as before should happen, except now all you need to do is run one line of code — create_text(node,filename). That will create an oText object that will run whatever’s in the node and filename that you specify.

You don’t need to attach this script to a simple key press, either. For example…

if position_meeting(_cursor[0],_cursor[1],self) and can_start_dialogue(){
if input_check("interact"){
create_text("getbox","dialogue.yarn");
}
}

Obviously, there’s some custom scripts in there (hi, Input!), but the gist is that it’s a point-and-click — if the player clicks while the cursor is over the object, it starts the text “getbox” in “dialogue.yarn”. (That’s from Schrodinger’s Catgirl, incidentally.) Hopefully that’s enough to get you thinking about how you’d like to implement Chatterbox in your own game!

Wait, What About Branches?

… Oh, yeah. Branches. Probably the whole reason you’re here. Alright, let’s display some branches. Yarn, and thus Chatterbox, calls these options. You can find a more detailed explanation in the Yarn documentation, but the short version is that to make an option, you write this: ->

Going back to Crochet, find your Start node and add some dialogue options. Here’s mine:

Lemi: This is some text.
Lemi: And here's some more text.
-> What about a choice?
Lemi: I guess that's fine.
-> Or maybe this?
Lemi: If you want!
Lemi: And that's that.

The space after the -> is very important! As for those indented lines: those are shortcut options. When the player selects the relevant option, Chatterbox will show the indented line(s) that comes after it, and then skip to after the choice.

Right now, GameMaker doesn’t know what to do if it sees an option, though, so it just won’t draw it. Let’s fix that. In oText’s Draw event, we’re going to make some changes. I’ll show you first, then explain it.

var _x = 100;
var _y = 100;
var _opty = 150;
if ChatterboxIsStopped(chatterbox){
instance_destroy(self);
} else {

draw_text(_x,_y,text);

if ChatterboxGetOptionCount(chatterbox) > 0{
for (var _i = 0; _i < ChatterboxGetOptionCount(chatterbox); _i++){
var _option = string(_i+1) +": " + ChatterboxGetOption(chatterbox, _i);
//_i starts at 0, but we want the choices to be numbered 1–2–3 not 0–1–2, so we +1 to it when we make our string to draw.

draw_text(_x,_opty,_option);
_opty+=30;
}
}
}

Here’s what we changed:

  1. The text positions are now set in variables up the top so we can move them more easily if we need to. Options are drawn 50 pixels underneath the main text.
  2. We check if there are options available in the current Chatterbox with ChatterboxGetOptionCount (which is 0 if there aren’t any).
  3. Then, we use a for-loop to draw each option, adding 30px to the _opty value each time so they appear underneath each other.
  4. We also add a number in front of each option because that’s my personal preference, but you can remove everything from _option except ChatterboxGetOption(chatterbox,_i) if you don’t like it.

If you run GameMaker now, you should be able to get to this step:

Except…GameMaker also doesn’t know how to select an option, and for that we want the Step event.

For Chatterbox, waiting for an option select is different from regular waiting, so we’ll slot our option select code under that if-block, like so:

if ChatterboxIsWaiting(chatterbox){

if keyboard_check_pressed(vk_space){
ChatterboxContinue(chatterbox);
text = ChatterboxGetContent(chatterbox,0);
nodeTitle = ChatterboxGetCurrent(chatterbox);
}

} else {
if (ChatterboxGetOptionCount(chatterbox)) //If Chatterbox is presenting the user with some options, check for that
{
var _select = undefined; // What the user selected.
//You'll need more of these if you have more than three options, of course!
if (keyboard_check_released(ord("1"))) _select = 0;
if (keyboard_check_released(ord("2"))) _select = 1;
if (keyboard_check_released(ord("3"))) _select = 2;
//If we've pressed a button, select that option
//There is almost certainly a more elegant way to do this but this tutorial is so long already...
if (_select != undefined) {
ChatterboxSelect(chatterbox, _select);
text = ChatterboxGetContent(chatterbox,0);
nodeTitle = ChatterboxGetCurrent(chatterbox);
}
}
}

Basically, if there are options, we create a new variable, “_select”. The user fills in that “_select” variable by pressing a number key, and we use that to choose the option to go to. The reason we do this is that later, if we also want mouse or keyboard controls, we can control “_select” using those too! … With some difficulty. UI is hard.

At this point, if we run Game Maker and trigger our dialogue node, we should get something that looks a bit like this…

…which, if you recall, looks like this in our text editor!

Now, this may not be exactly how you want to present your text … but that’s another tutorial. Hopefully this one has given you the tools you need to start experimenting with Chatterbox in your GameMaker project!

Credits: Juju and everyone who worked on Scribble and Chatterbox; Faulty for Crochet; Gleeson, from whose in-progress Chatterbox tutorial I referenced a bunch of this; everyone who made my original tutorial so popular (?!); everyone in the GameMaker Kitchen Discord that cheered me on while I wrote this. Hopefully there are fewer formatting errors in this one.

Follow the studio at @spiderlilygames on Twitter for updates on Schrodinger’s Catgirl, and more tutorials! And, let me know what you’d like to see next! Will I finally finish a Scribblebox tutorial…?!

P.S. If you liked this tutorial and found it useful, I do have a tip jar on Ko-fi. If five people chip in, I’ll commit to making the next tutorial right away. You can include a subject request in your donation message too if you like!

--

--

Spider Lily Studios

Making elegant and heartfelt indie games. Currently: Schrodinger's Catgirl (funded on Kickstarter!)