Learn about Dialogue Views, which present dialogue content to the user.
Yarn Spinner itself handles only the hard logic behind the dialogue flow, but it doesn't actually draw anything to the screen. This is the job of Dialogue Views. They are plugins that react to DialogueEvent
s fired by the Dialogue Runner and display them to the player.
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 Rust comes with a pre-built Dialogue View that handles common use cases: The Example Dialogue View. You'll see it used all over our examples. To use it, you simply add its plugin after the Yarn Spinner plugin proper:
And that's it! It will display whatever comes its way to the user and handle some basic input. As an added bonus, it will send out a SpeakerChangeEvent
whenever the active speaker has changed from its point of view, in case you want to e.g. rotate your camera there. If you do such an interaction, be sure to place your code in a Bevy system set that comes after the ExampleYarnSpinnerDialogueViewSystemSet
to avoid race conditions.
To create your own Dialogue View, simply create a plugin that handles the different variants of DialogueEvent
that come up during play. These are regular Bevy events that you can handle using an EventReader
. Make sure that you run your plugin in a Bevy system set after the YarnSpinnerSystemSet
to catch all events that were sent in a given frame.
For inspiration, check out the source code of the example Dialogue View.
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 MemoryVariableStorage
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.
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.
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 implementations of the trait VariableStorage
. To implement your own, check out the documentation. Once you have it, you can use it by calling DialogueRunnerBuilder::with_variable_storage
when building your Dialogue Runner.
Learn about the Bevy components that you use when working with Yarn Spinner for Rust.
Yarn Spinner for Rust is made up of a number of components. The most important of these are the Dialogue Runner, which loads and runs your scripts, and the Dialogue Views that show content to your player.
In this section, you'll learn about how to work with each of these.
While Bevy as a whole has assets, Yarn Spinner can associate specific assets with lines. These are always localised, such as voiceovers.
Before we jump into assets, let's first help you out if you don't care about localization. The mechanism in place for this is line metadata, which are strings you can add to Yarn lines after a hashtag:
A Dialogue View will be able to read the metadata "smiling", "laughing", and "smiling" again from LocalizedLine::metadata
and accordingly load things like character portraits. These annotations will also be written into the "comment" field of strings files, which are explained in the chapter Localisation.
Assets are fetched from the filesystem by structs implementing AssetProvider
. They need to be registered when creating a DialogueRunner
. For example, if you use the audio_assets
feature, you can register an asset provider for audio files by modifying the code found in the Quick Start like this:
The bundled example Dialogue View does not play any audio files, so you will need to write your own Dialogue View to make use of this feature.
The AudioAssetProvider
itself is just a specialized FileExtensionAssetProvider
. As the name suggests, it serves any assets based on their extension:
The FileExtensionAssetProvider
(and, by extension, the AudioAssetProvider
) will search for their assets in the directory assets/dialogue/<language>/<line-id.extension>
. So, for example, an AudioAssetProvider
serving up a voiceover for the line with the ID 41239 while the game is set to the language "de-CH" will search for assets/dialogue/de-CH/41239.mp3
.
Finally, you can implement AssetProvider
yourself with whatever custom behavior you desire. Check out the trait's documentation for the necessary methods.
The main way to actually manipulate the state of your dialog is through a DialogueRunner
. You create it from a YarnProject
(see Compiling Yarn Files) with either YarnProject::create_dialogue_runner()
or YarnProject::build_dialogue_runner()
. The first uses default configurations which should be alright for many use-cases, while the latter allows you to add or change functionality.
The actual navigation through a dialog is handled by a Dialogue View, which is responsible for back-and-forth interaction with the player. As such, most of the methods provided by a DialogueRunner
are to be called by such a view. The one you will want to call yourself, as seen in the Quick Start, is DialogueRunner::start_node
, which will tell the DialogueRunner
to start running from the provided node.
Variables need to be stored in some place. By default, they are kept in memory through the InMemoryVariableStorage
. This means that when you quit and reopen the game, all variables used in Yarn files will be empty again. Of course, this is suboptimal when you want to allow the player saving and loading their game state. To accomplish this, you can go one of two routes:
Manipulate the variables in the variable store. Read then when saving and write them when loading. You can access the variable storage through DialogueRunner::variable_storage()
.
Directory use a variable storage that stores its variables in a persistent way, such as a database or a file. You can change the underlying variable storage through the builder API discussed later in this chapter.
For information on how to create your own variable storage, see the chapter Variable Storage
Yarn files can contain user-defined functions and commands. These can be accessed with DialogueRunner::library()
and DialogueRunner::commands()
. For more information, see the chapters Custom Functions and Custom Commands.
We make a distinction between text, which are the written words organized into lines contained in Yarn files or in localization files, and assets, which are supplemental data associated with a line. Assets are referenced over a Bevy Handle
and can be used for things such as voiceover sound files or images that might need translation.
Of note is that using assets requires using localization, or at least thinking about it. As a consequence, language settings are split between text and assets. After all, a player might want to hear lines delivered in the original recorded language but read the text translated into their own language.
You can read more about how current language can be set for a DialogueRunner
in the localization chapter.
Text is provided by a TextProvider
. While it can be overwritten, the default StringsFileTextProvider
will be a good choice for nearly all users. The only reason you might have to create an own TextProvider
is if you want a very custom localization strategy, such as translating text automatically through AI.
Assets are provided by AssetProvider
s. In contrast to the TextProvider
, you might very well create your own AssetProvider
. For your convenience, Yarn Spinner already ships with an AudioAssetProvider
that you can use for voice lines and a FileExtensionAssetProvider
that can load any asset based on naming conventions and file extensions. See the chapter Assets.
Text and asset providers can be set through the builder API and accessed later with DialogueRunner::text_provider()
and DialogueRunner::asset_providers()
. If you know the exact type T
of AssetProvider
you want, you can call DialogueRunner::asset_provider::<T>()
instead.
As mentioned in the beginning of this chapter, a DialogueRunner
can be modified or extended on creation by using YarnProject::build_dialogue_runner()
. In fact, YarnProject::create_dialogue_runner()
is nothing but a shorthand for YarnProject::build_dialogue_runner().build()
.
You can use the builder API to inject your own implementations of traits used for the features presented in this chapter. DialogueRunnerBuilder::with_variable_storage
changes the underlying VariableStorage
and DialogueRunnerBuilder::with_text_provider
the TextProvider
. DialogueRunnerBuilder::add_asset_provider
adds an AssetProvider
to the set of asset providers called for each line presented to the player.