Only this pageAll pages
Powered by GitBook
Couldn't generate the PDF for 571 pages, generation stopped at 100.
Extend with 50 more pages.
1 of 100

Yarn Spinner 2.1

Loading...

Writing Your Dialogue

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Working With Unity

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Unity Sample Projects

Loading...

Loading...

Loading...

Guides

Loading...

About

Loading...

Loading...

Loading...

API Documentation

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Writing in Yarn

Yarn Spinner is a tool for writers. In this section, you'll the syntax of Yarn, 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.

Editing with VS Code

Learn about writing with the Yarn Spinner Visual Studio Code Extension.

The preferred way of writing your Yarn Spinner code is with the official Yarn Spinner Visual Studio Code Extension.

The official extension adds syntax highlight to VS Code's text editor, as well as a graph view that displays the nodes, and relationships between the nodes.

VS Code editing a Yarn script

If you've never used to Visual Studio Code before, head to their official website and install it for your operating system and platform, and then continue to Installing the Extension.

Visual Studio Code is a powerful, flexible, open source code editor for Windows, macOS, and Linux. It supports extensions, which allow it to perform a wide range of useful tasks. The Yarn Spinner Extension is one of these.

Installing the Extension

If you're already familiar with VS Code and VS Code extensions, you can just install the extension secretlab.yarn-spinner and you'll be ready to go.

Once you've got Visual Studio Code (often abbreviated to "VS Code", or "Code") installed on your system, you'll want to install the Yarn Spinner extension. To install the Yarn Spinner Extension:

  1. Launch Visual Studio Code.

  2. Open the Extensions view. To do this, select the blocks symbol from the left-hand sidebar, or press Command+Shift+K on macOS, or Control+Shift+X on Windows or Linux.

  3. Search for the Yarn Spinner Extension. With the Extensions view open, type "Yarn Spinner" into the search field.

  4. Install the Extension. Once the results have loaded, click "Install" next to the Secret Lab-provided Yarn Spinner Extension.

That's all you need to do to install the extension! You're ready to write and edit Yarn scripts with Visual Studio Code.

The Yarn Spinner Extension

Writing Yarn in VS Code

With the Yarn Spinner Visual Studio Code Extension installed, you can make new Yarn files, and edit existing ones, right inside the VS Code editor.

Making a new .yarn file

You can use the File menu -> New file command to make a new empty file. Simply save the file with a .yarn extension to activate the Yarn Spinner Extension features.

Opening an existing .yarn file

You can open any existing .yarn file, or collection of .yarn files, using VS Code:

With a .yarn file open, you can edit it in the text editor. The chosen .yarn file(s) will open, and you'll be able to work with them, edit them, and save them as needed:

You can show and hide the Graph View. If you click the "Show Graph" button, in the top right-hand corner, the Graph View will appear:

You can use the "Add Node" button to add new nodes. New nodes will appear in the Graph View, and in the text editor:

Links (for example, using the jump commands) between nodes will be visualised in the Graph View. You can use the edit button on a node in the Graph View to jump to the appropriate location in the text editor, and you can rearrange the nodes visually in the Graph View for ease of understanding the relationships between areas of your script:

You can also use the "Jump to Node" button to move to a specific node in the Graph View. This allows you to easily move between areas of your text script. Nodes in the graph view can be rearranged for convenience; this won't impact the text version of the yarn script other than updating the position (below the node title).

Start Here

Learn what you need to to get started with Yarn Spinner

Welcome to Yarn Spinner!

is the friendly tool for writing dialogue in games. It's easy for writers to use, and has powerful features for programmers.

Yarn Spinner is a dialogue system that lets you write interactive conversations in a simple, screenplay-like format, which can be loaded into your game and run.

When a conversation is running, Yarn Spinner sends your game lines of dialogue to show, options to let the player choose from, and commands to make things happen in your scene.

Yarn Spinner has been used in a number of critically acclaimed games, including , , , and many more.

It's free to download and use for free and commercial games, and is open source under the terms of the .

Yarn Spinner 2.1 is our latest release!

This documentation is still in active development. If you see an issue, please .

Yarn Spinner is made up of two things:

  1. Yarn files that contain the script for your game, and;

  2. The Yarn Spinner framework for your chosen game engine or method of delivery.

This documentation is for Yarn Spinner 2.1. You can find the old Yarn Spinner 1.0 documentation , if you still need it.

To use Yarn Spinner, you will need:

  1. An editor to write and edit .yarn scripts with. We recommend using the official Yarn Spinner Visual Studio Code extension, together with Visual Studio Code.

    • You can find details on installing VS Code and the official Yarn Spinner Visual Studio Code Extension here:

  2. A copy of Yarn Spinner for your game engine of choice. Currently, Yarn Spinner supports Unity, so you'll need Yarn Spinner Unity.

    • You can learn more about Yarn Spinner Unity here:

  3. A .yarn script (or a few of them). These will be the narrative, or bits of narrative, that Yarn Spinner uses to display and deliver your dialogue.

    • You can learn more about writing in Yarn here:

Yarn Spinner currently only officially supports Unity. There are community projects for other game engines, and we're officially working on more. Stay tuned for updates.

You can also learn more about the various , as well as here, if you'd like.

If you'd like to join the wider Yarn Spinner community, we also have a .

Selecting an existing .yarn file
The .yarn file open
The Graph View
The Graph View will show all nodes in the active .yarn file
Links between nodes in Visual Studio Code.
Yarn Spinner
Night in the Woods
A Short Hike
Lost in Random
MIT License
file an issue on GitHub
here
Editing with VS Code
Overview
Writing in Yarn
GitHub Repositories
The Team
Yarn Spinner Discord

Overview

Yarn Spinner for Unity is the set of components and scripts that make Yarn Spinner work inside a Unity project.

In this section, you’ll learn how to install, set up, and work with Yarn Spinner for Unity.

Yarn Spinner for Unity works with Unity version 2019.4 and later.

Components

Learn about the Unity components that you use when working with Yarn Spinner for Unity.

Yarn Spinner for Unity is made up of a number of components. The most important of these are the Dialogue Runner, which loads and runs your scripts, and the Dialogue Views that show content to your player.

In this section, you'll learn about how to work with each of these.

Audio Line Provider

Audio Line Provider is a Line Provider that fetches localized text for a line of dialogue, as well as a localized AudioClip.

Audio Line Provider will automatically use Addressable Assets, if the Addressables package is installed in your Unity project and

Inspector

Property
Description

Text Language Code

The language that the Audio Line Provider should use to fetch localised text for.

Audio Language

The language that the Audio Line Provider should use to fetch localised audio clips for.

C#

Any

Property in BuiltinTypes

Summary

Gets the type representing any value.

public static IType Any { get; };

Boolean

Property in BuiltinTypes

Summary

Gets the type representing boolean values.

public static IType Boolean { get; };

String

Property in BuiltinTypes

Summary

Gets the type representing strings.

public static IType String { get; };

Importing Yarn Files

There are two important kinds of files you'll use when working with Yarn Spinner for Unity:

  • Yarn Scripts are files that contain your written dialogue.

  • Yarn Projects are files that link all of your scripts together, and are used by the Dialogue Runner.

A Yarn Script, a string table, and a Yarn Project in Unity.

Flow Control

if statements

In 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

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.

Conditional Options

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 , , and , there's one last part of the Yarn language to learn about: commands.

Commands

In Yarn Spinner, you can send instructions to your game through commands. Commands look like this:

Commands are sent to your game's , 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 that make your game do what you need to.

Built-in Commands

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.

Making Your Own Commands

You can create your own commands, so that your scripts can . For more information on how to create them in Unity games, see .

Upgrading Yarn Scripts

Upgrading your Yarn Spinner 1.x scripts to Yarn Spinner 2.x is easy.

The Yarn Spinner Console (ysc) can upgrade your older .yarn scripts to the latest syntax.

The Yarn Spinner Console (ysc) is in heavy development, and is likely to have its own complete section here, in the documentation, before long. Until then, find further initial documentation via .

To upgrade your scripts:

Install the version of Yarn Spinner Console that matches your version of Yarn Spinner. You can do this via the . Choose Releases in the sidebar, and download the appropriate binary for your platform. If you just want the latest release, you can find it .

Run the upgrader on your scripts. To upgrade, you must run ysc on your command-line/terminal with the upgrade parameter and a path to a yarn file:

You can also run the upgrade on multiple scripts at once. To do this, you can pass in as many .yarn files as you want:

By default, ysc will replace the existing .yarn files you've passed in, as part of the upgrade. If you don't want it to do this, either make sure you have a backup, or pass in the --output-directory option, after the upgrade parameter. For example:

Options List View

Learn about Options List View, a Dialogue View that displays a list dialogue options.

Options List View is a that presents a list of options in a list.

When this view receives options from the Dialogue Runner, it creates an instance of the prefab you specify in the Option View Prefab property, and adds it as a child of the options list view.

Options List View only displays options, and doesn't display lines. You can use an additional Dialogue View to handle these, like a or a custom Dialogue View of your own.

Inspector

Property
Description

Variable Storage

Variable Storage components are responsible for storing and retrieving the values of variables in your Yarn scripts. When a Yarn script needs to get the value of a variable, it asks the Variable Storage for it; when a Yarn script sets the value of a variable, the Variable Storage is given the value.

Each game has different requirements for how variables are stored, which means that Yarn Spinner doesn't make any assumptions how the information is actually stored on disk. Instead, you can create your own custom Variable Storage script that implements the methods that Yarn Spinner needs.

If you don't have a game save system, you can use the component. This is a simple Variable Storage component that's built into Yarn Spinner.

The In-Memory Variable Storage stores everything in memory; when the game ends, all variables that have been stored are erased.

If you don't connect a Variable Storage to your Dialogue Runner, it will create an In-Memory Variable Storage when the game starts, and use that.

Custom Variable Storage Components

Every game's data storage requirements are different. For this reason, Yarn Spinner is designed to make it straightforward to create your own custom component for managing how Yarn scripts store and load variables in ways that work with the other parts of your game.

Custom Variable Storage components are subclasses of the abstract class VariableStorageBehaviour. To implement your own, you need to implement the following methods:

Line Provider

Line Providers are components that are responsible for taking the objects that the produces, and fetches the appropriate localised content for that line. Line Providers produce objects, which are sent to the Dialogue Runner's .

When a Yarn script runs, the Dialogue Runne produces Line objects. These objects contain information about the line, but not the text of the line itself. This is because it's the responsibility of the game to load the user-facing parts of the line, including the text of the line in the player's current language setting, as well as any other assets that may be needed to present the line (such as audio files for voiceover.)

Yarn Spinner comes with two built-in types of line providers:

  • is a Line Provider that fetches the text of a line, given a language to use.

  • is a Line Provider that fetches the text of a line as well as an , given languages to use.

If you don't set up a Line Provider for a Dialogue Runner, it will automatically create a Text Line Provider, and configure it to use the user's current language.

Text Line Provider

Text Line Provider is a Line Provider that fetches localized text for a line of dialogue, given the user's language.

Inspector

Property
Description

The Team

The Yarn Spinner project was started by Jon Manning, co-founder of .

Yarn Spinner is now maintained by , together with freelance game developer , Secret Lab colleague , and researcher and freelancer , together with a huge community of open source contributors. ❤️

You can .

Yarn Spinner Timeline

  • v0.9 was the first public release on 9 October 2015

  • v1.0 was released on 8 January 2020

  • v2.0 was released on 20 December 2021

  • v2.1 was released on 17 February 2022

About Secret Lab

is an independent games and creative technology studio located in Hobart, Australia.

We’ve been enthusiastically building video games, apps, and technology to showcase culture, history, arts, and narrative experiences since 2008.

We’re the proud creators of Yarn Spinner, and are currently working on the BAFTA- and IGF-winning Night in the Woods.

You can hire us to build your video games, interactive experiences, fancy apps, or to add features to, or support your integration of Yarn Spinner.

Community Projects

Learn about Yarn Spinner's amazing community projects, by the community, because they're community projects.

Community projects are written by our wonderful community. These are not supported or maintained by the Yarn Spinner team, but you might find them useful.

If you've written a high-quality project that works with Yarn Spinner, please get in touch via or ! We'd love to show it off. ❤️

  • , by : An implementation of a Language Server for the Yarn Spinner compiler, designed for use with Visual Studio and Visual Studio Code.

  • , by : A visual editor for writing Yarn script.

  • , by : An implementation of the Yarn Spinner compiler in GDScript.

  • , by : An implementation of Yarn Spinner for the game engine, based on GDYarn.

  • , by : A Visual Studio Code extension that embeds the Yarn Classic editor.

Number

Property in

Summary

Gets the type representing numbers.

Dialogue(Yarn.IVariableStorage)

Constructor in

Summary

Initializes a new instance of the class.

Parameters

Name
Description

UnloadAll()

Method in

Summary

Unloads all nodes from the Dialogue.

Library

Property in

Summary

Gets the that this Dialogue uses to locate functions.

Remarks

When the Dialogue is constructed, the Library is initialized with the built-in operators like + , - , and so on.

IsActive

Property in

Summary

Gets a value indicating whether the Dialogue is currently executing Yarn instructions.

LogDebugMessage

Property in

Summary

Invoked when the Dialogue needs to report debugging information.

NodeCompleteHandler

Property in

Summary

Gets or sets the that is called when a node is complete.

PrepareForLinesHandler

Property in

Summary

Gets or sets the that is called when the dialogue anticipates delivering some lines.

CurrentNode

Property in

Summary

Gets the name of the node that this Dialogue is currently executing.

Remarks

If has never been called, this value will be null .

Description

Property in

Summary

Gets a more verbose description of this type.

NodeNames

Property in

Summary

Gets the names of the nodes in the currently loaded Program.

ReturnType

Property in

Summary

Gets the type of value that this function returns.

LanguageCode

Property in

Summary

Gets or sets the 's locale, as an IETF BCP 47 code.

Remarks

This code is used to determine how the plural and ordinal markers determine the plural class of numbers.

For example, the code "en-US" represents the English language as used in the United States.

Name

Property in

Summary

Gets the name of this type.

DialogueException

Class in

Inherits from System.Exception

Summary

An exception that is thrown by when there is an error in executing a .

Parent

Property in

Summary

Gets the parent of this type.

Remarks

All types have as their ultimate parent type (except for itself.)

Parameters

Property in

Summary

Gets the list of the parameter types that this function is called with.

Remarks

The length of this list also determines the number of parameters this function accepts (also known as the function's arity ).

Name

Property in

Summary

Gets the name of this type.

public bool TryGetValue<T>(string variableName, out T result);
public void SetValue(string variableName, string stringValue);
public void SetValue(string variableName, float floatValue);
public void SetValue(string variableName, bool boolValue);
public void Clear();
public bool Contains(string variableName);
<<set $gold_amount to 5>>

Player: I'd like to buy a pie!

<<if $gold_amount < 10>>
    Baker: Well, you can't afford one!
<<endif>>
Player: I'd like to buy a pie!

<<if $gold_amount < 10>>
    Baker: Well, you can't afford one!
<<elseif $gold_amount < 15>>
    Baker: You can almost afford one!
<<else>>
    Baker: Here you go!
<<endif>>
Guard: You're not allowed in!

-> Sure I am! The boss knows me! <<if $reputation > 10>>
-> Please?
nodes
lines
options
variables
<<wait 2>>
<<setsprite ShipName happy>>
<<fade_out 1.5>>
// Wait for 2 seconds
<<wait 2>>

// Wait for half a second
<<wait 0.5>>
// Leave the dialogue now
<<stop>>

// Leave the dialogue if we don't have enough money
<<if $money < 50>>
    Shopkeeper: You can't afford my pies!
    <<stop>>
<<endif>>
Dialogue Runner
define your own custom commands
Creating Commands and Functions
$ ysc upgrade myOldYarnFile.yarn
ysc upgrade myOldYarnFile.yarn myOtherOldYarnFile.yarn
ysc upgrade --output-directory ./output myOldYarnFile.yarn
its GitHub project
Yarn Spinner Console GitHub Project
here

Canvas Group

The Canvas Group that the Options List View will control. The Canvas Group will be made active when the Options List View is displaying options, and inactive when not displaying options.

Option View Prefab

A prefab containing an Option View. The Options List View will create an instance of this prefab for each option that needs to be displayed.

Last Line Text

A TextMeshPro Text object that will display the text of the last line that appeared before options appeared. If this is not set, or no line has run before options are shown, then this property will not be used.

Fade Time

The time, in seconds, that the Options List View will take to fade in. If this is zero, the Options List View will appear immediately.

Show Unavailable Options

If this is turned on, then any options whose line condition has failed will still appear to the user, but they won't be selectable. If this is off, then these options will not appear at all.

Dialogue View
Option View
Line View
In-Memory Variable Storage
Line
Dialogue Runner
LocalizedLine
Dialogue Views
Text Line Provider
Audio Line Provider
Audio Clip
Secret Lab
Jon
Tim Nugent
Paris Buttfield-Addison
Mars Buttfield-Addison
support Yarn Spinner on Patreon
Secret Lab
Twitter
Discord
Yarn Spinner Language Server
pappleby
Crochet
FaultyFunctions
GDYarn
Kyperbelt
Wol
Bram Dingelstad
Godot
Yarn Loom
TranquilMarmot
public static IType Number { get; };
BuiltinTypes
public Dialogue(Yarn.IVariableStorage variableStorage)

Yarn.IVariableStorage variableStorage

The IVariableStorage that this Dialogue should use.

Dialogue
Dialogue
public void UnloadAll()
Dialogue
public Library Library { get; internal set; }
Dialogue
Library
public bool IsActive { get };
Dialogue
public Logger LogDebugMessage { get; set; }
Dialogue
public NodeCompleteHandler NodeCompleteHandler
{
            get; set; }
Dialogue
NodeCompleteHandler
public PrepareForLinesHandler PrepareForLinesHandler
{
            get; set; }
Dialogue
PrepareForLinesHandler
public string CurrentNode
{
            get; }
Dialogue
Continue()
string Description { get; }
IType
public IEnumerable<string> NodeNames
{
            get; }
Dialogue
public IType ReturnType { get; internal set; }
FunctionType
public string LanguageCode { get; set; }
Dialogue
Dialogue
public string Name { get; }
FunctionType
public class DialogueException : System.Exception
Yarn
Dialogue
Program
public IType Parent { get; }
FunctionType
Any
Any
public List<IType> Parameters { get; };
FunctionType
string Name { get; }
IType

Text Language Code

The language that the Text Line Provider should use to fetch localised text for.

A Text Line Provider

Dialogue Views

Learn about Dialogue Views, which present dialogue content to the user.

A Dialogue View is a kind of component that receives content from a Dialogue Runner, and presents it to the player. Dialogue Views are how the player sees your game's lines of dialogue, and how they select choices in the dialogue.

A Dialogue Runner can have multiple Dialogue Views. For example, you might have one Dialogue View that's designed to display lines of dialogue, and another that's in charge of displaying options to the player.

Because every game's needs are different, a Dialogue View is designed to be extremely customisable, and you can create your own custom dialogue views to suit the needs of your game.

However, because there are common patterns of how games work with dialogue, Yarn Spinner for Unity comes with some pre-built Dialogue Views that handle common use cases:

  • Line View is a Dialogue View that displays a single line of dialogue in a text box that's inside a canvas, and shows a button that the user can click to proceed.

  • Option List View is a Dialogue View that displays a collection of options in a list.

Option View

Learn about Options View, a component used to display an option in an Options List View.

An Option View is an object that the Options List View uses when presenting options. When the Dialogue Runner delivers options to your game, Options List View will create an Option View for each option that could be selected.

When the Option View is pressed, the Options List View will notify the Dialogue Runner of the user's selection.

Inspector

Property
Description

Text

A TextMeshPro text object that will display the text of the line.

Show Character Name

If this is turned on, the Text component will show any character name present in the option. If this is turned off, the character name will not be included.

In-Memory Variable Storage

The In-Memory Variable Storage component is a Variable Storage component that stores all variables in memory. These variables are erased when the game stops.

The In-Memory Variable Storage component is intended to be a useful tool for getting started, and to be replaced with a custom variable storage that meets your game's needs.

However, if your game has no need to save and restore game state, then this class can be used in your final game, too.

Inspector

Property
Description

Debug Text View

A Unity UI Text object that will display a summary of the variables that have been stored in this component. If this is not set, this property will not be used.

You can use this property to display a debug summary of your variables at run-time in your games.

Debug Variables

This area of the Inspector shows a summary of the variables. This works similarly to the Debug Text View property, but the summary is only ever shown in the Editor, and it doesn't require any setup.

GitHub Repositories

There are several official Yarn Spinner GitHub repositories:

  • Yarn Spinner Core — YarnSpinnerTool/YarnSpinner

    • This is the core of Yarn Spinner, and all other elements depend on this in some way.

  • Yarn Spinner Unity — YarnSpinnerTool/YarnSpinner-Unity

    • This is the components of Yarn Spinner that relate to the Unity integration

  • Yarn Spinner Console (ysc) — YarynSpinnerTool/YarnSpinner-Console

    • The Yarn Spinner command-line tool, for working with .yarn files on the command-line.

  • Visual Studio Code Extension — YarnSpinnerTool/VSCodeExtension

    • This is the VS Code extension for Yarn Spinner.

We also host several other public GitHub repositories related to Yarn Spinner:

  • Experimental Editor - YarnSpinnerTool/ExperimentalYarnSpinnerEditor

    • This is an experimental editor created by a student team. It may, or may not, be maintainted in the future. It targets Yarn Spinner 2.0.

  • Classic Yarn Editor - YarnSpinnerTool/YarnEditor

    • This is the Classic Yarn Editor, and is not supported by Yarn Spinner.

Text

Property in Command

Summary

Gets the text of the command.

public string Text { get; private set; }

TypeMappings

Property in BuiltinTypes

Summary

Gets a dictionary that maps CLR types to their corresponding Yarn types.

public static IReadOnlyDictionary<System.Type, Yarn.IType> TypeMappings { get; };

ParseMarkup(string)

Method in Dialogue

Summary

Parses a line of text, and produces a MarkupParseResult containing the results.

public MarkupParseResult ParseMarkup(string line)

Remarks

The MarkupParseResult 's Text will have any select , plural or ordinal markers replaced with the appropriate text, following this Dialogue 's LanguageCode .

Parameters

Name
Description

string line

The line of text to parse.

Returns

The results of parsing the markup.

Stop()

Method in Dialogue

Summary

Immediately stops the Dialogue .

public void Stop()

Remarks

The DialogueCompleteHandler will not be called if the dialogue is ended by calling Stop() .

GetTagsForNode(string)

Method in Dialogue

Summary

Returns the tags for the node nodeName .

public IEnumerable<string> GetTagsForNode(string nodeName)

Remarks

The tags for a node are defined by setting the tags header in the node's source code. This header must be a space-separated list.

Parameters

Name
Description

string nodeName

The name of the node.

Returns

The node's tags, or null if the node is not present in the Program.

LogErrorMessage

Property in Dialogue

Summary

Invoked when the Dialogue needs to report an error.

public Logger LogErrorMessage { get; set; }

DefaultStartNodeName

Field in Dialogue

Summary

The node that execution will start from.

public const string DefaultStartNodeName = "Start";

CommandHandler

Property in Dialogue

Summary

Gets or sets the CommandHandler that is called when a command is to be delivered to the game.

public CommandHandler CommandHandler
{
            get; set; }

Methods

Property in FunctionType

Summary

Gets the collection of methods that are available on this type.

public MethodCollection Methods { get };

DialogueCompleteHandler

Property in Dialogue

Summary

Gets or sets the DialogueCompleteHandler that is called when the dialogue reaches its end.

public DialogueCompleteHandler DialogueCompleteHandler
{
            get; set; }

NodeExists(string)

Method in Dialogue

Summary

Gets a value indicating whether a specified node exists in the Program.

public bool NodeExists(string nodeName)

Parameters

Name
Description

string nodeName

The name of the node.

Returns

true if a node named nodeName exists in the Program, false otherwise.

Description

Property in FunctionType

Summary

Gets a more verbose description of this type.

public string Description
{
            get; set; }

ExpandSubstitutions(string,IList<string>)

Method in Dialogue

Summary

Replaces all substitution markers in a text with the given substitution list.

public static string ExpandSubstitutions(string text, IList<string> substitutions)

Remarks

This method replaces substitution markers - for example, {0}

  • with the corresponding entry in substitutions . If text contains a substitution marker whose index is not present in substitutions , it is ignored.

Parameters

Name
Description

string text

The text containing substitution markers.

System.Collections.Generic.IList<string> substitutions

The list of substitutions.

Returns

text , with the content from substitutions inserted.

Methods

Property in IType

Summary

Gets the collection of methods that are available on this type.

MethodCollection Methods { get; }

NodeStartHandler

Property in Dialogue

Summary

Gets or sets the NodeStartHandler that is called when a node is started.

public NodeStartHandler NodeStartHandler
{
            get; set; }

LineHandler

Property in Dialogue

Summary

Gets or sets the LineHandler that is called when a line is ready to be shown to the user.

public LineHandler LineHandler
{
            get; set; }

Yarn Scripts

Learn about Yarn scripts, which are the assets that contain the dialogue you write.

A Yarn script is a text file containing your dialogue.

Yarn scripts need to be added to a Yarn Project in order to be used in your game.

Creating a New File

To create a new Yarn script in Unity, follow these steps:

  • Open the Assets menu, and choose Yarn Spinner -> Yarn Script.

  • Unity will create a new file. Type in a name for the file, and press return.

Creating a new Yarn script.

The new file that you've just created will contain a single node, which has the same name as the file.

Creating a Yarn Script in Unity is exactly the same as creating a .yarn file externally (i.e. in macOS Finder or Windows Explorer), and dragging it into the Assets folder of your Unity project, or directly into the Project pane (where the new Yarn Script we created through the process above appeared) in Unity.

Editing Yarn Scripts

To edit a Yarn script, double-click it in Unity. The file will open in your editor. When you save your changes and return to Unity, it will be re-compiled.

You can learn about our recommended editor, Visual Studio Code with the official Yarn Spinner Extension at: Editing with VS Code.

SetProgram(Program)

Method in Dialogue

Summary

Loads all nodes from the provided Program .

public void SetProgram(Program program)

Remarks

This method replaces any existing nodes have been loaded. If you want to load nodes from an additional Program, use the AddProgram(Program) method.

Parameters

Name
Description

program

The to use.

SetNode(string)

Method in Dialogue

Summary

Prepares the Dialogue that the user intends to start running a node.

public void SetNode(string startNode = DefaultStartNodeName)

Remarks

After this method is called, you call Continue() to start executing it.

If PrepareForLinesHandler has been set, it may be called when this method is invoked, as the Dialogue determines which lines may be delivered during the startNode node's execution.

Parameters

Name
Description

string startNode

The name of the node that will be run. The node have been loaded by calling or .

AddProgram(Program)

Method in Dialogue

Summary

Loads the nodes from the specified Program , and adds them to the nodes already loaded.

public void AddProgram(Program program)

Remarks

If Yarn.Dialogue.Program is null , this method has the effect as calling SetProgram(Program) .

Parameters

Name
Description

program

The additional program to load.

IType

Interface in Yarn

Summary

Defines properties that describe a type in the Yarn language.

public interface IType

Properties

Name
Description

Gets a more verbose description of this type.

Gets the collection of methods that are available on this type.

Gets the name of this type.

Gets the parent of this type.

Quick Start

Quickly get started with a simple scene.

Want to use Yarn Spinner in a new scene right away? Follow these steps.

Setting Up A Demo Scene

  1. Create a new empty Unity project, by following .

  2. Install Yarn Spinner into the project, by following the instructions in .

  3. Add the Dialogue System prefab to the scene:

    1. In the Project pane, scroll down to the Yarn Spinner folder. You'll find it in the Packages section.

    2. Inside the Yarn Spinner folder, open the Prefabs folder, and locate the Dialogue System prefab.

    3. Drag the Dialogue System into your scene.

  4. Create a new , by opening the Assets menu and choosing Create -> Yarn Spinner -> Yarn Script. Name the new file HelloYarn.

  5. Open the new Yarn script by double-clicking it.

    1. Select all of the text in the file, and delete it.

    2. Copy the text below, and paste it into the file.

You can learn about our recommended editor, Visual Studio Code with the official Yarn Spinner Extension at: .

  1. Save the file and return to Unity.

  2. Create a new that uses this script, by selecting the HelloYarn file, and clicking the Create New Yarn Project button in the Inspector. This will create a new Yarn Project called Project.

  3. Attach the Yarn Project to the Dialogue Runner, by selecting the Dialogue Runner in the Hierarchy, and dragging the Project into the Yarn Project field.

  4. Play the game by clicking the Play button at the top of the window. Your dialogue will appear!

CommandHandler

Delegate in

Inherits from System.MulticastDelegate

Summary

Represents the method that is called when the Dialogue delivers a .

Parameters

Name
Description

See Also

  • : Represents the method that is called when the Dialogue delivers a .

  • : Represents the method that is called when the Dialogue delivers an .

  • : Represents the method that is called when the Dialogue begins executing a node.

  • : Represents the method that is called when the Dialogue reaches the end of a node.

  • : Represents the method that is called when the dialogue has reached its end, and no more code remains to be run.

Continue()

Method in

Summary

Starts, or continues, execution of the current Program.

Remarks

This method repeatedly executes instructions until one of the following conditions is encountered:

  • The or is called. After calling either of these handlers, the Dialogue will wait until is called. Continue may be called from inside the or , or may be called at any future time.

  • The is called. When this occurs, the Dialogue is waiting for the user to specify which of the options has been selected, and must be called before is called again.)

  • The Program reaches its end. When this occurs, must be called before is called again.

  • An error occurs while executing the Program.

This method has no effect if it is called while the is currently in the process of executing instructions.

See Also

  • : Represents the method that is called when the Dialogue delivers a .

  • : Represents the method that is called when the Dialogue delivers an .

  • : Represents the method that is called when the Dialogue delivers a .

  • : Represents the method that is called when the Dialogue reaches the end of a node.

  • : Represents the method that is called when the dialogue has reached its end, and no more code remains to be run.

SetSelectedOption(int)

Method in

Summary

Signals to the that the user has selected a specified .

Remarks

After the Dialogue delivers an , this method must be called before is called.

The ID number that should be passed as the parameter to this method should be the field in the that represents the user's selection.

Parameters

Name
Description

See Also

  • : Represents the method that is called when the Dialogue delivers an .

  • : A set of s, sent from the to the game.

  • : Starts, or continues, execution of the current Program.

GetStringIDForNode(string)

Method in

Summary

Returns the string ID that contains the original, uncompiled source text for a node.

Remarks

A node's source text will only be present in the string table if its tags header contains rawText.

Because the class is designed to be unaware of the contents of the string table, this method does not test to see if the string table contains an entry with the line ID. You will need to test for that yourself.

Parameters

Name
Description

Returns

The string ID.

DialogueCompleteHandler

Delegate in

Inherits from System.MulticastDelegate

Summary

Represents the method that is called when the dialogue has reached its end, and no more code remains to be run.

See Also

  • : Represents the method that is called when the Dialogue delivers a .

  • : Represents the method that is called when the Dialogue delivers an .

  • : Represents the method that is called when the Dialogue delivers a .

  • : Represents the method that is called when the Dialogue begins executing a node.

  • : Represents the method that is called when the Dialogue reaches the end of a node.

OptionsHandler

Property in

Summary

Gets or sets the that is called when a set of options are ready to be shown to the user.

Remarks

The Options Handler delivers an to the game. Before can be called to resume execution, must be called to indicate which was selected by the user. If is not called, an exception is thrown.

Yarn.Program
Program
SetProgram(Program)
AddProgram(Program)
Yarn.Program
Description
Methods
Name
Parent
title: Start
---
Wow!
My first ever Yarn script in Unity!

-> Gosh!
-> Incredible!
-> I'm amazed!

Anyway, time to get writing!
===
the instructions in the Unity manual
Installation
Yarn script
Editing with VS Code
Yarn Project
public delegate void CommandHandler(Command command);

Yarn.Command command

The Command that has been delivered.

Yarn
Command
LineHandler
Line
OptionsHandler
OptionSet
NodeStartHandler
NodeCompleteHandler
DialogueCompleteHandler
public void Continue()
Dialogue
LineHandler
CommandHandler
Continue()
LineHandler
CommandHandler
OptionsHandler
SetSelectedOption(int)
Continue()
SetNode(string)
Continue()
Dialogue
LineHandler
Line
OptionsHandler
OptionSet
CommandHandler
Command
NodeCompleteHandler
DialogueCompleteHandler
public void SetSelectedOption(int selectedOptionID)

int selectedOptionID

The ID number of the Option that the user selected.

Dialogue
Dialogue
Option
OptionSet
Continue()
ID
Option
OptionsHandler
OptionSet
OptionSet
Option
Dialogue
Dialogue.Continue()
public string GetStringIDForNode(string nodeName)

string nodeName

The name of the node.

Dialogue
Dialogue
public delegate void DialogueCompleteHandler();
Yarn
LineHandler
Line
OptionsHandler
OptionSet
CommandHandler
Command
NodeStartHandler
NodeCompleteHandler
public OptionsHandler OptionsHandler
{
            get; set; }
Dialogue
OptionsHandler
OptionSet
Continue()
SetSelectedOption(int)
Option
SetSelectedOption(int)

Variables

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

Variables store information. Variables can store one of three types of information: numbers, strings, and booleans.

Type
Possible Values
Examples

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 ($).

Setting Variables

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:

<<set $greeting to "Hello, Yarn!">>

As with node titles, variable names must not contain spaces. They must be made up of only letters, numbers and underscores, and the first character must be a letter.

Variables and Types

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:

// Set some initial values in some variables
<<set $myCoolNumber to 7>>
<<set $myFantasticString to "wow, text!">>

// Now change them!
<<set $myCoolNumber to 8>>
<<set $myFantasticString to "incredible!">>

This works because while the value of each of the variable changes, the type doesn't. However, the following code will not work:

// Set some initial values in some variables
<<set $myCoolNumber to 7>>
<<set $myFantasticString to "wow, text!">>

// This will NOT work, because you can't change types!
<<set $myCoolNumber to "8">>
<<set $myFantasticString to 42>>

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.

<<set $variableName to "a string value">>

Variables and Expressions

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.

<<set $numberOfSidesInATriangle = 2 + 1>>

<<set $numberOfSidesInASquare = $numberOfSidesInATriangle + 1>>

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:

// This will NOT work, because you can't add a string and a number:
<<set $broken = "hello" + 1>>

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.)

Using Variables in Lines

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:

<<set $variableName to "a string value">>
The value of variableName is {$variableName}.
The value of variableName is a string value.

Variables and Storage

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.

Functions

A function is a block of code that provides a value to your Yarn scripts, which you can use in if statements, or store in variables.

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:

// Inside an if statement:
<<if dice(6) == 6>>
    You rolled a six!
<<endif>>

// Inside a line:
Gambler: My lucky number is {random_range(1,10)}!

Built-In Functions

Yarn Spinner comes with several built-in functions for you to use.

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 integer 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)

inc rounds n down to the nearest integer. If n is already an integer, inc 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.

Custom Functions

You can define your own custom functions in Yarn Spinner. For more information, see # Defining Commands and Functions .

Functions are not intended to be a way for you to send instructions to your game. For that purpose, you should use commands.

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 pure functions, and have no side effects besides returning a value based on parameters.

VariableStorage

Property in Dialogue

Summary

Gets or sets the object that provides access to storing and retrieving the values of variables.

public IVariableStorage VariableStorage { get; set; }

BuiltinTypes

Class in Yarn

Inherits from System.Object

Summary

Contains the built-in types available in the Yarn language.

public static class BuiltinTypes

Properties

Name
Description

Gets the type representing any value.

Gets the type representing boolean values.

Gets the type representing numbers.

Gets the type representing strings.

Gets a dictionary that maps CLR types to their corresponding Yarn types.

FunctionType

Class in Yarn

Inherits from System.Object

Summary

A type that represents functions.

public class FunctionType : IType

Remarks

Functions have parameters and a return type, and can be called from script. Instances of this type are created when the host application registers new functions (such as through using the RegisterFunction(string,Delegate) methods or similar.)

Properties

Name
Description

Gets a more verbose description of this type.

Gets the collection of methods that are available on this type.

Gets the name of this type.

Gets the list of the parameter types that this function is called with.

Gets the parent of this type.

Gets the type of value that this function returns.

Command

Struct in Yarn

Inherits from System.ValueType

Summary

A command, sent from the Dialogue to the game.

public struct Command

Remarks

You do not create instances of this struct yourself. They are created by the Dialogue during program execution.

Properties

Name
Description

Gets the text of the command.

See Also

  • Dialogue.CommandHandler: Gets or sets the CommandHandler that is called when a command is to be delivered to the game.

Dialogue Runner

Learn about the Dialogue Runner, which runs the contents of your Yarn Scripts and delivers lines, options and commands to your game.

The Dialogue Runner is the bridge between the dialogue that you've written in your Yarn scripts and the other components of your game. It's a component that's responsible for loading, running and managing the contents of a , and for delivering the content of your to the other parts of your game, such as your user interface.

Setting up a Dialogue Runner is the first step in adding dialogue to your game. To use a Dialogue Runner, you add it to a game object in your scene, connect it to , and provide it with a to run.

When you want to start running the dialogue in your game, you call the Dialogue Runner's method. When you do this, the Dialogue Runner will begin delivering lines, options and commands to its Dialogue Views.

The Dialogue Runner is designed to work with other components of Yarn Spinner for Unity:

  • The contents of your dialogue are delivered to your .

  • The values of are stored and retrieved using the Dialogue Runner's .

  • The content that users should see - that is, the text in their current language, voice over clips, and other asset - are retrieved using the Dialogue Runner's .

The bare-bones minimum that a Dialogue Runner needs in order to work is a Yarn Project and at least one Dialogue View. If you don't set up a Variable Storage or a Line Provider, the Dialogue Runner will use temporary placeholders.

Inspector

Property
Description
Any
Boolean
Number
String
TypeMappings
Description
Methods
Name
Parameters
Parent
ReturnType
Text

Yarn Project

The Yarn Project that this Dialogue Runner is running.

Variable Storage

The Variable Storage to store and retrieve variable data from. If you do not set this, the Dialogue Runner will create an In Memory Variable Storage for you at runtime.

Line Provider

The Line Provider to use to get user-facing content for each line. If you do not set this, the Dialogue Runner will create a Text Line Provider for you at runtime.

Dialogue Views

The Dialogue Views to send lines, options and commands to.

Start Automatically

If this is turned on, the Dialogue Runner will start running the node named Start Node when the scene starts. If this is not turned on, you will need to call StartDialogue to start running.

Start Node

If Start Automatically is turned on, the Dialogue Runner will start running this node when the scene starts. (If your Yarn Project does not contain a node with this name, an error will be reported.)

Automatically Continue Lines

If this is turned on, lines will automatically continue as soon as all Dialogue Views have finished presenting them. If this is turned off, the Dialogue Runner will wait for a Dialogue View to call OnViewUserIntentNextLine to continue.

Run Selected Options as Lines

If this is turned on, when the user chooses an option, the Dialogue Runner will run the selected option as if it were a Line.

Verbose Logging

If this is turned on, the Dialogue Runner will log information about the state of each line to the Console as it's run.

On Node Start

A Unity Event that's fired when the Dialogue Runner begins running a new node. This may be fired multiple times during a dialogue run.

On Node Complete

A Unity Event that's fired when the Dialogue Runner reaches the end of a node. This may be fired multiple times during a dialogue run.

On Dialogue Complete

A Unity Event that's fired when the Dialogue Runner stops running dialogue.

On Command

A Unity Event that's fired when a Command is encountered. This will only be called if no other part of the system has already handled the command, such as command handlers registered via YarnCommand or AddCommandHandler.

Yarn Project
Yarn scripts
Dialogue Views
Yarn Project
StartDialogue
Dialogue Views
variables
Variable Storage
Line Provider

Line View

Learn about Line View, a Dialogue View that displays a single line of dialogue on a Canvas.

Line View is a Dialogue View that displays a single line of dialogue inside a Unity UI canvas. When the Dialogue Runner encounters a line in your Yarn Script, the Line View will display it, wait for the user to indicate they're done reading it, and then dismiss it.

Line View only displays lines, and doesn't display options. You can use an additional Dialogue View to handle these, like an Options List View or a custom Dialogue View of your own.

Showing the Character's Name

If a line contains a character's name at the start, Line View can be configured to show the name in a separate text view to the line text itself. If the Character Name Text property is connected to a TextMeshPro Text object, then the character's name will appear in this object.

If you don't attach a Text object to the Character Name Text property, you can choose to either show the character name as part of the line (that is, in the Line Text view), or don't show it all.

Presenting Lines with Visual Effects

Line View can be configured to use visual effects when presenting lines.

  • You can choose to have the Line View fade in when a line appears, and fade out when the line is dismissed.

  • You can choose to have the text of the line appear, one letter at a time, with a "typewriter" effect.

Continuing to the Next Line

You can control how the player can decide to proceed to the next line.

You can set up a Button, or any other Unity UI control, to call the OnContinueClicked method on this line.

You can also make the Line View continue to the next line when the user performs some input:

  • If you set the Continue Action Type to Key Code, you can select a key on the keyboard that will continue to the next line on press.

  • If you set the Continue Action Type to Input Action, you can create an Action from an input device (such as from a keyboard, gamepad, or other method).

  • If you set the Continue action Type to Input Action from Asset, you can attach an Action from an Input Actions asset that you've set up elsewhere.

If you want to use Input Actions, your project will need to be set up to use the new Unity Input System.

Inspector

Property
Description

Canvas Group

The Canvas Group that the Line View will control. The Canvas Group will be made active when the Line View is displaying a line, and inactive when not displaying a line.

Line Text

A TextMeshPro Text object that the text of the line will be displayed in.

Use Fade Effect

If this is turned on, the Line View will fade the opacity of the Canvas Group from 0% to 100% opacity when lines appear, and fade back to 0% when lines are dismissed.

Fade In Time

The duration of the Fade effect when fading a new line in, in seconds. If this is zero, the line will appear immediately.

Fade Out Time

The duration of the Fade effect when fading a line out, in seconds. If this is zero, the line will disappear immediately.

Use Typewriter Effect

If this is turned on, the text of the line will appear one character at a time. This will take place after the Fade effect, if enabled.

On Character Typed

A Unity Event that's called every time the Typewriter effect displays new text.

Typewriter Effect Speed

The number of characters per second to display when performing a Typewrite effect. Larger values means that text will appear faster.

Character Name Text

A TextMeshPro Text object that will display the name of the character currently speaking the line.

Show Character Name In Line View

If this is turned on, lines that contain a character's name will display the name in the Line Text section. If it is turned off, character names will not be shown at all. This option is only available when Character Name Text is empty.

Continue Action Type

A drop-down list that selects how user input will be used to continue to the next line.

  • When set to None, the dialogue will not continue based on user input.

  • When set to Key Code, the dialogue will continue when the user presses the keyboard key specified in Continue Action Key Code.

  • When set to Input System Action, the dialogue will continue when the user performs the Action set up in Continue Action.

  • When set to Input System Action from Asset, the dialogue will continue when the user performs the Action specified by the Continue Action Reference field.

Continue Action Key Code

The keyboard key that the user should press to continue to the next line.

Continue Action

An Input Action that the user should perform to continue to the next line.

Continue Action Reference

An Input Action, stored inside an Input Actions asset, that the user should perform to continue to the next line.

Continue Button

A game object that will be made active when the line has finished appearing. This is intended to be used for controlling the appearance of a button that the user can interact with to continue to the next line.

Installation

This tutorial shows you how to install Yarn Spinner for Unity, the Unity integration for running Yarn and Yarn Spinner scripts in your Unity-based games.

There are two ways to install Yarn Spinner for Unity: via the Package Manager, and via a .unitypackage file.

We recommend installing it via the Package Manager, because of a few reasons: it's easier to update to new versions of Yarn Spinner as they become available, it doesn't get embedded in the source code of your game, and you have more control over what gets added to your project.

Install via the Unity Package Manager (recommended)

You can install the Yarn Spinner package into your project using the Package Manager window in Unity.

Yarn Spinner is available via the OpenUPM registry. This is the simplest way to install Yarn Spinner, and makes it easy to keep it up to date.

In order to follow the instructions in this section, your project needs to be using Unity 2020.1 or higher. If your project is using an earlier version of Unity, we recommend installing Yarn Spinner from Git.

Setting Up the OpenUPM Registry in Your Project

Before you can install Yarn Spinner from OpenUPM, you first need to configure your project so that it knows where to get the package from.

  1. In Unity, open the Edit menu, and choose Project Settings.

  2. In the list of sections at the left hand side of the window, select Package Manager.

This window is where you tell Unity about where to find packages that come from registries besides Unity's built-in one.

  1. In the Name field, type OpenUPM.

  2. In the URL field, type https://package.openupm.com.

  3. In the Scopes field, type dev.yarnspinner.

  4. Click Save.

When you're done, the settings window should look like this:

You can now install Yarn Spinner itself.

Installing the Yarn Spinner package

  1. Open the Window menu, and choose Package Manager.

  2. In the toolbar, click Packages: In Project, and choose My Registries.

  1. Yarn Spinner will appear in the list. Select it, and click Install.

Yarn Spinner will download and install into your project.

You can verify that everything is imported succesfully by looking for Yarn Spinner under Packages, in the Project pane.

Verify that Unity has the package by checking the Packages folder of the Project pane.

Install from GitHub

As an alternative to downloading Yarn Spinner from OpenUPM, you can install Yarn Spinner by downloading the package directly from GitHub, where the project's source code is stored.

Where possible, we recommend installing Yarn Spinner from OpenUPM rather than GitHub, because it's easier to update to new versions.

To install Yarn Spinner from GitHub, follow these instructions.

  1. In Unity, open the Window menu, and choose Package Manager.

  2. Click the + button, and choose "Add package from git URL".

  1. In the text field that appears, enter the following URL: https://github.com/YarnSpinnerTool/YarnSpinner-Unity.git.

Be sure to type the URL exactly as it appears in this document, or the installation won't work.

  1. The project will download and install. This might take a moment.

Install as a file

If you don't want to install Yarn Spinner as a package, you can download it as a .unitypackage file, and extract it into your project.

This approach works in all versions of Unity. However, because it installs the Yarn Spinner files directly into your project's Assets folder and doesn't do any versioning, updating to a newer version of Yarn Spinner is more complicated. Where possible, we recommend installing Yarn Spinner from OpenUPM or from Git.

To download and install the file, follow these steps:

  1. In Unity, open the Window menu, and choose Package Manager.

  2. Locate Text Mesh Pro in the list, and select it.

  3. Click the Install button to install Text Mesh Pro. This package is required for Yarn Spinner to work.

  4. Next, open your browser, and go to the most recent release of Yarn Spinner for Unity on GitHub.

  5. Download the .unitypackage for that release.

The release page on GitHub.
  1. Open the .unitypackage. Unity will ask which files you want to add. In almost every case, you'll want to import all files.

Importing the package.
  1. Click Import.

Next Steps

Once you've installed Yarn Spinner, you're ready to start using it!

Nodes, Lines, and Options

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.

Nodes

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, then the will translate them (and all jumps and options related) to use a _ (underscore) instead.

Writing Nodes in Plain Text

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.

Node Content

The body of a node is made up of three different kinds of content: lines, commands, and options.

Lines

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:

Options

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".

Options and Lines

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.

Nested Options

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 for 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.

title: Node_Title
---
Here are some lines!
Wow!
===
Mae: Well, this is great.
Mae: I mean I didn't expect a party or anything
Mae: but I figured *someone* would be here.
Mae: ...
Mae: Welcome home, Mae.
This is a line of dialogue, without a character name.
Speaker: This is another line of dialogue said by a character called "Speaker".
Companion: Hi there! What do you feel like doing today?

-> Player: I want to go swimming.
-> Player: I'd prefer to go hiking.
Companion: Hi there! What do you feel like doing today?

-> Player: I want to go swimming.
    Companion: Okay, let's go swimming.
-> Player: I'd prefer to go hiking.
    Companion: Cool, we'll go hiking then.
    
Player: Sounds good!
Companion: Hi there! What do you feel like doing today?

-> Player: I want to go swimming.
    Companion: Okay, let's go swimming.
    Companion: Where do you want to swim?
    -> Player: The lake!
        Companion: Nice! It's a great day for it.
    -> Player: The swimming pool!
        Companion: Oh, awesome! I heard they installed a new slide.
-> Player: I'd prefer to go hiking.
    Companion: Cool, we'll go hiking then.
    
Player: Sounds good!
title: Start
---
Companion: Hi there! What do you feel like doing today?

-> Player: I want to go swimming.
    Companion: Okay, let's go swimming.
    Companion: Where do you want to swim?
    -> Player: The lake!
        Companion: Nice! It's a great day for it.
    -> Player: The swimming pool!
        Companion: Oh, awesome! I heard they installed a new slide.
-> Player: I'd prefer to go hiking.
    Companion: Cool, we'll go hiking then.
    
Player: Sounds good!
===
title: Start
---
Companion: Hi there! What do you feel like doing today?

-> Player: I want to go swimming.
    Companion: Okay, let's go swimming.
    <<jump Swimming>>
-> Player: I'd prefer to go hiking.
    Companion: Cool, we'll go hiking then.
    <<jump Hiking>>
===
title: Swimming
---
Companion: Where do you want to swim?
-> Player: The lake!
    Companion: Nice! It's a great day for it.
-> Player: The swimming pool!
    Companion: Oh, awesome! I heard they installed a new slide.

<<jump Done>>
===
title: Hiking
---
Companion: Have you got your hiking boots ready?
-> Player: Yes.
    Companion: Great, let's go!
-> Player: No.
    Companion: We can swing by your place and pick them up!

<<jump Done>>
===
title: Done
---
Player: Sounds good!
===
upgrader script
Lines of dialogue running in Night in the Woods.

Yarn Namespace

Summary

Contains classes for working with compiled Yarn programs.

Classes

Name
Description

Contains the built-in types available in the Yarn language.

Co-ordinates the execution of Yarn programs.

An exception that is thrown by when there is an error in executing a .

A type that represents functions.

A collection of functions that can be called from Yarn programs.

A simple concrete implementation of that keeps all variables in memory.

A node of Yarn script, contained within a , and containing Yarn.Instruction s.

A compiled Yarn program.

Delegates

Name
Description

Represents the method that is called when the Dialogue delivers a .

Represents the method that is called when the dialogue has reached its end, and no more code remains to be run.

Represents the method that is called when the Dialogue delivers a .

Represents a method that receives diagnostic messages and error information from a .

Represents the method that is called when the Dialogue reaches the end of a node.

Represents the method that is called when the Dialogue begins executing a node.

Represents the method that is called when the Dialogue delivers an .

Represents the method that is called when the dialogue anticipates that it will deliver lines.

Interfaces

Name
Description

Defines properties that describe a type in the Yarn language.

Provides a mechanism for storing and retrieving instances of the Yarn.Value class.

Namespaces

Name
Description

Contains classes for compiling Yarn code.

Contains classes for working with markup in Yarn lines.

Contains classes for working with Yarn Spinner in the Unity game engine.

Structs

Name
Description

A command, sent from the to the game.

A line of dialogue, sent from the to the game.

A set of s, sent from the to the game.

Markup

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

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].

Overlapping Attributes

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:

Self-closing Attributes

Attributes can self-close:

A self-closing attribute has a length of zero.

The Close-All Marker

The marker [/] is the close-all marker. It closes all currently open attributes. For example:

Properties

Attributes can have properties:

This attribute 'wave' has a property called 'size', which has an integer value of 2.

Short-hand Properties

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.

Property Types

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:

Whitespace Trimming

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:

Showing Special Characters to the Player

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.

Escaping single [ and ] characters

If 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:

The nomarkup Attribute

If 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:

The character Attribute

The 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.

Text Replacement Markers

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.

Plurals across different languages

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.

Pluralising with 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:

Yarn Projects

Learn about Yarn Projects, which group your scripts together for use in a Dialogue Runner.

A Yarn Project is a file that links multiple together. Yarn projects are how Dialogue Runners work with your content.

Creating a New Yarn Project

To create a new Yarn Project, follow these steps:

  • Open the Assets menu, and choose Yarn Spinner -> Yarn Project.

  • Unity will create a new file. Type in a name for the file, and press return.

This new Yarn Project will be empty, and won't contain any references to other Yarn scripts yet.

Adding Yarn scripts to a Yarn Project

On their own, a Yarn Project doesn't do anything. In order to be useful, you need to add Yarn scripts to it.

To add Yarn scripts to a Yarn Project, follow these steps:

  • Select the Yarn Project in the Project Pane.

  • In the Inspector, open the Source Scripts property at the top of the pane.

  • Drag the Yarn script you want to add into the Source Scripts list.

  • Click Apply at the bottom of the pane.

Creating a Project from a Script

As an alternative to creating an empty project and adding scripts to it, you can create a new Yarn Project from a script. To do this, follow these steps:

  • Select the Yarn script in the Project pane.

  • In the Inspector, click the Create New Yarn Project button.

  • Clicking this button does two things:

    • A new Yarn Project will be created next to the Yarn script.

    • The new Yarn Project will be set up to include the Yarn script you created it from in its list of source scripts.

Managing Variables

A Yarn Project's inspector shows information about every that are used in the Yarn scripts. This section of the Inspector shows the name, type, description, and default value of each variable.

This information comes from the following locations:

  • All variables that have been declared in a Yarn script with a declare statement

  • All variables that have been manually added to this Yarn project

  • All variables that are used, but don't have a declare statement

Declaring a variable in a Yarn script by using a declare statement, or by manually adding it to the variables list, allows you to ensure that the variable is of the type you expect it to be, and that it has a description that explains the intended purpose of the variable.

If you don't declare a variable, Yarn Spinner will attempt to figure the variable's type out based on how it's used, and won't be able to provide a description.

Managing Localisations and Assets

When you write a Yarn script, you write it in a specific human language. This is referred to as the 'base' language of the script. It's called the base language because it's the one you start with, and the one you translate into other languages.

Unless you change it to something else, Yarn Spinner will set the base language to your computer's current locale.

You can set the base language of a Yarn Project in the Inspector by changing the Base Language setting.

If you want to translate your scripts into another language, or if you want to associate each line with assets (like voice over audio clips), you create a new Localisation. To learn about this process, see .

Using Yarn Projects with Dialogue Runners

Yarn Projects are used by Dialogue Runners. When a Dialogue Runner is told to start running dialogue, it reads it from the Yarn Project it's been provided.

If you try to start a Dialogue Runner and it doesn't have a Yarn Project, or the Yarn Project doesn't have any Yarn scripts, or if any of the Yarn scripts contain an error, the Dialogue Runner won't be able to run.

Inspector

Property
Description

Creating Custom Dialogue Views

Learn how to create Dialogue Views that are designed for the specific needs of your game.

While the Line View and Options List View are useful for lots of situations, your game might need to display lines and options in specific ways. In these situations, you can write your own custom Dialogue View, and handle the presentation of lines and options in ways that are entirely in your control.

Creating a Dialogue View

To create a Dialogue View, you subclass the class, and add it as a component to a game object in your scene. You can then add this game object to the Dialogue Views list on your scene's Dialogue Runner.

If you just want to skip straight to the sample code, take a look at the in the Yarn Spinner examples repository.

Presenting Lines and Options

On its own, an empty subclass of DialogueViewBase will not do anything useful. To make it display lines and options, you'll need to implement certain methods.

To understand how to create a custom Dialogue View, it's useful to understand how the works with content.

Yarn Spinner scripts deal in three different kinds of content: lines, options, and commands. Of these, only the first two - lines and options - are content that need to be shown directly to the player.

When the Dialogue Runner encounters lines or options, it first needs to determine the specific content the user needs to see. Once it has this, it sends the content to each of its Dialogue Views.

Your scene can have multiple Dialogue Views, and they can all do different things. It can be useful to create, for example, a Dialogue View that handles lines, and a separate Dialogue View that handles options.

Getting Localized Content

Lines and options are represented in compiled Yarn scripts as line IDs. A line ID is a unique identifier for the text of a line or an option. When Yarn Spinner needs to show a line or option to the user, it asks its to provide it with a object. This object contains the text of the line or option, in the user's current locale.

As discussed in , you can have different kinds of Line Providers; for example, the creates LocalizedLine objects that just contain text, while creates objects that also contain an .

When displaying a collection of options, each individual option has its own LocalizedLine.

Once a LocalizedLine has been created, the Dialogue Runner has everything that it needs to show content to the user. The next steps vary depending on whether it's showing a line or an option.

Presenting Lines

When Yarn Spinner encounters a line of dialogue, it calls the RunLine method on each Dialogue View. This method takes two parameters: the first is the LocalizedLine that the Line Provider created, and the second is a that the Dialogue View should call when the line has finished being presented.

In Dialogue Views, a line is presented when the user has received the entire line, and is ready to move on to the next line. The practical outcome of what this means depends on the Dialogue View itself; for example, a Dialogue View that plays voice-over audio might finish presenting when all of the audio has played, while a Dialogue View that gradually reveals the text of a line might finish presenting when all of the text is visible.

The Dialogue Runner will wait until all Dialogue Views report that they've finished presenting the line. Once this happens, it moves on to the next part of the dialogue.

If you're making a game where you want the dialogue to pause until the user gives a signal to proceed, your Dialogue View can pause the dialogue by not calling the completion handler until it receives the signal. Because the Dialogue Runner will wait until all Dialogue Views report that they're done, the dialogue will wait.

Interrupting Lines

At any point during a line's presentation, a Dialogue View can interrupt the line. It does this by calling the method, which is a delegate that's set by its controlling Dialogue Runner. When this method is called, all Dialogue Views that have not yet finished their presentation receive a call to their method.

InterruptLine is very similar to RunLine, in that it receives a line to present and a completion handler to call when the presentation is complete. However, while RunLine is expected to present the line at its own pace, InterruptLine is a signal to finish the presentation as quickly as possible.

As before, the actual details of this depend on the Line View. To continue the examples from before, a Dialogue View that plays voice-over audio might fade out the audio over a short period of time, or even cut off playback immediately; a Dialogue View that's gradually revealing text might reveal the remaining text all at once, or rapidly reveal the remaining text.

When a Dialogue View receives a call to InterruptLine, it should not call the completion handler that it received from the call to RunLine. Calls to interrupt a line supersede calls to run a line.

Any Dialogue View may request that a line be interrupted. If multiple Dialogue Views request it, only the first request does anything.

Dismissing Lines

When the last Dialogue View reports that its presentation is complete, either because RunLine finished its presentation, or because InterruptLine was called and it quickly finished its presentation, it needs to tell the dialogue views to get rid of the line, and potentially prepare for more content.

The Dialogue Runner does this by calling on all Dialogue Views. As with RunLine and InterruptLine before it, DismissLine receives a completion handler to call when it has finished dismissing the line.

As before, the details of how a line is dismissed vary with what the Dialogue View actually does. A Dialogue View that plays voice-over audio may not need to do anything to dismiss a line, because the playback has already finished; a Dialogue View that shows line text on screen might need to hide the text, possibly with an animation.

When the last Dialogue View reports that it has finished dismissing its line, the Dialogue Runner continues running the script.

Presenting Options

Options are slightly different to lines, in that they rely on receiving some kind of user input before the dialogue can continue: the Dialogue Runner needs to know which option was selected.

To handle options, Dialogue Views implement the method. This method receives an array of objects, each of which represents an option that can be shown to the user, as well as a completion handler.

When this method is called, the Dialogue View uses the information contained within the DialogueOption objects to present the choices to the player, and then awaits user input. Once it knows which option was selected, it calls the completion handler, passing in the of the selected option.

When the Dialogue Runner delivers options to its Dialogue Views, it expects exactly one of them to call the completion handler that RunOptions receives.

  • If none of them call it, then the Dialogue Runner will never receive the option that was selected (and will wait for it forever.)

  • If more than one of them call it, the Dialogue Runner will throw an error.

(In most situations, you will generally only have one Dialogue View in your scene that handles options. If you have more than one, then you will need to control which one of them will call their completion handler.)

Using Multiple Dialogue Views

Dialogue Runners can use multiple Dialogue Views. This is actually recommended, because it makes it easier to separate the code for handling lines, from that of running options.

All of the methods in are optional. If you don't implement a method, then the default implementation of that method is used instead; the default implementation either does nothing, or as close to nothing as it can while still working. For example, the default implementation of RunLine immediately signals that presentation is complete.

  • To create a Dialogue View that shows lines, implement RunLine, InterruptLine and DismissLine.

  • To create a Dialogue View that shows options, implement RunOptions.

  • To create a Dialogue View that supports both, implement all four.

Responding to Dialogue Advancement Signals

During gameplay, your user may wish signal that they want to advance the dialogue: that is, they want to proceed to the next line, or they want the current line to be presented more quickly.

To handle this case, subclasses of DialogueViewBase may implement the method , which can be called by other parts of the gam.

In most cases, it is generally appropriate for implementations of UserRequestedViewAdvancement to call the method, which tells the Dialogue Runner to interrupt the line (across all views) and to proceed to the next one. However, a Dialogue View may choose to perform other actions that deliver the line more quickly.

For example, in several text-based RPG games, dialogue is delivered as a text box, one letter at a time; when it's all delivered, the user can press the A button (to choose an arbitrary example) to proceed.

If, however, you press the A button while the text is still appearing, all of the text appears all at once (as though we'd jumped ahead).

Alternatively, if you pressed the B button while the text was still appearing, the line would be skipped, the dialogue would move to the next line.

UserRequestedViewAdvancement can be called by any part of your code. Additionally, you may wish to use , which is a class that listens for user input, and when it receives it, calls UserRequestedViewAdvancement on a view you specify.

Seeing it in Action

To demonstrate how a custom Dialogue View is put together, we've created , which demonstrates the above features and is heavily commented. For more information, see the code on .

Oh, [wave]hello[/wave] there!
Oh, hello there!
Oh, [wave]hello [bounce]there![/bounce][/wave]
Oh, [wave]hello [bounce]there![/wave][/bounce]
[wave/]
[wave][bounce]Hello![/]
[wave size=2]Wavy![/wave]
[wave=2]Wavy![/wave]
[wave wave=2]Wavy![/wave]
[mood=angry]Grr![/mood]
[mood="angry"]Grr![/mood]
A [wave/] B
A [wave trimwhitespace=false/] B 
// (produces "A  B")
Here's some square brackets, just for you: \[ \]
Here's some square brackets, just for you: [ ]
Here's a backslash! \\
Here's a backslash! \
[nomarkup]Here's a big ol' [ bunch of ] characters, filled [[]] with square [[] brackets![/nomarkup]
Here's a big ol' [ bunch of ] characters, filled [[]] with square [[] brackets!
CharacterA: Hello!
CharacterB: Oh, hi!
[character name="CharacterA"]CharacterA: [/character]Hello!
[character name="CharacterB"]CharacterB: [/character]Oh hi!
// In this example, the $gender variable is a string that
// contains either "m", "f", or "nb".

I think [select value={$gender} m="he" f="she" nb="they" /] will be there!

// Depending on the value of $gender, this line can appear
// as one of these possible options:
I think he will be there!

// or:
I think she will be there!

// or:
I think they will be there!
<<if $apple_count == 1>>
    You have one apple!
<<else>>
    You have {$apple_count} apples!
<<endif>>
PieMaker: Hey, look! [plural value={$pie_count} one="A pie" other="Some pies" /]!

// This will appear as either:
PieMaker: Hey, look! A pie!

// or: 
PieMaker: Hey, look! Some pies!
PieMaker: I just baked [plural value={$pie_count} one="a pie" other="% pies" /]!

// This will appear as, for example:
PieMaker: I just baked a pie!"

// or:
PieMaker: I just baked 4 pies!"
Runner: The race is over! I came in [ordinal value={$race_position} one="%st" two="%nd" few="%rd" other="%th" /] place!

// This will appear as, for example:
Runner: The race is over! I came in 1st place!

// or:
Runner: The race is over! I came in 23rd place!
BuiltinTypes
Dialogue
DialogueException
Dialogue
Program
FunctionType
Library
MemoryVariableStore
IVariableStorage
Node
Program
Program
CommandHandler
Command
DialogueCompleteHandler
LineHandler
Line
Logger
Dialogue
NodeCompleteHandler
NodeStartHandler
OptionsHandler
OptionSet
PrepareForLinesHandler
IType
IVariableStorage
Yarn.Compiler
Yarn.Markup
Yarn.Unity
Command
Dialogue
Line
Dialogue
OptionSet
Option
Dialogue
DialogueViewBase
SimpleSpeechBubbleLineView sample code
Dialogue Runner
Line Provider
LocalizedLine
Line Providers
Text Line Provider
Audio Line Provider
AudioClip
delegate
requestInterrupt
InterruptLine
DismissLine
RunOptions
DialogueOption
DialogueOptionID
DialogueViewBase
UserRequestedViewAdvancement
requestInterrupt
DialogueAdvanceInput
an example Dialogue View
GitHub

Source Scripts

The list of Yarn Scripts that this Yarn Project uses.

Declarations

The list of variables that are declared in this Yarn Project, or in the Yarn Scripts that this project uses.

Base Language

The language that the Yarn Scripts are written in.

Localisations

A mapping of languages to string tables and associated assets. See Adding Localizations and Assets to Projects for more information.

Search All Assemblies

If this is turned on, Yarn Spinner will search for custom commands and functions in all of your project's code and packages.

If this is turned off, it will only search for them in the default assembly, and in the assemblies specified in Assemblies to Search.

By default, this is turned on.

Assemblies To Search

If Search All Assemblies is turned off, Yarn Spinner will search for commands and actions in the assemblies you specify in this list, as well as the default assembly.

This list will only appear if Search All Assemblies is turned off.

Use Addressable Assets

If this is turned on, the Yarn Project will configure itself to look for assets using the Addressable Assets system.

This checkbox will only appear if the Addressable Assets package is installed in your project.

Update Asset Addresses

When you click this button, all of the assets in the folders specified in the Languages to Source Assets list will have their address updated to match the line ID they refer to.

Export Strings as CSV

When you click this button, all of the lines in the Yarn Scripts that this project uses will be written to a .csv file, which can be translated to other languages. See Adding Localizations and Assets to Projects for more information.

Update Existing Strings Files

When you click this button, all .csv strings files that are configured in the Languages to Source Assets list will be updated with any lines that have been added, modified or deleted since the strings file was created. See Adding Localizations and Assets to Projects for more information.

Add Line Tags to Scripts

When you click this button, any line of dialogue in the Source Scripts list that doesn't have a #line: tag will have one added. See Adding Localizations and Assets to Projects for more information.

Yarn scripts
variable
Adding Localizations and Assets to Projects
Creating a new Yarn script.
When a Yarn Project has no scripts added to it, you'll see this warning in its Inspector.
Adding scripts to a Yarn Project.
The 'Create New Yarn Project' button in the Inspector.
The list of variables in a Yarn Project.
Updating the base language of a Yarn Project

Localizations and Assets

Localization is the process of translating and adapting content to a specific language, region or culture.

Yarn scripts are written in human-readable language. This is generally a single language, and (most of the time) will be written in the language that your development team primarily speaks. The language that a Yarn project is written in is called the base language.

However, if you want your dialogue to be understood by people who don't speak this language, you will need to translate it. Yarn Spinner is designed to make it easy to extract the user-facing text of your dialogue into a strings file, which can then be translated into a different language, and then loaded at run-time. You can translate your project into as many languages as you'd like, and Yarn Spinner will handle it for you automatically.

Yarn Spinner is also designed around the idea that a line of dialogue may have assets associated with it. Most commonly, this means an audio file that contains an actor performing the line, so that it can be used in your game as a voice-over. These assets are also localisable.

I just want to add voiceover in a single language. Why do I need to localise, too?

The philosophy of Yarn Spinner's approach to localisation is: if you want your dialogue to be text-only, and in a single language, you don't need to do anything at all. If you want to do anything else, you will need to set up a localisation and manage it using Yarn Spinner's processes.

We've found that most users who want to start using Yarn Spinner want to quickly get dialogue on the screen, and don't want to do lots of work to get the basics going. That's why we make the simple use-case (text only, a single language) as easy to use as we can.

However, if you're building a game that's voice acted, it makes your life significantly easier if you build your systems with localisation in mind from the start. Additionally, if you have the resources to add voice-over to your project, you should also have the resources to translate your game to other languages (even if you only have voice-overs in a single language.)

To that end, we designed it so that voiceover is intimately tied to localisation, so that you have to at least start thinking about localisation at the start of the process.

Localisation Terminology

  • Localisation: A set of information that describes where to find text and assets for a given language.

  • Base language: The language that your Yarn script files are written in.

  • Strings file: A text document that contains translated versions of Yarn lines.

  • Line ID: A unique code that identifies a line of dialogue or an option in the original source text.

  • Localised line: The text of a line of dialogue, in a particular locale.

  • Localised line asset: An asset (for example, an audio clip) that's associated for a particular line, in a particular locale. For example, an audio clip containing the voiceover for the line "Hello there", in German.

  • Line provider: A component that receives line IDs from the Dialogue Runner, and fetches the localised line and localised line assets (if present) for the player's preferred locale.

Localisation Workflow

In Yarn Spinner, the localisation workflow works like this:

  1. Write your Yarn scripts. This original content is your 'base' localisation.

  2. Add a line ID to each line of dialogue in your script.

  3. Set up localisations in the Yarn Project for each of the languages you wish to support. (This includes your base language.)

  4. For each localisation besides your base language:

    1. Export a strings file.

    2. Translate its contents into another language.

    3. Associate the strings file with the localisation.

  5. If you have assets you want to use with your dialogue, associate the folder that contains those assets with the localisation they belong to, and set up a Line Provider that's able to use those assets (such as an Audio Line Provider.)

  6. During gameplay, set your Line Provider's language to the player's preferred language, and it will fetch the appropriate content for the player to see.

In the following sections, we'll go through each of these steps.

Writing Yarn Scripts

Every Yarn script is associated with a base language. By default, Yarn Spinner sets the base language to that of your current locale. For example, if your computer is set to use Australian English, then Yarn Spinner will use that as the base language.

The base language of a Yarn Script is controlled by the Yarn Project that it's a part of. You can change the language of your base localisation by changing the 'Base Language' setting on a Yarn Project.

Adding Line IDs

In order to match different versions of a line, you need to add a line id to each line of dialogue. A line ID is a tag that appears at the end of a line that uniquely identifies a line of dialogue in your game.

Here's an example of a line of dialogue with a line tag:

Gunther: I wanted orange! They gave me lemon-lime. #line:1a64a5

In this example, the line of dialogue has a line ID of 1a64a5.

Yarn Spinner can add line IDs to your dialogue for you. To do this, select your Yarn Project, and click 'Add Line Tags to Scripts'. Yarn Spinner will re-write all of the script files, adding a line ID to any line that doesn't already have one.

You can't generate a strings file unless all of the lines in all of the scripts in the Yarn Project have a line ID.

Set Up Localisations

When you want to prepare a Yarn Project for an additional language, you add a new Localisation in the Yarn Project.

Localisations are how you tell Yarn Spinner where to find the localised lines, and the localised line assets, for a given language.

To create a new Localisation, open the Localisations list in the Yarn Project's Inspector, and click the + button.

The localisation settings for a project. The base language is English, and two localisations have been set up: one for English, and one for Russian.

Localisations have the following properties:

Property
Description

Language ID

The language for this localisation.

Strings File

A Text Asset containing the translated lines for this Yarn Project's scripts. See for information on how to create these assets.

Assets Folder

A folder containing the localised assets for this localisation.

You can create a localisation for the base language. When you do this, you can provide localised assets for that language.

Creating a Translation

After you've set up a localisation, you can translate your dialogue into that localisation's language. To do this, you generate a strings file.

A strings file is a text-based spreadsheet, in comma-separated value form, that contains a translated version of your dialogue. Yarn Spinner can generate a strings file for you, based on the line IDs in the dialogue.

You don't need to create a strings file for your base localisation, because Yarn Spinner can generate that for you from your source Yarn scripts. Any localisation whose language ID is the same as your base language will be marked as 'Automatically included'.

To create a strings file, select a Yarn Project, and click the Export Strings as CSV button. Unity will ask where you want to save the file.

A passage of Yarn script, next to the strings file for those lines.

A strings file has the following structure:

Column
Description

language

The language code for this line.

When you export a strings file, this will be the Yarn project's base language.

id

The line ID for this line.

text

The text of the line, in the language indicated by the language column.

file

The file that the line was originally found in.

node

The node that the line was originally found in.

lineNumber

The line number of the file that the line was originally found in.

lock

A unique value that Yarn Spinner uses to detect if the line has been modified since the strings file was generated. Don't modify or delete this value.

comment

A note indicating the intent and tone of the line. This can be useful for translators who may not have the same background or context for how the line should be delivered.

Once you've exported a strings file, you can translate it into another language: for each row in the database, change the language column to the new language you're translating into, and the text column to the translated text of the line.

Only the language and text columns should be modified by the translator. Don't modify the others; in particular, if you modify the value in the id column, Yarn Spinner won't be able to link the translated line to the original version.

Once you have a strings file that's been translated into your target language, you can add it to your Localisation. To do this, drag and drop the translated strings file into the Strings File property of your localisation, and click Apply.

It's possible to update a strings file after you've made changes to your source scripts. For example, you might have added or removed lines, or made changes to the text.

To update a strings file, click the Update Existing Strings Files button at the bottom of the Inspector.

Yarn Spinner will update every strings file that's been added to the Localisations list: new lines will be added, removed lines will be deleted, and lines whose original text has changed since the last time the file was updated will have the text "NEEDS UPDATE" added to the end. This allows you to more easily find which lines need an updated translation.

Adding Localised Assets

Localised line assets are assets that are associated with a particular line, in a particular localisation. The most common example of this is voice-over lines, which are audio assets that are associated with each line.

Line Providers are responsible for fetching the appropriate assets for a given line and language. For example, the Audio Line Provider fetches audio clips, and provides them to voice-over dialogue views.

Selecting a Language at Run-time

The specific localised line, and localised line assets, that a line provider fetches depends on which language they have been configured to fetch.

The Text Line Provider has a single language option, which controls which language the line will appear in.

The Audio Line Provider has two language options: the language of the text, and the language of the audio files that are retrieved. This means that you can configure it to provide text in one language, and audio in another.

If a line provider is asked to retrieve content for a language that it doesn't have any assets for, it will retrieve the base language version instead.

Creating a Translation

Commands and Functions

Defining Commands

You can define your own commands, which allow the scripts you write in Yarn Spinner to control parts of the game that you've built.

In Unity, there are two ways to add new commands to Yarn Spinner: automatically, via the YarnCommand attribute, or manually, using the DialogueRunner's AddCommandHandler method.

The YarnCommand attribute

The YarnCommand attribute lets you expose methods in a MonoBehaviour to Yarn Spinner.

When you add the YarnCommand attribute to a method, you specify what name the command should have in Yarn scripts. You can then use that name as a command.

If the method is static, you call it directly. For example:

// Note that we aren't subclassing MonoBehaviour here; 
// static commands can be on any class.
public class FadeCamera {

    [YarnCommand("fade_camera")]
    public static void FadeCamera() {
        Debug.Log("Fading the camera!");
    }
}

If you save this in a file called FadeCamera.cs, you can run this code in your Yarn scripts like this:

<<fade_camera>>
// will print "Fading the camera!" in the console

If the method is not static, you call it with the name of the game object you want the command to run on.

For example:

public class CharacterMovement : MonoBehaviour {

    [YarnCommand("jump")]
    public void Jump() {
        Debug.Log($"{name} is jumping!");
    }
}

If you save this in a file called CharacterMovement.cs, create a new game object called MyCharacter, and attach the CharacterMovement script to that game object, you can run this code in your Yarn scripts like this:

<<jump MyCharacter>>
// will print "MyCharacter is jumping!" in the console

You can also use methods that take parameters. Yarn Spinner will take the parameters that you provide, and convert them to the appropriate type.

Methods that are used with YarnCommand may take the following kinds of parameters:

Type
Note

string

Passed directly to the function.

int

Parsed as an integer using .

float

Parsed as an integer using .

bool

The strings "true" and "false" are converted to their respective boolean values, true and false. Additionally, the name of the parameter is interpreted as true.

GameObject

Yarn Spinner will search all active scenes for a game object with the given name. If one is found, that game object will be passed as the parameter; otherwise, null will be passed.

Component (or its subclasses)

Yarn Spinner will search all active scenes for a game object with the given name, and then attempt to find a component of the parameter's type on that game object or its children. If one is found, that component will be passed as the parameter; otherwise, null will be passed.

Method parameters may be optional.

For example, consider this method:

[YarnCommand("walk")]
public void Walk(GameObject destination, bool dancing = false) {
    var position = destination.transform.position;

    // If the second parameter is used in the command,
    // and it's "true" or "dancing", use a dance 
    // animation
    if (dancing) {
        // set animation to a dance
    } else {
        // set animation to a regular walk
    }

    // walk the character to 'position'
}

This command could be called like this:

<<walk MyCharacter StageLeft>> // walk to the position of the object named 'StageLeft'

<<walk MyOtherCharacter StageRight dancing>> // walk to StageRight, while dancing

Adding commands through code

You can also add new commands directly to a Dialogue Runner, using the AddCommandHandler method.

AddCommandHandler takes two parameters: the name of the command as it should be used in Yarn Spinner, and a method to call when the function is run.

If you want to add a command using AddCommandHandler that takes parameters, you must list the types of those parameters.

For example, to create a command that makes the main camera look at an object, create a new C# script in Unity with the following code:

public class CustomCommands : MonoBehaviour {    

    // Drag and drop your Dialogue Runner into this variable.
    public DialogueRunner dialogueRunner;

    public void Awake() {

        // Create a new command called 'camera_look', which looks at a target. 
        // Note how we're listing 'GameObject' as the parameter type.
        dialogueRunner.AddCommandHandler<GameObject>(
            "camera_look",     // the name of the command
            CameraLookAtTarget // the method to run
        );
    }

    // The method that gets called when '<<camera_look>>' is run.
    private void CameraLookAtTarget(GameObject target) {
        if (target == null) {
            debug.Log("Can't find the target!");
        }
        // Make the main camera look at this target
        Camera.main.transform.LookAt(target.transform);
    }    
}

Add this script to any game object, and it will register the camera_look in the Dialogue Runner you attach.

You can then call this method like this:

<<camera_look LeftMarker>> // make the camera look at an object named LeftMarker

Making Commands Using Coroutines

Coroutines can be commands. If you register a command, either using the YarnCommand attribute, or the AddCommandHandler method, and the method you're using it with is a coroutine (that is, it returns IEnumerator, and yields objects like WaitForSeconds), Yarn Spinner will pause execution of your dialogue when the command is called.

For example, here's how you'd write your own custom implementation of <<wait>>. (You don't have to do this in your own games, because <<wait>> is already added for you, but this example shows you how you'd do it yourself.)

public class CustomWaitCommand : MonoBehaviour {    

    [YarnCommand("custom_wait")]
    static IEnumerator CustomWait() {

        // Wait for 1 second
        yield return new WaitForSeconds(1.0);
        
        // Because this method returns IEnumerator, it's a coroutine. 
        // Yarn Spinner will wait until onComplete is called.
    }    
}

This new method can be called like this:

<<custom_wait>> // Waits for one second, then continues running

Defining Functions

Functions are units of code that Yarn scripts can call to receive a value.

In additon to the built-in functions that come with Yarn Spinner, you can create your own.

To create a function, you use the YarnFunction attribute, or the AddFunction method on a Dialogue Runner. These work very similarly to commands, but with two important distinctions:

  1. Functions must return a value.

  2. Functions are required to be static.

For example, here's a custom function that adds two numbers together:

public class AdderFunction {
   [YarnFunction("add_numbers")]
   public static int AddNumbers(int first, int second)
   {
       return first + second;
   }
}

When this code has been added to your project, you can use it like this:

One plus one is {add_numbers(1, 1)}

Yarn functions can return the following types of values:

  • string

  • int

  • float

  • bool

Commands, Functions and Assembly Definitions

Yarn Spinner searches your code for methods that have the YarnCommand and YarnFunction attributes when your game first starts up, as well as when a Dialogue Runner is told to run a Yarn Project.

If the Yarn Project's "Search All Assemblies" option is turned on, every assembly definition is searched; if it's turned off, only the assembly definitions specified in the "Assemblies To Search" option is searched. Code that is not in an assembly definition is always included.

By default, Yarn Spinner searches every assembly definition. If you have a large codebase, putting all of the code that contains commands and functions in an assembly definition can reduce the amount of time Yarn Spinner needs to take to find all of the commands and functions.

If "Search All Assemblies" is turned off on your Yarn Projects, and you're seeing errors that mention that a command hasn't been registered, try turning "Search All Assemblies" on. If the error goes away, it means the code for those commands is in an assembly definition that the Yarn Project wasn't using.

Dialogue

Class in Yarn

Inherits from System.Object

Summary

Co-ordinates the execution of Yarn programs.

public class Dialogue : IAttributeMarkerProcessor

Constructors

Name
Description

Initializes a new instance of the class.

Fields

Name
Description

The node that execution will start from.

Methods

Name
Description

Loads the nodes from the specified , and adds them to the nodes already loaded.

Starts, or continues, execution of the current Program.

Replaces all substitution markers in a text with the given substitution list.

Returns the string ID that contains the original, uncompiled source text for a node.

Returns the tags for the node nodeName .

Gets a value indicating whether a specified node exists in the Program.

Parses a line of text, and produces a containing the results.

Prepares the that the user intends to start running a node.

Loads all nodes from the provided .

Signals to the that the user has selected a specified .

Immediately stops the .

Unloads all nodes from the Dialogue.

Properties

Name
Description

Gets or sets the that is called when a command is to be delivered to the game.

Gets the name of the node that this Dialogue is currently executing.

Gets or sets the that is called when the dialogue reaches its end.

Gets a value indicating whether the Dialogue is currently executing Yarn instructions.

Gets or sets the 's locale, as an IETF BCP 47 code.

Gets the that this Dialogue uses to locate functions.

Gets or sets the that is called when a line is ready to be shown to the user.

Invoked when the Dialogue needs to report debugging information.

Invoked when the Dialogue needs to report an error.

Gets or sets the that is called when a node is complete.

Gets the names of the nodes in the currently loaded Program.

Gets or sets the that is called when a node is started.

Gets or sets the that is called when a set of options are ready to be shown to the user.

Gets or sets the that is called when the dialogue anticipates delivering some lines.

Gets or sets the object that provides access to storing and retrieving the values of variables.

Convert.ChangeType
Convert.ChangeType
Dialogue(Yarn.IVariableStorage)
Dialogue
DefaultStartNodeName
AddProgram(Program)
Program
Continue()
ExpandSubstitutions(string,IList)
GetStringIDForNode(string)
GetTagsForNode(string)
NodeExists(string)
ParseMarkup(string)
MarkupParseResult
SetNode(string)
Dialogue
SetProgram(Program)
Program
SetSelectedOption(int)
Dialogue
Option
Stop()
Dialogue
UnloadAll()
CommandHandler
CommandHandler
CurrentNode
DialogueCompleteHandler
DialogueCompleteHandler
IsActive
LanguageCode
Dialogue
Library
Library
LineHandler
LineHandler
LogDebugMessage
LogErrorMessage
NodeCompleteHandler
NodeCompleteHandler
NodeNames
NodeStartHandler
NodeStartHandler
OptionsHandler
OptionsHandler
PrepareForLinesHandler
PrepareForLinesHandler
VariableStorage

Choose-Your-Path Game

This example project demonstrates making a simple dialogue-based game when beginning with only an empty Unity scene.

Goals

  1. Display Yarn dialogue in a Unity scene

  2. Allow a player to select between options to respond

  3. Add some static visuals

Materials

  • Yarn Spinner installed in Unity: https://github.com/YarnSpinnerTool/YSDocs/blob/versions/2.1/getting-started/installation-and-setup.md

  • Yarn Spinner set up in a text editor: Editing with VS Code

Instructions

Open a new Unity 3D project. Ensure Yarn Spinner has been added to the project in the Package Manager as per the Installation Instructions.

A new Unity 3D project has been made with no additional changes

If the sample empty scene is not visible, you'll need to open it. In the Project Window where project files are displayed, navigate to Assets > Scenes and select SampleScene.unity.

Creating a Runnable Script

Yarn Spinner for Unity comes with a pre-made UI layer and accompanying utility scripts to handle displaying lines and presenting options from Yarn files. In the Project Window again, navigate to Packages > Yarn Spinner > Prefabs and drag Dialogue System.prefab into the scene.

The Dialogue System has been added from the Project Window into the Scene

When the Dialogue System in the scene is selected, the Inspector will display the Yarn Project it is expecting line from. Here, a Yarn Project is a kind of linking file that groups Yarn script files together. To make one, navigate to a sensible place for the file to live (such as a new folder Assets > Dialogue) and right-click the Project Window pane to select Create > Yarn Spinner > Yarn Project.

The existence of Yarn Projects allows larger games with multiple dialogue systems (e.g. main story dialogue, barks, storylets) to separate into multiple projects that pass lines to different UI or systems. This allows an extra level of organisation above separate Yarn files which are typically used to separate story scenes or parts.

However, most games will need only a single Yarn Project.

Select the scene's Dialogue System again and drag the new Yarn Project into the labelled slot in the Inspector.

The new Yarn Project has been added to the Dialogue System's Dialogue Runner

Now the Yarn Project needs one or more Yarn Scripts to get dialogue from. Just like with the Yarn Project, navigate to the desired file location and select Create > Yarn Spinner > Yarn Script. Then, with the Yarn Project selected, drag the newly created script into the Inspector slot labelled Source Scripts. Click Apply.

The new Yarn Script has been added to the Yarn Project's Source Scripts

Filling Out Your Script

By default, a new Yarn Script begins with a single empty node with the name of the file. Open the file, rename the node to Start and put a single line of test dialogue. You may remove the tags field.

title: Start
---
This is a line of test dialogue.
===

Returning to Unity, pressing the ▶️ button results in the test line being displayed in front of the empty scene world. Pressing Continue will make the UI disappear, as it has reached the end of the script.

The test line from the Yarn Script has been displayed in the otherwise empty game

So it's time for the actual writing part. Here, I've opened my new Yarn Script in Visual Studio Code with the Yarn Spinner Extension installed as per the Installation Instructions. I've written a simple script about a conversation between a blue sphere 🔵, a red cube 🟥 and the player who plays a shape of their choice. Depending on how the player responds to their greeting, the other shapes will either be pleased to meet them or decide they are rude.

The new Yarn Script has been given some simple content

You can find this example script below to copy. Or if you need a refresher on how to represent your own story in Yarn, refer to the Syntax and File Structure guide.

title: Start
---
<<set $shapes_like_you to true>>
Sphere: Hello, I am Blue Sphere.
Cube: Hi there Sphere! I'm Red Cube.
Sphere: And who is this then?

-> I'm Capsule, but my friends call me "Tic Tac". No idea why...
    <<set $name to "Tic Tac">>
-> The name's Triquandle.
    <<set $name to "Triquandle">>
-> Pyramid. Why; who wants to know?
    <<set $name to "Pyramid">>
    <<set $shapes_like_you to false>>

<<if $shapes_like_you>>
    Sphere: Nice to meet you {$name}!
    Cube: Yeah, likewise!
<<else>>
    Sphere: No need to be so rude...
    Cube: Yeah, maybe you should be called Grumpy {$name}.
    Sphere: Ha! Totally.
<<endif>>
===

Once you've got a basic story, pop back into Unity and check the basics:

Yarn Spinner is displaying lines, advancing lines and selecting options correctly as per the script

Draw the Rest of the Owl

Once any desired visual assets have been added to the scene and the story has received any necessary fleshing out, the game is complete. If you've used this example to add dialogue to your own scene, you may skip ahead to Result. Otherwise, let's proceed!

For the shape example, let's add some "characters" to the scene. Use Menu > GameObject > 3D Object to add a Sphere, a Cube and a Plane to the scene. Scale up the Plane by adjusting the vaues in the Inspector to Scale = 10, 10, 10. To put the Sphere and Cube in front of the camera and make the Plane appear as a floor, they'll need to be moved. The following coordinates are about right, using the default location for the Main Camera:

Object
X
Y
Z

Main Camera

0

1

-10

Sphere

-1

1

-7.5

Cube

1

1

-7

Plane

0

0.5

0

Looking to the Game view, this should appear as two shapes on a floor with the dialogue UI in front.

3D Objects have been added to the Scene to act as characters

All this white makes them difficult to distinguish though, so let's colour each Object. Create basic Materials for each by right-clicking the Project Window in the desired file location and select Create > Material three times. Change the colour of each Material to three distinct colours by modifying the Albedo value in the Inspector.

Add a Material to each Object by selecting the desired object and dragging the Material into the Materials > Element 0 under Mesh Renderer in the Inspector.

Materials have been added to the 3D Objects in the Scene

This tutorial isn't here to teach you all of Unity. If you need some guidance about aspects outside of Yarn Spinner, you can check out our books on the topic or there are lots of helpful guides around the web, on YouTube, or created by Unity themselves!

Result

A playable branching story game with simple static visuals.

The game is complete and playable with visuals

An easy way to spice this up is to just add more dialogue with the same characters. Here is an example script that shows how a simple starter script made for testing can grow to a fuller conversation. And it doesn't stop there! Yarn Spinner is perfect for allowing growing projects to remain functional throughout.

Start.yarn
title: Start
---
<<set $shapes_like_you to true>>
Sphere: Hello, I am Blue Sphere.
Cube: Hi there Sphere! I'm Red Cube.
Sphere: And who is this then?

-> I'm Capsule, but my friends call me "Tic Tac". No idea why...
    <<set $name to "Tic Tac">>
-> The name's Triquandle.
    <<set $name to "Triquandle">>
-> Pyramid. Why; who wants to know?
    <<set $name to "Pyramid">>
    <<set $shapes_like_you to false>>

<<if $shapes_like_you>>
    Sphere: Nice to meet you {$name}!
    Cube: Yeah, likewise!
    Cube: Do you wanna play a game?
    -> Yeah, sure!
        Sphere: Let's play Two Truths and a Lie then!
        <<jump TwoTruthsGame>>
    -> Ugh, no.
<<endif>>
// if they didn't offer to play a game, or they did but you said no,
// then they don't like you now
<<jump BadEnding>>
===
title: TwoTruthsGame
---
<<set $rounds_won to 0>>
Cube: I'll go first! Hmmm...
Cube: I have 6 faces. I have 12 edges. My favourite colour is blue.
Cube: What do you think is the lie?
-> The number of faces.
-> The one about edges.
-> Your favourite colour, obviously.
    <<set $rounds_won += 1>>
Sphere: I agree.

<<if $rounds_won > 0>>
    Cube: You're too smart!
<<else>>
    Cube: Wrong! Haha
<<endif>>
Cube: Of course my favourite colour is red!

Sphere: My turn!
Sphere: I am oblate. I am prolate. I am neither.
-> The first one.
-> The second one.
-> The neither.
-> Hang on, that doesn't work.
    That's two lies and one truth!
    <<set $rounds_won += 1>>
Sphere: Oh wait, I think I've mucked it up...

<<if $rounds_won == 2>>
    Cube: You won both rounds, congrats!
<<elseif $rounds_won == 1>>
    Cube: Well, at least you got one right.
<<else>>
    Sphere: At least we both lost together.
<<endif>>

-> That was great!
    <<jump GoodEnding>>
-> This game is lame.
    <<jump BadEnding>>
===
title: GoodEnding
---
Cube: I think we're friends now.
Sphere: Agreed.
Cube: What do you think, {$name}?
-> BFFs, for sure!
-> As if I'd be friends with you two!
    <<jump BadEnding>>
===
title: BadEnding
---
Sphere: No need to be so rude...
Cube: Yeah, maybe you should be called Grumpy {$name}.
Sphere: Ha! Totally.
===

Now, let's move onto an example where Yarn Spinner leverages the power of Unity to change things in the scene as well as running dialogue...

Yarn Variables and Variable Storage

When writing Yarn scripts, variables come in handy for storing state and user preferences that can persist and impact story dialogue or choices later on. When using Yarn Spinner for Unity, variables from Yarn scripts can be accessed in C# code by using the provided InMemoryVariableStorage, which acts as a simple dictionary to store variable names with their current values.

This looks something like this:

This allows Yarn types String, Number and Boolean to be stored in memory, and then accessed by this wrapper class that converts them to the C# equivalents string, float and bool, ready for use in your code.

InMemoryVariableStorage is flexible and extensible, and has utilities for things such as initialising with default variables declared, or serialising to and from JSON. But what if you want to add very custom behaviour to how variables are stored? To keep values somewhere other than in memory, or add side effects to certain operations in a way that wouldn’t work by just extending this default variable storage? Well, you can define your own.

What makes a Variable Storage?

Like other parts of the Unity API for Yarn Spinner, Variable Storage is made possible with the use of abstract classes. work a little bit like interfaces or protocols in other languages, in that they define a class that cannot be instantiated but can be used to make others. In this way, an abstract class is like a set of constraints for some hypothetical subclass you will define later: it can declare certain methods which your subclass must implement for it to work, and it can contain implementations or values of its own which act as defaults that you may or may not choose to override.

In Yarn Spinner for Unity, VariableStorageBehaviour is an abstract class that can be inherited from. It specifies the methods which Yarn Spinner may call at runtime, which are expected to be dealt with in your implementation:

Now, Yarn Spinner does not care how your custom VariableStorageBehaviour works beyond that. It simply assumes that you are doing something sensible, and that your subclass will provide the functionality it expects. Some of those expectations cannot be constrained in code, like the required method declarations can, so there is a level of trust here that you (as the implementer of this black box subclass which Yarn Spinner has never seen) will:

  1. Actually store values somewhere. Your code will still compile if your SetValue() methods are empty or otherwise throw away the values they are given, but this will mean your TryGetValue() methods will never be able to work.

  2. Actually get the right value for the given key. Your code will still compile if your TryGetValue() methods return random values from the aether, but this will make your use of these variables in your Yarn script effectively nonsensical. Likewise if you allow setting of multiple values with the same key.

  3. Actually get rid of values when asked to. Your code will still compile if your Clear() method does nothing, but this means that Yarn script progress or state may never be reset correctly.

  4. Actually check if a key already exists. Your code will still compile if your Contains() method always returns false, but this will lead to overwriting existing values the next time someone tries to SetValue() a seemingly unused key that already had a value.

So let’s assume you are not some chaos demon and you actually want to make a Variable Storage that works the way the Yarn Spinner runtime expects, so that you get variables that actually work. You need:

  • A way to store values of the given types, each associated with a unique key.

  • A way to get those values back, as the expected type.

  • A way to get rid of all the previously stored values.

If you were a masochist, you could write a class whose SetValue() method printed out the given key and value on a piece of paper, Contains() and TryGetValue() methods that took a snapshot with a camera placed above the printer and read the values back, and a Clear() method that pushed the paper from the printer tray into a shredder. Yarn Spinner would not care, because it would still do those three things (though probably unreliably, and with some storage limitations).

Some more typical examples of things that gamemakers have wanted their variable storage to do are:

  • Instead of storing variables in memory in a dictionary, store them on disk or in a database.

  • Instead of just setting values in the Variable Storage when asked, also update some corresponding variables on the C# side or call a UnityEvent to notify other components that a value has changed.

  • Instead of simply getting and setting values, run them via some sanitation or transformation, or even interface with an external API.

So let’s break down how you would go about implementing one of those more sensible ideas...

Let’s make a custom Variable Storage!

In this example, let’s replace the default Variable Storage implementation with one that stores values in a SQL database. The example code shown makes use of the library—an open source .Net API for SQL—for the creation of a database and tables, but uses vanilla SQL query strings in place of the convenience bindings which are specific to that library.

SQL is a domain-specific language and set of related frameworks that allow the creation and manipulation of relational databases. This will not be a guide to SQL, as there are many good ones already out there, but the TL;DR of SQL is: data is stored in tables, each column has a name and a type, each row is an entry, and some entries may reference entries in other tables that hold related information. SQL queries can be used to connect information from across tables, to get the fields of information you want.

To begin, we need to make a custom class for our new Variable Storage, which should inherit from the VariableStorageBehavour abstract class.

If you are following along, your IDE will probably complain at this point, because this empty class does not fulfil the requirements defined by the abstract superclass. To conform, we need at least the six methods listed earlier.

So let’s have a think about how each of these would need to work, given a backing of SQL. We need to be able to insert values into tables, check if a value exists in tables with the given key, return the corresponding value for a given key, and remove all entries from tables.

But first, before any values can be set, the database needs to already exist. Set up like this conventionally occurs in the Start() method:

Next, to create the tables we need to store values in, we need to declare a class that represents a single entry. Its class name will becomes the table name by default, and its field names and types will become the column names and types. Because each column can only hold one type, we’ll need one table for each type.

These classes would look something like this:

The column that will be used to reference or fetch values—and is thus required to be unique within that table—is specified by the [PrimaryKey] decorator.

Then, to create an empty table in the database, we can call the database connector’s CreateTable() method with the class we want to represent.

Those familiar with SQL may notice that these tables do not reference each other and thus this is not an ideal use case for SQL. But this is a minimal example for a method that would be more effectively used in larger games with more complex schemas for their data storage or persistence.

Now we can begin filling out our empty method declarations. Beginning with the easiest, Clear() is just a matter of telling each table in the database to remove all its entries. The query for this is DELETE * FROM TableName, where the * means all entries. Executing a query on the database is as simple as calling Execute() on the database connector with a string parameter of the desired query.

Now to the fiddliest method, TryGetValue() is the method that needs to figure out whether a value exists for the given key and, if so, return it as the correct type. This requires a little bit of .

First we need to do some switching of which table we need to look for the value in:

Then, within each, we should look for that key within the corresponding table. To return only the value from any row that matches our variable name we specify Select ColumnName FROM TableName WHERE (conditions to match).

To make sure the compiler knows what T is at compile time, results must be cast to object and then back to T (thanks, C#!).

Next, before we can begin inserting values into tables, we first want to make sure a value doesn’t already exist for that key in another table. We can do this by creating a utility method that uses a lookup query to check if a value exists with that key in a specific table. This can take advantage of our TryGetValue() implementation:

...which can then also be used as the basis for our Contains() method, by checking them all:

This utility method then also comes in handy when defining the SetValue() methods, which would each look something like this:

In production, you should always validate and sanitise input before inserting it into SQL, in case our string value itself contains invalid syntax or partial SQL commands. Otherwise, you may leave yourself open to SQL injection attacks.

And lo! We should now have a fully functioning SQL-backed custom Variable Storage for Yarn Spinner. Simply replace the Variable Storage component on the DialogueRunner game object in your scene to put your custom implementation to work.

As far as Yarn Spinner is concerned, this should behave exactly as the provided InMemoryVariableStorage does at runtime, even though the entire storage model and behaviour has changed.

Using this simple method of overriding methods in the inbuilt VariableStorageBehaviour abstract class, you can make a custom Variable Storage backed by virtually anything to suit your needs!

Where to go to learn more

Check out the or ask the community in the !

You can download the full implementation of the script made in this guide . Or you may also like to read through the default implementation of InMemoryVariableStorage .

Example.yarn
<<set $testVariable = 1>>
Example.cs
variableStorage = GameObject.FindObjectOfType<InMemoryVariableStorage>();
float testVariable;
variableStorage.TryGetValue("$testVariable", out testVariable);
variableStorage.SetValue("$testVariable", testVariable + 1);

TryGetValue(string variableName, out T result)

Look to see if variableName exists and can be cast to the given type and, if so, return its value.

SetValue(string variableName, string stringValue)

Store the value stringValue and somehow attribute it with the key variableName.

SetValue(string variableName, float floatValue)

Store the value floatValue and somehow attribute it with the key variableName.

SetValue(string variableName, bool boolValue)

Store the value boolValue and somehow attribute it with the key variableName.

Clear()

Remove, release or otherwise un-attribute all previously set variable names, such that calling TryGetValue() without first calling SetValue() with the same key would now fail.

Contains(string variableName)

Return whether a particular variableName exists as a key in the storage at this moment.

using UnityEngine;
using Yarn.Unity;
using SQLite;

public class SQLVariableStorage : VariableStorageBehaviour {}
public override bool TryGetValue<T>(string variableName, out T result) {}
public override void SetValue(string variableName, string stringValue) {}
public override void SetValue(string variableName, float floatValue) {}
public override void SetValue(string variableName, bool boolValue) {}
public override void Clear() {}
public override bool Contains(string variableName) {}
void Start() {
        // pick a place on disk for the database to save to
        string path = Application.persistentDataPath + "/db.sqlite";
        // create a new database connection to speak to it
        db = new SQLiteConnection(path);
        // TODO: create the tables we need ??
        // ...
}
public class YarnString {
	[PrimaryKey]
	public string key { get; set; }
	public string value { get; set; }
}
public class YarnFloat {
	[PrimaryKey]
	public string key { get; set; }
	public float value { get; set; }
}
public class YarnBool {
	[PrimaryKey]
	public string key { get; set; }
	public bool value { get; set; }
}
void Start() {
        // pick a place on disk for the database to save to
        string path = Application.persistentDataPath + "/db.sqlite";
        // create a new database connection to speak to it
        db = new SQLiteConnection(path);
        // create the tables we need
        db.CreateTable<YarnString>();
        db.CreateTable<YarnFloat>();
        db.CreateTable<YarnBool>();
}
public override void Clear() {
    db.Execute("DELETE * FROM YarnString;");
    db.Execute("DELETE * FROM YarnBool;");
    db.Execute("DELETE * FROM YarnFloat;");
}
public override bool TryGetValue<T>(string variableName, out T result) {
    if (typeof(T) == typeof(string)) {
        // TODO: search YarnString for variableName
    } else if (typeof(T) == typeof(bool)) {
        // TODO: search YarnBool for variableName
    } else if (typeof(T) == typeof(float)) {
        // TODO: search YarnFloat for variableName
    }
    result = default(T);
    return false;
}
public override bool TryGetValue<T>(string variableName, out T result) {
    string query = "";
    List<object> results = null;
    // try to get a value from the given table, as a generic object
    if (typeof(T) == typeof(string)) {
        query = $"SELECT value FROM YarnString WHERE key = {variableName}";
    } // else if ...
    // (other cases go here)
    
    // if a result was found, convert it to type T and assign it
    results = db.Query<object>(query);
    if (results?.Count > 0) {
        result = (T)results[0];
        return true;
    }
    
    // otherwise TryGetValue has failed
    result = default(T);
    return false;
}
private bool Exists(string variableName, System.Type type) {
    if (type == typeof(string)) {
        string stringResult;
        if (TryGetValue<string>(variableName, out stringResult)) {
            return (stringResult != null);
        }
    } else if (type == typeof(bool)) {
        string boolResult;
        if (TryGetValue<string>(variableName, out boolResult)) {
            return (boolResult != null);
        }
    } else if (type == typeof(float)) {
        string floatResult;
        if (TryGetValue<string>(variableName, out floatResult)) {
            return (floatResult != null);
        }
    }
    return false;
}
public override bool Contains(string variableName) {
    return Exists(variableName, typeof(string)) || 
        Exists(variableName, typeof(bool)) || 
        Exists(variableName, typeof(float));
}
public override void SetValue(string variableName, string stringValue) {
    // check it doesn't exist already in other table
    if (Exists(variableName, typeof(bool))) {
            throw new System.ArgumentException($"{variableName} is a bool.");
    // check if doesn't exist already in other other table
    } else if (Exists(variableName, typeof(float))) {
            throw new System.ArgumentException($"{variableName} is a float.");
    }
    // if not, insert or update row in this table to the given value
    string query = "INSERT OR REPLACE INTO YarnString (key, value)";
    query += $"VALUES ({variableName}, {stringValue})";
    db.Execute(query);
}
Abstract classes in C#
sqlite-net
C# generics
documentation on Variable Storage
Yarn Spinner Discord Server
here
here
Artist’s Impression of a malicious custom VariableStorage that does not look after its values.

NPC Dialogue Game

This example project demonstrates making a simple non-linear dialogue-based game when beginning with some pre-existing assets.

Goals

  1. Display Yarn dialogue in a Unity scene

  2. Allow a player to select between options to respond

  3. Allow a player to select among available characters to speak to

  4. Use Yarn Spinner to trigger a command that changes the interactability of characters

Materials

  • Yarn Spinner installed in Unity

  • Yarn Spinner set up in a text editor

  • Starter Asset Package downloaded and unzipped

Instructions

Open a new Unity 3D project. Ensure Yarn Spinner has been added to the project in the Package Manager as per the Installation Instructions.

Drag the provided Asset Package into the Project Window where project files are displayed in Unity to import them into the project.

The provided assets are being imported

This package includes the following assets and functionality:

  1. A simple, static environment called Graveyard which also contains four character models.

  2. A C# script that provides simple functions for the character objects.

  3. A Timeline that stores the hovering animation for the Ghost character.

  4. Some Lights that turn on and off to indicate when a Grave character is speaking.

Creating a Runnable Script

The next step is to import the Dialogue System and hook up a Yarn Project and Yarn Script. If you have completed Example Project 1 or Example Project 2 before, you may skip ahead to Filling Out Your Script. Otherwise, let's proceed!

Yarn Spinner for Unity comes with a pre-made UI layer and accompanying utility scripts to handle displaying lines and presenting options from Yarn files. In the Project Window again, navigate to Packages > Yarn Spinner > Prefabs and drag Dialogue System.prefab into the scene.

The Dialogue System has been added from the Project Window into the Scene

When the Dialogue System in the scene is selected, the Inspector will display the Yarn Project it is expecting line from. Here, a Yarn Project is a kind of linking file that groups Yarn script files together. To make one, navigate to a sensible place for the file to live (such as Assets > Dialogue) and right-click the Project Window pane to select Create > Yarn Spinner > Yarn Project.

The existence of Yarn Projects allows larger games with multiple dialogue systems (e.g. main story dialogue, barks, storylets) to separate into multiple projects that pass lines to different UI or systems. This allows an extra level of organisation above separate Yarn files which are typically used to separate story scenes or parts.

However, most games will need only a single Yarn Project.

Select the scene's Dialogue System again and drag the new Yarn Project into the labelled slot in the Inspector.

The new Yarn Project has been added to the Dialogue System's Dialogue Runner

Now the Yarn Project needs one or more Yarn Scripts to get dialogue from. Just like with the Yarn Project, navigate to the desired file location and select Create > Yarn Spinner > Yarn Script. Then, with the Yarn Project selected, drag the newly created script into the Inspector slot labelled Source Scripts. Click Apply.

The new Yarn Script has been added to the Yarn Project's Source Scripts

Filling Out Your Script

By default, a new Yarn Script begins with a single empty node with the name of the file. Open the file, rename the node to Start and put a single line of test dialogue. You may remove the tags field.

title: Start
---
This is a line of test dialogue.
===

Returning to Unity, pressing the ▶️ button results in the test line being displayed in front of the graveyard scene. Pressing Continue will make the UI disappear, as it has reached the end of the script.

The test line from the Yarn Script has been displayed in the otherwise non-interactable scene

It's time to plan a story. In the scene there are four characters—Ghost, LeftGrave (Louise), CenterGrave (Carol), and RightGrave (Ruby)—and the intent of this game is for the player to be able to interact with them in virtually any order to complete the objectives of the game. This game format typically accompanies stories where the player must piece together information from smaller tidbits given to them when they speak to different characters.

For example: neither Witness A nor B knew who stole the cookie from the kitchen, however:

  • Witness A knew the cookie was taken in the morning.

  • Witness B knew that Suspects A and B entered the kitchen in the morning and afternoon, respectively.

Together, their clues show that Suspect A must have eaten the cookie.

So, when the game begins, Ghost will present some mystery. Once a brief context-establishing conversation ends, the player will be free to select which character to speak to next. Speaking to each of the Grave characters will present a clue, provided the required prerequisite clues are known. At any time, the player can present their collated clues to Ghost. If their clues are complete, Ghost will tell them they solved the mystery and the game will end.

This short story provides a looping circuit through four paths, and results in the player reaching the ending after an undetermined number of conversations (though there is a hypothetical minimum, there is no maximum). A railroad diagram representation of the story would look as follows:

So it's time for the actual writing part. Here, I've opened my new Yarn Script in Visual Studio Code with the Yarn Spinner Extension installed as per the Installation Instructions. I've written a minimal script that follows the planned story, as a skeleton that can be expanded on later.

The new Yarn Script has been given some simple content

In this script, selecting the correct conversation option when speaking to each character will yield a new clue. However, the correct option is only available if the player has the required prerequisite clues. So no matter the order the player speaks to the graves, they must acquire clues in the order A then B then C.

The most notable part about this script is that there are no jump statements in the file at all; each node is completely disconnected from the rest. Instead, we will be requesting and jumping to specific nodes manually from within Unity.

You can find this example script below to copy. Or if you want to make you own version and need a refresher on how to represent it in Yarn, refer to the Syntax and File Structure guide.

GhostyLads.yarn
title: Start
---
<<set $hasClueA to false>>
<<set $hasClueB to false>>
<<set $hasClueC to false>>
Ghost: Welcome to the graveyard! Unfortunately, you're just in time for an unsolved mystery...
Ghost: You'll have to speak to these three to figure out what happened!
===
title: LeftGraveLouise
---
Louise: What do you want to know?
-> Something that will get me no clues?
-> Something that will get me Clue A? <<if not $hasClueA>>
    <<set $hasClueA to true>>
-> Something relating to existing Clue A? <<if $hasClueA>>
-> Something relating to existing Clue B? <<if $hasClueB>>
-> Something relating to existing Clue C? <<if $hasClueC>>
Louise: ~additional dialogue~
Louise: Ok, bye!
===
title: CenterGraveCarol
---
Carol: What do you want to know?
-> Something that will get me no clues?
-> Something that will get me Clue B? <<if $hasClueA and not $hasClueB>>
    <<set $hasClueB to true>>
-> Something relating to existing Clue A? <<if $hasClueA>>
-> Something relating to existing Clue B? <<if $hasClueB>>
-> Something relating to existing Clue C? <<if $hasClueC>>
Carol: ~additional dialogue~
Carol: Ok, bye!
===
title: RightGraveRuby
---
Ruby: What do you want to know?
-> Something that will get me no clues?
-> Something that will get me Clue C? <<if $hasClueB and not $hasClueC>>
    <<set $hasClueC to true>>
-> Something relating to existing Clue A? <<if $hasClueA>>
-> Something relating to existing Clue B? <<if $hasClueB>>
-> Something relating to existing Clue C? <<if $hasClueC>>
Ruby: ~additional dialogue~
Ruby: Ok, bye!
===
title: Ghost
---
Ghost: Are you ready to tell me what happened?
-> Yes
    Ghost: Well, what do you know?
    -> I have no clues.
    -> I have clue A. <<if $hasClueA>>
    -> I have clues A and B. <<if $hasClueB>>
    -> I have clues A, B and C. <<if $hasClueC>>
        <<jump Ending>>
    Ghost: That doesn't sound right...
-> No
Ghost: Go on and speak to those three!
===
title: Ending
---
Ghost: You solved it!
===

Once you've got a basic story, pop back into Unity and check the basics:

Yarn Spinner is displaying lines, advancing lines and selecting options correctly as per the script

Note that at this point, there is no way to progress beyond the intro conversation with Ghost. All other nodes cannot be reached with the code we have written so far.

Making Players Interactable

In this game, the player should be able to select an NPC in the scene and have it trigger their repsective conversation. This requires a few things:

  • Code to begin dialogue from a specific node when a character object is interacted with.

  • Code to disable scene interaction when any character is already speaking.

  • Code to disable character interaction when a specific character should not be interactable.

In Assets > Scripts there is a C# script that has code to do these things (see headers below), so we just need to connect it to the appropriate places. But first, let's step through what it does.

// this file is attached to every character in the scene and so will affect only
// the targeted character object when functions are called

// disable scene interaction, activate speaker indicator, and
// run dialogue from {conversationStartNode}
private void StartConversation();

// reverse StartConversation's changes: 
// re-enable scene interaction, deactivate indicator, etc.
private void EndConversation();

// make character not able to be clicked on
public void DisableConversation();

Add the YarnInteractable script to each character in the game: Ghost, LeftGrave, CenterGrave, and RightGrave. Make sure to set their respective conversationStartNode values in the Inspector to match what they are called in the Yarn script.

A character is made interactable through the addition of the YarnInteractable script with the correct conversationStartNode value

First up is the code for beginning a conversation. This requires running dialogue from a specific node when a character is interacted with. Running dialogue is a simple matter of telling the DialogueRunner to begin dialogue and passing the name of the node to begin from as a string.

// first we need a handle on the DialogueRunner, so we may as well get it when
// this object is first added to the scene
private DialogueRunner dialogueRunner;

public void Start() {
    dialogueRunner = FindObjectOfType<Yarn.Unity.DialogueRunner>();
}

// then we need a function to tell Yarn Spinner to start from {specifiedNodeName}
public string conversationStartNode;

private void StartConversation() {
    dialogueRunner.StartDialogue(conversationStartNode);
}

To run this when a character is interacted with, simple override the OnMouseDown() function that exists for every GameObject (which this class inherits from). Checking the IsDialogueRunning property of the DialogueRunner is a simple way to ignore interaction whenever starting dialogue would interrupt an existing conversation.

private bool interactable; // whether this character should be enabled right now
// (begins true, but may not always be true)

public void OnMouseDown() {
    // if this character is enabled and no conversation is already running
    if (interactable && !dialogueRunner.IsDialogueRunning) {
        // then run this character's conversation
        StartConversation();
    }
}

This handles beginning a conversation, but what if other changes are needed while a character is speaking? Well, having a function that is triggered when a conversation ends would allow properties to be set in StartConversation that can then be reversed once dialogue has ended.

In a typical game, several changes would be triggered when beginning or ending dialogue, such as changing UI mode and starting and stopping a speaking animation on the relevant character or similar. So it's sensible to have bookend functions that hold all this code, even if we won't be doing anything useful with EndConversation()until later.

To trigger a function when a conversation ends, a listener can be added to the DialogueRunner that will fire a specified function when a certain event occurs. The onDialogueComplete event happens whenever the runner reaches the end of its current conversation path.

// this would go in the Start() function, right after finding the DialogueRunner
// object, so that the listener begins before dialogue does the first time
dialogueRunner.onDialogueComplete.AddListener(EndConversation);

Next, let's define the function it will call. A key consideration here is that every object using this YarnInteractable script will be notified of dialogue completion every time it happens. Each one is registering a listener, and each will have its own EndConversation() function be called.

So to save some work, we can check whether this is the instance that should care about the event. This can be done just by setting a boolean when the conversation begins, that says this is the character that is currently speaking.

private bool isCurrentConversation;

private void StartConversation() {
    isCurrentConversation = true;
    // TODO *begin animation or turn on speaker indicator or whatever* HERE
    dialogueRunner.StartDialogue(conversationStartNode);
}

private void EndConversation() {
    if (isCurrentConversation) { 
        // TODO *stop animation or turn off indicator or whatever* HERE
        isCurrentConversation = false;
    }
}

Returning to Unity, press the ▶️ button and see that this now allows a new conversation to be triggered by interacting with any character after another has finished speaking.

Clicking on a YarnInteractable object runs the corresponding node dialogue

At this point, this may seem done, but there is a critical issue here. Looking back at the earlier goals:

Why that third thing? Well, because the current state of the game allows the player to:

  1. Speak to the Ghost to begin the story.

  2. Ask the Graves about the mystery.

  3. Speak to the Ghost once they have collected the necessary clues.

  4. Have Ghost tell them they ✨ solved the mystery ✨ and say goodbye.

  5. Go back and still ask the Graves about the mystery that is already solved(?!)

So there needs to be a way to tell a specific character "no, you are done, don't speak to the player any more". This can be done with a simple Yarn Command.

In YarnInteractable.cs there is a simple function that sets a flag that the OnMouseDown() function checks when deciding whether to start a conversation. Turning this into a command simple requires adding the Yarn Command decorator above the function, with the string that will become the command keyword in any yarn scripts.

[YarnCommand("disable")]
public void DisableConversation() {
    interactable = false;
}

Back in the Yarn script, call disable once for each character when the story ends, to save the player from going back and having a confusion conversation about an already-solved mystery.

title: Ending
---
Ghost: You solved it!
<<disable Ghost>>
<<disable LeftGrave>>
<<disable CenterGrave>>
<<disable RightGrave>>
===

And that's it for the dialogue behaviours! Back in Unity, characters should speak when interacted with—but not when it would interrupt another, or when the story has ended.

Characters can now be selected and will speak when appropriate

Draw the Rest of the Owl

Now that all the behaviours are working and the skeleton story plays through correctly, it's time to replace the skeleton placeholder script with the full story and add some polish. In Assets > Dialogue find the file GhostyLads_FinalVersion.yarn. In the Yarn Project created earlier, replace the script you placed in Source Scripts earlier with this script instead.

The Yarn script for the Scene has been replaced with a full story version

Next, let's add an indicator so the player more easily knows which character is currently speaking.

Because I am not an animator, I have used only static objects as characters in this demo game. To indicate who is speaking, I will be using a simple spotlight that turns on and off above the speaker. I will not be taking questions at this time.—Mars, 2022.

In the scene, each grave object also contains a green spotlight which is currently assigned to a variable called lightIndicatorObject in YarnInteractable.cs, so a snippet of code in each of theStartConversation() and EndConversation() functions can quickly turn it on and off for the relevant character.

private void StartConversation() {
    isCurrentConversation = true;
    if (lightIndicatorObject != null) {
        lightIndicatorObject.intensity = defaultIndicatorIntensity;
    }
    dialogueRunner.StartDialogue(conversationStartNode);
}

private void EndConversation() {
    if (isCurrentConversation) {
        if (lightIndicatorObject != null) {
            lightIndicatorObject.intensity = 0;
        }
        isCurrentConversation = false;
    }
}

Now, a light should turn on above any grave who is currently speaking.

The current speaker is now indicated by a green spotlight

Result

A playable whodunnit-like game with multiple characters that can be spoken to in any order to solve a mystery from partial clues available.

The game is complete and playable with characters that can be spoken to in any order

An easy way to spice this up is to just try replacing provided assets with ones of your own choosing. There are plenty of publically available asset packs with permissive licenses to play with, and re-theming a starter game and building from there can be easier than starting from scratch.

You could add more characters, or even design a more complex conversation structure that must be navigated to solve the mystery! Yarn Spinner is great at telling complex non-linear stories where player choices matter.

Choose-Your-Path Game with Visuals

This example project demonstrates making a simple dialogue-based game when beginning with some pre-existing assets.

Goals

  1. Display Yarn dialogue in a Unity scene

  2. Allow a player to select between options to respond

  3. Use Yarn Spinner to trigger commands that change the scene, camera and characters

Materials

  • Yarn Spinner installed in Unity

  • Yarn Spinner set up in a text editor

  • Starter Asset Package downloaded and unzipped

Instructions

Open a new Unity 3D project. Ensure Yarn Spinner has been added to the project in the Package Manager as per the Installation Instructions.

Drag the provided Asset Package into the Project Window where project files are displayed in Unity to import them into the project.

The provided assets are being imported

To see the Scene containing the imported assets, you'll need to open it. In the Project Window, navigate to Assets > Scenes and select SpaceJourney.unity.

This package includes with the following assets and functionality:

  1. A simple, static environment called Spaceship.

  2. Three Character models that each come with 6 available poses and 5 facial expressions.

  3. UI Layers that appear in front of the camera to present a black screen or title screen.

  4. Location Markers which are named but invisible objects in the scene that can act as anchors to move other objects to predefined positions.

  5. Four C# scripts that are attached to some of the above objects to provide simple functions.

So the Scene looks somewhat like this, except that Locations and the markers within them are invisible. Here, orange diamond 🔶 icons are markers intended for the camera to move to and blue circle 🔵 icons are intended for characters.

You can see these markers yourself in Unity by selecting each marker in Location Markers and allocating them an Icon using the dropdown at the top of the Inspector. The markers named Camera are the camera markers and the ones named like Left or Right are character markers.

Camera markers have been assigned 🔶 icons and character markers 🔵 icons

Creating a Runnable Script

The next step is to import the Dialogue System and hook up a Yarn Project and Yarn Script. If you have completed Example Project 1 before, you may skip ahead to Filling Out Your Script. Otherwise, let's proceed!

Yarn Spinner for Unity comes with a pre-made UI layer and accompanying utility scripts to handle displaying lines and presenting options from Yarn files. In the Project Window again, navigate to Packages > Yarn Spinner > Prefabs and drag Dialogue System.prefab into the scene.

The Dialogue System has been added from the Project Window into the Scene

When the Dialogue System in the scene is selected, the Inspector will display the Yarn Project it is expecting line from. Here, a Yarn Project is a kind of linking file that groups Yarn script files together. To make one, navigate to a sensible place for the file to live (such as Assets > Dialogue) and right-click the Project Window pane to select Create > Yarn Spinner > Yarn Project.

The existence of Yarn Projects allows larger games with multiple dialogue systems (e.g. main story dialogue, barks, storylets) to separate into multiple projects that pass lines to different UI or systems. This allows an extra level of organisation above separate Yarn files which are typically used to separate story scenes or parts.

However, most games will need only a single Yarn Project.

Select the scene's Dialogue System again and drag the new Yarn Project into the labelled slot in the Inspector.

The new Yarn Project has been added to the Dialogue System's Dialogue Runner

Now the Yarn Project needs one or more Yarn Scripts to get dialogue from. Just like with the Yarn Project, navigate to the desired file location and select Create > Yarn Spinner > Yarn Script. Then, with the Yarn Project selected, drag the newly created script into the Inspector slot labelled Source Scripts. Click Apply.

The new Yarn Script has been added to the Yarn Project's Source Scripts

Filling Out Your Script

By default, a new Yarn Script begins with a single empty node with the name of the file. Open the file, rename the node to Start and put a single line of test dialogue. You may remove the tags field.

title: Start
---
This is a line of test dialogue.
===

Returning to Unity, pressing the ▶️ button results in the test line being displayed in front of the empty scene world. Pressing Continue will make the UI disappear, as it has reached the end of the script. If you do not see this, read onward for a common fix.

The test line from the Yarn Script has been displayed in the otherwise empty game

If you only see a black screen, the included fade-to-black layer is turned on and blocking the camera from seeing the scene. Hide this by selecting UI Layers from the Scene Hierarchy and unchecking the box at the top of the Inspector.

The Title Layer and Fade Layer objects have been hidden from the Scene

It's time to plan a story. In this Asset Package there are three character models called Engineer, Crewmate and Captain.

These low-poly spacefarers live and work on a spaceship with the player. It's a new day on the job in Space Fleet, the player is in the corridor and they must decide which of their three shipmates they're going to speak to. The choices presented are:

  • The Engineer, who will complain to the player about his job.

  • The Crewmate, who the player will attempt to convince should give them extra rations.

  • The Captain, who will try to judge whether the player is ready for action.

After a short conversation with the chosen character, a shipwide alert requests all hands report to the bridge. When the player arrives, the Captain reveals that space pirates are attacking. Then one of two things happens:

  1. If the player chose to speak to the Captain earlier, and succeeded in convincing her that they were ready for action, the player is sent to fight off the pirates and save the day.

  2. If the player either didn't speak to the Captain, or failed to convince her that they were ready for action, the Crewmate is sent instead.

This short story provides an initial choice between three paths, and results in the player achieving one of two endings. A tree representation of the story would look as follows:

So it's time for the actual writing part. Here, I've opened my new Yarn Script in Visual Studio Code with the Yarn Spinner Extension installed as per the Installation Instructions. I've written a minimal script that follows the planned story, as a skeleton that can be expanded on later.

The new Yarn Script has been given some simple content

You can find this example script below to copy. Or if you want to make you own version and need a refresher on how to represent it in Yarn, refer to the Syntax and File Structure guide.

SpaceJourney.yarn
title: Start
---
Player: Another day in Space Fleet. Might go have a chat...
// pick a person to speak to
-> Go see the Engineer as per orders
    <<jump TalkToEngineer>>
-> Meet up with your friend
    <<jump TalkToCrewmate>>
-> Go and talk to the Captain
    <<jump TalkToCaptain>>
===
title: TalkToEngineer
---
Engineer: Hello! I am the Engineer.
<<jump BridgeEnding>>
===
title: TalkToCrewmate
---
Crewmate: Hello! I am your Crewmate.
<<jump BridgeEnding>>
===
title: TalkToCaptain
---
Captain: Hello! I am the Captain.
Player: I want to talk to go on more missions.
Captain: Do you think you are ready?
-> Yes
    <<set $away_mission_readiness += 1>>
    // if so, ask again
    Captain: Really?
    -> ...yes?
        <<set $away_mission_readiness += 1>>
    -> Actually, no.
-> No
// now go to the ending
<<jump BridgeEnding>>
===
title: BridgeEnding
---
// everyone reports to the bridge
Captain: Pirates!
Player: Oh no!
// now change who goes depending on player actions
<<if $away_mission_readiness < 2>>
    Captain: Crewmate, go deal with those pirates!
    Crewmate: Yes, Captain.
<<else>>
    Captain: Player, you were just telling me how ready for this you are. 
    Captain: Go deal with those pirates!
    Player: Hooray!
<<endif>>
===

Once you've got a basic story, pop back into Unity and check the basics:

Yarn Spinner is displaying lines, advancing lines and selecting options correctly as per the script

Adding Commands

Speaking to an empty void is all well and good, but this particular game is going to be more compelling if it makes use of the provided assets to make dynamic visuals. So to empower our Yarn script to invoke changes in Unity, we'll make some Commands. For this project, we'll make commands to:

  • Move the Camera to preset locations, as if the player is moving.

  • Turn on and off UI elements, to create nice transitions during Scene changes.

  • Move Character models to preset locations, as if they are entering and exiting the Scene.

  • Change Character model animations and textures, as if they are showing different emotions.

The first two will need to exist throughout the Scene, while the next two should attach to specific Character objects so each can be controlled independently. In Assets > Scripts there are four C# scripts that have code to do each of these things (see headers below), so we just need to create commands that make the functionality available to Yarn scripts in the project.

// this file is attached to an empty GameObject in the Scene to make these
// functions available whenever the Scene is running

// moves Main Camera to location {location}>Camera in the scene
private void MoveCamera(Location location);

// fades in from a black screen over {time} seconds
private Coroutine FadeIn(float time = 1f);

// fades out to a black screen over {time} seconds
private Coroutine FadeOut(float time = 1f);
// this file is attached to every Character in the scene and so will affect only
// the targeted Character when functions are called

// moves Character to location {location}>{markerName} in the scene
public void Move(Location location, string markerName);

// tells the Character animator to jump to state {poseName} 
public void SetPose(string poseName);

// sets character expression texture to {expressionName} texture
public void SetExpression(string expressionName);
// YOU WILL NOT HAVE TO CHANGE THIS FILE
// this file is attached to the FadeLayer in the Scene and its functions only
// exist for functions in SceneDirector.cs to call them

// fades CanvasGroup to transparency {alpha} over {time} seconds
public IEnumerator ChangeAlphaOverTime(float alpha, float time);
// YOU WILL NOT HAVE TO CHANGE THIS FILE
// this file is attached to the parent Location object of markers in the Scene 
// and its functions only exist for functions in SceneDirector.cs and Character.cs 
// to call them

// finds marker {markerName} in this Location, if one exists
public Transform GetMarkerWithName(string markerName);

Scene-Wide Commands

Code for the scene-wide commands are included in Assets > Scripts > SceneDirector.cs. To make functions from the script available throughout the project, it is attached to an otherwise empty GameObject in the Scene called Scene Director.

With our first command we want to be able to be able to move the Scene's Main Camera to an invisible marker in the Scene with the given name. The function from SceneDirector.cs that we want to be able to invoke from Yarn is called MoveCamera() and it looks like this:

// moves camera to camera location {location}>Camera in the scene
private void MoveCamera(Location location) {
    Transform destination = location.GetMarkerWithName("Camera");
    Camera.main.transform.position = destination.position;
    Camera.main.transform.rotation = destination.rotation;
}

It takes a Location in the Scene, from the eligible options Title, Corridor and Bridge. **It then finds the location and facing of the marker named Camera in that Location** and sets the camera location and facing to that of the marker.

If the camera moves to the Title location, the Title Layer element will fill the screen and appear as if a splash screen was being shown. If moved to the Corridor or Bridge locations, it acts as the point of view of the player who is then seen to be currently in that location. The default camera location is Title.

Here, we want to make a Yarn command called camera that takes a location name and knows to pass it off to the MoveCamera() function in C# to make it happen. This will mean when the player has to move to the bridge, the Yarn script can just say <<camera Bridge>>.

Making a command that can then be used in Yarn is as simple as registering a Command Handler. A Command Handler tells the Dialogue System that a Yarn command exists with a given name, how many additional pieces of information it needs, and which C# function to pass this information to when it's called. Then, when the game runs, the Dialogue System will handle talking to C# for you.

Command Handlers have two important requirements:

  1. They must be created before the command can ever be called. Usually, this means you want to make it as part of the initial creation of the scene or the object it's attached to.

  2. They must be attached to the Dialogue System's Dialogue Runner object. It's the thing passing lines of dialogue to the scene that has to know to change behaviour if the next line it receives is a command instead of dialogue.

To satisfy the first point, we can register any Command Handlers in a function called Awake() that every Unity object has by default. This function is called when the object is created, and because our empty Scene Director object is always in the Scene this means it gets created as soon as the Scene does. Registering Command Handlers in the Awake() function of this object therefore means they will be registered before anything else happens when the game is run.

To satisfy the second, we need to find the Dialogue Runner in the scene and assign it to a variable in C# that we can then attach Command Handlers to. Because there is only one Dialogue Runner in the Scene, we can find it by asking Unity to give us all the objects in can find in the Scene of type DialogueRunner.

Altogether, this means two simple lines in the Awake() function of SceneDirector.cs:

// find the Dialogue Runner
dialogueRunner = FindObjectOfType<Yarn.Unity.DialogueRunner>();
// register Command Handler for <<camera NAME_OF_LOCATION>>
dialogueRunner.AddCommandHandler<Location>("camera", MoveCamera);

The Dialogue Runner now supports a command called camerathat has a parameter of type Location and will defer to the function MoveCamera() to make it happen.

Return to the Yarn script and add commands of syntax <<camera (location name)>> to the appropriate places:

  • Add <<camera Corridor>> to the top of the Start node.

  • Add <<camera Bridge>> to the top of the node where characters should move to the bridge.

  • To show the title before the game begins, add <<camera Title>> at the top of Start and then a call to the inbuilt <<wait (number of seconds)>> command to hold for a second or two before moving to the Corridor.

If you hid the Title Layer object earlier, be sure to unhide it now by selecting it and re-ticking the box at the top of the Inspector. If necessary, unhide UI Layers and re-hide just the Fade Layer.

These minimal changes to the Yarn script...

title: Start
---
<<camera Title>>
<<wait 2>> // hold for 2 seconds before changing
<<camera Corridor>>
// ... [lines omitted]
===
title: BridgeEnding
---
// everyone reports to the bridge
<<camera Bridge>>
// ... [lines omitted]
===

...should now result in the camera moving around the empty environment in the appropriate points in the script. Returning to Unity, press the ▶️ button and playthrough to check this works correctly.

When Yarn script needs to pass an argument of a project-specific type (like Location is) it simply searches the scene for objects of that type with the given name, so make sure you spell and capitalise Location names exactly as they are in the Scene.

The camera now moves around the Scene as commands are reached in the Yarn script

Onto the next command! Smash cuts are fine, but nice transitions are fancier. In the Scene there is a flat black layer called Fade Layer that sits in front of the camera. Changing its opacity can make the camera appear to fade to and from black. Back in SceneDirector.cs there is a line in the Awake() function that finds the objects of type Fade Layer in the Scene (there is only the one) and keeps it in a variable called fadeLayer, similar to how the Dialogue Runner was found earlier.

// find the Fade Layer
fadeLayer = FindObjectOfType<FadeLayer>();

Remember to unhide the Fade Layer if you hid it earlier, otherwise this command won't be able to find it. Re-tick the box at the top of the Inspector.

Then further down the file there are short functions called FadeIn() and FadeOut() that do just that, by changing the opacity of this stored layer over the given number of seconds (or defaulting to 1 second if no argument is provided).

// fades in from a black screen over {time} seconds
private Coroutine FadeIn(float time = 1f) {
    return StartCoroutine(fadeLayer.ChangeAlphaOverTime(0, time));
}

// fades out to a black screen over {time} seconds
private Coroutine FadeOut(float time = 1f) {
    return StartCoroutine(fadeLayer.ChangeAlphaOverTime(1, time));
}

These functions are a little different in that instead of returning nothing like the MoveCamera() function did, these functions return a Coroutine. This gives Yarn Spinner a handle to the process it triggered so that for operations that take time (like fading in a screen over a second or so) it knows not to trigger the next line of dialogue until that process has completed.

Again, the functionality that performs the actual opacity change is contained in a C# script attached to the relevant GameObject. In this case it is a file called FadeLayer.cs attached to the Fade Layer.

Adding commands for fadeIn and fadeOut works just like before. In the Awake() function of SceneDirector.cs by adding Command Handlers to the previously found Dialogue Runner.

// Handlers for <<fadeIn DURATION>> and <<fadeOut DURATION>>
dialogueRunner.AddCommandHandler<float>("fadeIn", FadeIn);
dialogueRunner.AddCommandHandler<float>("fadeOut", FadeOut);

Back in the Yarn script, add a <<fadeOut>> and <<fadeIn>> to either side of each camera or node change to make nice fade-to-black transitions between story parts. Because no argument is provided, this will perform a 1 second fade.

Including transitions between conversation nodes even if they occur in the same Corridor location will hide the characters appearing that will be implemented next.

A black overlay fades in and out to cover camera move transitions

All this took is a few more additions to the Yarn script:

title: Start
---
<<fadeIn>>
<<camera Title>>
<<wait 2>> // hold for 2 seconds before changing
<<fadeOut>>
<<camera Corridor>>
<<fadeIn>>
// ... [lines omitted]
===
title: TalkToEngineer
---
<<fadeOut>>
<<fadeIn>>
// ... [lines omitted]
===
title: TalkToCrewmate
---
<<fadeOut>>
<<fadeIn>>
// ... [lines omitted]
===
title: TalkToCaptain
---
<<fadeOut>>
<<fadeIn>>
// ... [lines omitted]
===
title: BridgeEnding
---
<<fadeOut>>
// everyone reports to the bridge
<<camera Bridge>>
<<fadeIn>>
// ... [lines omitted]
<<fadeOut>>
===

Character-Specific Commands

The next command will allow character models to be placed in the Scene whenever they are part of the current conversation. We could write these as before in SceneDirector.cs with a function that takes a Character to change and what to change about them, but instead we're going to try out another type of command.

This time, we're going to add commands to the script that's attached to each Character-typed object in the scene, found at Assets > Scripts > Character.cs. This script has three main functions we want to use: Move(), SetPose() and SetExpression().

In our example nobody ever leaves a location while the player is still there so there's no need to implement hide/show-like functionality; we can just cycle Characters between predefined Locations in the Scene.

First up in Character.cs, there is a function called Move() that accepts a Location object and the name of a marker in that Location. It looks very similar to the MoveCamera() function used earlier:

// moves character to location {location}>{markerName} in the scene
public void Move(Location location, string markerName) {
    Transform destination = location.GetMarkerWithName(markerName);
    transform.position = destination.position;
    transform.rotation = destination.rotation;
}

Much like before with MoveCamera(), Yarn Spinner is able to find the Game Object you are trying to command by searching the Scene for one with the right type and name. This is used to find the Location that should be passed as the first function argument, but also to specify the target Character this function should be called on.

So if we want a command like <<place (location) (marker name)>> we need to add an extra argument in between to specify the target Character object: <<place (character) (loc...>>.

But this can't be done like before with a Command Handler registered when the object is created. Because Character.cs is attached to every Character object in the Scene, the Awake() function would be called every time any Character was created. This would result in the Dialogue Runner receiving multiple registrations of command with the same name but that are requested to pass off to different objects. Yarn Spinner won't allow that, so we have to instead annotate the relevant functions using the Yarn Command decorator.

Declaring a Yarn Command is as simple as adding [YarnCommand("command name")] to the declaration of a function. Add the following decorator to just above the Move() function:

// moves character to location {location}>{markerName} in the scene
[YarnCommand("place")]
public void Move(Location location, string markerName) {

Now in the Yarn script, just add commands to move the relevant Character to the desired marker in the current Location by adding <<place (character name) (location) (marker name)>> wherever needed. Placing characters before calling <<fadeIn>> will ensure they are there before the shot appears, so the Character won't seem to pop into existence a fraction later.

Just a handful of additions to the Yarn script:

// ... [lines omitted]
title: TalkToEngineer
---
<<fadeOut>>
<<place Engineer Corridor Left>>
<<fadeIn>>
// ... [lines omitted]
===
title: TalkToCrewmate
---
<<fadeOut>>
<<place Crewmate Corridor Center>>
<<fadeIn>>
// ... [lines omitted]
===
title: TalkToCaptain
---
<<fadeOut>>
<<place Captain Corridor Right>>
<<fadeIn>>
// ... [lines omitted]
===
title: BridgeEnding
---
<<fadeOut>>
// everyone reports to the bridge
<<camera Bridge>>
<<place Engineer Bridge Left>>
<<place Crewmate Bridge Center>>
<<place Captain Bridge Right>>
<<fadeIn>>
// ... [lines omitted]
===

And now our Characters appear in the Scene whenever they're part of the current conversation.

Characters now appear in the Scene as commands are reached in the Yarn script

Next, we're going to add some pose and facial expression changes to make Characters respond to the changing mood of the story. Primarily because it's a bit weird that everyone looks so cheerful during a crisis...

The SetPose() function in Character.cs accepts the name of a pose and tells the animator attached to the Character model to move the model to that pose. The available poses for each model are defined by their underlying type seen in Assets > Art > CharacterBaseModels and the Asset Package has come with the following for both male and female models:

  • neutral

  • hand-on-hip

  • arms-out

  • hand-at-mouth

  • pointing

  • hands-on-hips

Mostly these just move their arms into different basic positions. The function looks like this:

// tell the animator to jump to state {poseName} 
public void SetPose(string poseName) {
    animator.Play("Base Layer." + poseName, 0);
}

As with the place command, add a decorator to SetPose() to declare a command called pose.

[YarnCommand("pose")]
public void SetPose(string poseName) {

Now characters can be moved using <<pose (character) (pose name)>> wherever called for in the script. In particular, adding appropriately unhappy-looking poses during the Bridge scene will make Characters seem to respond to the story.

title: BridgeEnding
---
// ... [lines omitted]
<<pose Engineer hands-on-hips>>
<<pose Captain hand-at-mouth>>
<<fadeIn>>
Captain: Pirates!
Player: Oh no!
<<pose Captain pointing>>
// ... [lines omitted]
===

Next, do the exact same thing for expressions. The options included for each character in the Asset Package are:

  • neutral

  • happy

  • angry

  • smirk

  • surprised

Facial expressions for the included characters are just a texture applied to the model's face, so changing expression just means asking the texture renderer to change face texture.

// sets character expression texture to {expressionName} texture
public void SetExpression(string expressionName){
    // find the expression with the same name as we are looking for
    Expression expressionToUse = FindExpressionWithName(expressionName);
    SetFaceTexture(expressionToUse.texture);
}

Decorate with [YarnCommand("expression")] and, back in the Yarn script, place calls to <<expression (character) (expression name)>> wherever appropriate.

Characters now respond appropriately to the threat of pirate attack

And that's it for commands! We successfully implemented commads to:

Draw the Rest of the Owl

Now that all the commands are hooked up and the skeleton story plays through correctly, it's time to replace the skeleton placeholder script with the full story. In Assets > Dialogue find the file SpaceJourney_FinalVersion.yarn. In the Yarn Project created earlier, replace the script you placed in Source Scripts earlier with this script instead.

The Yarn script for the Scene has been replaced with a full story version

Result

A playable visual novel-type game with multiple characters and scenes and sensible transitions between them.

The game is complete and playable with visuals, transitions and animations

An easy way to spice this up is to just try replacing provided assets with ones of your own choosing. There are plenty of publically available asset packs with permissive licenses to play with, and re-theming a starter game and building from there can be easier than starting from scratch.

Or you could try your hand at C# and create more advanced commands that can be made available to Yarn. Add new Locations, or camera motion. The sky's the limit! Yarn Spinner is perfect for allowing iterative and creative development.

Now, let's move onto an example that uses Yarn Spinner to run different dialogue based on players interacting with different NPCs...