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

Yarn Spinner 2.3

Loading...

Loading...

Beginner's Guide

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Writing Dialogue in Yarn

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Yarn Spinner for Unity

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

Unity Tutorial Projects

Loading...

Loading...

Loading...

Advanced Guides

Loading...

Loading...

About

Loading...

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

Start Here

If you're new to Yarn Spinner, or want a refresher on getting started or navigating the documentation, then this is the place to be.

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 free and open source and has been used in thousands of amazing games, including , , , , , , , , and .

Yarn Spinner is not just a single project or tool, but is a collection of projects and tools. It consists of a language to write your narratives in—Yarn—and tools to display, and use your Yarn in game engines, like Unity, and tools to translate, localize, and connect voice over to your stories.

To get started, dive into the .

Are you brand new to Yarn Spinner?

You should start with our Beginner's Guide.

No need to install anything first, just start with the Beginner's Guide, and work your way through three guided tutorials on the syntax of writing Yarn scripts, installing a text editor to write Yarn scripts, and then integrating Yarn scripts with your game, and installing Yarn Spinner as a package into your game engine.

We'd also recommend joining the Yarn Spinner Discord, where you can show off your work, and ask for help.

Yarn Spinner is provided as free, open source software by the team at Yarn Spinner Pty. Ltd. We want this to continue being our full time jobs. You can help by hiring us to help integrate Yarn Spinner in your games, build specific features you want, or consult, and by backing our Patreon.

Yarn Spinner
Night in the Woods
A Short Hike
Lost in Random
Dredge
Frog Detective
Button City
Escape Academy
Baladins
Unbeatable
Beginner's Guide

Welcome

The three-step beginner's guide to learning the basics of Yarn Spinner.

Getting Started

The best way to get started with Yarn Spinner, using the Core Components, is to work through our Beginner's Guide, which is a gentle introduction to:

  1. Syntax Basics — Using Try Yarn Spinner to learn the basic syntax for writing Yarn, the language for writing dialogue in Yarn scripts.

  2. Writing Narratives — Moving to Yarn Spinner for Visual Studio Code, and learning how to structure narratives and stories using features of the Yarn language.

  3. — Using Yarn Spinner for Unity or Yarn Spinner for Godot, and integrating a Yarn script with what’s happening in the game engine, as well as one simple way of nicely displaying your dialogue in-game.

This beginner's guide complements the rest of the documentation, and provides a starting point that covers all the basics necessary for making the most of the rest of the documentation. We strongly recommend you work your way through this guide.

Yarn Spinner Components

A quick summary of the various projects that make up the various components and experiments of the overall Yarn Spinner project.

Yarn Spinner isn't a single project, but is a collection of projects. Conceptually, we think of Yarn Spinner as having Core Components, some Add-ons, and some Yarn Labs Experiments.

These are production-ready components, with stable, established, released versions:

  • Yarn, the language you write your dialogue and narrative in: you write Yarn scripts.

Using a Game Engine

C#

Yarn Spinner for Visual Studio Code
, the
extension
for the popular free text editor, Visual Studio Code, that gives it an understanding of the Yarn language, and helps you to write Yarn
scripts
.
  • Yarn Spinner for Unity, the package you use to import and use your Yarn scripts in games you build in Unity.

  • Try Yarn Spinner, an online tool that allows you to write Yarn scripts and Play them in a web browser. It's useful to write basic Yarn, and test things out. It's just a website you can visit!

  • These are projects that supply additional features to Yarn Spinner, and exist as add-ons to the free, open source projects that comprise the bulk of Yarn Spinner:

    • Dialogue Wheel for Yarn Spinner

    • Speech Bubbles for Yarn Spinner

    These are experimental projects that are likely to eventually be released, but are currently in early, or experimental stages:

    • Yarn Spinner for Unreal, the package you use to import and use your Yarn scripts in games you build in Unreal.

    • Yarn Spinner for Godot, the package you use to import and use your Yarn scripts in games you build in Godot.

    If you're new to Yarn Spinner, we recommend that your next step is working through our three-step Beginner's Guide.

    Yarn Spinner Projects

    If you're new to Yarn Spinner, you don't necessarily need to understand the components just yet. We strongly recommend starting with our three-step Beginner's Guide.

    Core Components

    Yarn Spinner for Unreal is moving from Yarn Labs to Core Components in late-2023.

    Add-ons

    Yarn Labs Experiments

    Start learning

    Using a Game Engine

    The third step in our three-step beginner's guide to Yarn Spinner: making games in a game engine powered by Yarn.

    For this final step in the beginner's guide, you can choose your preferred engine:

    • Yarn Spinner for Unity (2021.3 or newer, Officially Supported)

    • Yarn Spinner for Unreal (5.1 or newer, coming Late-2023)

    We also provide experimental Yarn Labs support for:

    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.

    Importing Yarn Files

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

    • are files that link your Yarn Scripts together, and are used by the Dialogue Runner.

    • are files that contain your written dialogue.

    Installing the Extension

    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.

    Writing in Yarn

    Yarn Spinner is a tool for writers. In this section, you'll learn the syntax of Yarn, and learn how to write Yarn scripts for use in your game. You'll also learn how to use the various tools that are available for creating your content.

    You'll need an editor before you can write Yarn scripts, so we recommend checking out before going too far.

    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.

    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:

    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 , which loads and runs your scripts, and the that show content to your player.

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

    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:

    For a complete tutorial on how to build an entirely custom variable storage system, see .

    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 uses when presenting options. When the 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.

    Property
    Description

    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.

    If you've never used to Visual Studio Code before, for your operating system and platform, and then continue to .

    Any

    Property in

    Gets the type representing any value.

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

    Unreal support is currently in testing. Official release, along with tutorials, in late-2023. Join the official Yarn Spinner Discord for news and updates, or support us on Patreon.

    Yarn Spinner for Godot (C# edition 4.0 or newer).
    Editing with VS Code
    Dialogue Runner
    Dialogue Views
    Yarn Projects
    Yarn Scripts
    A Yarn Script, a string table, and a Yarn Project in Unity.

    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.

    head to their official website and install it
    Installing the Extension
    VS Code editing a Yarn script

    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.

    Inspector

    Options List View
    Dialogue Runner

    Text

    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);
    Yarn Variables and Custom Variable Storage
    public static IType Any { get; };

    Summary

    BuiltinTypes

    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; };

    DefaultStartNodeName

    Field in Dialogue

    Summary

    The node that execution will start from.

    public const string DefaultStartNodeName = "Start";

    Boolean

    Property in BuiltinTypes

    Summary

    Gets the type representing boolean values.

    public static IType Boolean { get; };

    NodeExists(string)

    Method in Dialogue

    Summary

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

    Parameters

    Name
    Description

    string nodeName

    The name of the node.

    Returns

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

    Number

    Property in BuiltinTypes

    Summary

    Gets the type representing numbers.

    public static IType Number { get; };

    String

    Property in BuiltinTypes

    Summary

    Gets the type representing strings.

    public static IType String { get; };

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

    LanguageCode

    Property in Dialogue

    Summary

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

    public string LanguageCode { get; set; }

    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.

    NodeCompleteHandler

    Property in Dialogue

    Summary

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

    public NodeCompleteHandler NodeCompleteHandler
    {
                get; set; }

    CurrentNode

    Property in Dialogue

    Summary

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

    public string CurrentNode
    {
                get; }

    Remarks

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

    UnloadAll()

    Method in Dialogue

    Summary

    Unloads all nodes from the Dialogue.

    public void UnloadAll()

    IsActive

    Property in Dialogue

    Summary

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

    public bool IsActive { get };

    LogErrorMessage

    Property in Dialogue

    Summary

    Invoked when the Dialogue needs to report an error.

    public Logger LogErrorMessage { get; set; }

    NodeNames

    Property in Dialogue

    Summary

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

    public IEnumerable<string> NodeNames
    {
                get; }

    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; }

    LogDebugMessage

    Property in Dialogue

    Summary

    Invoked when the Dialogue needs to report debugging information.

    public Logger LogDebugMessage { get; set; }

    DialogueCompleteHandler

    Property in Dialogue

    Summary

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

    public DialogueCompleteHandler DialogueCompleteHandler
    {
                get; set; }

    Library

    Property in Dialogue

    Summary

    Gets the Library that this Dialogue uses to locate functions.

    public Library Library { get; internal set; }

    Remarks

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

    public bool NodeExists(string nodeName)
    Search for the Yarn Spinner Extension. With the Extensions view open, type "Yarn Spinner" into the search field.
  • 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

    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.

    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:

    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 its GitHub project.

    Yarn Spinner Console GitHub Project
    here
    $ ysc upgrade myOldYarnFile.yarn
    ysc upgrade myOldYarnFile.yarn myOtherOldYarnFile.yarn

    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:

    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.

    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 part of a 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 , which has the same name as the file.

    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.

    Commands

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

    <<wait 2>>
    <<setsprite ShipName happy>>
    <<fade_out 1.5>>

    Commands are sent to your game's Dialogue Runner, just like lines and options are. Commands are not shown to the player directly; instead, they're used for things like stage directions.

    Yarn Spinner comes with some built-in commands; however, to get the most usefulness out of them, you'll want to define your own custom commands that make your game do what you need to.

    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.

    // Wait for 2 seconds
    <<wait 2>>
    
    // Wait for half a second
    <<wait 0.5>>

    stop

    The stop command immediately ends the dialogue, as though the game had reached the end of a node. Use this if you need to leave a conversation in the middle of an if statement, or a shortcut option.

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

    Line Provider

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

    When a Yarn script runs, the Dialogue Runner 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 three built-in types of line providers:

    • Text Line Provider 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.

    • is a Line Provider that fetches the text and any localised assets from .

    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 the Yarn Project is configured to use Addressable Assets.

    Use this Line Provider if you are using the system. If you are using the system, use the instead.

    Inspector

    Property
    Description

    Text Language Code

    GitHub Repositories

    Learn about the official GitHub repositories for the projects.

    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. You probably never need to touch this yourself.

    • Yarn Spinner for Unity —

      • This is the components of Yarn Spinner that relate to the Unity integration. This is likely to be something that you install via Unity's Package Manager, but never download directly.

    • Yarn Spinner for Unreal —

      • This is the components of Yarn Spinner that relate to the Unreal integration. This is currently an experimental labs project, but is scheduled for a supported release in late-2023

    • Yarn Spinner for Godot —

      • This is the components of Yarn Spinner that relate to the Godot C# integration. This is an experimental labs project, and is not officially supported yet.

    • Yarn Spinner Console (ysc) —

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

    • Visual Studio Code Extension —

      • This is the VS Code extension for Yarn Spinner. You're likely to install this via the VS Code Extensions system.

    Options List View

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

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

    When this view receives options from the Dialogue Runner, it creates an instance of the Option View 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

    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 or sets the that is called when a command is to be delivered to the game.

    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.

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

    Dialogue(Yarn.IVariableStorage)

    Constructor in Dialogue

    Summary

    Initializes a new instance of the Dialogue class.

    public Dialogue(Yarn.IVariableStorage variableStorage)

    Parameters

    Name
    Description

    variableStorage

    The that this Dialogue should use.

    Text Line Provider

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

    Use this Line Provider if you are using the system. If you are using the system, use the instead.

    A Text Line Provider

    Inspector

    Property
    Description

    Text Language Code

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

    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.

    Unity Quick Start

    Quickly get started with a simple scene.

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

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

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

    Previewing Your Dialogue

    You can run your Yarn script inside the Visual Studio Code extension, without having to import it into a game. This means that you can write content for your game even if the game isn't yet ready to have dialogue added to it. It's also useful for quickly checking to see if your Yarn script works the way that you want it to before testing it in your game.

    To preview your dialogue in Visual Studio Code, open the Command Palette by pressing Command-Shift-P (Control-Shift-P on Windows), and type "Preview Dialogue". Your current Yarn project will open to the side, and you can begin playing through your script.

    While playing through your dialogue in preview mode, there are several features that can be useful to test out a script.

    Dialogue Advance Input

    Learn about Dialogue Advance Input, a component that can signal to a Dialogue View that the user wants to proceed to the next piece of content.

    Dialogue Advance Input is a component that receives user input, and uses it to signal to a Dialogue View that the user wants to advance to the next piece of content. Dialogue Advance Input is generally used to implement a 'press spacebar to continue / skip' mechanic.

    To use a Dialogue Advance Input, create a new game object, and attach a Dialogue Advance Input component to it using the Add Component button.

    You can control what specific input the component is looking for by changing the Continue Action Type setting:

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

    Continue()

    Method in

    Starts, or continues, execution of the current Program.

    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.

    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.

    Many of these projects target an older version of Yarn Spinner, or are unmaintained:

    • , by : A Rust implementation of Yarn.

    • , by : A Python runner for Yarn.

    SetSelectedOption(int)

    Method in

    Signals to the that the user has selected a specified .

    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.

    Name
    Description

    ExpandSubstitutions(string,IList<string>)

    Method in

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

    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.

    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.

    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.

    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:

    BuiltinTypes

    Class in

    Inherits from System.Object

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

    Name
    Description

    Unity Localised Line Provider

    Unity Localised Line Provider is a Line Provider that fetches localized text and assets for a line of dialogue from a String Table and optionally from an Asset Table, based on the project's current localization settings.

    Property
    Description

    ParseMarkup(string)

    Method in

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

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

    Name

    SetProgram(Program)

    Method in

    Loads all nodes from the provided .

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

    Name
    Description

    GetStringIDForNode(string)

    Method in

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

    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.

    Name

    GetTagsForNode(string)

    Method in

    Returns the tags for the node nodeName .

    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.

    Name
    Description

    CommandHandler

    Delegate in

    Inherits from System.MulticastDelegate

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

    Name
    Description

    SetNode(string)

    Method in

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

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

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

    Name
    Description

    LineHandler

    Property in

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

    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.

    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.

    Audio Line Provider
    Audio Clip
    Unity Localised Line provider
    Unity's Localization system
    YarnSpinnerTool/YarnSpinner-Unity
    YarnSpinnerTool/YarnSpinner-Unreal
    YarnSpinnerTool/YarnSpinner-Godot
    YarynSpinnerTool/YarnSpinner-Console
    YarnSpinnerTool/VSCodeExtension

    Text

    Gets the text of the command.

    See Also

    Dialogue.CommandHandler
    CommandHandler
    Option List View
    Yarn.IVariableStorage
    IVariableStorage

    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.

    Built-In Localisation
    Unity Localisation
    Unity Localised Line Provider

    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.

    Line View
    In-Memory Variable Storage

    Crochet, by FaultyFunctions: A visual editor for writing Yarn script.

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

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

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

  • If you've made something cool that works with Yarn Spinner, please get in touch via Twitter or Discord! We'd love to show it off. ❤️

    Yarn Slinger
    Jan Hohenheim
    YarnRunner-Python
    Relay Pro

    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

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

    node
    Yarn Project
    Built-In Localisation
    Unity Localisation
    Unity Localised Line Provider

    (Optional) The Asset Table Collection containing localised assets. If an Asset Table is provided, then the Unity Localised Line Provider will fetch localised assets for each line, based on the line's ID.

    Strings Table

    The String Table Collection containing localised line text. See Unity Localization to learn how to populate it with your project's dialogue.

    Use this Line Provider if you are using the Unity Localisation system. If you are using the Built-In Localisation system, use the Text Line Provider or the Audio Line Provider instead.

    Inspector

    Assets Table

    Changing the starting node: By default, the dialogue preview will start playing from a node named "Start", if one is present in your Yarn files. If you want to play from a different node, open the list of nodes and choose the node you want to start from.
  • Changing whether lines are shown one at a time, or all at once: By default, the dialogue preview shows each line and command one and a time. You can change this to delivering everything all at once by opening the Settings menu and changing the setting to "Show Lines All At Once".

  • Showing the contents of variables: You can see the current contents of all variables in your script by opening the Settings menu and choosing "Show Variables". A list of variables will appear, and as you play through your script, they'll update when your script changes their contents.

  • Changing whether 'unavailable' lines are shown: Options can be marked as 'unavailable' depending on whether or not a test has passed. By default, options that fail this test are not shown in the dialogue preview at all; to change this to showing all options (including ones that the user can't choose), open the Settings menu and choose "Show Unavailable Options".

  • Restarting the script: When you click the Restart button, the dialogue preview will restart from the currently selected starting node.

  • You can export a stand-alone HTML file containing a runnable version of your Yarn script, which you can send to other people for them to play. This can be particularly useful for writers who want to get feedback on their scripts before working with other team members to integrate their content.

    To export your script, click the Export button, and choose where to save the file. The file can then be sent anywhere you like, and can be opened in all major web browsers.

    Showing the Dialogue Preview

    By default, the dialogue preview will begin playing from the node named "Start". If no node named "Start" exists, then the dialogue preview will begin playing from the first node in one of your files. You can choose which node to start playing from in the menu at the top-right of the preview area.

    Preview Features

    Previewing dialogue in Visual Studio Code. On the left hand side, Yarn script is being edited, and on the right hand side, the same script is being played through.

    Exporting Your Script

    Making Your Own Commands

    Creating Commands and Functions

    int selectedOptionID

    The ID number of the Option that the user selected.

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

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

    • Dialogue.Continue(): Starts, or continues, execution of the current Program.

    Summary

    Remarks

    Parameters

    Dialogue
    Dialogue
    Option
    OptionSet
    Continue()
    ID
    Option

    See Also

    Name
    Description

    string text

    The text containing substitution markers.

    System.Collections.Generic.IList<string> substitutions

    The list of substitutions.

    text , with the content from substitutions inserted.

    Summary

    Remarks

    Parameters

    Dialogue

    Returns

    Description

    string line

    The line of text to parse.

    The results of parsing the markup.

    Summary

    Remarks

    Parameters

    Dialogue
    MarkupParseResult
    MarkupParseResult
    Text
    Dialogue
    LanguageCode

    Returns

    Description

    string nodeName

    The name of the node.

    The string ID.

    Summary

    Remarks

    Parameters

    Dialogue
    Dialogue

    Returns

    string nodeName

    The name of the node.

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

    Summary

    Remarks

    Parameters

    Dialogue

    Returns

    public LineHandler LineHandler
    {
                get; set; }

    Summary

    Dialogue
    LineHandler
    // 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>>
    ysc upgrade --output-directory ./output myOldYarnFile.yarn
    public void SetSelectedOption(int selectedOptionID)
    public static string ExpandSubstitutions(string text, IList<string> substitutions)
    public MarkupParseResult ParseMarkup(string line)
    public string GetStringIDForNode(string nodeName)
    public IEnumerable<string> GetTagsForNode(string nodeName)
    Add a Dialogue System to the scene:, by opening the GameObject menu and choosing Yarn Spinner -> Dialogue System.
  • Create a new Yarn script, by opening the Assets menu and choosing Create -> Yarn Spinner -> Yarn Script. Name the new file HelloYarn.

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

    1. Save the file and return to Unity.

    2. Create a new Yarn Project 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. Projects are collections of Yarn scripts that get compiled together, and can be used with a Dialogue Runner.

    3. Make the Dialogue Runner use the Project by dragging the Project you just made into the Dialogue Runner's Yarn Project field.

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

    Setting Up A Demo Scene

    the instructions in the Unity manual
    Installation for Unity
    title: Start
    ---
    Wow!
    My first ever Yarn script in Unity!
    
    -> Gosh!
    -> Incredible!
    -> I'm amazed!
    
    Anyway, time to get writing!
    ===

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

    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.

  • Property
    Description

    Dialogue View

    The that will be signalled when the user performs the continue action.

    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.

    Dialogue Advance Input isn't a Line View itself, but it's designed to work with other line views, to interrupt and control the flow of dialogue.

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

    The OptionsHandler is called. When this occurs, the Dialogue is waiting for the user to specify which of the options has been selected, and SetSelectedOption(int) must be called before Continue() is called again.)
  • The Program reaches its end. When this occurs, SetNode(string) must be called before Continue() is called again.

  • An error occurs while executing the Program.

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

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

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

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

    • : 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.

    public void Continue()

    Summary

    Remarks

    Dialogue
    LineHandler
    CommandHandler
    Continue()
    LineHandler
    CommandHandler

    See Also

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

    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:

    Links between nodes in Visual Studio Code.

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

    Making a new .yarn file

    Opening an existing .yarn file

    Selecting an existing .yarn file
    The .yarn file open
    The Graph View

    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.

    public static class BuiltinTypes

    Summary

    Properties

    Yarn

    The to use.

    public void SetProgram(Program program)

    Summary

    Remarks

    Parameters

    Dialogue
    Program
    AddProgram(Program)

    program

    The that has been delivered.

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

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

    • NodeStartHandler: 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.

    public delegate void CommandHandler(Command command);

    Summary

    Parameters

    Yarn
    Command

    command

    See Also

    string startNode

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

    public void SetNode(string startNode = DefaultStartNodeName)

    Summary

    Remarks

    Parameters

    Dialogue
    Dialogue
    Continue()
    PrepareForLinesHandler

    The Yarn Spinner Team

    Learn about the glorious history of Yarn Spinner.

    Yarn Spinner is maintained by Yarn Spinner Pty. Ltd., an Australian company based in beautiful Hobart, Tasmania.

    Hobart, Tasmania, Australia. It's pretty nice.

    Yarn Spinner Pty. Ltd. was founded by the team that created Yarn Spinner—Jon Manning, Tim Nugent, and Paris Buttfield-Addison—all colleagues and contributors at Secret Lab, a game development studio, known for working on Night in the Woods, the Australian ABC Play School games, and more.

    Yarn Spinner is now maintained by the Yarn Spinner Pty. Ltd. team: Jon Manning, Tim Nugent, and Paris Buttfield-Addison, with regular help from researcher and freelancer Mars Buttfield-Addison, and a huge community of open source contributors. ❤️

    You can support Yarn Spinner on Patreon.

    Yarn Spinner Timeline

    • v0.9 was released in October 2015, this was the first public release;

    • v1.0 was released in January 2020;

    • v2.0 was released in December 2021;

    • v2.1 was released in February 2022;

    • v2.2 was released in April 2022;

    • v2.3 was released in July 2023.

    Yarn Spinner Pty. Ltd. is a tools development company located in Hobart, Australia. Yarn Spinner designs and builds the open source Yarn Spinner project, and offers consulting, integration, and development services for custom Yarn Spinner integrations, features, and more.

    Yarn Spinner's core team are also the founders and long-term team members of Secret Lab Pty. Ltd.

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

    Secret Lab has been enthusiastically building video games, apps, and technology to showcase culture, history, arts, and narrative experiences since 2008, and is the longest running games studio in Tasmania.

    Secret Lab is proud to be the original creators of Yarn Spinner, and is currently working on the BAFTA- and IGF-winning Night in the Woods, the interactive story game, I Feel Fine (written by Ryan North), and the adventure-puzzle game, Leonardo's Moon Ship (written by writer of Pixar's Ratatouille, Jim Capobianco).

    to build your video games, interactive experiences, fancy apps, and more.

    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

    Yarn Spinner for Unity

    The third step in our beginner's guide, focusing on getting up and running with Yarn Spinner for Unity.

    This step of the beginner's guide helps you move from writing Yarn scripts outside of a game engien, to initial integration steps to turn them into a game with Unity.

    First, launch the Unity Hub, and create a new project for Unity 2021.3 or newer.

    In the new empty project, open the Edit menu -> Project Settings..., and choose the Package Manager section in the left column of the Project Settings window that appears.

    In the middle area of the window, add an entry named OpenUPM, with the URL set to https://package.openupm.com, and the Scopes set to dev.yarnspinner. Then, click Save.

    Installation for Unity

    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.

    The main way we recommend to install Yarn Spinner for Unity is via the Package Manager.

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

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

    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.

    Flow Control

    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

    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:

    Unity Localisation

    In addition to Yarn Spinner's own , your game can also use the Unity Localization package.

    Both the Unity Localization and Built-In Localisation approaches are very similar to one another, but there are some caveats and extra steps to make them play together.

    Watch a video where Yarn Spinner developer Jon Manning walks you through using Yarn Spinner with Unity's Localisation package:

    Before doing anything with Yarn Spinner, you will need to set up your Unity project to use the Unity Localization system. To install and set up Unity Localization, follow the instructions on the .

    Once you have followed these instructions, your project should now:

    1. Have the Unity Localization package installed

    Yarn.Program program

    The additional program to load.

    NodeCompleteHandler
    DialogueCompleteHandler
    Any
    Boolean
    Number
    String
    TypeMappings
    Yarn.Program
    Program
    NodeCompleteHandler
    DialogueCompleteHandler
    Yarn.Command
    Command
    SetProgram(Program)
    AddProgram(Program)
    Editing with VS Code

    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.

    Dialogue View
    Unity Input System

    About Yarn Spinner Pty. Ltd.

    About Secret Lab Pty. Ltd.

    Secret Lab Pty. Ltd.
    You can hire Secret Lab
    , 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.

    You can use the elseif and else statements to handle different situations in an if statement.

    An elseif statement has an expression that gets checked if the if statement, or any previous elseif statements, don't run.

    An else statement doesn't have an expression, and runs if the if and any elseif don't run.

    For example:

    This script will show different lines depending on the value of $gold_amount. The checks are done from top to bottom, which means that in order for an elseif or else to run, all of the checks above it have to have failed.

    • If it's less than 10, the line "Well, you can't afford one!" will run.

    • Otherwise, if it's less than 15, the line "You can almost afford one!" will run.

    • Otherwise, the line "Here you go!" will run.

    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.

    Now that you know how to work with nodes, lines, options and variables, there's one last part of the Yarn language to learn about: commands.

    if statements

    elseif and else

    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

    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.

    <<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?
    This process adds the OpenUPM Package Registry, which is one of the ways we distribute the offical Yarn Spinner for Unity package.

    With the OpenUPM Package Registry added to the project, you can close the Project Settings window and open the Window menu -> Package Manager. In the Package Manager window that appears, choose the Packages dropdown, and change the view to Packages: My Registries.

    Changing the packages displayed to you in the Package Manager to the ones in registries you've added.

    This will show the packages available from the registry (and scope) that you just added, which is likely to only be Yarn Spinner at this point. Select the Yarn Spinner package from the column on the left of the Package Manager window, and click the Install button found on the bottom right. Yarn Spinner for Unity will be downloaded and installed inside your project.

    Yarn Spinner for Unity provides a way to get the contents of your Yarn scripts into Unity, which allows you to construct a game around your dialogue. This beginner's guide shows guides you through one simple way of using Yarn Spinner for Unity to do this.

    The provided Yarn Spinner views use the Unity package TextMesh Pro to display text. This means you will need to install it before using Yarn Spinner. To do this, open the Window menu and choose -> TextMesh Pro -> Import TMP Essential Resources.

    In the empty project that now has the Yarn Spinner for Unity package installed, right click in the Hierarchy and choose Yarn Spinner -> Dialogue Runner. This will add a new Dialogue System prefab to your scene, which we'll be working with in a moment.

    Creating a new Dialogue Runner in a scene.

    The Dialogue Runner that has been added to your scene is a prefab supplied by Yarn Spinner for Unity that acts as a bridge between the dialogue written in your Yarn scripts, and everything that happens in Unity.

    Specifically, it works with two different things, which are key concepts when working with Yarn Spinner for Unity:

    • a Yarn Project

    • a Dialogue View

    First, we'll look at the Yarn Project. A Yarn Project is a Untiy asset that lives on disk. Create one by right-clicking in the Assets pane and choose Create -> Yarn Spinner -> Yarn Project.

    Creating a Yarn Project in your project's assets.

    With the new Yarn Project created, name it FirstProject, and then use the same menu to create a Yarn Script. Name the Yarn Script MyStory.

    A Yarn Project and a Yarn Script in your prokect's assets.

    The Yarn Script you've created is actually a .yarn file that's now named MyStory.yarn. Double click it in the Assets pane to open it in Visual Studio Code.

    Put the following Yarn script into MyStory.yarn, save the file and return to Unity:

    To connect the Yarn Project you created to the Dialogue Runner that's in the scene, select the Dialogue Runner in the Hierarchy and drag the FirstProject from the Assets pane into the Yarn Project slot, as shown here:

    The Inspector for the Dialogue System, showing the Yarn Project asset you created in the Yarn Project slot of the Dialogue Runner.

    With that done, select the Yarn Project FirstProject in the Assets pane, and look to the Inspector.

    You'll see the Source Files field contains **/*.yarn — this tells this specific Yarn Project to look for all .yarn files in the same folder as the Yarn Project asset, and any subfolders. Thus, any .yarn files next to it will be included as part of the project, which means MyStory.yarn will be found.

    At this point, you can play your project, and step through the dialogue in the default Yarn Spinner for Unity Line View and Options List View:

    To understand how this is working, it's important to understand the concept of Yarn Spinner Dialogue Views. Select the Dialogue System in the Hierarchy, and expand the Dialogue Views section:

    This section is where you specify which DIalogue Views should be used to display the content coming from the Yarn script(s): in other words, how the lines of dialogue, and choices, should be displayed.

    A Dialogue Runner can have multiple Dialogue Views. For example, by default the Dialogue System prefab has one Dialogue View that's designed to display lines of dialogue (Line View), and another that's in charge of displaying options to the player (Options List View).

    All Dialogue Views receive all lines and options, and it's up to them to handle them appropriately. So the Line View that we supply will only displays lines that are not options, and the Options List View will only display lines that are options.

    With that, we've reached the end of our beginner's guide. You're ready go forth and build games with Yarn Spinner! You're also equipped to work with the rest of the documentations here! Don't forget to join the Discord, to chat with other Yarn Spinner users, the Yarn Spinner team, seek help, and share your work.

    Installing Yarn Spinner for Unity

    Adding OpenUPM, the Package Registry that distributes Yarn Spinner for Unity, to your Unity project.

    We recommend sticking with the Beginner's Guide for your first use of Yarn Spinner for Unity, but if you thirst to learn more, you can explore other ways to install Yarn Spinner for Unity at: .

    Using Yarn Spinner for Unity

    If you work with a preexisting game that you're adding Yarn Spinner to, you may already have TextMesh Pro in your project.

    Instead of right-clicking in the Hierarchy, you can also use the GameObject menu -> Yarn Spinner -> Dialogue Runner.

    Creating a Yarn Project

    MyStory.yarn

    What's a Line View and an Options List View?

    While Yarn Spinner for Unity supplies some basic Dialogue Views, you can also create your own. To learn about this visit , but do note that it is a significant step from this Beginner's Guide.

    Next steps with Yarn Spinner for Unity

    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.

    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.

    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.

    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#current.

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

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

    Install via the Unity Package Manager (recommended)

    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

    OpenUPM registry

    Installing the Yarn Spinner package

    Install from GitHub

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

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

    Next Steps

    The contents of your dialogue are delivered to your Dialogue Views.
  • The values of variables are stored and retrieved using the Dialogue Runner's Variable Storage.

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

  • Property
    Description

    Yarn Project

    The that this Dialogue Runner is running.

    Variable Storage

    Yarn Project
    Yarn scripts
    Dialogue Views
    Yarn Project
    StartDialogue

    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.

    If your game is using the , your Dialogue Runner must use a .

    Inspector

    Created and configured one or more Locales for your project

  • Created a string table collection.

  • With these done you should now have your project set up correctly, and have a string table collection for your locales with no entries inside. Yarn Spinner will fill this string table with content that it extracts from your Yarn Scripts.

    To fill a string table with content from a Yarn project, follow these steps:

    1. Select the Yarn Project, and go to its Inspector.

    2. Enable the Use Unity Localisation System setting.

    3. Set the Base Language to your desired language. This must be ensure its one of the locales that you have configured for your project.

    4. In the String Table Collection field, add the String Table Collection that you want to populate with line content.

    5. Click Apply.

    A correctly configured Yarn project for using the Unity Localization package.

    You can check that the string table has been filled with content by opening the Window menu, and choosing Asset Management -> Localization Tables. You can then view the contents of your string table. The Key of each string will be the #line ID from the Yarn files.

    When the Yarn Project importer adds your lines into the string table, it uses the Base language field you set in the Inspector to determine which locale in your String Table Collection should have the lines added into.

    If your project doesn't have a Locale which matches your Base Language, Yarn Spinner will attempt to find an appropriate Locale to use. To ensure that the importer uses the correct Locale, be sure to specify it in the Inspector.

    When a Yarn script is run, the Dialogue Runner receives line IDs from the Yarn Project, and must determine what localised content should be shown to the player, using a line provider. In order for the Dialogue Runner to fetch localised data from the Unity string table, you use a Unity Localised Line Provider.

    To configure it, all that needs to be done is hook your string table collection up to the Strings field of the Unity Localised Line Provider.

    During gameplay, the Unity Localised Line Provider will fetch content from your string table depending on the game's current locale setting. You can control this at run-time by using the locale selector at the top-right corner of your Game View.

    The Locale Selector control, in the top-right corner of the Game View.

    In addition to localising the strings that make up your lines, you can also localise assets that go with each line, such as voice-over audio, or custom objects that store other localised data.

    To localise assets in Unity Localisation, you create and populate an Asset Table. Yarn Spinner doesn't automatically populate Asset Tables for you like it does String Tables, because Yarn Spinner doesn't manage your assets like it does with your lines.

    Instead, you can create an Asset Table that contains assets with the same key as your lines. For example, if you have a line in your Yarn script that has the line ID "line:tom-1", then the string table will have an entry with the key line:tom-1. To create a voice-over asset to go with this line, you can create an asset table that contains an entry with the key tom-1, and maps to an audio file.

    The Unity Localised Line Provider will automatically match String Table entries and Asset Table entries if they have the same key, and then deliver them to your Dialogue Views for use. To do this, ensure that your Unity Localised Line Provider has an Asset Table configured in the Inspector.

    A Unity Localised Line Provider, configured with a String Table for line text, and an Asset Table for voice-over.

    Because both Yarn Spinner and Unity use the same marker for their string interpolation and manipulation ({ and }), you can't use the Unity Localization smart strings in Yarn Spinner content.

    The Built-In Localisation system is simpler, but has fewer features.

    In this document, we'll refer to the 'Localization' package that Unity provides as 'Unity Localization', to reduce the chance of confusion.

    Getting Started

    built-in localisation system
    Unity Localization package's documentation

    When localising your game's dialogue using Yarn Spinner, we recommend creating a separate string table collection for your dialogue, set apart from other localised content like button labels. It can make it a little easier to manage.

    Importing Default Strings

    String Table Locale Fallback

    Using the Strings

    The Dialogue Runner's Inspector will tell you when you need to use a Unity Localised Line Provider. You can click the button it provides to quickly add and configure one. You can also set one up manually, using the Add Component menu.

    Localising Assets

    Potential Trip-ups and Caveats

    Tags and Metadata

    A tag is a piece of information that you can add to content in Yarn Spinner that adds additional context or detail about that content.

    There are two places you can add tags in Yarn scripts: you can add them to nodes, and you can add them to lines. Tags aren't shown to the user; instead, they're used by your game, or by Yarn Spinner itself.

    Tags in lines

    Tags can be added to the end of lines and options. Tags on lines start with a hash symbol (#), and cannot contain spaces. You can add as many tags as you like to line, but they must all be on the same line in the script.

    Here's an example of a line with two tags:

    Homer: Hi, I'd like to order a tire balancing. #tone:sarcastic #duplicate

    Accessing tags

    Tags that you add to a line can be accessed from your game. The way that you access them depends on your game engine. For example, to access them in a Unity game, you use the LocalizedLine.Metadata property.

    Some tags are used by Yarn Spinner itself, while all others are used by your own code, so it's up to you what content they should have, and how to handle them.

    Special tags

    Certain tags are used internally by Yarn Spinner, or are added for you if they don't exist.

    The Yarn Spinner compiler adds a #lastline tag to every line that comes just before a set of options.

    For example, the following excerpt:

    is treated as though it had been written as:

    In a Unity game, you can use this tag in a custom to be notified ahead of time when the player is about to be shown options.

    The #line tag uniquely identifies a single line or option across all of your game's dialogue. This is used to identify lines for localisation. Every line's #line tag must be unique. If a line or option doesn't have a #line tag, it will be automatically added for you.

    Here's an example of some Yarn script with #line tags:

    For more details (including what the tag should look like), see .

    Nodes can also have tags, which you can use to add labels that describe the node.

    Node tags they work a bit differently than line tags: they are defined in the header with the tags key, and they don't have to begin with a hash symbol (#).

    Here's an example of a node with two tags:

    You can access the tags on a node via the Dialogue object's method.

    The metadata of a line is only composed of tags. Because of this, you may find that the Yarn Spinner code and documentation refer to line tags and line metadata interchangeably.

    Nodes can have other metadata in their headers. This metadata isn't exposed through the API, which means it's mostly used to store additional information for whoever is writing the Yarn dialogue or for editors to make use of.

    However, currently there is one header that defines specific behavior within the Yarn Spinner compiler: the tracking header.

    Nodes can track whether they have already been visited during the game. For this to work, the Yarn Spinner compiler needs to add some special code to the node. To avoid creating this code for nodes that don't need it, the compiler only adds this code if it finds a call to the visited() function with the node name in it.

    In some cases, you may need the compiler to add this special code to a node even if no corresponding visited() call exists. To direct the compiler to do this, include the tracking header with the value of always:

    Additionally, using a value of never instructs the compiler to never add this special code to the node. If you use the visited function with a node set to never use tracking, it will always return false.

    For more information on visit tracking, see the documentation for .

    Tags and metadata may seem very complicated at first, and their uses may not be clear. The following example use cases explain how they can be used in your game. Keep in mind that this is not an exhaustive list of use cases.

    Yarn provides to let you change attributes for specific parts of a line. In case most of your attributes apply to entire lines (for example, the color of a line), it may be easier to just use tags instead.

    The #lastline tag can be used to display the last line of dialogue along with any options. This is handled within your code by checking if a line has the #lastline tag, and if it does, storing it before continuing with the execution of the Yarn dialogue.

    Since metadata isn't shown directly to the player, you can use metadata for any internal workflows or tooling. For example, instead of tracking lines that need to be revised outside the Yarn files (which could lead to syncing problems), you could add line tags (such as #needsrevision) to the appropriate lines directly in the Yarn files, and process these lines as part of an internal tool or workflow. The Unity integration automatically generates a CSV file with all lines that contain metadata, making this super easy!

    As referenced before, the Yarn Spinner integration for Unity uses line tags to link localised dialogue lines. This is better explained in the section.

    Aside from that, every piece of metadata can be used by translators and adapters to help them understand how the text is being used, thus leading to better localised text.

    Some games may require that certain lines of dialogue are displayed somewhere other than the dialogue window (for example, as flavor text for an item description, or in an item that acts as a log). Instead of manually duplicating these lines (which adds overhead during development and localisation), tags can be used along with code that checks for the tags and duplicates the lines while the game is running.

    Crediting Yarn Spinner

    Learn how to use the Yarn Spinner brand in your game, and how to acknowledge your use of Yarn Spinner.

    This document is a living document, and might change from time to time. Please check back occasionally! If you have any questions, or want an official written answer, or written permission, or something else—even if it's weird, we love weird—please email legal AT yarnspinner.dev

    Thank you for using Yarn Spinner! 💚 This page contains information on how to use the Yarn Spinner name and logo in projects that make use of Yarn Spinner, or in media coverage of Yarn Spinner.

    There are two parts to crediting Yarn Spinner: what you're required to do if you use Yarn Spinner, and, optionally, what we'd appreciate you doing.

    This page outlines the requirements, the things we'd appreciate you doing, and provides downloads of the Yarn Spinner logo.

    What You're Required To Do

    Yarn Spinner is provided to you under the terms of the MIT License. This means that if you use Yarn Spinner in a game, software, or any other work, you are required to include a copy of Yarn Spinner's license in that work.

    You can find a copy of Yarn Spinner's license on GitHub. It's also included in the packaged versions of Yarn Spinner (such as via the Unity Package Manager, and reproduced here:

    Yarn Spinner License (MIT License)

    You are required to include a copy of this with your game. There's no firm rules as to how you do this. We have some suggestions, though:

    • on a screen in your game, along with other software licenses

    • in a file that lists all licenses related to your game

    • in an accompanying PDF, or similar, electronic manual or documentation

    Basically, as long as a copy of Yarn Spinner's version of the MIT license is distributed with your game, you're good to go.

    In addition to including a copy of the Yarn Spinner's MIT license in your game, we'd very much appreciate it if you include Yarn Spinner in your game's credits, such as the initial splash screen, or in your end-game credits, or somewhere else of your choosing. Yarn Spinner is freely released, under a permissive open source license, and we appreciate you telling people that Yarn Spinner helped make your game.

    If you credit Yarn Spinner, you can use the logo, which are available below, a text credit, or both. If you use a text credit, we suggest the following:

    Dialogue powered by Yarn Spinner — https://yarnspinner.dev

    You can reword this however you'd like, but we'd prefer if you kept the name 'Yarn Spinner', and the link to the website.

    Yarn Spinner® is a trademark of Secret Lab Pty. Ltd., the original creators, and is licensed to Yarn Spinner Pty. Ltd, which is a spinoff company to look after the project.

    For the purposes of crediting Yarn Spinner, acknowledging that you're using Yarn Spinner, or discussing, teaching, or tutorialising Yarn Spinner and other simlar uses, we give you permission to use the Yarn Spinner name and logo on packaging, promotional/advertising materials, splash screens, in publications, seminars, conference talks, and web sites, and anything else in relation to your Yarn Spinner-powered software.

    When using the Yarn Spinner name and logo, please do your best adhere to the following general guidelines:

    1. The Yarn Spinner trademark can only be part of a product name (e.g. "Big Talk for Yarn Spinner") if it's a true reflection (i.e. the product works with Yarn Spinner).

    2. The Yarn Spinner trademark can be used in a referential manner or alongside a referential phrase (e.g. "SimOptometrist is built with Yarn Spinner", "Aunty Edna's Grand Adventure uses Yarn Spinner", "Big Talk is compatible with Yarn Spinner", "Bobby Yarner teaches Yarn Spinner", "Learn Yarn Spinner with Dr Eyeballs");

    3. The game, or project that you're including the Yarn Spinner trademark must, in fact, work with, use, or relate to Yarn Spinner in some way;

    If you have any questons on this, ask the Yarn Spinner team in the Discord, tweet at us, or email us at legal AT yarnspinner.dev — we're friendly, and really just want to help.

    When you use the Yarn Spinner trademark, logo, or brand to credit Yarn Spinner in your game (or other work), you should try your best to conform your use to our brand guidelines.

    You may not edit, modify, distort, recolour, or change the Yarn Spinner logo unless you show us what you've done (and receive our approval), or ask our permission. We're happy for you to totally diverge from our colours and branding, if we talk about it with you, so please email us at legal AT yarnspinner.dev to chat — we're friendly, we promise!

    You can download a copy of the Yarn Spinner logo for inclusion in your credits below. We explicitly give you permission to use these logos, in accordance with the guidelines on this page:

    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:

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

    visited returns a boolean value of true if the node with the title of node_name has been entered and exited at least once before, otherwise returns false. Will return false if node_name doesn't match a node in project.

    visted_count returns a number value of the number of times the node with the title of node_name has been entered and exited, otherwise returns 0. Will return 0 if node_name doesn't match a node in project.

    random returns a random number between 0 and 1 each time you call it.

    random_range returns a random integer between a and b, inclusive.

    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 rounds n to the nearest integer.

    round_places rounds n to the nearest number with places decimal points.

    floor rounds n down to the nearest integer, towards negative infinity.

    ceil rounds n up to the nearest integer, towards positive infinity.

    inc rounds n up to the nearest integer. If n is already an integer, inc returns n+1.

    dec rounds n down to the nearest integer. If n is already an integer, dec returns n-1.

    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 rounds n down to the nearest integer, towards zero.

    You can define your own custom functions in Yarn Spinner. For more information, see .

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

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

    To localise your Yarn scripts, you specify the 'base language' that your scripts are written in. You then add unique line ID tags to each line that identify each line. Finally, the localisation system reads your tagged lines and fills the string table for your base language. You can then add additional translations for your lines to the string tables for other languages.

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

    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:

    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.

    Once you've added line IDs to your Yarn scripts, they're ready to be used in your game's localisation system. Yarn Spinner works with the , and can prepare your string tables and fetch content from those tables at run-time.

    If you'd prefer to not use Unity's Localization package, Yarn Spinner also provides a built-in localisation system, which is described in

    Writing Narratives

    The second step in our three-step beginner's guide to Yarn Spinner: writing Yarn scripts using the Yarn Spinner for Visual Studio Code Extension.

    To use Yarn Spinner for Visual Studio Code, you’ll first need to install Visual Studio Code.

    First, download Visual Studio Code from the official website:

    Once it’s downloaded, install it, and open it up. Choose the View menu → Extensions, to open the Extensions Marketplace:

    In the Extensions Marketplace, search for “Yarn Spinner”, and install the extension provided by Secret Lab:

    Install Yarn Spinner for Visual Studio Code.

    Create a new, empty file in Visual Studio Code (VS Code), and add something like the following to it (it’s just the Party example, from the end of the previous step):

    Save the file somewhere sensible as TestYarn1.yarn

    Yarn Spinner for Godot

    The third step in our beginner's guide, focusing on getting up and running with Yarn Spinner for Godot.

    This step of the beginner's guide helps you move from writing Yarn scripts outside of a game engine, to initial integration steps to turn them into a game with Godot

    First, launch Godot 4.1 (C# version) and create a new project.

    Download a from the , or clone the repository somewhere.

    Locate the addons/ directory in your new local copy of Yarn Spinner for Godot:

    Put a copy of this directory into your new Godot project, either by dragging the folder in your file manager (e.g. Finder or Explorer) into the folder of the Godot project, or by dragging from your file manager into the FileSystem dock of your Godot project:

    In-built Localisation

    This page covers what you need to know to use the internal localisation system built into Yarn Spinner. This supports both the localisation of the text, so the lines themselves, and your assets needed for them.

    Watch a video where Yarn Spinner developer Jon Manning walks you through using the Built-In Localisation System:

    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.

    Localisations have the following properties:

    Line View

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

    Line View is a 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.

    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.

    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.

    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
    Unity Localization system
    Unity Localised Line Provider

    The use of the Yarn Spinner trademark cannot imply endorsement, sponsorhip, or an inaccurate representation of the relationship between you, your game/project, and the Yarn Spinner project, team, Yarn Spinner Pty. Ltd., or Secret Lab Pty Ltd;

  • The use of the Yarn Spinner trademark should not show Yarn Spinner, the Yarn Spinner team, Yarn Spinner Pty. Ltd., Secret Lab Pty Ltd, or any related group or entity in an false or derogatory manner, or in association with any hate speech, or criminal or illegal activities.

  • What We'd Appreciate You Doing

    Yarn Spinner logos

    The MIT License (MIT)
    
    Copyright (c) Yarn Spinner Pty. Ltd., Secret Lab Pty. Ltd., and Yarn Spinner contributors.
    
    Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
    
    The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
    
    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
    YarnSpinner-Logo-Crediting (available in colour, white on black, and black on white, in the zip file)
    YarnSpinner-Logo-Crediting.zip
    archive · 1MB
    Open
    YarnSpinner-LogoWithText-Crediting (available in zip file)
    YarnSpinner-LogoWithText-Crediting.zip
    archive · 244KB
    Open

    #lastline

    The #lastline tag will not be automatically added if there is any content, such as an if statement or a command, between the line and some options. In these situations, you may wish to manually add the tag yourself.

    #line

    Tags in nodes

    Other metadata

    The tracking header

    Example use cases

    Controlling attributes for an entire line

    Displaying the last line of dialogue along with options

    Internal workflows

    Localisation

    Showing specific lines of dialogue outside a dialogue window

    Dialogue View
    Adding Line IDs
    GetTagsForNode
    Functions
    markup
    Localization and Assets

    Built-In Functions

    visited(string node_name)

    visited_count(string node_name)

    random()

    random_range(number a, number b)

    dice(number sides)

    round(number n)

    round_places(number n, number places)

    floor(number n)

    ceil(number n)

    inc(number n)

    dec(number n)

    decimal(number n)

    int(number n)

    This is different to floor, because floor rounds to negative infinity.

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

    # Defining Commands and Functions
    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.

    Workflow

    Writing Yarn Scripts

    Adding Line IDs

    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.

    Using Localised Content in Games

    component
    Yarn Project
    Unity-provided Localization package
    Built Yarn Spinner localisation system
    Installation for Unity
    Creating Custom Dialogue Views
    Hello there.
    -> Hi!
    -> What's up?
    Hello there. #lastline
    -> Hi!
    -> What's up?
    Mechanic: You're in orbit of Jupiter, at a rest station along the main tourism lines. There's a meteorite headed towards here that'll completely destroy this station in three days. #line:4c49c5
    Mechanic: And you're a wayfinding robot bolted to the floor of said Jupiter Tourist Station. #line:5b6256
    -> Bolted to the floor?! #line:f65d07
    	Mechanic: Yeah, like all tourist helper bots. #line:1b159b
    -> Three days?! #line:40eaf7
    	Mechanic: More or less. I wouldn't make any long-term plans. #line:3a6c94
    title: Train_Dialogue
    tags: #camera2 background:conductor_cabin
    ---
    Why did you stop the train?
    Now we won't arrive in time at the next stop!
    ===
    title: Node_Name
    tracking: always
    ---
    I know how many times you've been here.
    ===
    // 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)}!
    Gunther: I wanted orange! They gave me lemon-lime. #line:1a64a5
    title: Start
    tags:
    ---
    Narrator: Oh, hello!
        -> Hi, where am I?
            Narrator: You're in Unity!
                -> Oh.
                <<jump Oh>>
                -> How did I get here?
                <<jump Unity>>
    ===
    
    title: Oh
    ---
    Narrator: Yeah, fun, right?
    ===
    
    title: Unity
    ---
    Narrator: Someone read the Beginner's Guide!
    ===
    (the filename is not important, but the
    .yarn
    extension is).

    With the new file open, look at the bottom right-hand corner of the VSCode window, and verify that the file is recognised as a Yarn Spinner file:

    Yarn Spinner for Visual Studio Code allows you to do a whole bunch of useful things in VSCode, including syntax highlighting, viewing your nodes in a graph, and more. Let’s take a look at some of them.

    The most obvious immediate feature is that Yarn’s syntax will be appropriately identified and highlighted in the editor. Very useful. You can see this in action in your TestYarn1.yarn file, showing the Party example.

    But, so far, this is pretty similar to the features of Try Yarn Spinner. Let’s go deeper.

    Press the Show Graph button in the top right-hand corner of the window, or open the Command Palette and choose Yarn Spinner: Show Graph. A graph view, showing your nodes from the open yarn script will appear!

    Yarn Spinner for Visual Studio code has a few other tricks up its sleeve when it comes to making your Yarn script writing experience better.

    For example, if you add a comment using the triple slash /// you can describe what a variable is for, and Yarn Spinner for Visual Studio will display this description whenever you hover over a variable.

    So, if you add a /// comment above the declaration of $partyHats, like this:

    You’ll then be able to hover over $partyHats and see the description, like this:

    You can also hold Command (on macOS) or Control (on Windows and Linux) and click on the name of a node (for example inside a <<jump>> statement), and the editor will jump to that node. You can also do this from the Graph View.

    You can also use Yarn Spinner for Visual Studio Code to easily find out what references a node, by looking above each node’s header. The amount of references to that node will be shown, as well as a shortcut to jump to that node in the Graph View:

    Clicking the references will show you all the references to that node.

    The Graph View is useful out of the box, but with a few tweaks to your Yarn scripts you can make it even more effective.

    For example, you can assign a colour to nodes by adding a color header, like so:

    Then, your node will have a colour indicator.

    You can also put nodes in groups by adding a group header, like so:

    Then, your nodes will be visually grouped in the Graph View:

    Neither the colour nor the group will have any impact on your game’s use of the dialogue. It’s just for ease-of-use of the Graph View.

    You can also use the tags header, with a space-separated list of tags. This doesn’t impact anything in the editor, but is available to be queried from Yarn Spinner within Unity, for you to do what you want with.

    Take what you’ve learned so far, and write a story that makes use of:

    • nodes

    • jump statements

    • variables

    • if statements (and elseif)

    • setting variables

    • options, nested options, and conditional options

    • built-in functions

    … and is nicely laid out in Yarn Spinner for Visual Studio Code, with groups, colours, and a nicely presented Graph View.

    You story should be playable using the Preview feature of Yarn Spinner for Visual Studio Code, accessible via the Command Palette.

    Installing Visual Studio Code

    Using Yarn Spinner for Visual Studio Code

    Party Yarn script
    title: Party
    ---
    <<declare $partyHats = 0>>
    
    https://code.visualstudio.com
    /// Counts the number of Party Hats that the player has. Starts at 0.
    <<declare $partyHats = 0>>
    title: Party
    color: red
    ---
    // the node's content is here
    ===
    title: Party
    color: red
    group: main
    ---
    // this node's content is here
    ===
    
    title: OverHere
    color: green
    group: other_places
    ---
    // this node's content is here
    ===
    
    title: OverThere
    color: purple
    group: other_places
    ---
    // this node's content is here
    ===

    Syntax highlighting

    Graph view

    We’re not here to teach you VS Code, but one thing that’s important to point out is the **Command Palette.**You can access it by pressing Command+Shift+P or Control+Shift+P on your keyboard:

    The Command Palette allows you to type a few characters to filter by all the commands available in VSCode. With Yarn Spinner for Visual Studio Code installed, you’ll have access to a collection of Yarn Spinner-related commands:

    • Preview Dialogue will open a playable preview of your dialogue inside VSCode/

    Yarn script awareness

    Improving the graph view

    Writing a story

    Next, choose the Project menu -> Tools -> C# -> Create C# solution. This will create a C# project for you. We have to do this to trigger the creation of the .csproj file, which is necessary to let Godot know about the Yarn Spinner plugin.

    Next, open the project folder in Visual Studio Code. In the sidebar of VS Code, the .csproj file and add the following line to it, inside the <Project> </Project> tags, but not inside an <ItemGroup> or <PropertyGroup>:

    Your brand new project should look something like this in VSCode:

    The .csproj for your project.

    Save the tweaked .csproj file and return to Godot, everything is almost ready to go. Click the Build button in the very top right-hand corner of the Godot window. This will trigger a build of the C# solution for the project, which is required to make Godot aware of Yarn Spinner for Godot.

    Once the build is complete, open the Project menu -> Project Settings, change to the Plugins tab, and tick the enabled box next to the Yarn Spinner for Godot plugin:

    The Project Settings, showing the Plugins tab.

    With that, you're ready to go!

    In your Godot project, click the Instantiate Child Scene button:

    The chain-link button, for instantiating a child scene.

    And navigate into the addons/YarnSpinner-Godot/Scenes folder of your project, and choose the DefaultDialogueSystem.tscn file as the scene to instantiate:

    Choosing the provided DefaultDialogueSystem.tscn.

    Your Scene dock will look like this showing a node hierarchy that's entirely based on the DefaultDialogueSystem.tscn scene that you instantiated:

    The DefaultDialogueSystem instantiated into your scene.

    Next, create a new Yarn Project by right-clicking in the FileSystem dock, and choosing Resource...:

    Creating a new Resource in the FileSystem dock.

    Then filter to the YarnProject resource type, and click the Create button:

    Creating a new YarnProject resource.

    Name the new Yarn Project FirstProject.tres.

    Next, create a new Yarn script (a file with a .yarn extension) by right-clicking in the FileSystem dock, and choosing New TextFile:

    Creating a new TextFile, in order to add a Yarn script.

    In the resulting New Text File... window, select All Files (*) from the dropdown in the corner, set the File name to MyStory.yarn, and click the Save button:

    Naming the file for a new Yarn script.

    It may take a moment, but Godot will import your new .yarn file, and it will appear in the FileSystem dock. When it's appeared, double-click on the Yarn Project, FirstProject.tres in the FileSystem dock and look to the Inspector, making sure that res://MyStory.yarn is in the list of Source Scripts, which are the Yarn scripts that compromise the new project:

    The Yarn Project, with the Yarn script identified as a Source Script.

    Next, open the MyStory.yarn file in VS Code, and add the following Yarn script to it, before saving it and returning to Godot:

    Select the the DialogueRunner node in the Scene dock, and look to the Inspector. Assign the Yarn Project you created to the DialogueRunner by dragging the FirstProject.tres Yarn Project from the FileSystem dock into the Yarn Project slot of the Inspector:

    The new Yarn Project resource assigned as the Yarn Project for the DialogueRunner.

    Finally, enter Start as the Start Node, and tick the box next to Starts Automatically:

    Setting the Start Node, and that we want this DialogueRunner to start automatically.

    Save your scene as Demo.tscn, and run the game. At this point, you can play your project, and step through the dialogue in the default Yarn Spinner for Godot Line View and Options List View:

    With that, we've reached the end of our beginner's guide. You're ready go forth and build games with Yarn Spinner! You're also equipped to work with the rest of the documentations here! Don't forget to join the Discord to chat with other Yarn Spinner users, the Yarn Spinner team, seek help, and share your work. \

    This version of the Yarn Spinner for Godot Beginner's guide was written for plugin version 0.1.5. If you are using a newer version of the plugin, please reference a newer version of the documentation. Yarn Spinner for Godot is a Yarn Labs project. It is not fully, or officially supported, and may break, or change at any time.

    Installing Yarn Spinner for Godot

    copy of the latest version of Yarn Spinner for Godot
    GitHub repository
    The addons directory in a local copy of Yarn Spinner for Godot.
    The FileSystem dock in Godot, after dragging the addons directory in.
      <Import Project="addons\YarnSpinner-Godot\YarnSpinner-Godot.props" />

    Using Yarn Spinner for Godot

    You could also instantiate the DefaultDialogueSystem.tscn into your scene at a lower part of the hierarchy, instead of the root node, to display dialogue using the provided default UI, instead.

    MyStory.yarn

    Next steps with Yarn Spinner for Godot

    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.

    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.

    To create a strings file, select a Yarn Project, and click the "Export Strings and Metadata as CSV" button. Unity will ask where you want to save the strings file (the metadata file will have the same name as the strings file, but with a "-metadata" appended to it).

    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.

    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.

    The metadata file contains the id, file, node, and lineNumber columns (which have the same values as in the strings file). Additionally, it contains a metadata column with all the metadata of a line. Only lines that contain metadata will be present in this file. For more information on metadata, see Tags and Metadata.

    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.

    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.

    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.

    The other option for localisation is to use the Unity Localization package. The Unity Localisation package has more features, but is more complex.

    Set Up Localisations

    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.

    Your project always includes at least one localisation, which is for the base language.

    Creating a Translation

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

    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.

    You can also provide the metadata file to the translator to give them more context and improve localisation accuracy.

    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

    Selecting a Language at Run-time

    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.

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

  • The Dialogue Runner will automatically proceed to the next piece of content once all dialogue views have reported that they've finished with a line.

    If the 'Auto Advance' option on a Line View is turned on, then the Line View will signal that it's done with a line as soon as all visual effects have finished.

    If 'Auto Advance' is turned off, then the Line View will not signal that it's done when the effects have finished, and the line's delivery will stop. To make the Line View finish up, you can call the UserRequestedViewAdvancement method, which tells the Line View that the user wants to proceed. The built-in Dialogue System prefab comes set up with a 'Continue' button that calls this method. You can also call this method from code, or use the Dialogue Advance Input component to trigger it based on user input.

    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.

    Auto Advance

    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

    Presenting Lines with Visual Effects

    Dialogue View

    Continuing to the Next Line

    Inspector

    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, you can use the to convert 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.

    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.

    The --- marker indicates where the body begins. After this point, you can put all of your Yarn script.

    The === marker indicates where the node ends; after this point, you can begin another node.

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

    When you write Yarn Spinner dialogue, just about every line of text that you write in a node is a line. When a node is run, it runs each line, one at a time, and sends it to your game.

    A line of dialogue is just the thing you want some entity or character to say, usually beginning with the name of the entity speaking.

    For example, consider the following Yarn code from Night in the Woods:

    When this code is run in the game, it looks like this:

    Yarn Spinner sends each of these lines, one at a time, to the game. The game is responsible for taking the text, and presenting it to the player; in the case of Night in the Woods, this means drawing the speech bubble, animating each letter in, and waiting for the user to press a key to advance to the next line.

    Lines of dialogue can contain just about any text, except for some special characters that Yarn Spinner uses to add extra information to a line.

    If there is a set of characters without spaces before a colon (:) at the beginning of the line, Yarn Spinner will mark that as the name of the character. This information will then be passed to your game, so that you can change the way that lines are shown based on the character who's saying them. For example:

    When you want to let the player decide what to say, you use an option. Options let you show multiple potential lines of dialogue to the player, and let the player select one.

    Options are marked with a -> symbol. You write as many options as you'd like the player to see, and the player chooses one of them. The content of the option is like any other line of dialogue.

    For example, consider the following code:

    In this example, the line "Hi there! What do you feel like doing today?" will run. The player will then be given the choice to say either "I want to go swimming", or "I'd prefer to go hiking".

    Shortcut options can have their own lines, which are run when the option is selected. If a different option is selected, they won't run. To write this, indent the lines that belong to a shortcut option.

    In the following code, different lines will run based on which of the two shortcut options are selected.

    This script will start with the line, "Hi there! What do you feel like doing today?". The player then has the choice of saying either "I want to go swimming", or "I'd prefer to go hiking". Depending on their choice, either the line "Okay, let's go swimming" or "Cool, we'll go hiking then". Finally, no matter what was selected, the line "Sounds good!" will run.

    In addition to containing lines, options can also contain other options.

    You can nest options as much as you like. However, this can get a bit challenging to read. It's often a good idea to use the <<jump>> command to jump to a different node:

    Separating dialogue segments into nodes can aid in writing neater files that are easier to edit as they grow.

    Sometimes it makes sense for the options presented or the outcomes of selecting different options to vary based on other things the player has done or said up until this point. This requires the use of logic and variables, which we'll discuss in the next section.

    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 store information. Variables can store one of three types of information: numbers, strings, and booleans.

    Type
    Possible Values
    Examples

    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.

    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.

    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.

    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.

    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.

    Yarn Namespace

    Contains classes for working with compiled Yarn programs.

    Name
    Description

    Show Graph will open the same graph view, showing your nodes, as the Show Graph button does.

  • The three Export Dialogue as… commands will, unsurprisingly, allow you to export your dialogue in a variety of different formats. These are mostly outside the scope of this workshop, however Export Dialogue as HTML… will give you an entirely self-contained HTML copy of the playable preview of your yarn script. Fun!

  • Partygoer A: We're having a party!
    Partygoer B: Yeah, we are!
    Partygoer A: Want a party hat?
    -> Yes, please!
    <<set $partyHats = $partyHats + 1>>
    Partygoer A: Here you go!
    Partygoer B: Look over here!
    <<jump OverHere>>
    -> No, thanks. I despise happiness. I relish misery.
    Partygoer A: We see that.
    Partygoer B: Why don't you go over there?
    <<jump OverThere>>
    -> I'd rather go over here...
    <<jump OverThere>>
    ===
    title: OverHere
    ---
    Partygoer C: Oh hi. Can I borrow a party hat?
    Partygoer C: I lost mine.
    <<if $partyHats > 0>>
    Partygoer C: Looks like you can spare a hat.
    -> Sure, here you go.
    <<set $partyHats = $partyHats - 1>>
    <<jump OverThere>>
    -> No. Never. Absolutely not.
    <<jump OverThere>>
    <<else>>
    Partygoer C: Oh, you don't have any to spare.
    Partygoer C: Cya around, I guess.
    <<jump Party>>
    <<endif>>
    ===
    title: OverThere
    ---
    Partygoer D: Hi!
    <<if $partyHats > 0 and $partyHats <= 2>>
    Partygoer D: You're definitely fun.
    -> Why?
    Partygoer D: You like party hats.
    -> Thanks! You're fun too.
    <<jump Party>>
    <<elseif $partyHats > 2>>
    Partygoer D: You have too many hats!
    Partygoer D: It really scares me.
    -> Sorry...
    <<jump Party>>
    <<else>>
    Partygoer D: You should go party somewhere else in this party.
    -> Oh, bye, then...
    <<jump Party>>
    <<endif>>
    ===

    If this is turned on, the Line View will finish presenting the line, and then wait. This is useful for games where the user has control over the timing of lines of dialogue. If this is turned off, the Line View will signal to the Dialogue Runner that it's done showing the line once all animations are complete.

    Hold Time

    If Auto Advance is turned on, the Line View will wait this many seconds after all animations are complete before signalling that it's done showing the line. This option is only available when Auto Advance is turned on.

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

    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.

    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.

    Node Content

    Lines

    Options

    Options and Lines

    Nested Options

    upgrader script
    Lines of dialogue running in Night in the Woods.
    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!
    ===
    title: Start
    tags:
    ---
    Narrator: Oh, hello!
        -> Hi, where am I?
            Narrator: You're in Godot!
                -> Oh.
                    <<jump Oh>>
                -> How did I get here?
                    <<jump Godot>>
    ===
    
    title: Oh
    ---
    Narrator: Yeah, fun, right?
    ===
    
    title: Godot
    ---
    Narrator: Someone read the Beginner's Guide!
    ===

    Number

    Any whole or decimal number

    1, 2.5, 3468900, -500

    String

    Any sequence of letters, numbers and other characters, enclosed in quotes.

    "Hello", "✓", "A whole sentence."

    Boolean

    Either the value true or the value false.

    true, false

    Every variable has a name. In Yarn Spinner, all variable names start with a dollar sign ($).

    Declaring a variable means telling Yarn Spinner that a variable exists, what it's meant to be used for, and what initial value it has.

    To declare a variable, you use the <<declare>> statement:

    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:

    Each variable can only store one type of value. Variables can change their value at any time, but they can never change their type.

    For example, the following code will work:

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

    You can work with the values inside variables. For example, numbers can be multiplied, strings can be added together, and boolean values can have logical operations (like and and or) applied to them. When values are used together like this, it's called an expression.

    An expression needs to be a single type. You can't work with values of different types in a single expression. For example, the following code will not work:

    Yarn Spinner supports the following logical operators. Most of these have multiple ways being written:

    • Equality: eq or is or ==

    • Inequality: neq or !

    • Greater than: gt or >

    • Less than: lt or <

    • Less than or equal to: lte or <=

    • Greater than or equal to: gte or >=

    • Boolean 'or'': or or ||

    • Boolean 'xor': xor or ^

    • Boolean 'not': not or !

    • Boolean 'and': and or &&

    • Addition: +

    • Subtraction: -

    • Multiplication: *

    • Division: /

    • Truncating Remainder Division: %

    • Brackets: ( to open the brackets and ) to close them.

    Yarn Spinner follows a fairly standard order of operations, and falls back to using left to right when operators are of equivalent priority.

    The order of operations is as follows:

    1. Brackets

    2. Boolean Negation

    3. Multiplication, Division, and Truncating Remainder Division

    4. Addition, Subtraction

    5. Less than or equals, Greater than or equals, Less than, Greater than

    6. Equality, Inequality

    7. Boolean AND, Boolean OR, Boolean XOR

    To show the contents of a variable, you put it inside braces ({ }) inside a line. The value of that variable will appear in its place.

    For example:

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

    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.

    Variables

    /// The name of the player.
    <<declare $playerName = "Player">>
    
    /// The number of gold pieces that the player has.
    <<declare $gold = 0>>
    
    /// Is the door to the dungeon unlocked?
    <<declare $doorUnlocked = false>>
    <<set $greeting to "Hello, Yarn!">>
    // 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!">>
    // 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>>
    <<set $variableName to "a string value">>
    <<set $numberOfSidesInATriangle = 2 + 1>>
    
    <<set $numberOfSidesInASquare = $numberOfSidesInATriangle + 1>>
    // This will NOT work, because you can't add a string and a number:
    <<set $broken = "hello" + 1>>

    Declaring Variables

    If you add a comment with three slashes /// above a declaration, editor tools like the Visual Studio Code extension will use it to explain what a variable is when it's used elsewhere.

    For example, here's a variable that has the following declaration:

    When you hover the mouse over it in Visual Studio Code, a popup will appear that shows the description:

    If you use a variable without declaring it, Yarn Spinner will try to figure out what type it should have based on how it's being used in your scripts, as well as what initial value it should have - zero for numbers, false for booleans, and blank text for strings. When a variable is not declared, we call that an implicit declaration.

    If you declare a variable, you can make sure that the type of the variable is what you intend it to be. Declaring a variable also lets you control what the variable's initial value is, and lets you add descriptive comments that explain the purpose of the variable to other people (or to your future self!)

    Setting Variables

    As with node titles, variable names must not contain spaces. While they can contain a range of different characters the first character must be a letter. In general your variables will be made up of only letters, numbers and underscores.

    Variables and Types

    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.

    Variables and Expressions

    Yarn Spinner provides built-in functions for converting between certain types:

    • The string function converts values of any type into a string.

    • The number

    Logical operators

    Maths operators

    Order of operations

    Using Variables in Lines

    Variables and Storage

    Creating a new 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.

    Yarn Projects include all Yarn Scripts that the project finds in the Source Files directory. By default, that means all Yarn Scripts in the same directory as the Yarn Project, and all of that directory's children.

    When you add a Yarn Script to the same folder as a Yarn Project, it will automatically be included in the Yarn Project. When you make changes to the script, the Yarn Project will automatically be re-imported.

    You can change the locations that a Yarn Project looks for Yarn Scripts by modifying the Source Files setting. Each entry in the Source Files setting is a search pattern.

    Pattern
    Description
    Examples

    *

    any filename

    "*.yarn" will find "One.yarn" and "Two.yarn".

    **/*

    You can add as many entries to the Source Files field as you like. If a file is matched by multiple patterns, it will only be included once.

    An example of a custom Source Files setting. In this example, the Yarn Project will use all .yarn files in the same folder and its sub-folders, as well as the file Common.yarn in the folder above it.

    A Yarn script can be included in more than one Yarn Project.

    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.

    The 'Create New Yarn Project' button in the Inspector.
    • Clicking this button does two things:

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

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

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

    The Inspector will show information about every variable in the project. If you use a declare statement to declare a variable, you can control the initial value of a variable, as well as its description. 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.

    The list of variables in a Yarn Project.

    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.

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

    Updating the base language of a Yarn Project

    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 Adding Localizations and Assets to Projects.

    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.

    Property
    Description

    Source Scripts

    The list of places that this Yarn Project looks for Yarn Scripts.

    Base Language

    The language that the Yarn Scripts are written in.

    If you are upgrading your version of Yarn Spinner from version 2.2 or earlier, you will need to upgrade your Yarn Project. To do this, select the Yarn Project, and click Upgrade Yarn Project.

    After upgrading your Yarn Project, you will need to set up any localisations you had previously configured on your project. Follow the instructions in Built-In Localisation System or Unity Localisation, depending on what your game is using.

    You will also need to either move all of your Yarn Scripts into the same folder as the Yarn Project, or update the Yarn Project's Source Files setting to tell the Yarn Project where to find your scripts.

    Upgrading an old-style Yarn Project from Yarn Spinner 2.2 or earlier

    Watch a video where Yarn Spinner developer Jon Manning walks you through upgrading a Yarn Project:

    If you are upgrading your version of Yarn Spinner from version 2.2 or earlier, you will need to upgrade your Yarn Project. See Upgrading Yarn Projects to learn how to do this.

    Creating a New Yarn Project

    Yarn scripts
    The Yarn Project inspector. The configurable properties of the Yarn Project are visible at the top, and the information about the imported project is visible at the bottom.

    Adding Yarn scripts to a Yarn Project

    Creating a Project from a Script

    Managing Variables

    Managing Localisations and Assets

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

    Using Yarn Projects with Dialogue Runners

    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

    Upgrading Yarn Projects

    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.

    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 Line Provider to provide it with a LocalizedLine object. This object contains the text of the line or option, in the user's current locale.

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

    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.

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

    At any point during a line's presentation, a Dialogue View can interrupt the line. It does this by calling the requestInterrupt 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 InterruptLine 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.

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

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

    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 RunOptions method. This method receives an array of DialogueOption 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 DialogueOptionID of the selected option.

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

    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 UserRequestedViewAdvancement, which can be called by other parts of the game.

    In most cases, it is generally appropriate for implementations of UserRequestedViewAdvancement to call the requestInterrupt 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.

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

    To access the tags on a line, you use the Metadata property on the LocalizedLine objects you receive. It's up to your code to decide what to do with the tags themselves.

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

    Creating a Dialogue View

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

    Presenting Lines and Options

    DialogueViewBase
    Dialogue Runner

    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

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

    Presenting Lines

    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 until your view tells it to continue.

    Interrupting Lines

    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.

    Dismissing Lines

    Presenting Options

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

    Using Multiple Dialogue Views

    Responding to Dialogue Advancement Signals

    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.

    Accessing Line Metadata

    Yarn Spinner will automatically add certain tags to lines. For example, the #lastline tag is automatically added to any line that's immediately followed by options, which allows your dialogue view to change its behaviour when options are about to appear.

    Seeing it in Action

    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.

    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.

    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.

    Name
    Description

    Contains classes for compiling Yarn code.

    Contains classes for working with markup in Yarn lines.

    Name
    Description

    A command, sent from the to the game.

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

    BuiltinTypes

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

    Summary

    Classes

    Delegates

    Interfaces

    Namespaces

    Structs

    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.

    Creating a Translation
    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.)

    LineHandler

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

    Logger

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

    NodeCompleteHandler

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

    NodeStartHandler

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

    OptionsHandler

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

    PrepareForLinesHandler

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

    Yarn.Unity

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

    OptionSet

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

    Dialogue
    DialogueException
    Dialogue
    Program
    FunctionType
    Header
    Library
    MemoryVariableStore
    IVariableStorage
    Node
    Program
    Program
    CommandHandler
    Command
    DialogueCompleteHandler
    IType
    IVariableStorage
    Yarn.Compiler
    Yarn.Markup
    Command
    Dialogue
    Line
    Dialogue
    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.)

  • Hovering over a variable to see its description
    /// What day number it is. Starts on day 0, ends on day 3.
    <<declare $day = 0 as number>>

    any path, including subdirectories

    "**/*.yarn" will find "One.yarn" and "Subfolder/Two.yarn".

    ..

    the parent folder

    "../*.yarn" will find "One.yarn" in the parent folder.

    Localisations

    A mapping of languages to string tables and associated assets.

    This list will only appear if the project is not using the Unity Localisation system. See Adding Localizations and Assets to Projects for more information.

    Use Addressable Assets

    If this is turned on, the Yarn Project will be set up to tell other parts of the game that localised assets like audio files should be fetched using the Addressable Assets system.

    This checkbox will only appear if the Addressable Assets package is installed in your project, and if the project is not using the Unity Localisation System.

    Use Unity Localisation System

    If this is turned on, the Yarn Project will use the Unity Localisation System to store line data in.

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

    Unity Localisation String Table

    The String Table Collection that the Yarn Project uses. When the project is imported or reimported, this String Table will be filled with line content that comes from the project's Yarn Scripts.

    This field will only appear if project is using the Unity Localisation system.

    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.

    This checkbox will only appear if the project is not usin the Unity Localisation system. 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.

    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:

    Oh, [wave]hello[/wave] there!

    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:

    Oh, hello there!

    Attributes represent ranges of the plain text that have additional information. They contain a position, a length, and their name, as well as their properties.

    In this example, a single attribute will be generated, with a position of 4, a length of 5, and a name of "wave".

    Attributes are opened like [this], and closed like [/this].

    Attributes can overlap:

    You can put multiple attributes inside each other. For example:

    You can close an attribute in any order you like. For example, this has the same meaning as the previous example:

    Attributes can self-close:

    A self-closing attribute has a length of zero.

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

    Attributes can have properties:

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

    Attributes can have short-hand properies, like so:

    This is the same as saying this:

    This attribute 'wave' has a property called 'wave', which has an integer value of 2. The name of the attribute is taken from the first property.

    Properties can be any of the following types:

    • Integers

    • Floats

    • 'true' or 'false'

    • Strings

    Single words without quote marks are parsed as strings. For example, the two following lines are identical:

    If a self-closing attribute has white-space before it, or it's at the start of the line, then it will trim a single whitespace after it. This means that the following text produces a plain text of "A B":

    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.

    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 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 is used to mark the part of the line that identifies the character that's speaking.

    Yarn Spinner will attempt to add this character for you, by looking for character names in lines that look like this:

    The markup parser will mark everything from the start of the line up to the first : (and any trailing whitespace after it) with the character attribute. This attribute has a property, name, which contains the text from the start of the line up to the :. If a : isn't present, or a character attribute has been added in markup, it won't be added.

    This means that the example above is treated the same as this:

    You can use this to trim out the character names from lines in your game.

    Certain attributes in Yarn Spinner's markup are "replacement" markers, which Yarn Spinner uses to insert or replace text based on the value of a variable. There are three built-in replacement markers:

    • The select marker uses the value of a variable to choose an outcome.

    • The plural marker uses the value of a number to decide on the plural class for that number.

    • The ordinal marker uses the value of a number to decide on the

    All three of these markers have a property called value, and use this to decide what text should be used in the line.

    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:

    The plural and ordinal markers take a number in its value property, and use that to determine the plural or ordinal number class of that value.

    Different languages have different rules for how numbers are pluralised.

    In many languages, the term you use to refer to a thing depends on the the number of that thing. This is known as a plural class: in English, you can have one apple, but many apples, and you have have one mouse, but many mice.

    However, the rules vary significantly across different languages. English has two: "single", and "other". However, for example, Polish has multiple.

    • In English, you say "one apple, two apples, five apples".

    • In Polish, you say "jedno jabłko, dwa jabłka, pięć jabłek".

    Notice how the Polish word for "apple", "jabłko", takes multiple forms as the number changes, whereas it takes two forms in English.

    In Yarn Spinner, individual lines are replaced depending on the user's locale, but the logic surround them is not. This means that, if you want to be able to translate your game into multiple languages, you can't write Yarn code like this:

    If you did it this way, the logic would only work for languages that have the same rules for plurals as English. (There are several of them that do, but far more that don't.)

    Complicating this further, there are two main kinds of plural classes: cardinal plural classes, and ordinal plural classes.

    • Cardinal plural classes are the kind we just saw (for example, "one apple, two apples").

    • Ordinal plural classes refer to the positioning of a thing; in English, ordinal numbers are things like "1st, 2nd, 3rd."

    As with cardinal plural classes, different languages have different ordinal plural classes.

    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

    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:

    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 on 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 not static, you call it with the name of the game object you want the command to run on.

    For example, if you have a script called CharacterMovement that has a method Leap, you can add a YarnCommand attribute to it to make it available to your Yarn scripts:

    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:

    If the method is static, you call it directly, without providing a game object name. For example:

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

    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

    Method parameters may be optional.

    For example, consider this method:

    This command could be called like this:

    You can see a list of commands that you've registered with the YarnCommand attribute by opening the Window menu and choosing Yarn Spinner -> Commands.

    This will show a list of every command that you can call in your Yarn script, what parameters they take, and which parameters are optional.

    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:

    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:

    We provide two different means of handling commands in Yarn Spinner: the AddCommandHandler method and the YarnCommand attribute. Both of these provide effectively the same functionality, and under-the-hood the YarnCommand attribute is even a wrapper around the AddCommandHandler call. So if there are two different ways to achieve the same thing when should you use each one?

    • The YarnCommand attribute allows you to tag specific methods as being a command, Yarn Spinner will then automatically handle the binding and connection of the the command in text to the method call in C#.

    • AddCommandHandler method allows you to manually connect a method in C# to a command in Yarn, letting you set the name of the command and which method it connect to, giving you the control over the binding.

    Most of the time, we feel that the YarnCommand attribute is the better option, because it is easier to use, and maps well to how we find most people use commands - that is, calling specific methods on specific GameObjects.

    This convenience, however, does come at a cost of flexibility, because your YarnCommands either need to be on static methods, or follow specific calling conventions, which may not be what you need or want.

    The YarnCommand attribute works best in our opinion when your commands are calling into specific GameObjects in your scene, which means that it works very well for moving, animating, or changing characters and items in a scene.

    For larger gameplay changing moments, such as loading new scenes, moving between dialogue and the rest of your game, or for more global events like saving the game or unlocking an achievement, the AddCommandHandler method is better.

    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 ), Yarn Spinner will pause execution of your dialogue when the command is called.

    Additionally, if your method returns a Coroutine object, Yarn Spinner will wait for that coroutine to complete. You can create and return a Coroutine by using the method.

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

    This new method can be called like this:

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

    In additon to the 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:

    When this code has been added to your project, you can use it in any expression, like an if statement, or inside a line:

    Yarn functions can return the following types of values:

    • string

    • int

    • float

    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:

    • Yarn Spinner set up in a text editor:

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

    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.

    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. Add one by opening the GameObject menu, and choosing Yarn Spinner > Dialogue System.

    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.

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

    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. Name the new script Start, and place it in the same folder as the Yarn Project. This will make the Yarn Script be included in the Yarn Project.

    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.

    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.

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

    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 .

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

    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 values 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

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

    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.

    A playable branching story game with simple static 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.

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

    Dialogue

    Class in Yarn

    Inherits from System.Object

    Summary

    Co-ordinates the execution of Yarn programs.

    public class Dialogue : IAttributeMarkerProcessor

    Constructors

    Name
    Description
    Name
    Description
    Name
    Description
    Name
    Description
    ordinal class
    for that number.

    many

  • other

  • 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
    Here's some square brackets, just for you: \[ \]
    Here's some square brackets, just for you: [ ]
    [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!

    Overlapping Attributes

    Self-closing Attributes

    The Close-All Marker

    Properties

    Short-hand Properties

    Property Types

    Whitespace Trimming

    If you don't want to trim whitespace, add a property trimwhitespace, set to false:

    Showing Special Characters to the Player

    Escaping single [ and ] characters

    If you need to show a blackslash in your text, use two blackslashes:

    This will appear as:

    The nomarkup Attribute

    The character Attribute

    Text Replacement Markers

    select

    plural and ordinal

    Plurals across different languages

    Pluralising with plural and ordinal

    bool

    public class CharacterMovement : MonoBehaviour {
    
        [YarnCommand("leap")]
        public void Leap() {
            Debug.Log($"{name} is leaping!");
        }
    }
    <<leap MyCharacter>>
    // will print "MyCharacter is leaping!" in the console
    // 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!");
        }
    }
    <<fade_camera>>
    // will print "Fading the camera!" in the console

    string

    Passed directly to the function.

    int

    Parsed as an integer using .

    [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'
    }
    <<walk MyCharacter StageLeft>> // walk to the position of the object named 'StageLeft'
    
    <<walk MyOtherCharacter StageRight dancing>> // walk to StageRight, while dancing
    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);
        }    
    }
    <<camera_look LeftMarker>> // make the camera look at an object named LeftMarker
    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.
        }    
    }
    <<custom_wait>> // Waits for one second, then continues running
    public class AdderFunction {
       [YarnFunction("add_numbers")]
       public static int AddNumbers(int first, int second)
       {
           return first + second;
       }
    }
    <<if add_numbers(1,1) == 2>>
       One plus one is {add_numbers(1, 1)}
    <<endif>>

    If you're using Unity 2021.1 or earlier, you'll need to use the Window -> Yarn Spinner -> Update Yarn Commands menu item whenever you add, remove or change a YarnCommand-tagged method.

    If you're using Unity 2021.2 or later, this is done for you automatically.

    Finding Yarn Commands in your Project

    This feature is available in Unity 2021.2 and later.

    Adding commands through code

    YarnCommand vs AddCommandHandler

    Making Commands Using Coroutines

    Defining Functions

    If you're using Unity 2021.1 or earlier, you'll need to use the Window -> Yarn Spinner -> Update Yarn Commands menu item whenever you add, remove or change a YarnFunction-tagged method.

    If you're using Unity 2021.2 or later, this is done for you automatically.

    Coroutines
    WaitForSeconds
    StartCoroutine
    Functions
    built-in functions
    title: Start
    ---
    This is a line of test dialogue.
    ===
    title: Start
    ---
    
    /// Whether the shapes like you or not.
    <<declare $shapes_like_you = false as bool>>
    
    /// The player's name. The player chooses this.
    /// It starts empty.
    <<declare $name = "" as string>>
    
    <<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>>
    ===

    Main Camera

    0

    1

    -10

    Instructions

    Creating a Runnable Script

    Depending on your version of Unity, a window might appear asking you to import TextMesh Pro assets. If this appears, click 'Import TMP Essentials'.

    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.

    Filling Out Your 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 . Otherwise, let's proceed!

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

    Result

    Start.yarn
    Installation for Unity
    Editing with VS Code
    Installation Instructions
    Installation Instructions
    Syntax and File Structure guide
    A new Unity 3D project has been made with no additional changes
    The Dialogue System has been added to the Scene
    The new Yarn Project has been added to the Dialogue System's Dialogue Runner
    The new Yarn Script has been added to the Yarn Project's Source Scripts
    The test line from the Yarn Script has been displayed in the otherwise empty game
    Yarn Spinner is displaying lines, advancing lines and selecting options correctly as per the script
    3D Objects have been added to the Scene to act as characters
    Materials have been added to the 3D Objects in the Scene
    The game is complete and playable with visuals

    Initializes a new instance of the class.

    The node that execution will start from.

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

    Starts, or continues, execution of the current Program.

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

    Fields

    Methods

    Properties

    Sphere

    -1

    1

    -7.5

    Cube

    1

    1

    -7

    Plane

    0

    0.5

    0

    Result
    check out our books on the topic
    A [wave trimwhitespace=false/] B 
    // (produces "A  B")
    Here's a backslash! \\
    Here's a backslash! \
    title: Start
    ---
    
    /// Whether the shapes like you or not.
    <<declare $shapes_like_you = false as bool>>
    
    /// The player's name. The player chooses this.
    /// It starts empty.
    <<declare $name = "" as string>>
    
    <<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>>
    ===

    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.

    Convert.ChangeType

    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.

    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.

    Dialogue(Yarn.IVariableStorage)
    Dialogue
    DefaultStartNodeName
    AddProgram(Program)
    Program
    Continue()
    CommandHandler
    CommandHandler
    CurrentNode
    Convert.ChangeType
    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()
    DialogueCompleteHandler
    DialogueCompleteHandler
    IsActive
    LanguageCode
    Dialogue
    Library
    Library
    LineHandler
    LineHandler
    LogDebugMessage
    LogErrorMessage
    NodeCompleteHandler
    NodeCompleteHandler
    NodeNames
    NodeStartHandler
    NodeStartHandler
    OptionsHandler
    OptionsHandler
    PrepareForLinesHandler
    PrepareForLinesHandler
    VariableStorage

    Yarn Variables and Custom 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:

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

    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.

    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.

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

    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.

    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.

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

    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:

    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!

    Check out the or ask the community in the !

    Frequently Asked Questions / "How Do I...?"

    Common questions and solutions for using Yarn Spinner in Unity.

    Yarn Spinner doesn't do text rendering, you have to use existing Unity systems like TextMeshPro. Fortunately, TMP supports HTML-like rich text tags. See the .

    However, this bespoke approach is impractical for longer scripts or bigger projects. We recommend using TextMeshPro's Style Sheets, which make it much easier to write consistently styled text. See the .

    Yarn Spinner doesn't handle text rendering. You'll need a separate wavy text system, like .

    Markup lets you mark a range of text (words, phrases) in a generic way, for whatever use. You could use it to style text, add sentence markers, make clickable words, etc.

    Note that YS only processes the text data. You must still code the actual markup effect yourself. See .

    Wrap the variable (or any expression) in curly braces ({

    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.

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

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

    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);
    }

    What makes a Variable Storage?

    Let’s make a custom Variable Storage!

    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.

    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.

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

    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.

    Where to go to learn more

    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 .

    Abstract classes in C#
    sqlite-net
    C# generics
    documentation on Variable Storage
    Yarn Spinner Discord Server
    Artist’s Impression of a malicious custom VariableStorage that does not look after its values.
    ,
    }
    ) to evaluate and output it. For more info, see
    .

    To read Yarn variables from C#, use . To write Yarn variables from C#, use .

    To read and write C# variables from Yarn, you must first code in C#.

    Then call the functions and commands in Yarn:

    See the previous answers on working with variables. But we recommend avoiding any "sync" pattern, because then you'll have to track and maintain the same data in two different places. Programmers usually prefer a . Data should live in only one place. Variables should either live in Yarn or live in C#, and not in both.

    To save the current node, save the value of somewhere, e.g. to Unity's . Then to restore it, call and pass in the saved node name.

    To save variables, see . Then to load variables, call . These methods use Unity's built-in JSON utility to serialize a dictionary of variables to Unity's .

    For custom save systems, create your own by subclassing VariableStorageBehaviour and implementing its methods. Study as an example. For more info, see .

    It is not currently possible to save or restore the specific line that the dialogue is running.

    To jump to a node from Yarn, use <<jump (nodeName)>>. See .

    To jump to a node with C#, just call , even if there's already dialogue running.

    Jumping to a specific line in a node is currently not supported. Instead, .

    In most cases, use the .

    For more control, call on a Dialogue View, or on a Line View. See .

    Yarn Spinner automatically adds a #lastline tag to a line when the next step is a set of options. Create a that uses to check for "lastline" and perform the behavior you want.

    To display anything in Yarn Spinner, use a component. for dialogue, for choices.

    Most projects will need custom views. We recommend a modular architecture where each UI element has its own LineView component. For example, a nameplate bubble has a that displays , while the dialogue text window is another Line View that displays . See .

    For a working example, see the "Visual Novel" sample. (In Unity, go to Window > Package Manager, and select Yarn Spinner package. Expand the "Samples" dropdown and select "Visual Novel" and import it.) Specifically, see which inherits from DialogueViewBase, and changes the character name window background color (among other effects) based on the character name.

    Create a custom dialogue view with a custom effect based on Typewriter() (see ) to detect the next text character and pause accordingly.

    Write input code to detect clicking / tapping, then call DialogueRunner.StartDialogue().

    The example tutorial can walk you through this step-by-step.

    This implementation will vary for every game, so we purposely do not attempt to design a one-size-fits-all generic NPC system. Here's some example pseudo-code to make your own:

    For a working example, see the "Space" sample. (In Unity, go to Window > Package Manager, and select Yarn Spinner package. Expand the "Samples" dropdown and select "Space" and import it.) Specifically, see for how to search for nearby NPCs from a list.

    The math / code is a little complicated. Calculate the NPC's on-screen position, then convert this screen position to UI canvas space, and reposition the dialogue bubble.

    For a working example, see the "3D" sample. (In Unity, go to Window > Package Manager, and select Yarn Spinner package. Expand the "Samples" dropdown and select "3D" and import it.) Specfically, see which has a method WorldToAnchoredPosition() that does a lot of this UI positioning math.

    This is more about Unity UI rather than Yarn Spinner. For a working example, see the "Phone Chat" sample. (In Unity, go to Window > Package Manager, and select Yarn Spinner package. Expand the "Samples" dropdown and select "Phone Chat" and import it.)

    To make a resizing dialogue bubble that automatically fits text, you will need a complex UI setup. Study the UI game objects and components in the sample scene. For more context about how it works, see .

    This mainly involves Unity UI, and assumes that your project already has a system where a player can input text like a TMPro Input Field component. If the player input needs to happen in the middle of dialogue execution then you can trigger it with a Yarn Command, using a coroutine to wait for the player input if needed.

    Once you have the player input value, you can store it in a C# variable and access it through a Yarn function, or store that value in a Yarn story variable. .

    The intended workflow is to generate and compile Yarn Projects at editor time, not runtime. See .

    There is no real technical limit on the number of Yarn scripts or the size of Yarn Projects. You decide how to organize your data, and every project has different needs. Some factors to consider:

    • Simplicity. Putting everything into one big script file or one big project file is simpler sometimes.

    • Ease of writing. Writers may prefer to think in terms of one file per scene, one file per chapter.

    • Localization. 1 Yarn Project = 1 CSV spreadsheet per language. When translating, it is usually easier to work with fewer files, rather than fragmenting the translation across many files. As a workaround for games that need multiple Yarn Projects, you may prefer to create a single editor-only Yarn Project that's just for generating string files and translations. See .

    A crash bug exists in versions of Yarn Spinner earlier than 2.3 for these platforms. If you're able to upgrade your version of Yarn Spinner, the best fix is to upgrade to the most recent version of Yarn Spinner.

    If you can't upgrade your version of Yarn Spinner, a workaround for this issue is to open the Build Settings window in Unity, and set the "IL2CPP Code Generation" setting to "Faster (smaller) builds."

    Some devs use YS to manage all in-game localized text, like UI strings. This use isn't intended, but it's possible. Manually create a Yarn.Line struct, set the line ID (see ), and then pass the struct into .

    Please visit the for more information. Thanks for thinking of us!

    Alice: This text is <b>bold!</b>
    Bob: This text is <color=red>red!</color>
    
    // NOTE: '#' is a special character in Yarn, used for line tags.
    // To use a HTML color hex code, you must "escape" the "#" character with a "\"
    Carol: Wow I'm <color=\#ff00ff>purple</color>!
    // Yarn script example of custom "wavy text" markup.
    Oh, [wave]hello[/wave] there!
    
    // After compiling, text will look like: "Oh, hello there!"
    // And then the resulting markup data will look like:
    // - name: "wave"
    // - position: 4
    // - length: 5

    Text

    How do I style text? How do I make some words bold, italic, colorful, etc?

    How do I animate wavy text, like in Night In The Woods?

    How do I use Yarn Markup?

    Variables

    How do I print the value of a variable in dialogue?

    TextMeshPro: Rich Text docs
    TextMeshPro: Style Sheets docs
    Text Animator
    Markup

    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.

    here
    here
    <<set $variableName to "a string value">>
    The value of variableName is {$variableName}.
    // This will appear as "The value of variableName is a string value."
    variableStorage = GameObject.FindObjectOfType<InMemoryVariableStorage>();
    float testVariable;
    variableStorage.TryGetValue("$testVariable", out testVariable);
    variableStorage.SetValue("$testVariable", testVariable + 1);
    static int myNumber = 10;
    
    // note: all Yarn Functions must be static
    [YarnFunction("getMyNumber")]
    public static int GetMyNumber() { 
        return myNumber; 
    }
    
    // Yarb Commands can be static or non-static
    [YarnCommand("setMyNumber")]
    public static void SetMyNumber(int newNumber) { 
        myNumber = newNumber;
    }
    My number is { getMyNumber() }!
    <<setMyNumber 999>>
    But now it's { getMyNumber() }!
    // in your custom Typewriter effect, replace the "while (accumulator >= secondsPerLetter)..." block with this one:
    while (accumulator >= secondsPerLetter) {
       text.maxVisibleCharacters += 1;
       onCharacterTyped?.Invoke();
       accumulator -= secondsPerLetter;
    
       // Don't pause on the last character
       if (text.maxVisibleCharacters >= characterCount) continue;
    
       // Extra pause on punctuation
       var nextChar = text.text[text.maxVisibleCharacters - 1];
       if (nextChar.Equals('.') || nextChar.Equals(',') || nextChar.Equals('?') || nextChar.Equals('!')) {
           yield return new WaitForSeconds(0.3f);
       }
    
       accumulator += Time.deltaTime;
       yield return null;
    }
    if (player presses SPACE)
        then find the nearest NPC
        get that NPC's dialogue node name
        call DialogueRunner.StartDialogue() with the NPC's dialogue node
        disable player movement
    Yarn.Line targetLine = new Yarn.Line();
    targetLine.ID = "line:lineid"; // replace 'lineid' with the actual line ID
    LocalizedLine outputLine = LineProvider.GetLocalizedLine(targetLine);
    Debug.Log(outputLine.Text.Text);
    

    How do I read / write Yarn variables from a C# script?

    Don't forget the $ when writing the variable's name!

    How do I read / write C# variables from a Yarn script?

    If you're using Unity 2021.1 or earlier, you'll need to ask Yarn Spinner to update some code in your project that registers these C# methods as Yarn functions. To do this, open the Window menu, and choose Yarn Spinner -> Update Yarn Commands.

    You don't need to do this if you're using Unity 2021.2 or later.

    How do I 'sync' variables between Yarn and C#?

    How do I load and save data / variables / dialogue state? (Like for a save game system)

    Control flow

    How do I jump to a specific node? How do I switch nodes while dialogue is running?

    How do I jump to a specific line in a node?

    Interaction / UI

    How do I continue dialogue with key/button press instead of clicking the continue button?

    How do I show the last line of text when options are shown? How do I skip the last line of text before a set of options?

    How do I show the character name / portrait? How do I customize dialogue display?

    How do I make the Line View's Typewriter effect pause on punctuation?

    How do I play a Yarn node when I click / tap on an object?

    How do I play a Yarn node when I approach an object and press a button? (RPG-like talking to NPCs)

    How do I position a speech bubble above an NPC's head, like in A Short Hike?

    How do I implement a resizing dialogue bubble / SMS messaging interface?

    How do I get text from a Text Input field into my Yarn story?

    System

    How do I generate a Yarn Project at runtime? How do I load/compile Yarn scripts at runtime?

    Compiling a Yarn script at run-time is more complex than it first appears, because it often interacts with the very specific needs of your game, and we can't provide a one-size-fits-all approach to it. If you want to implement run-time loading in your own game, the place to start looking is the API documentation for the namespace. Please note that this is not something that we encourage people who are new to Yarn Spinner to do!

    How many Yarn files should I have? Can my entire game be in one project or script? Or one project per scene? Is my project or file too big?

    I'm seeing crashes on startup in my WebGL / iOS / Android / IL2CPP project.

    Localization

    How do I fetch any Yarn localized string in C#?

    Other

    How do I credit Yarn Spinner in my game?

    Variables
    VariableStorageBehaviour.TryGetValue<T>()
    VariableStorageBehaviour.SetValue()
    Yarn Functions and Commands
    "single source of truth"
    DialogueRunner.CurrentNodeName
    PlayerPrefs
    DialogueRunner.StartDialogue()
    DialogueRunner.SaveStateToPlayerPrefs()
    DialogueRunner.LoadStateFromPlayerPrefs()
    PlayerPrefs
    variable storage
    InMemoryVariableStorage.cs
    Guide: Yarn Variables and Variable Storage
    Nodes, Lines, and Options
    DialogueRunner.StartDialogue()
    jump to the start of a node
    Dialogue Advance Input
    UserRequestedViewAdvancement()
    OnContinuedClicked()
    Creating Custom Dialogue Views
    Custom Dialogue View
    YarnProject.lineMetadata.GetMetadata()
    Dialogue View
    Line View
    Options List View
    Dialogue Character Name View
    LocalizedLine.CharacterName
    LocalizedLine.TextWithoutCharacterName
    Creating Custom Dialogue Views
    VNManager.cs
    Effects.cs
    NPC Dialogue Game
    PlayerCharacter.cs
    YarnCharacterView.cs
    this Unity UI Layout Groups explainer by Hallgrim Games
    FAQs for how to access variables in Yarn and YarnSpinner are here
    Yarn Projects
    Localizations and Assets
    Localization
    GetLocalizedLine()
    Crediting Yarn Spinner page
    Yarn.Compiler

    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

    • Yarn Spinner installed in Unity

    • Yarn Spinner set up in a text editor

    • downloaded and unzipped

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

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

    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.

    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. Open the GameObject menu, and choose Yarn Spinner > Dialogue System.

    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.

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

    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. Name it whatever you like - "GhostyLads" will do - and place it in the same folder as the Yarn Project.

    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.

    Returning to Unity, pressing the Play ▶️ 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.

    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.

    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 . I've written a minimal script that follows the planned story, as a skeleton that can be expanded on later.

    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.

    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 .

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

    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.

    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.

    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.

    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.

    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.

    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.

    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.

    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.

    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.

    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.

    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.

    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.

    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. Delete the Yarn Script that you created earlier, and find the GhostyLads_FinalVersion.yarn file in the Assets > Dialogue folder. Move it into the same folder as the Yarn Project.

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

    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.

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

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

    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.

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

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

    Materials

    Instructions

    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!

    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.

    Filling Out Your Script

    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.

    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.

    GhostyLads.yarn

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

    If you're using Unity 2021.1 or earlier, once you've made this change to your code, open the Window menu and choose Yarn Spinner -> Update Yarn Commands to update the code that makes this command available to your Yarn scripts.

    Draw the Rest of the Owl

    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.

    Result

    Starter Asset Package
    Installation Instructions
    Installation Instructions
    Syntax and File Structure guide
    The provided assets are being imported
    The Dialogue System has been added from the Project Window into the Scene
    The new Yarn Project has been added to the Dialogue System's Dialogue Runner
    The new Yarn Script has been added to the Yarn Project's Source Scripts
    The test line from the Yarn Script has been displayed in the otherwise non-interactable scene
    The new Yarn Script has been given some simple content
    Yarn Spinner is displaying lines, advancing lines and selecting options correctly as per the script
    A character is made interactable through the addition of the YarnInteractable script with the correct conversationStartNode value
    Clicking on a YarnInteractable object runs the corresponding node dialogue
    Characters can now be selected and will speak when appropriate
    The Yarn script for the Scene has been replaced with a full story version
    The current speaker is now indicated by a green spotlight
    The game is complete and playable with characters that can be spoken to in any order
    title: Start
    ---
    This is a line of test dialogue.
    ===
    // 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();
    // 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);
    }
    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 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);
    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;
        }
    }
    [YarnCommand("disable")]
    public void DisableConversation() {
        interactable = false;
    }
    title: Ending
    ---
    Ghost: You solved it!
    <<disable Ghost>>
    <<disable LeftGrave>>
    <<disable CenterGrave>>
    <<disable RightGrave>>
    ===
    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;
        }
    }
    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!
    ===

    Syntax Basics

    The first step in our three-step beginner's guide to Yarn Spinner: learning the syntax of Yarn with Try Yarn Spinner.

    Writing Yarn with Try Yarn Spinner

    When you first start learning Yarn, the best tool to work with is Try Yarn Spinner, so fire up your web browser—no installation necessary!

    Open your web browser, and navigate to Try Yarn Spinner at https://try.yarnspinner.dev

    In Yarn, everything you write is text. Yarn files are just plain text files with a .yarn extension.

    Everything inside a Yarn file is structured around nodes and lines.

    In Try Yarn Spinner, the first node to run is always called Start, so we’ll write that now.

    title: Start
    ---
    Narrator: Hi, I'm the narrator for this beginner's guide!
    ===

    Copy and paste, or write, the above Yarn script into Try Yarn Spinner!

    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.

    So this node is titled “Start”.

    Node titles are not shown to the player and must start with a letter, and can contain any letters, numbers and underscores, but cannot contain a period or other symbols.

    Node headers can actually contain any number of lines with the structure key: value. This can be used to store additional information, such as the in-game location the conversation is taking place.

    If you put the above Yarn, or something very similar, into and click the Test button, you’ll see the dialogue appear in the right side of the screen.

    Let’s make it a bit more complex. Update the Yarn to look like the following:

    Update the script in Try Yarn Spinner to the above Yarn and try running it.

    In this node, there are the following elements:

    • the header, with the node’s title

    • the -- marker, which indicates where the body of a node begins

    • some lines, representing a Narrator speaking…

    Let’s chat about lines…

    Almost everything is a line 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, Yarn Spinner 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.

    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:

    After the lines of the Narrator speaking you’ll notice some lines that are indented, and start with a -> interspersed amongst other lines from the Narrator:

    These lines are called options, and they are how you let the player decide what to say. Options let you show multiple potential lines of dialogue to the player, who is then able to choose one.

    As you might’ve guessed, an option line is denoted by starting the line with a ->

    After that, the content of the line is like any other line.

    If you put the above, updated Yarn, or something very similar, into and click the Test button, you’ll see the dialogue appear in the right side of the screen, and as you progress through it, eventually you’ll hit some options, which are presented to the player:

    Depending on the choice of option, one set of lines from the Narrator, reacting to the player’s choice, will be displayed.

    You can also nest options inside other options:

    Put the above script in Try Yarn Spinner, and click Test to run it!

    The only new thing that’s happening here is that options are inside other options. If you play this, inside Try Yarn Spinner, then you’ll be able to interact with the options based on the choices you make.

    Have lots of options can get complex, so it’s often a good opportunity to break your story up into nodes and jump between them. We’ll do that next.

    You might have noticed that it’s a bit hard for you, the writer, to follow what’s going on, when you start nesting too many options, have lots and lots of lines of dialogue, or a combination thereof.

    When things start to become too much for a single node, you can break things into multiple nodes and use the <<jump>>statement to, you guessed it… jump to another node.

    Here’s an example of the <<jump>> statement in action:

    Put the above script in Try Yarn Spinner, and click Test to run it, and observe how the jumps work.

    By now, hopefully, you’re seeing what’s going on here, but let’s go through it to make sure.

    In this yarn script there are three nodes: Start, Alright, and Love and, depending on the choice made by the player inside the Start node, the conversation will jump to Alright (if they’re a bit rude) or Love (if they’re somewhat polite).

    The jump statement is used to move the narrative to a different node, and takes a single parameter, which is the full title of the node you want to jump to, and you can see it in use in our Start node:

    Separating dialogue segments into nodes can aid in writing neater files that are easier to edit as they grow.

    Sometimes it makes sense for the options presented or the outcomes of selecting different options to vary based on other things the player has done or said up until this point. This requires the use of logic and variables, which we'll discuss next.

    Of course, Yarn is actually a full programming language, which means it has support for writing code that stores information in variables.

    Yarn has three types of variables, numbers, strings, and booleans:

    Type
    Possible Values
    Examples

    Variables can be declared, set, and checked.

    A variable is declared using a declare statement. It looks like this:

    This declare statement declares a new variable named $characterName and stores the value "Shadowheart" inside it. When you first declare a variable, that sets its type.

    So this variable, $characterName, will always be of type string, because we declared it with a string, "Shadowheart".

    Here’s an example of declaring two more variables, one of type number, and one of type boolean:

    Every variable you use must have a name; in Yarn, all variable names start with a dollar sign ($).

    As with node titles, variable names must not contain spaces. While they can contain a range of different characters the first character must be a letter.

    Your variable names will be made up of only letters, numbers and underscores.

    Of course, it’s not much use to declare a variable, if you can’t update it later. We call updating the contents of a variable setting. A variable is set using the set statement.

    Here’s how we’d update the $characterName variable to a new name:

    Because $characterName was declared as type string, you cannot then set it to a number, or a boolean, so the following will not work:

    A variable must always have a value. There is no concept of null, or an empty variable in Yarn.

    You can work with the values inside variables. For example, numbers can be multiplied, strings can be added together, and boolean values can have logical operations (like and and or) applied to them.

    When values are used together like this, it's called an expression:

    An expression needs to be a single type. You can't work with values of different types in a single expression. For example, the following code will not work:

    Yarn Spinner supports the following logical operators. Most of these have multiple ways being written:

    • Equality: eq or is or ==

    • Inequality: neq or !

    Maths operators

    • Addition: +

    • Subtraction: -

    • Multiplication: *

    Order of operations

    Yarn Spinner follows a fairly standard order of operations, and falls back to using left to right when operators are of equivalent priority.

    The order of operations is as follows:

    1. Brackets

    2. Boolean Negation

    3. Multiplication, Division, and Truncating Remainder Division

    4. Addition, Subtraction

    Variables by themselves aren’t much good if they cannot have some sort of impact on your narrative. So, of course, they can!

    The most straightforward way to use a variable is to show the contents of it in a line. To do this we refer to the variable by name inside a line, and put it inside braces, like this: {$characterName}.

    For example:

    Put the above script in Try Yarn Spinner, and click Test to run it.

    In addition to storing information, variables are useful for controlling what's shown to the player. To do this, you use ifstatements, elseif statements, else, endif, statements.

    An if statement allows you to control whether a collection of lines is shown or not. When you write an if statement, you provide an expression, which is checked.

    The if, elseif, else, and endif statements look much like all the other statements we’ve been using: <<if>>, <<elseif>>, <<else>>, and <<endif>>.

    Here’s an example:

    Put the above script in Try Yarn Spinner, and click Test to run it.

    In this example, after the Narrator meets the character, and says it’s nice to meet them, greeting them by name, we use <<if $charName is "Bruce">> to check if the variable $charName is set to the value "Bruce".

    If it is, then we have some nice lines about the Narrator also being named that, otherwise we use <<elseif $charName is "Notbruce">> to check if the variable $charName is set to the value "Notbruce", and provide some lines from the Narrator if it is.

    If $charName is set to neither of those values, then we hit the <<else>> statement, which catches all other possible values, and we provide some lines that say their name—whatever it is—is a lazy one. Then we hit the <<endif>> statement, which ends the entire if statement.

    When presenting options to the player using the -> syntax, you may want to make some options not available. You can do this by adding a condition to the option, making it a conditional 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.

    It’s important to remember that 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.

    Update the following example to use a conditional option at some point:

    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 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 randomfunction returns a different random number every time you call it.

    • Functions let you get data from your game back into your scripts.

    Yarn Spinner provides the following built-in functions:

    • visited(string node_name)

      • visited returns a boolean value of true if the node with the title of node_name has been entered and exited at least once before, otherwise returns false. Will return false

    For example, you can use functions inside if statements, and in regular lines, as they largely behave like variables. For example:

    Write some yarn using functions! We recommend the dice function, the random_range function, and the visited and visited_count functions.

    So far, you’ve learned that Yarn scripts are plaintext files (stored outside of Try Yarn Spinner, they have the .yarn extension). Let’s do a quick recap…

    Yarn scripts are composed of nodes, which must always start with at least a title header followed by a ---, and end with a ===:

    Inside each node are lines, which are simple lines of dialogue that are delivered to the player, and often have a character name at the beginning:

    Lines can also be options, which provide choices to the player:

    These options can be nested:

    You can also have multiple nodes, and jump between them using the jump statement:

    And you can declare variables of three different types—numbers, strings, and booleans—and use them in lines, and for flow control. You’ve also learned about conditional options and built-in functions.

    The next example recaps everything you’ve learned so far, so build it up slowly, piece by piece, in a new Try Yarn Spinner tab, so you understand how it’s working. Try to improve it, or make it longer by adding conditional options and use of built-in functions.

    With that all said, in the next part, we’ll take things further and start using Yarn Spinner for Visual Studio Code.

    Choose-Your-Path Game with Visuals

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

    You can watch a video version of this tutorial presented by Yarn Spinner's Jon Manning and Mars Buttfield-Addison, from the Game Developers Conference in 2021.

    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

    • downloaded and unzipped

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

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

    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.

    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.

    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. Open the GameObject menu, and choose Yarn Spinner > Dialogue System.

    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.

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

    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. Name it whatever you like - "Start" will do - and put it in the same folder as the Project.

    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.

    Returning to Unity, pressing the Play ▶️ 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.

    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 . I've written a minimal script that follows the planned story, as a skeleton that can be expanded on later.

    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 .

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

    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.

    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.

    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:

    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:

    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

    These minimal changes to the Yarn script...

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

    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.

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

    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.

    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.

    All this took is a few more additions to the Yarn script:

    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:

    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:

    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:

    And now our Characters appear in the Scene whenever they're part of the current conversation.

    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

    Mostly these just move their arms into different basic positions. The function looks like this:

    As with the place command, add a decorator to SetPose() to declare a command called pose.

    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.

    Next, do the exact same thing for expressions. The options included for each character in the Asset Package are:

    • neutral

    • happy

    • angry

    • smirk

    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.

    Decorate with [YarnCommand("expression")] and, back in the Yarn script, place calls to <<expression (character) (expression name)>> wherever appropriate.

    And that's it for commands! We successfully implemented commads to:

    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. Delete the Yarn Script that you created earlier, and find the file SpaceJourney_FinalVersion.yarn in the Assets/Dialogue folder. Move this file into the folder with the Yarn Project.

    A playable visual novel-type game with multiple characters and scenes and sensible transitions between them.

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

    and the === marker, which indicates the end of a node

    "Hello", "✓", "A whole sentence."

    Boolean

    Either the value true or the value false.

    true, false

    Greater than: gt or >
  • Less than: lt or <

  • Less than or equal to: lte or <=

  • Greater than or equal to: gte or >=

  • Boolean 'or'': or or ||

  • Boolean 'xor': xor or ^

  • Boolean 'not': not or !

  • Boolean 'and': and or &&

  • Division: /

  • Truncating Remainder Division: %

  • Brackets: ( to open the brackets and ) to close them.

  • Less than or equals, Greater than or equals, Less than, Greater than

  • Equality, Inequality

  • Boolean AND, Boolean OR, Boolean XOR

  • if
    node_name
    doesn't match a node in project.
  • visited_count(string node_name)

    • visted_count returns a number value of the number of times the node with the title of node_name has been entered and exited, otherwise returns 0. Will return 0 if node_name doesn't match a node in project.

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

    • dec 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.

  • Number

    Any whole or decimal number

    1, 2.5, 3468900, -500

    String

    Getting a bit more complex

    Options provide choices within nodes

    Nesting options within options

    Jumping between nodes

    Logic and variables

    You can learn more about this in the Writing in Yarn guide. We recommend finishing this Beginner's Guide first, though.

    Declaring variables

    Setting variables

    Expressions and variables

    Operators

    Checking and using variables

    Using variables with flow control

    Conditional options

    You can see this in action in Try Yarn Spinner, where unavailable options will be displayed with a strike though!

    Solution

    Functions

    Building narratives with Yarn scripts

    Try Yarn Spinner
    Try Yarn Spinner
    Running a script with the Test button in Try Yarn Spinner

    Any sequence of letters, numbers and other characters, enclosed in quotes.

    title: Start
    ---
    Narrator: Hi, I'm the narrator for this beginner's guide!
    Narrator: I'm talking to you with Yarn Spinner!
    Narrator: What do you think of all this, then?
        -> It's alright, I guess.
            Narrator: Well, that's not very nice.
            Narrator: I'm trying my best here.
        -> It's great. I love it.
            Narrator: Oh, you're too kind.
            Narrator: I'm just doing my job.
    ===
    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"
    title: Start
    ---
    Narrator: Hi, I'm the narrator for this beginner's guide!
    Narrator: I'm talking to you with Yarn Spinner!
    Narrator: What do you think of all this, then?
        -> It's alright, I guess.
            Narrator: Well, that's not very nice.
            Narrator: I'm trying my best here.
        -> It's great. I love it.
            Narrator: Oh, you're too kind.
            Narrator: I'm just doing my job.
    ===
    title: Start
    ---
    Narrator: Hi, I'm the narrator for this beginner's guide!
    Narrator: I'm talking to you with Yarn Spinner!
    Narrator: What do you think of all this, then?
        -> It's alright, I guess.
            Narrator: Well, that's not very nice.
            Narrator: I'm trying my best here.
                -> Are you really? I don't think so.
                    Narrator: Well, I never!
                    Narrator: I'm going to have to have a word with the writer.
                -> Oh, OK. I'm sorry.
                    Narrator: That's better.
                    Narrator: I'm glad we could resolve this.
        -> It's great. I love it.
            Narrator: Oh, you're too kind.
            Narrator: I'm just doing my job.
                -> You're doing a great job.
                    Narrator: Oh, stop it, you.
                    Narrator: You're making me blush.
                -> You're a natural.
                    Narrator: Oh, you.
                    Narrator: I'm just doing my job.
    ===
    title: Start
    ---
    Narrator: Hi, I'm the narrator for this beginner's guide!
    Narrator: I'm talking to you with Yarn Spinner!
    Narrator: What do you think of all this, then?
        -> It's alright, I guess.
            <<jump Alright>>
        -> It's great. I love it.
            <<jump Love>>
    ===
    
    title: Alright
    ---
    Narrator: Well, that's not very nice.
    Narrator: I'm trying my best here.
        -> Are you really? I don't think so.
            Narrator: Well, I never!
            Narrator: I'm going to have to have a word with the writer.
        -> Oh, OK. I'm sorry.
            Narrator: That's better.
            Narrator: I'm glad we could resolve this.
    ===
    
    title: Love
    ---
    Narrator: Oh, you're too kind.
    Narrator: I'm just doing my job.
        -> You're doing a great job.
            Narrator: Oh, stop it, you.
            Narrator: You're making me blush.
        -> You're a natural.
            Narrator: Oh, you.
            Narrator: I'm but a humble narrator.
    ===
    title: Start
    ---
    Narrator: Hi, I'm the narrator for this beginner's guide!
    Narrator: I'm talking to you with Yarn Spinner!
    Narrator: What do you think of all this, then?
        -> It's alright, I guess.
            <<jump Alright>>
        -> It's great. I love it.
            <<jump Love>>
    ===
    <<declare $characterName = "Shadowheart">>
    <<declare $goldAmount = 100>>
    <<declare $hasAmulet = false>>
    <<set $characterName to "Karlach">>
    <<set $characterName to 42>>
    <<set $hamCounts = 2 + 1>>
    
    <<set $numberOfPets = $numberOfPets + 1>>
    <<set $lemons = "hello" + 1>> // this will not work
    title: Start
    ---
    <<declare $characterName = "Player">>
    
    Narrator: Hi there!
    Narrator: What's your actual name, anyway?
        -> My name is Bruce.
            <<set $characterName to "Bruce">>
        -> My name is not Bruce.
            <<set $characterName to "Notbruce">>
        -> My name doesn't matter...
    Narrator: Ah, nice to meet you, {$characterName}!
    ===
    title: Start
    ---
    <<declare $charName = "Player">>
    
    Narrator: Hi there!
    Narrator: What's your actual name, anyway?
        -> My name is Bruce.
            <<set $charName to "Bruce">>
        -> My name is not Bruce.
            <<set $charName to "Notbruce">>
        -> My name doesn't matter...
    Narrator: Ah, nice to meet you, {$charName}!
      <<if $charName is "Bruce">>
        Narrator: I'm Bruce, too!
        Narrator: What a coincidence!
      <<elseif $charName is "Notbruce">>
        Narrator: That seems like a strange name, but I won't judge.
      <<else>> 
        Narrator: That's a lazy name.
        Narrator: You should be ashamed of yourself.
      <<endif>>
    Narrator: Well, goodbye, {$charName}!
    ===
    Guard: You're not allowed in!
    -> Sure I am! The boss knows me! <<if $reputation > 10>>
    -> Please?
    title: Start
    ---
    <<declare $charName = "Player">>
    
    Narrator: Hi there!
    Narrator: What's your actual name, anyway?
        -> My name is Bruce.
            <<set $charName to "Bruce">>
        -> My name is not Bruce.
            <<set $charName to "Notbruce">>
        -> My name doesn't matter...
    Narrator: Ah, nice to meet you, {$charName}!
      <<if $charName is "Bruce">>  
        Narrator: I'm Bruce, too!
        Narrator: What a coincidence!
      <<elseif $charName is "Notbruce">> 
        Narrator: That seems like a strange name, but I won't judge.
      <<else>>
        Narrator: That's a lazy name.
        Narrator: You should be ashamed of yourself.
      <<endif>>
    Narrator: Well, goodbye, {$charName}!
    ===
    // 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)}!
    title: Party
    ---
    
    ===
    title: Party
    ---
    Partygoer A: We're having a party!
    Partygoer B: Yeah, we are!
    ===
    title: Party
    ---
    Partygoer: We're having a party!
    	-> I don't like parties.
    	-> I love parties!
    	-> PARTY!
    ===
    title: Party
    ---
    Partygoer: We're having a party!
    	-> I don't like parties.
    		Partygoer: You should.
    			-> I don't. But I can pretend.
    				Partygoer: Good idea! Let's party!
    			-> I prefer to nap.
    				Partygoer: (whispers) we all do.
    	-> I love parties!
    	-> PARTY!
    ===
    title: Party
    ---
    Partygoer A: We're having a party!
    Partygoer B: Yeah, we are!
    	-> I do enjoy a party.
    		<<jump Enjoy>>
    	-> I was told to say the password "antiquarian".
    		<<jump SecretParty>>
    ===
    
    title: SecretParty
    ---
    Secret Partygoer: Woohoo! That's the password for the secret, better party!
    Secret Partygoer: Welcome!
    ===
    
    title: Enjoy
    ---
    Partygoer A: We all enjoy a party!
    Partygoer B: I'll party until I cannot party anymore!
    ===
    title: Party
    ---
    <<declare $partyHats = 0>>
    Partygoer A: We're having a party!
    Partygoer B: Yeah, we are!
    Partygoer A: Want a party hat?
        -> Yes, please!
            <<set $partyHats = $partyHats + 1>>
            Partygoer A: Here you go!
            Partygoer B: Look over here!
                <<jump OverHere>>
        -> No, thanks. I despise happiness. I relish misery.
            Partygoer A: We see that.
            Partygoer B: Why don't you go over there?
                <<jump OverThere>>
        -> I'd rather go over here...
            <<jump OverThere>>
    ===
    
    title: OverHere
    ---
    Partygoer C: Oh hi. Can I borrow a party hat? 
    Partygoer C: I lost mine.
    <<if $partyHats > 0>>
        Partygoer C: Looks like you can spare a hat.
            -> Sure, here you go.
                <<set $partyHats = $partyHats - 1>>
                <<jump OverThere>>
            -> No. Never. Absolutely not.
                <<jump OverThere>>
    <<else>>
        Partygoer C: Oh, you don't have any to spare.
        Partygoer C: Cya around, I guess.
        <<jump Party>>
    <<endif>>
    ===
    
    title: OverThere
    ---
    Partygoer D: Hi!
    <<if $partyHats > 0 and $partyHats <= 2>>
        Partygoer D: You're definitely fun.
            -> Why?
                Partygoer D: You like party hats.
                    -> Thanks! You're fun too.
                        <<jump Party>>
    <<elseif $partyHats > 2>>
        Partygoer D: You have too many hats!
        Partygoer D: It really scares me.
            -> Sorry...
                <<jump Party>>
    <<else>>
        Partygoer D: You should go party somewhere else in this party.
            -> Oh, bye, then...
                <<jump Party>>
    <<endif>>
    ===
    title: Start
    ---
    <<declare $charName = "Player">>
    
    Narrator: Hi there!
    Narrator: What's your actual name, anyway?
        -> My name is Bruce.
            <<set $charName to "Bruce">>
        -> My name is not Bruce.
            <<set $charName to "Notbruce">>
        -> My name doesn't matter...
    Narrator: Ah, nice to meet you, {$charName}!
    <<if $charName is "Bruce">>
        Narrator: I'm Bruce, too!
        Narrator: What a coincidence!
    <<elseif $charName is "Notbruce">>
        Narrator: That seems like a strange name, but I won't judge.
    <<else>>
        Narrator: That's a lazy name.
        Narrator: You should be ashamed of yourself.
    <<endif>>
    Narrator: Well, goodbye, {$charName}!
    	-> Bye, bye!
    	**-> Bye, Brucey! <<if $charName is "Bruce">>**
    ===
    Location Markers which are named but invisible objects in the scene that can act as anchors to move other objects to predefined positions.
  • Four C# scripts that are attached to some of the above objects to provide simple functions.

  • Change Character model animations and textures, as if they are showing different emotions.
    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.

    pointing

  • hands-on-hips

  • surprised

    Change Character model animations and textures, as if they are showing different emotions.
    title: Start
    ---
    This is a line of test dialogue.
    ===
    // 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);
    // 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;
    }
    // find the Dialogue Runner
    dialogueRunner = FindObjectOfType<Yarn.Unity.DialogueRunner>();
    // register Command Handler for <<camera NAME_OF_LOCATION>>
    dialogueRunner.AddCommandHandler<Location>("camera", MoveCamera);
    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]
    ===
    // find the Fade Layer
    fadeLayer = FindObjectOfType<FadeLayer>();
    // 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));
    }
    // Handlers for <<fadeIn DURATION>> and <<fadeOut DURATION>>
    dialogueRunner.AddCommandHandler<float>("fadeIn", FadeIn);
    dialogueRunner.AddCommandHandler<float>("fadeOut", FadeOut);
    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>>
    ===
    // 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;
    }
    // moves character to location {location}>{markerName} in the scene
    [YarnCommand("place")]
    public void Move(Location location, string markerName) {
    // ... [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]
    ===
    // tell the animator to jump to state {poseName} 
    public void SetPose(string poseName) {
        animator.Play("Base Layer." + poseName, 0);
    }
    [YarnCommand("pose")]
    public void SetPose(string poseName) {
    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]
    ===
    // 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);
    }

    Instructions

    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!

    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.

    Filling Out Your Script

    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.

    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>>
    ===

    Adding Commands

    Scene-Wide Commands

    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.

    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.

    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.

    Including transitions between conversation nodes even if they occur in the same Corridor location will hide the characters appearing that will be implemented next.

    Character-Specific Commands

    If you're using Unity 2021.1 or earlier, open the Window menu, and choose Yarn Spinner -> Update Yarn Commands. This updates the code that makes sure that the "place" command is available to your Yarn script.

    If you're using a later version of Unity, you don't need to do this.

    If you're using Unity 2021.1 or earlier, open the Window menu and choose Yarn Spinner -> Update Yarn Commands to update the list of available commands.

    If you're using Unity 2021.1 or earlier, update your Yarn commands now by choosing Window -> Yarn Spinner -> Update Yarn Commands.

    Draw the Rest of the Owl

    Result

    Starter Asset Package
    Installation Instructions
    Installation Instructions
    Syntax and File Structure guide
    Creating a new 3D project in the Unity Hub.
    The provided assets are being imported
    Camera markers have been assigned 🔶 icons and character markers 🔵 icons
    The Dialogue System has been added from the Project Window into the Scene
    The new Yarn Project has been added to the Dialogue System's Dialogue Runner
    The new Yarn Script has been added to the Yarn Project's Source Scripts
    The test line from the Yarn Script has been displayed in the otherwise empty game
    The Title Layer and Fade Layer objects have been hidden from the Scene
    The new Yarn Script has been given some simple content
    Yarn Spinner is displaying lines, advancing lines and selecting options correctly as per the script
    The camera now moves around the Scene as commands are reached in the Yarn script
    A black overlay fades in and out to cover camera move transitions
    Characters now appear in the Scene as commands are reached in the Yarn script
    Characters now respond appropriately to the threat of pirate attack
    The Yarn script for the Scene has been replaced with a full story version
    The game is complete and playable with visuals, transitions and animations

    Building a Custom Dialogue Runner

    "Getting Additional Control Over The Dialogue or: How I Learned To Stop Worrying And Build My Own Custom Dialogue Runner Class"

    This page describes advanced usage of Yarn Spinner. Unless you really need total control over how Yarn Spinner works in your Unity project, we recommend using the built-in class rather than creating your own.

    If you really do need total control, read on!

    While for the most part we think the best way to use Yarn Spinner is to use the components provided and replace and supplement them as needed there are going to be times when you need more control over the dialogue itself. In these cases you will need to directly interface with the dialogue and to build your own dialogue runner. This will give you full control over the dialogue and let you control the timing and triggering of important events.

    By the end of this guide we are going to have recreated the same scene and code as can be found in the Minimal Viable Dialogue sample.

    ⚠️ This guide is not for beginners: You should be very comfortable with Yarn Spinner and Unity before using this!

    The Depths of Yarn Spinner

    Before we can really start building our own dialogue system we need to take a bit of a look at what Yarn Spinner does and how it works currently. The starting point for Yarn Spinner is the Yarn dialogue files themselves, once you have your Yarn files written and ready is where Yarn Spinner takes over. These files get associated together into a Yarn Project, this project reads through and compiles the Yarn files into three pieces:

    • strings tables

    • metadata table

    • binary program

    The strings table is a that holds all the text of the Yarn, the actual written dialogue. This isn't strictly necessary and could be stored in other parts of the output of the compilation but by having it separate it allows for easier localisation and proofing. The metadata table is similar, it is also a that holds all the metadata or hashtags each line of dialogue has. This metadata is used for a variety of reasons and changes on a game-by-game basis, as with the strings table this could be embedded in other elements but by having it standalone just simplifies certain elements. The binary program is a compiled representation of the Yarn as a series of as simulated on a stack machine. The binary program is the actual thing that Yarn Spinner will use to move through your dialogue, jump between nodes, create flow control, everything. Once the program has been compiled Yarn Spinner doesn't even look at the Yarn files.

    These three pieces each have their own parts in what gets delivered to your game. The strings and metadata tables are used by the to create the your views use, and the compiled program is used by the Dialogue class and the Dialogue Runner, in conjunction with the variable storage, to determine the story flow.

    What this means is if we are making our own runner we can still use some of the existing pieces, especially the line providers, the variable storage, and the compiler itself. Now while you could make your own interface into this compiled output from scratch, unless you are porting Yarn Spinner over to an entirely different technology stack you'd be better off using the interface into it that the existing runner uses, the . So that is what we will be doing in this guide.

    This isn't a deep dive into how the specifics of Yarn Spinner works there is one part that does need to be covered, and that is how lines work. When you write some lines in your Yarn like the following:

    You have two lines (identified by their ids ab12c and 34de5) that inside the strings table (simplified for this example) will be something like the following:

    ID
    Text

    The compiled output of the yarn has no understanding or knowledge of the text itself, it just knows that it will at some point run a line with the id of ab12c and then after another line with the id of 34de5. So to get the actual text this is where the line providers come in, they read the strings table and then return the actual text so that other parts of the game can use it. Even then there are further stages, substitutions need to occur and markup needs to be parsed, but the core is a line of text as far as the Yarn Spinner compiler is concerned is an id and nothing more, other pieces make it actual text. We do it this way for two main reasons, localisation and code bloat. If the text of the line was stored in the compiled program either we'd need to also store every localised variant also or have some other system to let the dialogue runner know that "no actually I know you have the line with some text but ignore it and use this text instead, thanks". Both of these approaches just makes the compiled output much much bigger and complicates the flow of the program, hence why we split them out.

    The first thing before we make any new code is to decide how our custom dialogue runner is going to be different from the built-in one, for if its just the same why bother making it? The question now comes "what will our runner do that is different?" The current runner works by sending Dialogue Views information when important events occur and then relying on completion callbacks to control the flow. Every view gets told about important events and once every view has run their completion callback the runner continues. This means all timing is the responsibility of the views and that might be less than ideal for your needs, so lets change that in our runner. Let's make it so that all timing is solely the responsibility of the runner, and it won't give two hoots about what any other part of the game is doing, if its told to advance it will advance. With our goal set lets lay the basis of our scene.

    1. If you have not already done so import Yarn Spinner

    2. Create a new scene in Unity, name it whatever you like

    3. Create a new C# file, name it MinimalDialogueRunner.cs

    These are the basic bits we will need for our runner, in particular the Yarn.Dialogue is of most interest to us as it is how we will interface to the compiled version of the Yarn narrative.

    Yarn can be thought of as an event driven story language, you start the story by going to a particular node in the story, then you advance through the story. Every time you reach a line of dialogue you show it (somehow), and then advance onto the next piece. When you hit a command you handle it (somehow), and then advance. When you hit a set of options you get the users input on which option to go with (somehow), and then advance. Eventually you finish that node and either end the story there or jump into a new node and start the process all over again. This gives us our main events we need to handle, lets stub them out now, add the following methods to our new runner:

    Hopefully these are understandable enough from their names, but the public ones are intended to allow us as the game developer control the flow, and the private ones are for the runner itself to make use of before letting other parts of the game know what has happened. In general inside each of these methods we are going to be doing some setup and configuration so that when we let other parts of the game know everything is in a manner they can consume without too much work. Now there are two more events that aren't really events but should be handled, we need to let the line provider know to expect lines and we need to have a means of querying what nodes exist in the project, so we should add them now.

    Next step is to work out how to let the rest of the game know when important events have happened. While there are lots of ways we could do this, we will do this through the system. We are going with Unity Events because they are super easy to create, you hook them up via the editor (which is nice), and their limitations won't bother us for this sample. We'll need six different events with various parameters so let's make them now:

    Each of these basically maps to a specific method we defined above and can be thought of as the public facing side of those methods. Ok now its time to draw the rest of the owl!

    Here we will be making the implementation of the methods we defined above and dispatching the events from inside of them, let's get cracking.

    Replace the StartDialogue and StopDialogue methods with the following:

    These methods are very minimal in what they do, they set some flags for state and otherwise just let the dialogue object itself know things have changed.

    Replace the HandleOptions method with the following:

    In this method we do a few more steps, we go through each option in the option set and configure them so that other parts of our game can make use of them. We do this by first getting the line inside of the option from our line provider and then we do any necessary substitution of variables inside of the line, and then finally we parse any markup inside of the line. With that done the line is ready, we then make a new DialogueOption with that line and other relevant option info, in particular the availability and ID of the option, and then add that to an array. Once we have run this process on every option in the set of options we fire off the event with the array of our configured options for the rest of the game to use.

    Replace the HandleCommand method with the following:

    When it comes to commands there are two parts, if the command is wait we will handle it, otherwise we send it off for other parts of the game to deal with. In this method we split the command up into its constituent parts, we use an existing method to do this so that situations like <<unlock "room seven">> are split up into the elements of unlock and room seven, although we could do this ourselves if we want. Once that is done we check if the command is a wait command and if it is we run a coroutine to wait for the duration, otherwise we fire off our command event to let the rest of the game worry about the command.

    Replace the HandleLine method with the following:

    Much like with the options, here we are getting a line from our line provider, performing any substitution, parsing markup and then sending it off. Unlike the options however we only have to do this once as lines come in one at a time.

    Finally now we do the three simple events, HandleNodeStarted, HandleNodeEnded, HandleDialogueComplete:

    With these done we have now handled all the events that our runner needs to ensure are handled, all that remains is to implement the Continue and SetSelectedOption methods. These two will allow us advance the dialogue and choose options, so lets add them now:

    Both of these are similar, they both advance the dialogue itself onto the next event and in the case of SetSelectedOption chooses an option first. With that done we are finished with the events and the flow, now we need to setup the runner and dialogue so that they can actually do something with all these events we just handled.

    Everything done so far has been about the handling and dispatching of events and flow we now need to configure the runner and dialogue class so that it can actually do something. First lets configure the various pieces of the runner itself, add the following code:

    One method CreateDialogueInstance we haven't written yet and will get to in a second, but the rest of this is setting up the pieces the runner will need. In particular we make sure we have a variable storage and line provider and that both are ready to be used, everything else happens in CreateDialogueInstance so lets make that now:

    The dialogue class being configured here is structurally very similar to our own runner, we have hooks that are run when important events happen. Its realistically the Dialogue class that does most of the heavy lifting, we are acting as a dispatching and Unity friendly window into the Dialogue. With that finished our custom dialogue runner is done, time to hook it up to something to make it dance.

    The existing views Yarn Spinner provides assume that you are using the callback model the runner has, ours no longer does that so they are incompatible. So we will make some new ones, based off the originals but to handle our custom runners approach instead of the original. Before going any further its worth saying we could modify the runner so that it mimics the callback model and we could reuse the existing views, we chose not to do that because it isn't really a great example of taking control over the dialogue if you end up just rebuilding the exact same (or very similar) thing.

    We'll get started with the line view first, and to speed things up we are gonna be reusing most of the code from the existing LineView.

    1. Back inside Unity create a new C# file

    2. Name it MinimalLineView

    3. Add the following code:

    Most of the code here is unchanged from the original. The main differences is we are no longer derived from the DialogueViewBase class but are just normal gameobjects and all the method signatures for handling the dialogue events no longer have a callback method. Now when they have done their stuff they just call Continue on our runner.

    Much like with our line view our options view will also be mostly copy-pasted from the existing OptionListView class and modified as in. We will still be using the OptionView class and default prefab just to save time, so this will require a little bit of emulating some callbacks so that works, but won't be too much.

    1. Create a new C# file

    2. Name it MinimalOptionsView

    3. Add the following code:

    Again this is almost identical to the original code, just with some tweaks here and there. Of interest is the RunLine method, when this is hooked up later it will let us get access to the line and we don't have to do anything to advance the dialogue because in the line view that is doing that side of it.

    So our views are ready, but we still have a lot of other events we haven't handled and we also don't currently have a way to start the dialogue, let's fix that.

    . Make a new C# file . Name it DialogueSupportComponent . Add the following code:

    This class basically just logs every other event, in a more complete scenario you might have to do more but for us just logging is fine. We also have a very crude means of starting the dialogue now, by pressing the Spacebar.

    Now for the last little bits and pieces, we need to hook everything up in the scene, to speed things up we are gonna be using the Dialogue System prefab and reworking it to use our components.

    1. Add a Dialogue System Prefab into the scene

    2. Select the prefab in the hierarchy

    3. In the Inspector unpack the prefab

    4. Remove the Dialogue Runner component from the prefab and add in our Minimal Dialogue Runner component in its place

    Now the UI and components are in their right place time to connect everything into our runner.

    1. Select the Minimal Runner component in the hierarchy

    2. Connect the Command Needs Handling event up to the Dialogue Support Component's HandleCommand method

    3. Connect the Node Started event up to the Dialogue Support Component's LogNodeStarted method

    ; if you see this, you saw us doing stuff behind the scenes!

    Our runner is now fully hooked up, lets give it a go.

    1. Create a new Yarn file

    2. Add some content

    3. Create a Yarn Project

    4. Hook your yarn file up to the project

    We've done an awful lot in this guide, created new runners and UI to accompany them, and as part of it taken a deeper look into how Yarn Spinner itself works. There is still a lot more to do before you could ever use this runner in an actual production game, for a start it doesn't set up default variables, but this is the basis of a much larger world. Hopefully now if you find yourself wanting a different flow to your architecture or am needing more control over the process of handling your Yarn you will have the tools needed to do it yourself.

    Open it and add the following:

    Add a Dialogue Support Component to the prefab

  • Expand the Prefab, go into the Canvas -> Line View

  • Remove the Line View component

  • Add our Minimal Line View component in its place

  • Hook up the Line Text, Character Name Text, and Continue Button fields to their respective UI elements in the UI

  • Select the Options List View inside of Canvas -> Options List View

  • Remove the Options List View

  • Add our Minimal Options View in its place

  • Hook up the Last Line Text, and Options View Prefab

  • Connect the Node Ended event up to the Dialogue Support Component's LogNodeEnded method

  • Connect the Dialogue Complete event up to the Dialogue Support Component's LogDialogueComplete method

  • Connect the Options Need Presentation event up to the Minimal Options View's RunOptions method

  • Connect the Line Needs Presentation event up to the Minimal Options View's and Minimal Line View's RunLine method

  • Hook the project up to the Dialogue Runner

  • Start the scene

  • Press the Spacebar

  • Sit back and enjoy your story powered by your runner

  • ab12c

    Oh hi there

    34de5

    Hey

    Lines and Providers

    Our Dialogue Runner

    Handling the main events

    Dispatch the events

    Drawing the rest of the owl

    Configuring the dialogue

    Building the new Views

    Line View

    Options View

    And everything else

    Hooking it all up in the scene

    The End?

    CSV
    CSV table
    op codes
    Line Providers
    LocalizedLines
    Dialogue class
    UnityEvent
    Test link
    Dialogue Runner
    A: Oh hi there #line:ab12c
    B: Hey #line:34de5
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using Yarn.Unity;
    using UnityEngine.Events;
    using System.Linq;
    
    public class MinimalDialogueRunner : MonoBehaviour
    {
        public YarnProject project;
        public VariableStorageBehaviour VariableStorage;
        public LineProviderBehaviour LineProvider;
    
        public bool isRunning { get; internal set; } = false;
    
        private Yarn.Dialogue dialogue;
    }
    public void StartDialogue(string nodeName = "Start") {}
    public void StopDialogue() {}
    private void HandleOptions(Yarn.OptionSet options) {}
    private void HandleCommand(Yarn.Command command) {}
    private void HandleLine(Yarn.Line line) {}
    private void HandleNodeStarted(string nodeName) {}
    private void HandleNodeEnded(string nodeName) {}
    private void HandleDialogueComplete() {}
    public void Continue() {}
    public void SetSelectedOption(int optionIndex) {}
    private void PrepareForLines(IEnumerable<string> lineIDs) { LineProvider.PrepareForLines(lineIDs); }
    public bool NodeExists(string nodeName) => dialogue.NodeExists(nodeName);
    public UnityEvent<DialogueOption[]> OptionsNeedPresentation;
    public UnityEvent<string[]> CommandNeedsHandling;
    public UnityEvent<LocalizedLine> LineNeedsPresentation;
    public UnityEvent<string> NodeStarted;
    public UnityEvent<string> NodeEnded;
    public UnityEvent DialogueComplete;
    public void StartDialogue(string nodeName = "Start")
    {
        if (isRunning)
        {
            Debug.LogWarning("Can't start a dialogue that is already running");
            return;
        }
        isRunning = true;
        dialogue.SetNode(nodeName);
        dialogue.Continue();
    }
    public void StopDialogue()
    {
        dialogue.Stop();
        isRunning = false;
    }
    private void HandleOptions(Yarn.OptionSet options)
    {
        DialogueOption[] optionSet = new DialogueOption[options.Options.Length];
        for (int i = 0; i < options.Options.Length; i++)
        {
            var line = LineProvider.GetLocalizedLine(options.Options[i].Line);
            var text = Yarn.Dialogue.ExpandSubstitutions(line.RawText, options.Options[i].Line.Substitutions);
            dialogue.LanguageCode = LineProvider.textLanguageCode;
            line.Text = dialogue.ParseMarkup(text);
    
            optionSet[i] = new DialogueOption
            {
                TextID = options.Options[i].Line.ID,
                DialogueOptionID = options.Options[i].ID,
                Line = line,
                IsAvailable = options.Options[i].IsAvailable,
            };
        }
        OptionsNeedPresentation?.Invoke(optionSet);
    }
    private void HandleCommand(Yarn.Command command)
    {
        var elements = Yarn.Unity.DialogueRunner.SplitCommandText(command.Text).ToArray();
    
        if (elements[0] == "wait")
        {
            if (elements.Length < 2)
            {
                Debug.LogWarning("Asked to wait but given no duration!");
                return;
            }
            float duration = float.Parse(elements[1]);
            if (duration > 0)
            {
                IEnumerator Wait(float time)
                {
                    isRunning = false;
                    yield return new WaitForSeconds(time);
                    isRunning = true;
                    Continue();
                }
                StartCoroutine(Wait(duration));
            }
        }
        else
        {
            CommandNeedsHandling?.Invoke(elements);
        }
    }
    private void HandleLine(Yarn.Line line)
    {
        var finalLine = LineProvider.GetLocalizedLine(line);
        var text = Yarn.Dialogue.ExpandSubstitutions(finalLine.RawText, line.Substitutions);
        dialogue.LanguageCode = LineProvider.textLanguageCode;
        finalLine.Text = dialogue.ParseMarkup(text);
    
        LineNeedsPresentation?.Invoke(finalLine);
    }
    private void HandleNodeStarted(string nodeName)
    {
        NodeStarted?.Invoke(nodeName);
    }
    private void HandleNodeEnded(string nodeName)
    {
        NodeEnded?.Invoke(nodeName);
    }
    private void HandleDialogueComplete()
    {
        isRunning = false;
        DialogueComplete?.Invoke();
    }
    public void Continue()
    {
        if (!isRunning)
        {
            Debug.LogWarning("Can't continue dialogue when we aren't currently running any");
            return;
        }
    
        dialogue.Continue();
    }
    public void SetSelectedOption(int optionIndex)
    {
        if (!isRunning)
        {
            Debug.LogWarning("Can't select an option when not currently running dialogue");
            return;
        }
        dialogue.SetSelectedOption(optionIndex);
        dialogue.Continue();
    }
    void Awake()
    {
        if (VariableStorage == null)
        {
            VariableStorage = gameObject.AddComponent<InMemoryVariableStorage>();
        }
        dialogue = CreateDialogueInstance();
        dialogue.SetProgram(project.GetProgram());
    
        if (LineProvider == null)
        {
            LineProvider = gameObject.AddComponent<TextLineProvider>();
        }
        LineProvider.YarnProject = project;
    }
     private Yarn.Dialogue CreateDialogueInstance()
    {
        var dialogue = new Yarn.Dialogue(VariableStorage)
        {
            LogDebugMessage = delegate (string message)
            {
                Debug.Log(message);
            },
            LogErrorMessage = delegate (string message)
            {
                Debug.LogError(message);
            },
    
            LineHandler = HandleLine,
            CommandHandler = HandleCommand,
            OptionsHandler = HandleOptions,
            NodeStartHandler = HandleNodeStarted,
            NodeCompleteHandler = HandleNodeEnded,
            DialogueCompleteHandler = HandleDialogueComplete,
            PrepareForLinesHandler = PrepareForLines
        };
        return dialogue;
    }
    using System;
    using System.Collections;
    using UnityEngine;
    using TMPro;
    using UnityEngine.UI;
    using Yarn.Unity;
    
    public class MinimalLineView : MonoBehaviour
    {
        [SerializeField] internal CanvasGroup canvasGroup;
    
        [SerializeField] internal bool useFadeEffect = true;
    
        [SerializeField]
        [Min(0)]
        internal float fadeInTime = 0.25f;
    
        [SerializeField]
        [Min(0)]
        internal float fadeOutTime = 0.05f;
    
        [SerializeField] internal TextMeshProUGUI lineText = null;
    
        [SerializeField] internal bool showCharacterNameInLineView = true;
    
        [SerializeField] internal TextMeshProUGUI characterNameText = null;
    
        [SerializeField] internal bool useTypewriterEffect = false;
    
        [SerializeField]
        [Min(0)]
        internal float typewriterEffectSpeed = 0f;
    
        [SerializeField] internal GameObject continueButton = null;
    
        [SerializeField]
        [Min(0)]
        internal float holdTime = 1f;
    
        LocalizedLine currentLine = null;
    
        Effects.CoroutineInterruptToken currentStopToken = new Effects.CoroutineInterruptToken();
    
        private MinimalDialogueRunner runner;
    
        private void Awake()
        {
            canvasGroup.alpha = 0;
            canvasGroup.blocksRaycasts = false;
        }
        void Start() { runner = FindObjectOfType<MinimalDialogueRunner>(); }
        private void Reset() { canvasGroup = GetComponentInParent<CanvasGroup>(); }
    
        public void DismissLine()
        {
            currentLine = null;
    
            StartCoroutine(DismissLineInternal());
        }
    
        private IEnumerator DismissLineInternal()
        {
            var interactable = canvasGroup.interactable;
            canvasGroup.interactable = false;
    
            if (useFadeEffect)
            {
                yield return StartCoroutine(Effects.FadeAlpha(canvasGroup, 1, 0, fadeOutTime, currentStopToken));
                currentStopToken.Complete();
            }
    
            canvasGroup.alpha = 0;
            canvasGroup.blocksRaycasts = false;
            canvasGroup.interactable = interactable;
            runner.Continue();
        }
    
        public void RunLine(LocalizedLine dialogueLine)
        {
            StopAllCoroutines();
            StartCoroutine(RunLineInternal(dialogueLine));
        }
    
        private IEnumerator RunLineInternal(LocalizedLine dialogueLine)
        {
            IEnumerator PresentLine()
            {
                lineText.gameObject.SetActive(true);
                canvasGroup.gameObject.SetActive(true);
    
                if (continueButton != null)
                {
                    continueButton.SetActive(false);
                }
    
                if (characterNameText != null)
                {
                    characterNameText.text = dialogueLine.CharacterName;
                    lineText.text = dialogueLine.TextWithoutCharacterName.Text;
                }
                else
                {
                    if (showCharacterNameInLineView)
                    {
                        lineText.text = dialogueLine.Text.Text;
                    }
                    else
                    {
                        lineText.text = dialogueLine.TextWithoutCharacterName.Text;
                    }
                }
    
                if (useTypewriterEffect)
                {
                    lineText.maxVisibleCharacters = 0;
                }
                else
                {
                    lineText.maxVisibleCharacters = int.MaxValue;
                }
    
                if (useFadeEffect)
                {
                    yield return StartCoroutine(Effects.FadeAlpha(canvasGroup, 0, 1, fadeInTime, currentStopToken));
                    if (currentStopToken.WasInterrupted) {
                        yield break;
                    }
                }
    
                if (useTypewriterEffect)
                {
                    canvasGroup.alpha = 1f;
                    canvasGroup.interactable = true;
                    canvasGroup.blocksRaycasts = true;
                    yield return StartCoroutine(
                        Effects.Typewriter(
                            lineText,
                            typewriterEffectSpeed,
                            null,
                            currentStopToken
                        )
                    );
                    if (currentStopToken.WasInterrupted) {
                        yield break;
                    }
                }
            }
            currentLine = dialogueLine;
    
            yield return StartCoroutine(PresentLine());
    
            currentStopToken.Complete();
            lineText.maxVisibleCharacters = int.MaxValue;
            canvasGroup.alpha = 1f;
            canvasGroup.blocksRaycasts = true;
    
            if (holdTime > 0)
            {
                yield return new WaitForSeconds(holdTime);
            }
            if (continueButton != null)
            {
                continueButton.SetActive(true);
            }
        }
    
        public void OnContinueClicked()
        {
            if (currentLine == null)
            {
                return;
            }
            DismissLine();
        }
    }
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using Yarn.Unity;
    using TMPro;
    
    public class MinimalOptionsView : MonoBehaviour
    {
        [SerializeField] CanvasGroup canvasGroup;
    
        [SerializeField] OptionView optionViewPrefab;
    
        [SerializeField] TextMeshProUGUI lastLineText;
    
        [SerializeField] float fadeTime = 0.1f;
    
        [SerializeField] bool showUnavailableOptions = false;
    
        List<OptionView> optionViews = new List<OptionView>();
    
        LocalizedLine lastSeenLine;
    
        private MinimalDialogueRunner runner;
    
        public void Start()
        {
            runner = FindObjectOfType<MinimalDialogueRunner>();
    
            canvasGroup.alpha = 0;
            canvasGroup.interactable = false;
            canvasGroup.blocksRaycasts = false;
        }
    
        public void Reset() { canvasGroup = GetComponentInParent<CanvasGroup>(); }
    
        public void RunLine(LocalizedLine dialogueLine) { lastSeenLine = dialogueLine; }
    
        public void RunOptions(DialogueOption[] options)
        {
            System.Action<Yarn.Unity.DialogueOption> onOptionSelected = delegate(DialogueOption selectedOption)
            {
                StartCoroutine(OptionViewWasSelectedInternal(selectedOption));
                IEnumerator OptionViewWasSelectedInternal(DialogueOption selectedOption)
                {
                    yield return StartCoroutine(Yarn.Unity.Effects.FadeAlpha(canvasGroup, 1, 0, fadeTime));
                    runner.SetSelectedOption(selectedOption.DialogueOptionID);
                }
            };
    
            foreach (var optionView in optionViews) { optionView.gameObject.SetActive(false); }
    
            while (options.Length > optionViews.Count)
            {
                var optionView = CreateNewOptionView(onOptionSelected);
                optionView.gameObject.SetActive(false);
            }
    
            int optionViewsCreated = 0;
    
            for (int i = 0; i < options.Length; i++)
            {
                var optionView = optionViews[i];
                var option = options[i];
    
                if (option.IsAvailable == false && showUnavailableOptions == false) { continue; }
    
                optionView.gameObject.SetActive(true);
    
                optionView.Option = option;
                optionView.OnOptionSelected = onOptionSelected;
    
                if (optionViewsCreated == 0)
                {
                    optionView.Select();
                }
    
                optionViewsCreated += 1;
            }
    
            if (lastLineText != null)
            {
                if (lastSeenLine != null)
                {
                    lastLineText.gameObject.SetActive(true);
                    lastLineText.text = lastSeenLine.Text.Text;
                }
                else
                {
                    lastLineText.gameObject.SetActive(false);
                }
            }
    
            StartCoroutine(Effects.FadeAlpha(canvasGroup, 0, 1, fadeTime));
    
            OptionView CreateNewOptionView(System.Action<Yarn.Unity.DialogueOption> callback)
            {
                var optionView = Instantiate(optionViewPrefab);
                optionView.transform.SetParent(transform, false);
                optionView.transform.SetAsLastSibling();
    
                optionView.OnOptionSelected = callback;
                optionViews.Add(optionView);
    
                return optionView;
            }
        }
    }
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class DialogueSupportComponent : MonoBehaviour
    {
        MinimalDialogueRunner runner;
        void Start() { runner = FindObjectOfType<MinimalDialogueRunner>(); }
    
        void Update()
        {
            if (Input.GetKeyUp(KeyCode.Space))
            {
                if (!runner.isRunning)
                {
                    runner.StartDialogue();
                }
            }
        }
    
        public void HandleCommand(string[] commandText)
        {
            Debug.Log($"Received a command: {commandText[0]}");
            runner.Continue();
        }
        public void LogNodeStarted(string node) { Debug.Log($"entered node {node}"); }
        public void LogNodeEnded(string node) { Debug.Log($"exited node {node}"); }
        public void LogDialogueEnded() { Debug.Log("Dialogue has finished"); }
    }