Integrating dialogues with Flame (part 2)

Lim Chee Keen
5 min readFeb 28, 2024

--

This is a continuation of part 1 of integrating dialogues with Flame and is part of the series Building a 2d-top down RPG with Flutter and Flame.

In part 1 we set up our .yarn dialogue script, YarnProject class and UI for handling the dialogue lines. In this article, we will show the advanced features of the Jenny API to make dialogues more robust. We will detail the following:

  1. Choices
  2. Commands
  3. Variables
  4. User defined commands

We start with choice. To indicate to Yarn that the line has a choice, we use the ‘->’ notation followed by text to be displayed. When the YarnProject class parses this line, it will know to invoke the onChoiceStart callback in the dialogueView class from part 1. Here is an example .yarn script called “test” which has 2 true or false choice options. We initiate the dialogue the same way as stated in part 1.

<<character "Ms Margret" MsMargret>>
title: test
---
MsMargret: Good day to you. You can call me Ms Margert.
MsMargret: I teach Geography at Gomiland Elementary School.
MsMargret: Do you know about sustainability? Let me give you a test.
MsMargret: Question 1 - All plastics can be recycled.
-> True
MsMargret: Incorrect. Only about 75% of plastics can be melted and molded to produce new plastics.
MsMargret: The remaining 25% of plastics do not soften when exposed to heat, making them near-impossible to recycle
-> False
MsMargret: Correct. Only about 75% of plastics can be melted and molded to produce new plastics.
MsMargret: The remaining 25% of plastics do not soften when exposed to heat, making them near-impossible to recycle.
MsMargret: Question 2 - The creator of plastic bags says they were meant to be used only once.
-> False
MsMargret: Incorrect. The inventor of plastic bags was Swedish engineer Sten Gustaf Thulin.
MsMargret: His son said his dad thinks that the idea that people would simply throw these away would be bizarre.
-> True
MsMargret: Correct again! The inventor of plastic bags was Swedish engineer Sten Gustaf Thulin.
MsMargret: His son said his dad thinks that the idea that people would simply throw these away would be bizarre.
MsMargret: No prizes for getting full marks but come see me if you get a code by contributing to sustainability efforts.

In the script above, there are 2 forks in the conversation. The first is when the NPC says: “Question 1 — All plastics can be recycled”. If the player chooses “True”, the dialogue ends with 2 more lines. However if the player chooses “False”, the player will be given 3 more lines with the 3rd line being another set of choices, again either “True” of “False”. The forks are denoted by the extra indent. It looks like this in the Gomiland game.

Incorporating choices in dialogues for a simple quiz

Next, we will take a look at adding commands to the .yarn script. These are predefined functions that can be included to do something when a script is running. You can find the full list here. Below is a script where we want to have an NPC choose from a list of 5 possible dialogues each time we talk to him.

<<character Man>>
<<declare $rand = 0>>
title: man
---
<<set $rand = dice(5)>>
<<if $rand == 1>>
Man : Plastic takes a really long time to decompose.
Man : Using less single use plastics makes the most sense.
<<elseif $rand == 2>>
Man : Have you downloaded the Online Citizen app yet?
<<elseif $rand == 3>>
Man : Reduce, reuse, recycle — every bit counts!
<<elseif $rand == 4>>
Man : Have you been to Gomiland National Park?
Man : It's my favorite place!
<<elseif $rand == 5>>
Man : Learning about how recycling works is interesting.
<<endif>>
===

In the code above, we declared a variable $rand and initialised it to 0. Declare is a command to create a variable as part of this dialogue. Actually “character” is a command as well to declare characters (we just haven’t mentioned it until now). You would notice 2 other commands: “set” and “if”/ “elseif”/ “endif”. These do exactly what you think they do — set a variable and perform if-else logic flows. There is one more feature here, see if you can spot it… it’s a function call. If you wondered what “dice()” is, that is a pre-defined function. These are similar to commands, but written in a more familiar fashion, i.e. a function call. The full list of functions can be found here.

This is the result.

NPC selects from a list of 5 dialogues each time

Last but not least, we will take a look at adding variables and user defined commands to the .yarn script. These 2 are adding in a similar way so we can show these together. This script incorporates everything we have talked about so far and it is the most complex.

title: buy_bag
---
{$friendName}: Hello {$playerName}, your collection bag looks pretty small.
{$friendName}: Want to get a bigger bag for 500 coins?
-> No
{$friendName}: No problem. I know the company that sells them.
{$friendName}: They use recycled materials to make their bags as well!
-> Yes
<<if $coins < 500>>
{$friendName}: Looks like you don't have enough coins. Come back when you do!
<<else>>
<<upgradeBag>>
{$friendName}: Here you go! A bag that can hold 20 pieces of trash.
{$friendName}: You can give your old one to Himiko. She will hand them out to people who don't have one.
<<endif>>
===

Variables are written in the form {$variable_name} and user defined commands are written in the same way you would a regular command (the command “upgradeShoe” is an example). To call this script, we have to add a few more lines of code to the YarnProject.

YarnProject yarnProject = YarnProject()..strictCharacterNames = false;

yarnProject
..variables.setVariable('\$playerName', playerName) // set veriables
..variables.setVariable('\$coins', coinAmount)
..variables.setVariable('\$friendName', friendName)
..commands.addCommand0('upgradeBag', upgradeBag) // set user defined commands
..parse(await rootBundle.loadString(path_to_yarn_file));
DialogueRunner dialogueRunner = DialogueRunner(
yarnProject: yarnProject, dialogueViews: [dialogueControllerComponent]);
await dialogueRunner.startDialogue('buy_bag');

...

void upgradeBag() {
game.gameStateBloc.add(const SetBagSize(20));
deductCoins(game, 500);
}

This is how to add variables and commands to a YarnProject class. Notice that we also set strictCharacterNames to false. This is necessary because our NPC name is dynamic. This is the result.

When player chooses to buy a bigger bag, coins are reduced and bag size is increased

There are 2 points to remember, especially if you are new. The first one is that you have to add an escaped “$” when adding dynamic variables. Second is that you have to add all variables and UserDefinedCommands (your functions) to the YarnSpinner object before parsing the .yarn dialogue file.

Added great dialogue is key to a successful RPG. Flame’s Jenny API can create interesting dialogues with choices and custom functions. Hope this tutorial helps you create memorable conversations with NPCs in your games!

--

--

Lim Chee Keen

Former Navy Captain Turned Software Engineer | Flutter & React developer | ML & AI programmer | Co-founder for Group Buy service