Putting It Together
Commands let your Yarn Scripts tell your game to do something. Functions let them ask your game for a value. The scene below uses both.
In the scene, Rosa hears the door, walks over to the lamp and lights it, then gives the player a key if they don’t already have one. The inventory in the top right shows the key once she’s handed it over.
The scene
The Dialogue System comes from . Its Dialogue Runner has Start Automatically turned on, with Start Node set to CommandsFunctionsDemo. The Lamp’s Light starts with its Intensity at 0.
The Yarn Script
title: CommandsFunctionsDemo
---
<<play_sound door_creak>>
Rosa: Oh! You gave me a fright.
Rosa: The lamp's been out for {days_since_lamp_lit()} days.
<<move Rosa 3 0>>
Rosa: Let me get it going again.
<<light_lamp>>
Rosa: There. Much better.
<<if not has_item("brass key")>>
Rosa: You'll need this to get up the stairs.
<<give_item "brass key">>
<<endif>>
<<if has_item("brass key")>>
Rosa: Now you've got the key. Up you go!
<<endif>>
===days_since_lamp_lit() returns 12, which is shown in the line.YarnTask, so the dialogue waits until she has finished walking.has_item("brass key") returns false, so Rosa hands over the key.true, because the player now has the key.The game code
The script on the Game GameObject adds the Commands and Functions when the scene starts:
using System.Collections;
using System.Collections.Generic;
using TMPro;
using UnityEngine;
using Yarn.Unity;
public class Game : MonoBehaviour
{
[SerializeField] DialogueRunner dialogueRunner;
[SerializeField] AudioSource audioSource;
[SerializeField] AudioClip doorCreak;
[SerializeField] Light lampLight;
[SerializeField] TextMeshProUGUI inventoryLabel;
readonly List<string> inventory = new List<string>();
void Awake()
{
dialogueRunner.AddCommandHandler<string>("play_sound", PlaySound);
dialogueRunner.AddCommandHandler("light_lamp", LightLamp);
dialogueRunner.AddCommandHandler<string>("give_item", GiveItem);
dialogueRunner.AddFunction<int>("days_since_lamp_lit", DaysSinceLampLit);
dialogueRunner.AddFunction<string, bool>("has_item", HasItem);
UpdateInventory();
}
void PlaySound(string soundName)
{
if (soundName == "door_creak")
{
audioSource.PlayOneShot(doorCreak);
}
}
IEnumerator LightLamp()
{
var elapsed = 0f;
while (elapsed < 0.8f)
{
elapsed += Time.deltaTime;
lampLight.intensity = Mathf.Lerp(0f, 2f, elapsed / 0.8f);
yield return null;
}
}
void GiveItem(string itemName)
{
inventory.Add(itemName);
UpdateInventory();
}
int DaysSinceLampLit()
{
return 12;
}
bool HasItem(string itemName)
{
return inventory.Contains(itemName);
}
void UpdateInventory()
{
inventoryLabel.text = "Inventory\n" + string.Join("\n", inventory);
}
}<<give_item>> adds to and has_item() checks.<<play_sound>>. It starts the sound and returns, so the dialogue doesn’t wait for it.<<light_lamp>>. It’s a coroutine, so the dialogue waits while the lamp fades on over 0.8 seconds.<<give_item>>. It returns nothing, so the dialogue doesn’t wait for it. UpdateInventory redraws the inventory.days_since_lamp_lit() returns a number.has_item() checks the same inventory that <<give_item>> adds to, so its answer changes once the key has been given.<<move>> isn’t added in Awake. It’s a method marked with [YarnCommand] in the script on Rosa, which Yarn Spinner adds to the Dialogue Runner by itself:
using UnityEngine;
using Yarn.Unity;
public class Character : MonoBehaviour
{
[YarnCommand("move")]
public async YarnTask Move(float x, float y)
{
var start = transform.position;
var end = new Vector3(x, y, start.z);
var elapsed = 0f;
while (elapsed < 1f)
{
elapsed += Time.deltaTime;
transform.position = Vector3.Lerp(start, end, elapsed);
await YarnTask.Yield();
}
}
}static, so it’s a Command on a GameObject. In Yarn, the first argument, Rosa, says which GameObject to call it on. x and y come from the other two arguments, converted to float.async and returns a YarnTask, so the dialogue waits until the move is done.What it shows
Every Command in the scene changes something you can see or hear. Two of them, <<move>> and <<light_lamp>>, make the next line wait: one is an async method and the other is a coroutine. <<play_sound>> and <<give_item>> return nothing, and the dialogue carries on without waiting.
Each Function returns a value the Yarn Script uses: days_since_lamp_lit() puts a number in a line, and has_item() picks which lines Rosa says. The second has_item() check gives a different answer from the first because <<give_item>> has changed the inventory it reads.