Commands

A Command is a line in a Yarn Script that tells your game to do something. It’s written inside << and >>, with the Command’s name first and any arguments after it:

title: CommandsExample
---
<<play_sound door_creak>>
Rosa: Oh! You gave me a fright.
<<move Rosa 300 0>>
<<wait 1>>
Rosa: Here, you'll need this.
<<give_item "brass key" 1>>
===
Three Commands in a node. The first word inside each is the Command’s name, and the rest are its arguments.

When the dialogue reaches a Command, the Dialogue Runner calls the Blueprint function or C++ code you’ve connected to it. Once that has finished, the dialogue carries on.

sequenceDiagram
  participant R as Dialogue Runner
  participant A as BP_Lighthouse
  participant Rosa as Rosa (BP_Character)
  Note over R: #60;#60;play_sound door_creak#62;#62;
  R->>A: play_sound("door_creak")
  A-->>R: returns
  Note over R: Shows "Oh! You gave me a fright."
  Note over R: #60;#60;move Rosa 300 0#62;#62;
  R->>Rosa: move(300, 0)
  Rosa-->>R: returns true
  Note over Rosa: Rosa moves for 1 second
  Rosa->>R: Complete Blocking Command
  Note over R: Carries on with #60;#60;wait 1#62;#62;
The first two Commands from the node above. The Dialogue Runner calls a function for each one. The move function returns true, so the dialogue waits until Rosa has finished moving and Complete Blocking Command is called.

Writing a Command

There are several ways to connect a Command to your game. When the dialogue reaches a Command, the Dialogue Runner looks for it in this order:

  1. the built-in Commands, <<wait>> and <<stop>>,
  2. Commands you’ve added by name, with the Register Command Handler node or AddCommandHandler() in C++ (see Adding Commands by name),
  3. a function with the same name as the Command, on one of the Dialogue Runner’s Command Handler Objects,
  4. a function with the same name as the Command, on the Actor named by the Command’s first argument, or on one of that Actor’s components.

The last two need no registering beyond the function itself, and the Dialogue Runner converts each argument to the type of the function’s parameter. They work the same way for Blueprint functions and C++ UFUNCTIONs.

Commands on a Command Handler Object

The Dialogue Runner has an array called Command Handler Objects, in the Commands part of its Yarn Spinner section. When the dialogue reaches a Command, the Dialogue Runner looks at each object in the array, in order, for a function whose name is exactly the Command’s name, including its case. A function called play_sound is <<play_sound>>.

To make <<play_sound>> a function on the Actor that has your Dialogue Runner:

  1. Open the Actor’s Blueprint, for example BP_Lighthouse.
  2. Add a function called play_sound, with one input: Sound Name, a String.
  3. Make the function play the sound. For example, if Sound Name is door_creak, call Play Sound 2D with your door sound.
  4. In the Event Graph, after Event BeginPlay, drag in the Yarn Dialogue Runner component, get its Command Handler Objects, and connect an Add node with Self as the item.
PLACEHOLDER — Screenshot needed
A Blueprint graph in BP_Lighthouse. Event BeginPlay connects to an Add node whose target array is Command Handler Objects from the Yarn Dialogue Runner component, and whose item is Self. Beside it, the play_sound function graph takes Sound Name, compares it with door_creak, and calls Play Sound 2D.

From C++, add the object to the array yourself: DialogueRunner->CommandHandlerObjects.Add(this);. The function has to be a UFUNCTION, and its name has to match the Command’s name exactly, so a C++ function for <<play_sound>> is called play_sound.

<<play_sound door_creak>>
Calling the play_sound function from Yarn.

Commands on an Actor in the Level

If no Command Handler Object has a matching function, the Dialogue Runner treats the Command’s first argument as the name of an Actor. It looks for that Actor in the Level, and calls the function named after the Command on it, or on one of its components that has it. The rest of the arguments are passed to the function.

Rosa (Self)
DefaultSceneRoot Scene Component
Body Static Mesh Component
A made-up Actor called Rosa in the Level, placed from a Blueprint called BP_Character. BP_Character has a function called move.

For <<move Rosa 300 0>>, the Dialogue Runner finds the Actor called Rosa and calls its move function with 300 and 0:

  1. Open BP_Character, and add a function called move, with two inputs: X and Y, both Float.
  2. Add an output called ReturnValue, a Boolean, and set it to true on the Return Node. That makes the dialogue wait, as described in Making the dialogue wait.
  3. In the function, call a custom event that does the moving, like Start Move, passing X and Y. A Blueprint function can’t contain latent nodes like Move Component To or Delay, so the moving happens in the event.
  4. In Start Move, use Move Component To on the root component, with Over Time set to 1. From its Completed pin, call Complete Blocking Command on the Dialogue Runner.
PLACEHOLDER — Screenshot needed
Two Blueprint graphs in BP_Character. The move function takes X and Y, calls the Start Move custom event with them, and returns true from a ReturnValue output. The Start Move event calls Move Component To on DefaultSceneRoot over 1 second, and its Completed pin calls Complete Blocking Command on the Yarn Dialogue Runner.
<<move Rosa 300 0>>
Calling the move function on the Actor called Rosa.

If every character in your game uses the same Blueprint, each one gets a <<move>> Command, and the first argument says which character moves. If there’s no Actor with that name, the Command isn’t handled (see When a Command isn’t found). If there is, but neither it nor its components have the function, the Dialogue Runner also logs a warning saying so.

In the editor, the Dialogue Runner matches the first argument against each Actor’s object name and its label, the name shown in the Outliner. A packaged game only matches the object name, which for an Actor placed in the Level is usually something like BP_Character_C_1, not the label you gave it. For Commands that have to work in a packaged game, use a Command Handler Object or add the Command by name.

Arguments

Arguments are separated by spaces. To pass an argument with a space in it, put it in double quotes: <<give_item "brass key" 1>>.

For a function on a Command Handler Object or an Actor, each argument is converted to the type of the function’s parameter:

Parameter typeWhat you write in Yarn
StringAny text.
Integer, FloatA number, like 3 or 0.5.
Booleantrue or false.
Vector, Rotator and other structsThe struct in Unreal’s text format, like (X=100,Y=200,Z=0) for a Vector. Put it in double quotes if it has spaces.
How Command arguments are converted to each parameter type.

If there are fewer arguments than parameters, the rest of the parameters get their type’s default, like 0, an empty String or false. Extra arguments are ignored. If an argument can’t be converted, the function is still called, with the parameter left at its default. Turn on the Dialogue Runner’s Verbose Logging to see a warning in the Output Log when that happens.

Commands you add by name get every argument as text, in an array of Strings, and convert them yourself.

To use a variable’s value as an argument, put it in curly braces: <<give_item {$reward} 1>>.

Making the dialogue wait

The dialogue carries on as soon as your function returns. To make it wait until something has finished, like an animation or a sound, make it a blocking Command. The dialogue then stays where it is until you call Complete Blocking Command on the Dialogue Runner.

  • For a function on a Command Handler Object or an Actor, return true from an output called ReturnValue, a Boolean. In C++, return a bool. Returning false, or having no output, means the dialogue carries on straight away.
  • For a Command added by name, use the Register Blocking Command Handler node, or AddBlockingCommandHandler() in C++. The dialogue always waits for these.
DialogueRunner->AddBlockingCommandHandler(TEXT("wave"), [this](const TArray<FString>& Parameters)
{
	PlayWaveAnimation();
	GetWorldTimerManager().SetTimer(WaveTimer, [this]()
	{
		DialogueRunner->CompleteBlockingCommand();
	}, 1.5f, false);
});
A blocking Command in C++. The dialogue waits until the timer calls CompleteBlockingCommand(). PlayWaveAnimation and WaveTimer stand in for your own Actor’s function and FTimerHandle.

You can call Complete Blocking Command from inside the handler, if the Command turns out to have nothing to wait for. If no blocking Command is waiting, it logs a warning and does nothing.

For more on waiting in Commands, see Async and Cancellation.

Adding Commands by name

You can add a Command to a Dialogue Runner yourself, with any name, and connect it to a Blueprint event or a C++ lambda. The handler gets the Command’s arguments as an array of Strings.

In Blueprints:

  1. After Event BeginPlay, add a Register Command Handler node.
  2. Connect your Dialogue Runner to Dialogue Runner, and type the Command’s name in Yarn, like give_item, into Command Name.
  3. Drag from the Handler pin and add a custom event. It has one input, Parameters, an array of Strings.
  4. In the event, read the arguments with Get: index 0 is the first argument. Convert numbers with String To Integer or String To Float.
PLACEHOLDER — Screenshot needed
A Blueprint graph. Event BeginPlay connects to Register Command Handler, with the Yarn Dialogue Runner component in Dialogue Runner and give_item in Command Name. The Handler pin connects to a custom event called On Give Item, whose Parameters array goes into two Get nodes, index 0 for the item name and index 1, through String To Integer, for the count.

In C++, call AddCommandHandler() on the Dialogue Runner. To use the plugin from your game’s C++ code, add "YarnSpinner" to your module’s dependencies in its .Build.cs file.

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "GameCommands.generated.h"

class UYarnDialogueRunner;

UCLASS()
class AGameCommands : public AActor
{
	GENERATED_BODY()

protected:
	virtual void BeginPlay() override;

private:
	UPROPERTY()
	TObjectPtr<UYarnDialogueRunner> DialogueRunner;

	TMap<FString, int32> Inventory;
};
Lines 18–19 The Dialogue Runner, found when the game starts.
Line 21 Your game’s own state. Here, a map from item names to counts.
GameCommands.h. An Actor that adds a «give_item» Command.
#include "GameCommands.h"
#include "YarnDialogueRunner.h"

void AGameCommands::BeginPlay()
{
	Super::BeginPlay();

	DialogueRunner = FindComponentByClass<UYarnDialogueRunner>();

	DialogueRunner->AddCommandHandler(TEXT("give_item"), [this](const TArray<FString>& Parameters)
	{
		if (Parameters.Num() < 2)
		{
			return;
		}

		const FString& ItemName = Parameters[0];
		const int32 Count = FCString::Atoi(*Parameters[1]);
		Inventory.FindOrAdd(ItemName) += Count;
	});

	DialogueRunner->StartDialogue(TEXT("CommandsExample"));
}
Line 8 Find the Dialogue Runner on this Actor.
Line 10 Add the Command. The first argument is its name in Yarn, without the << and >>.
Lines 12–15 The arguments arrive as text, so check there are enough of them.
Lines 17–19 <<give_item "brass key" 1>> passes brass key and 1. Convert the count to a number, and add it to the inventory.
Line 22 Start the dialogue once the Command has been added.
GameCommands.cpp. The Dialogue Runner is a component on the same Actor, with Auto Start turned off.
A Dialogue Runner with Auto Start on starts the dialogue when it begins play. A component begins play before its Actor’s Event BeginPlay runs, so the dialogue can reach a Command before your Blueprint or C++ has added it. Turn Auto Start off, and call Start Dialogue after adding your Commands.

To remove a Command, use the Unregister Command Handler node, or call DialogueRunner->RemoveCommandHandler(TEXT("give_item")).

If you add a Command with a name that’s already been added, the new one replaces the old one. You can’t replace wait or stop: the Dialogue Runner logs a warning, and the handler is never called.

Commands marked with YarnCommand

In C++, you can mark a UFUNCTION with YarnCommand metadata, and add every marked function on an object at once with Register Commands From Object. The function takes the Command’s arguments as an array of Strings:

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "AudioCommands.generated.h"

UCLASS()
class AAudioCommands : public AActor
{
	GENERATED_BODY()

protected:
	virtual void BeginPlay() override;

	UFUNCTION(meta = (YarnCommand = "play_sound"))
	void PlaySound(const TArray<FString>& Parameters);
};
AudioCommands.h. PlaySound is marked as the «play_sound» Command.

The YarnCommand metadata gives the Command’s name in Yarn. If you leave the name empty, the function’s name is used. The function has to take exactly one parameter, a const TArray<FString>&.

#include "AudioCommands.h"
#include "YarnCommandLibrary.h"
#include "YarnDialogueRunner.h"

void AAudioCommands::BeginPlay()
{
	Super::BeginPlay();

	UYarnDialogueRunner* DialogueRunner = FindComponentByClass<UYarnDialogueRunner>();
	UYarnCommandLibrary::RegisterCommandsFromObject(DialogueRunner, this);
}

void AAudioCommands::PlaySound(const TArray<FString>& Parameters)
{
	if (Parameters.Num() > 0)
	{
		UE_LOG(LogTemp, Log, TEXT("Playing %s"), *Parameters[0]);
	}
}
Line 10 Add every function on this object marked with YarnCommand or YarnFunction.
Lines 13–19 <<play_sound door_creak>> calls this with door_creak as the first element of Parameters.
AudioCommands.cpp. The Dialogue Runner is a component on the same Actor.

To remove them all again, use Unregister Commands From Object.

UFUNCTION metadata isn’t included in a packaged game. Instead, the editor saves the marked functions it finds in your project’s Config/DefaultGame.ini, in the Yarn Spinner Command Registry section of Project Settings, and a packaged game reads them from there. The editor updates it when your C++ or Blueprints change. If you change YarnCommand metadata and package your game without opening the editor in between, open the editor once, or reimport a Yarn Project, so the saved list is up to date.

Built-in Commands

Two Commands are always available:

CommandWhat it does
<<wait 2>>Waits for the number of seconds you give it before the dialogue carries on. With no number, it waits one second. It stops counting while the game is paused.
<<stop>>Ends the dialogue immediately.
Commands that are always available.

When a Command isn’t found

If the dialogue reaches a Command that isn’t connected to anything, the Dialogue Runner logs an error like YarnDialogueRunner: Unhandled command 'play_sound' in the Output Log, and carries on with the dialogue.

To handle those Commands yourself, bind to the Dialogue Runner’s On Unhandled Command event. It passes the whole Command as text in Command Text, like play_sound door_creak. The error is still logged. See Dialogue Runner for its other events.

To make the dialogue wait while you handle an unknown Command, turn off the Dialogue Runner’s Continue On Unhandled Command, in the Behaviour part of its Yarn Spinner section. The dialogue then stays where it is until you call Continue on the Dialogue Runner.

DialogueRunner->OnUnhandledCommand.AddDynamic(this, &AGameCommands::HandleUnhandledCommand);
Binding to OnUnhandledCommand in C++. HandleUnhandledCommand is a UFUNCTION on your Actor that takes a const FString&.

Autocomplete in VS Code

The Yarn Spinner extension for VS Code can suggest your Commands as you type, and warn you about ones it doesn’t know. It reads them from a .ysls.json file that the plugin writes next to each .yarnproject file.

The plugin writes the file when a Yarn Project is imported or reimported, after a Blueprint compiles, and after C++ code is reloaded. To write it yourself, right-click a Yarn Project in the Content Browser and choose Generate YSLS File.

The file includes:

  • C++ UFUNCTIONs marked with YarnCommand or YarnFunction, with their parameters and their doc comment,
  • Register Command Handler, Register Blocking Command Handler and Register Function Handler nodes in your Blueprints, when the name is typed into the node rather than connected from another node. Only the name is included.

Commands added with AddCommandHandler() in C++, and functions found on Command Handler Objects or Actors, aren’t included. To have a C++ Command suggested, mark it with YarnCommand and add it with RegisterCommandsFromObject().

For a setup that uses Commands and Functions together, see Putting It Together.

Next step Functions Getting values from your game to use in lines and conditions.