The Dialogue Runner has two helpers for getting lines ready:
- The Line Provider gets each line’s text, in the right language, ready to show.
- The Asset Provider finds files that go with each line, like images, and loads them before they’re needed.
The Line Provider
When a line runs, the Dialogue Runner gives the Line Provider the line’s ID, and gets back a line that’s ready for the Presenters:
flowchart TB
id["Line ID: line:a1b2c3d"]
presenters["Presenters"]
id -->|"1. find the text"| text["Rosa: You've got {0} coins."]
text -->|"2. fill in values"| subs["Rosa: You've got 15 coins."]
subs -->|"3. read markup"| presenters- It finds the text in the current language, from Godot’s translations, or from your Yarn Script if there’s no translation. See Localising Your Game.
- It fills in values, like
{$coins}or{days_since_lamp_lit()}, which the dialogue has already worked out. - It reads the markup, like
[b]or[wave], so the Presenters know what to show. See Markup.
In a translation, each value is written as a number in braces, like {0}. Translators should keep these, and can move them to wherever they belong in their language:
keys,en,de
YARN_line:c3d4e5f,Rosa: You've got {0} coins.,Rosa: Du hast {0} Münzen.The Line Provider’s language settings are in the Dialogue Runner’s Line Provider property. See When a line isn’t translated. From GDScript, get it with dialogue_runner.get_line_provider().
Shadow lines
Sometimes the same line appears in more than one place, like a greeting that can come up in several nodes. A shadow line is a copy of another line that shares its translations and voice clip, so it only needs translating and recording once.
Give the copy a #shadow: tag with the ID of the line it copies, instead of a #line: tag:
title: Morning
---
Rosa: Hello again. #line:a1b2c3d
===
title: Evening
---
Rosa: Hello again. #shadow:a1b2c3d
===A shadow line must have exactly the same text as the line it copies. If it doesn’t, the Yarn Project won’t compile, and you’ll see “Shadow lines must have the same text as their source”.
The Asset Provider
The Dialogue Runner makes an Asset Provider when the scene starts. It isn’t in the Inspector. Get it from GDScript with dialogue_runner.get_asset_provider().
Its main use is finding a file for a line, by the line’s ID. It looks in two folders:
| Kind of file | Folder | File types |
|---|---|---|
| Audio | res://audio/dialogue/ | .ogg, .wav, .mp3 |
| Images | res://images/dialogue/ | .png, .jpg, .webp |
Name each file after its line’s ID, without the line: part. An image for the line tagged #line:a1b2c3d is res://images/dialogue/a1b2c3d.png. If the ID has a : or / after the line: part, replace it with _ in the file name.
To get a line’s file, call get_texture() for an image, or get_audio() for audio, with the line’s ID. Each returns null if there’s no file for the line.
This Presenter shows a picture for each line that has one:
extends YarnDialoguePresenter
@export var picture: TextureRect
func run_line(
line: YarnLine,
token: YarnCancellationToken = null
) -> void:
var assets := dialogue_runner.get_asset_provider()
picture.texture = assets.get_texture(line.line_id)
await token.wait_for_next_content()null if there isn’t one, which clears the picture.The Voice-Over Presenter doesn’t use the Asset Provider. It finds its clips itself. See Voice-Over.
Loading files before they’re needed
Before a node’s lines run, the Dialogue Runner asks the Asset Provider to start loading their files in the background. By the time a line runs, its image or audio has usually loaded, so get_texture() and get_audio() don’t have to wait for the file.
Using your own file names
If your files aren’t named after line IDs, tell the Asset Provider which file belongs to which line. Do it before the dialogue starts:
var assets := dialogue_runner.get_asset_provider()
assets.register_asset("line:a1b2c3d", "res://portraits/rosa_happy.png")
assets.register_assets({
"line:e5f6a7b": "res://portraits/rosa_sad.png",
"line:c9d0e1f": "res://portraits/tom_confused.png",
})Or list them in a CSV file, and load it with load_mappings_from_csv(). The first row is headings, and it’s skipped. After that, each row has a line ID and a path:
line_id,path
line:a1b2c3d,res://portraits/rosa_happy.png
line:e5f6a7b,res://portraits/rosa_sad.pngA file you’ve registered is used for its line before the Asset Provider looks in its folders.
To look in different folders, set audio_base_path and image_base_path on the Asset Provider.
Choosing files with your own tag
Naming files after line IDs means the IDs have to stay the same. If you’d rather pick a line’s file yourself, give the line a tag of your own, like #picture:lamp, and read it in your Presenter. Tags are in the line’s metadata, without the #:
extends YarnDialoguePresenter
## Shows the picture named in a line's #picture: tag.
@export var picture: TextureRect
func run_line(
line: YarnLine,
token: YarnCancellationToken = null
) -> void:
picture.texture = _picture_for(line)
await token.wait_for_next_content()
func _picture_for(line: YarnLine) -> Texture2D:
for tag in line.metadata:
if tag.begins_with("picture:"):
var file := tag.trim_prefix("picture:")
return load("res://images/pictures/%s.png" % file)
return nullpicture:. #picture:lamp is in metadata as picture:lamp.res://images/pictures/.picture: tag, so no picture.The lines keep their own line tags, so you can still use Add Line Tags to Yarn Scripts. Files loaded this way don’t go through the Asset Provider, so they aren’t loaded before they’re needed.
The demo scene
In this scene, Rosa is getting the player ready to climb the tower. She mentions the old lamp, the key, the back door and a map, and you want a picture of each one next to her line:
The writer tags each line with the picture that goes with it, like #picture:lamp, and this scene uses PicturePresenter from Choosing files with your own tag to show it. Rosa’s last line has no #picture: tag, so the frame is empty. Under the frame, a label shows which file each picture came from.
title: PicturesDemo
---
Rosa: This is the old lamp from the tower. #picture:lamp
Rosa: Here's the key to get in. #picture:key
Rosa: The door's round the back. #picture:door
Rosa: I'll draw you a map, so you don't get lost. #picture:map
Rosa: Good luck up there.
===#picture: tag names a file in res://images/pictures/, without the .png.#picture: tag, so the frame is empty.The Dialogue Runner has two Presenters in its Presenters list, and it gives every line to both of them:
- The Line Presenter shows Rosa’s text in the box at the bottom.
PicturePresenterlooks through the line’s tags. For#picture:lamp, it loadsres://images/pictures/lamp.pngand puts it in the frame. For a line with no#picture:tag, it clears the frame.
Both Presenters then wait for the player. When the player presses continue, the token asks both of them for the next content. Once both return, the Dialogue Runner moves on to the next line. The label under the frame isn’t part of either Presenter. The scene’s own script checks which picture is showing, and shows the path of its file.
extends Control
@export var dialogue_runner: YarnDialogueRunner
@export var picture: TextureRect
@export var file_label: Label
func _ready():
# Start again when the conversation ends.
dialogue_runner.dialogue_completed.connect(
_start, CONNECT_DEFERRED)
_start()
func _start() -> void:
dialogue_runner.start_dialogue("PicturesDemo")
func _process(_delta: float) -> void:
# Show which file the picture came from.
if picture.texture == null:
file_label.text = "No image for this line"
else:
file_label.text = picture.texture.resource_path