All pages
Powered by GitBook
1 of 5

Loading...

Loading...

Loading...

Loading...

Loading...

Components

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.

Dialogue Runner and a High Level Overview

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.

Dialog Flow

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.

Variable Storage

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

Functions and Commands

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.

Text and Assets

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

Builder API

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.

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

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 . To implement your own, check out the documentation. Once you have it, you can use it by calling when building your Dialogue Runner.

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.

In-Memory Variable Storage

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.

Custom Variable Storages

VariableStorage
DialogueRunnerBuilder::with_variable_storage
MemoryVariableStorage

Assets

While Bevy as a whole has assets, Yarn Spinner can associate specific assets with lines. These are always localised, such as voiceovers.

Using Metadata Instead of Assets

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:

title: Start
---
Granny: It's hard to believe that it's over, isn't it? #smiling
Granny: Funny how we get attached to the struggle. #laughing
Granny: Promise me that you'll take care of yourself, okay? #smiling
===

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.

Asset Providers

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:

fn spawn_dialogue_runner(mut commands: Commands, project: Res<YarnProject>) {
    let mut dialogue_runner = project
        .build_dialogue_runner()
        .add_asset_provider(AudioAssetProvider::new())
        .build();
    dialogue_runner.start_node("Start");
    commands.spawn(dialogue_runner);
}

The bundled example Dialogue View does not play any audio files, so you will need to write your own to make use of this feature.

The AudioAssetProvider itself is just a specialized . As the name suggests, it serves any assets based on their extension:

The (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 yourself with whatever custom behavior you desire. Check out the trait's documentation for the necessary methods.

FileExtensionAssetProvider
FileExtensionAssetProvider
AssetProvider
Dialogue View
fn spawn_dialogue_runner(mut commands: Commands, project: Res<YarnProject>) {
    let image_provider = FileExtensionAssetProvider::new()
        .with_file_extensions(file_extensions! {
           Image: ["png", "jpg", "jpeg"],
        });
    let mut dialogue_runner = project
        .build_dialogue_runner()
        .add_asset_provider(image_provider)
        .build();
    dialogue_runner.start_node("Start");
    commands.spawn(dialogue_runner);
}

Dialogue Views

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

Example Dialogue View

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:

app.add_plugins((
    DefaultPlugins,
    YarnSpinnerPlugin::new(),
    ExampleYarnSpinnerDialogueViewPlugin::new(),
));

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.

Custom Dialogue Views

To create your own Dialogue View, simply create a plugin that handles the different variants of that come up during play. These are regular that you can handle using an . Make sure that you run your plugin in a Bevy system set after the to catch all events that were sent in a given frame.

For inspiration, check out the source code of the .

DialogueEvent
Bevy events
EventReader
YarnSpinnerSystemSet
example Dialogue View