Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Yarn Spinner is a tool for writers. In this section, you'll learn the syntax of Yarn, and learn how to write Yarn scripts for use in your game. You'll also learn how to use the various tools that are available for creating your content.
You'll need an editor before you can write Yarn scripts, so we recommend checking out Editing with VS Code before going too far.
The Yarn language is a full programming language, which means it has support for writing code that let you control how the dialogue in your game works. In this section, you'll learn how to use variables to control your dialogue.
Variables store information. Variables can store one of three types of information: numbers, strings, and booleans.
Number
Any whole or decimal number
1, 2.5, 3468900, -500
String
Any sequence of letters, numbers and other characters, enclosed in quotes.
"Hello", "✓", "A whole sentence."
Boolean
Either the value true or the value false.
true, false
Every variable has a name. In Yarn Spinner, all variable names start with a dollar sign ($
).
Declaring a variable means telling Yarn Spinner that a variable exists, what it's meant to be used for, and what initial value it has.
To declare a variable, you use the <<declare>>
statement:
If you add a comment with three slashes ///
above a declaration, editor tools like the Visual Studio Code extension will use it to explain what a variable is when it's used elsewhere.
For example, here's a variable that has the following declaration:
When you hover the mouse over it in Visual Studio Code, a popup will appear that shows the description:
If you use a variable without declaring it, Yarn Spinner will try to figure out what type it should have based on how it's being used in your scripts, as well as what initial value it should have - zero for numbers, false for booleans, and blank text for strings. When a variable is not declared, we call that an implicit declaration.
If you declare a variable, you can make sure that the type of the variable is what you intend it to be. Declaring a variable also lets you control what the variable's initial value is, and lets you add descriptive comments that explain the purpose of the variable to other people (or to your future self!)
You put information into a variable by using the <<set>>
command. For example, the following code puts a string, "Hello, Yarn!"
, into a variable called $greeting
:
As with node titles, variable names must not contain spaces. While they can contain a range of different characters the first character must be a letter. In general your variables will be made up of only letters, numbers and underscores.
Each variable can only store one type of value. Variables can change their value at any time, but they can never change their type.
For example, the following code will work:
This works because while the value of each of the variable changes, the type doesn't. However, the following code will not work:
In earlier versions of Yarn Spinner, variables could also be null
, which represented "no value". Starting with Yarn Spinner 2.0, variables are never null
. All variables are required to have a value.
You can work with the values inside variables. For example, numbers can be multiplied, strings can be added together, and boolean values can have logical operations (like and and or) applied to them. When values are used together like this, it's called an expression.
An expression needs to be a single type. You can't work with values of different types in a single expression. For example, the following code will not work:
Yarn Spinner provides built-in functions for converting between certain types:
The string
function converts values of any type into a string.
The number
function converts values of any type into a number (if it can be interpreted as one.)
The bool
function converts values of any type into a boolean value (if it can be interpreted as one.)
Yarn Spinner supports the following logical operators. Most of these have multiple ways being written:
Equality: eq
or is
or ==
Inequality: neq
or !
Greater than: gt
or >
Less than: lt
or <
Less than or equal to: lte
or <=
Greater than or equal to: gte
or >=
Boolean 'or'': or
or ||
Boolean 'xor': xor
or ^
Boolean 'not': not
or !
Boolean 'and': and
or &&
Addition: +
Subtraction: -
Multiplication: *
Division: /
Truncating Remainder Division: %
Brackets: (
to open the brackets and )
to close them.
Yarn Spinner follows a fairly standard order of operations, and falls back to using left to right when operators are of equivalent priority.
The order of operations is as follows:
Brackets
Boolean Negation
Multiplication, Division, and Truncating Remainder Division
Addition, Subtraction
Less than or equals, Greater than or equals, Less than, Greater than
Equality, Inequality
Boolean AND, Boolean OR, Boolean XOR
To show the contents of a variable, you put it inside braces ({ }
) inside a line. The value of that variable will appear in its place.
For example:
Yarn Spinner doesn’t manage the storage of information in variables itself. Instead, your game provides a variable storage object to Yarn Spinner before you start running dialogue.
When Yarn Spinner needs to know the value of a variable, it will ask the variable storage object you’ve given it. When Yarn Spinner wants to set the value of a variable, it will provide the value and the name of the variable. In this way, your game has control over how data is stored.
The specifics of how variables need to be stored will vary depending on what game engine you're using Yarn Spinner in. To learn more about variable storage in Unity, see Variable Storage.
A function is a block of code that provides a value to your Yarn scripts, which you can use in , or store in .
In Yarn Spinner scripts, functions perform two main kinds of task:
Functions let you get values that change over time, or that depend on other values. For example, the random
function returns a different random number every time you call it.
Functions let you get data from your game back into your scripts.
You call a function inside an expression. For example:
Yarn Spinner comes with several built-in functions for you to use.
visited(string node_name)
visited
returns a boolean value of true
if the node with the title of node_name
has been entered and exited at least once before, otherwise returns false
. Will return false
if node_name
doesn't match a node in project.
visited_count(string node_name)
visted_count
returns a number value of the number of times the node with the title of node_name
has been entered and exited, otherwise returns 0
. Will return 0
if node_name
doesn't match a node in project.
format_invariant(number n)
format_invariant
returns a string representation of n
, formatted using the invariant culture. This is useful for embedding numbers in commands, where the command expects the number to be formatted using the invariant culture. For example, <<give_gold {$gold}>>
, which might end up as give_gold 4,51
in German, but give_gold 4.51
in English, can now be <<give_gold {format_invariant($gold)}>>
, which will always be give_gold 4.51
.
random()
random
returns a random number between 0 and 1 each time you call it.
random_range(number a, number b)
random_range
returns a random number between a
and b
, inclusive.
dice(number sides)
dice
returns a random integer between 1 and sides
, inclusive.
For example, dice(6)
returns a number between 1 and 6, just like rolling a six-sided die.
round(number n)
round
rounds n
to the nearest integer.
round_places(number n, number places)
round_places
rounds n
to the nearest number with places
decimal points.
floor(number n)
floor
rounds n
down to the nearest integer, towards negative infinity.
ceil(number n)
ceil
rounds n
up to the nearest integer, towards positive infinity.
inc(number n)
inc
rounds n
up to the nearest integer. If n
is already an integer, inc
returns n+1
.
dec(number n)
dec
rounds n
down to the nearest integer. If n
is already an integer, dec
returns n-1
.
decimal(number n)
decimal
returns the decimal portion of n
. This will always be a number between 0 and 1. For example, decimal(4.51)
will return 0.51
.
int(number n)
int
rounds n
down to the nearest integer, towards zero.
This is different to floor
, because floor
rounds to negative infinity.
You can define your own custom functions in Yarn Spinner. For more information, see .
Functions are not intended to be a way for you to send instructions to your game. For that purpose, you should use .
In particular, functions are not guaranteed to be called in the same order as they appear in your code, or even be called at all if Yarn Spinner believes the result can be cached. As much as possible, custom functions should be , and have no side effects besides returning a value based on parameters.
A tag is a piece of information that you can add to content in Yarn Spinner that adds additional context or detail about that content.
There are two places you can add tags in Yarn scripts: you can add them to nodes, and you can add them to lines. Tags aren't shown to the user; instead, they're used by your game, or by Yarn Spinner itself.
Tags can be added to the end of lines and options. Tags on lines start with a hash symbol (#
), and cannot contain spaces. You can add as many tags as you like to line, but they must all be on the same line in the script.
Here's an example of a line with two tags:
Tags that you add to a line can be accessed from your game. The way that you access them depends on your game engine. For example, to access them in a Unity game, you use the LocalizedLine.Metadata property.
Some tags are used by Yarn Spinner itself, while all others are used by your own code, so it's up to you what content they should have, and how to handle them.
Certain tags are used internally by Yarn Spinner, or are added for you if they don't exist.
#lastline
The Yarn Spinner compiler adds a #lastline
tag to every line that comes just before a set of options.
For example, the following excerpt:
is treated as though it had been written as:
In a Unity game, you can use this tag in a custom Dialogue View to be notified ahead of time when the player is about to be shown options.
The #lastline
tag will not be automatically added if there is any content, such as an if
statement or a command, between the line and some options. In these situations, you may wish to manually add the tag yourself.
#line
The #line
tag uniquely identifies a single line or option across all of your game's dialogue. This is used to identify lines for localisation. Every line's #line
tag must be unique. If a line or option doesn't have a #line
tag, it will be automatically added for you.
Here's an example of some Yarn script with #line
tags:
For more details (including what the tag should look like), see Adding Line IDs.
Nodes can also have tags, which you can use to add labels that describe the node.
Node tags they work a bit differently than line tags: they are defined in the header with the tags
key, and they don't have to begin with a hash symbol (#
).
Here's an example of a node with two tags:
You can access the tags on a node via the Dialogue object's GetTagsForNode method.
The metadata of a line is only composed of tags. Because of this, you may find that the Yarn Spinner code and documentation refer to line tags and line metadata interchangeably.
Nodes can have other metadata in their headers. This metadata isn't exposed through the API, which means it's mostly used to store additional information for whoever is writing the Yarn dialogue or for editors to make use of.
However, currently there is one header that defines specific behavior within the Yarn Spinner compiler: the tracking
header.
tracking
headerNodes can track whether they have already been visited during the game. For this to work, the Yarn Spinner compiler needs to add some special code to the node. To avoid creating this code for nodes that don't need it, the compiler only adds this code if it finds a call to the visited()
function with the node name in it.
In some cases, you may need the compiler to add this special code to a node even if no corresponding visited()
call exists. To direct the compiler to do this, include the tracking
header with the value of always
:
Additionally, using a value of never
instructs the compiler to never add this special code to the node. If you use the visited
function with a node set to never use tracking, it will always return false
.
For more information on visit tracking, see the documentation for Functions.
Tags and metadata may seem very complicated at first, and their uses may not be clear. The following example use cases explain how they can be used in your game. Keep in mind that this is not an exhaustive list of use cases.
Yarn provides markup to let you change attributes for specific parts of a line. In case most of your attributes apply to entire lines (for example, the color of a line), it may be easier to just use tags instead.
The #lastline
tag can be used to display the last line of dialogue along with any options. This is handled within your code by checking if a line has the #lastline
tag, and if it does, storing it before continuing with the execution of the Yarn dialogue.
Since metadata isn't shown directly to the player, you can use metadata for any internal workflows or tooling. For example, instead of tracking lines that need to be revised outside the Yarn files (which could lead to syncing problems), you could add line tags (such as #needsrevision) to the appropriate lines directly in the Yarn files, and process these lines as part of an internal tool or workflow. The Unity integration automatically generates a CSV file with all lines that contain metadata, making this super easy!
As referenced before, the Yarn Spinner integration for Unity uses line tags to link localised dialogue lines. This is better explained in the Localization and Assets section.
Aside from that, every piece of metadata can be used by translators and adapters to help them understand how the text is being used, thus leading to better localised text.
Some games may require that certain lines of dialogue are displayed somewhere other than the dialogue window (for example, as flavor text for an item description, or in an item that acts as a log). Instead of manually duplicating these lines (which adds overhead during development and localisation), tags can be used along with code that checks for the tags and duplicates the lines while the game is running.
if
statementsIn addition to storing information, variables are useful for controlling what's shown to the player. To do this, you use if
statements.
An if
statement allows you to control whether a collection of content is shown or not. When you write an if
statement, you provide an expression, which is checked; if that expression evaluates to a "true" value, then all of the content in between the <<if>>
and <<endif>>
statements are run.
For example, consider the following code:
This example will set a variable, $gold_amount
, to 5. It will then show the line "I'd like to buy a pie!", and before it continues, it will check to see if $gold_amount
is less than 10. If that's the case (which it will be!), the line "Well, you can't afford one!" will run.
elseif
and else
You can use the elseif
and else
statements to handle different situations in an if
statement.
An elseif
statement has an expression that gets checked if the if
statement, or any previous elseif
statements, don't run.
An else
statement doesn't have an expression, and runs if the if
and any elseif
don't run.
For example:
This script will show different lines depending on the value of $gold_amount
. The checks are done from top to bottom, which means that in order for an elseif
or else
to run, all of the checks above it have to have failed.
If it's less than 10, the line "Well, you can't afford one!" will run.
Otherwise, if it's less than 15, the line "You can almost afford one!" will run.
Otherwise, the line "Here you go!" will run.
The expression used in an if
and elseif
statement must result in a boolean value (that is, true or false.) For exame,<<if 1>>
isn't allowed, but <<if 1 == 1>>
is.
When presenting options to the player, you may want to make some options not available. You can do this by adding a condition to the option.
For example, if you have a variable that tracks your player's "reputation points", called $reputation
, you might want to make certain options only available if the value of $reputation
is high enough.
Conditions on options are done by adding an if
statement to the end of the option. They look like this:
When Yarn Spinner runs this collection of options, it will check the expression inside the if
statement. If the expression is false
, then the option will be marked as unavailable.
Yarn Spinner always delivers every option in an option group to the game; it's up to the game to decide what to do with options that are marked as unavailable.
For example, an unavailable option might be shown to the user, but not selectable, so that the user can see that they could have been able to say that if circumstances had been different.
Now that you know how to work with nodes, lines, options and variables, there's one last part of the Yarn language to learn about: commands.
In Yarn Spinner, you can send instructions to your game through commands. Commands look like this:
Commands are sent to your game's Dialogue Runner, just like lines and options are. Commands are not shown to the player directly; instead, they're used for things like stage directions.
Yarn Spinner comes with some built-in commands; however, to get the most usefulness out of them, you'll want to define your own custom commands that make your game do what you need to.
There are two built-in commands in Yarn Spinner: wait
, and stop
.
wait
The wait
command pauses the dialogue for a specified number of seconds, and then resumes. You can use integers (whole numbers), or decimals.
stop
The stop
command immediately ends the dialogue, as though the game had reached the end of a node. Use this if you need to leave a conversation in the middle of an if
statement, or a shortcut option.
You can create your own commands, so that your scripts can send directions to your game. For more information on how to create them in Unity games, see Creating Commands and Functions.
In Yarn Spinner, all of your dialogue is stored in .yarn
files. Yarn files are just plain text files, which you can edit in any text editor.
Yarn Spinner files contain nodes. Nodes are where you put your dialogue. You can have as many nodes as you link in a file. Nodes are used to separate out parts of the story, and make it easier to manage longer stories and branching.
Each node has, at the very minimum, a collection of headers, and a body. All nodes have at least one header, which is the title. The title is the name of the node, and the body contains the Yarn script that contains your game's dialogue.
The title of a node is important, because your game uses node titles to tell Yarn Spinner which node to start running. You also use the title of a node when you want to jump to another node.
Node titles are not shown to the player.
Node titles must start with a letter, and can contain letters, numbers and underscores.
So FirstNode, First_Node and Node1 valid, but First Node and 1stNode are not.
Node names cannot contain a . (period).
Node names were able to contain a period in Yarn Spinner 1, and if your Yarn Spinner 1 .yarn
scripts have periods in the node names, you can use the upgrader script to convert them (and all jumps and options related) to use a _
(underscore) instead.
If you're using a text editor to write Yarn scripts, you'll need to write the node's header.
If you're using a graphical editor to write Yarn scripts, like Yarn Editor, it will handle this for you, and you can skip this section.
The plain-text version of a Yarn node looks like this:
In this example, the node's title is Node_Title
, which is set on the first line in the title
header. You can also add any other headers that you want.
Node headers can contain any number of lines with the structure key: value
. This can be used to store additional information, such as the location the conversation is taking place.
The ---
marker indicates where the body begins. After this point, you can put all of your Yarn script.
The ===
marker indicates where the node ends; after this point, you can begin another node.
The body of a node is made up of three different kinds of content: lines, commands, and options.
When you write Yarn Spinner dialogue, just about every line of text that you write in a node is a line. When a node is run, it runs each line, one at a time, and sends it to your game.
A line of dialogue is just the thing you want some entity or character to say, usually beginning with the name of the entity speaking.
For example, consider the following Yarn code from Night in the Woods:
When this code is run in the game, it looks like this:
Yarn Spinner sends each of these lines, one at a time, to the game. The game is responsible for taking the text, and presenting it to the player; in the case of Night in the Woods, this means drawing the speech bubble, animating each letter in, and waiting for the user to press a key to advance to the next line.
Lines of dialogue can contain just about any text, except for some special characters that Yarn Spinner uses to add extra information to a line.
If there is a set of characters without spaces before a colon (:) at the beginning of the line, Yarn Spinner will mark that as the name of the character. This information will then be passed to your game, so that you can change the way that lines are shown based on the character who's saying them. For example:
When you want to let the player decide what to say, you use an option. Options let you show multiple potential lines of dialogue to the player, and let the player select one.
Options are marked with a ->
symbol. You write as many options as you'd like the player to see, and the player chooses one of them. The content of the option is like any other line of dialogue.
For example, consider the following code:
In this example, the line "Hi there! What do you feel like doing today?" will run. The player will then be given the choice to say either "I want to go swimming", or "I'd prefer to go hiking".
Shortcut options can have their own lines, which are run when the option is selected. If a different option is selected, they won't run. To write this, indent the lines that belong to a shortcut option.
In the following code, different lines will run based on which of the two shortcut options are selected.
This script will start with the line, "Hi there! What do you feel like doing today?". The player then has the choice of saying either "I want to go swimming", or "I'd prefer to go hiking". Depending on their choice, either the line "Okay, let's go swimming" or "Cool, we'll go hiking then". Finally, no matter what was selected, the line "Sounds good!" will run.
In addition to containing lines, options can also contain other options.
You can nest options as much as you like. However, this can get a bit challenging to read. It's often a good idea to use the <<jump>>
command to jump to a different node:
Separating dialogue segments into nodes can aid in writing neater files that are easier to edit as they grow.
Sometimes it makes sense for the options presented or the outcomes of selecting different options to vary based on other things the player has done or said up until this point. This requires the use of logic and variables, which we'll discuss in the next section.
Markup allows you to add attributes into your text, like [a]hello[/a]
. These attributes can be used by your game to do things like change the formatting of the text, add animations, and more.
When text is parsed, the tags are removed from the text, and you receive information about the range of the plain text that the attributes apply to.
Attributes apply to ranges of text:
Yarn Spinner will take this text, and produce two things: the plain text, and a collection of attributes. The plain text is the text without any markers; in this example it will be:
Attributes represent ranges of the plain text that have additional information. They contain a position, a length, and their name, as well as their properties.
In this example, a single attribute will be generated, with a position of 4, a length of 5, and a name of "wave".
Attributes are opened like [this]
, and closed like [/this]
.
Attributes can overlap:
You can put multiple attributes inside each other. For example:
You can close an attribute in any order you like. For example, this has the same meaning as the previous example:
Attributes can self-close:
A self-closing attribute has a length of zero.
The marker [/]
is the close-all marker. It closes all currently open attributes. For example:
Attributes can have properties:
This attribute 'wave' has a property called 'size', which has an integer value of 2.
Attributes can have short-hand properies, like so:
This is the same as saying this:
This attribute 'wave' has a property called 'wave', which has an integer value of 2. The name of the attribute is taken from the first property.
Properties can be any of the following types:
Integers
Floats
'true' or 'false'
Strings
Single words without quote marks are parsed as strings. For example, the two following lines are identical:
If a self-closing attribute has white-space before it, or it's at the start of the line, then it will trim a single whitespace after it. This means that the following text produces a plain text of "A B":
If you don't want to trim whitespace, add a property trimwhitespace
, set to false
:
You may want to show text containing the [
and ]
characters to your player. To prevent the markup parser from treating as special characters, you can escape them. Text that has been escaped will be treated as plain text, and will not be interpreted by the parser.
There are two ways to escape your markup: escaping single characters, and using the nomarkup
attribute.
[
and ]
charactersIf you need to escape a single square bracket character, put a backslash \
in front of it:
This will appear to the player as:
The backslash will not appear in the text.
If you need to show a blackslash in your text, use two blackslashes:
This will appear as:
nomarkup
AttributeIf you want to escape a longer run of text, or if you have many square brackets, escaping a single character at a time can be cumbersome. In these cases, you may want to escape an entire region of text, using the nomarkup
attribute. This attribute makes the parser ignore any markup characters inside it.
If you want to include characters like [
and ]
, wrap them in the nomarkup
attribute:
This will appear as:
character
AttributeThe character
attribute is used to mark the part of the line that identifies the character that's speaking.
Yarn Spinner will attempt to add this character for you, by looking for character names in lines that look like this:
The markup parser will mark everything from the start of the line up to the first :
(and any trailing whitespace after it) with the character
attribute. This attribute has a property, name
, which contains the text from the start of the line up to the :
. If a :
isn't present, or a character
attribute has been added in markup, it won't be added.
This means that the example above is treated the same as this:
You can use this to trim out the character names from lines in your game.
Certain attributes in Yarn Spinner's markup are "replacement" markers, which Yarn Spinner uses to insert or replace text based on the value of a variable. There are three built-in replacement markers:
The select
marker uses the value of a variable to choose an outcome.
The plural
marker uses the value of a number to decide on the plural class for that number.
The ordinal
marker uses the value of a number to decide on the ordinal class for that number.
All three of these markers have a property called value
, and use this to decide what text should be used in the line.
select
The select
marker is the simplest of the built-in replacement markers. It takes the value of the value
property, and uses that to choose a replacement.
It's especially useful for when you need to insert a gendered pronoun in a line:
plural
and ordinal
The plural
and ordinal
markers take a number in its value
property, and use that to determine the plural or ordinal number class of that value.
Different languages have different rules for how numbers are pluralised.
In many languages, the term you use to refer to a thing depends on the the number of that thing. This is known as a plural class: in English, you can have one apple, but many apples, and you have have one mouse, but many mice.
However, the rules vary significantly across different languages. English has two: "single", and "other". However, for example, Polish has multiple.
In English, you say "one apple, two apples, five apples".
In Polish, you say "jedno jabłko, dwa jabłka, pięć jabłek".
Notice how the Polish word for "apple", "jabłko", takes multiple forms as the number changes, whereas it takes two forms in English.
In Yarn Spinner, individual lines are replaced depending on the user's locale, but the logic surround them is not. This means that, if you want to be able to translate your game into multiple languages, you can't write Yarn code like this:
If you did it this way, the logic would only work for languages that have the same rules for plurals as English. (There are several of them that do, but far more that don't.)
Complicating this further, there are two main kinds of plural classes: cardinal plural classes, and ordinal plural classes.
Cardinal plural classes are the kind we just saw (for example, "one apple, two apples").
Ordinal plural classes refer to the positioning of a thing; in English, ordinal numbers are things like "1st, 2nd, 3rd."
As with cardinal plural classes, different languages have different ordinal plural classes.
plural
and ordinal
Yarn Spinner is able to take a number and the user's current locale, and determine the correct cardinal or ordinal plural class of that number, for that locale. You can then use the plural class to decide on what text to show.
plural
and ordinal
have a property called value
, just like select
. They then have a property for each of the current locale's plural classes. These can be:
one
two
few
many
other
The two markers differ based on what kind of plural class they work with:
plural
selects a number's cardinal plural class.
ordinal
selects a number's ordinal plural class.
Not every language uses every category; for example, English only uses "one" and "other" for cardinal plural classes.
For each of these properties, you provide the text that should appear.
For example:
You can include the actual value in the resulting text by using the %
character. This character will be replaced with the value provided to the value
property:
The ordinal
marker works similarly, but uses the ordinal plural class: