All pages
Powered by GitBook
1 of 1

Loading...

Godot (GDScript)

Learn how to use Yarn Spinner for Godot (GDScript). This Quickstart is here to help you get up and running during the Alpha Period for Yarn Spinner for Godot (GDScript).

This is an Alpha release of . There will be bugs, we might change the API or features with an update, or something may break. We do not recommend you use this to ship a game just yet. If you want to ship a game using Yarn Spinner for Godot, until the GDScript version leaves testing, you should only use

This gets you from zero to running dialogue in about five minutes. It assumes you already know how to write Yarn, and how to use Godot.

Yarn Spinner for Godot (GDScript) requires Godot 4.6.x or higher.

Yarn Spinner for Godot (GDScript) will always be available for free. But similar to our Unity version, keep an eye out for a Yarn Spinner Plus edition to become available for sale soon. We rely on your support to keep our tools free and accessible.

If you want to support us during the Alpha period, you can support us on or . GitHub sponsors of $25 and above, and Patreon members of the "Scribe" or above tier will receive a license to the paid Plus version when it is released.

Please submit issues or feature requests via this form during the pre-release period:

Install the Plugin

Grab the plugin from https://github.com/YarnSpinnerTool/YarnSpinner-Godot-GDScript

  1. Copy addons/yarn_spinner/ into your Godot project's addons/ directory.

  2. Go to Project > Project Settings > Plugins and enable Yarn Spinner.

Make sure you have a .yarnproject and your .yarn files, then drop them into your Godot project. The .yarnproject tells the compiler which .yarn files to include.

If ysc (the Yarn Spinner Console tool) is on your system PATH, compilation is automatic. Whenever Godot imports or reimports the .yarnproject, the plugin runs ysc compile for all .yarn files in scope. Editing a .yarn file or the .yarnproject itself triggers a reimport, so you never need to compile manually.

Install ysc globally from

If ysc isn't on your PATH, you can compile manually:

You can also set the path to ysc in the import options for the .yarnproject file in the Godot inspector, or in Project Settings > Yarn Spinner > YSC Path.

Here's a minimal scene tree exmple:

  1. YarnDialogueRunner -- add a Node, attach the YarnDialogueRunner script. In the inspector, set Yarn Project to your .yarnproject file.

  2. LinePresenter -- add a PanelContainer, attach the YarnLinePresenter script. Give it a RichTextLabel child named TextLabel for dialogue text, a Label

In your game script, connect the presenters to the runner and start dialogue:

That's it. Run the scene and your dialogue plays.

Commands let Yarn tell your game to do things. The easiest way is the naming convention -- prefix any method with _yarn_command_ and the runner finds it automatically.

Make sure auto_discover_commands is enabled on the dialogue runner (it is by default). The runner scans the scene tree on startup and registers everything it finds.

If a command returns void, dialogue continues immediately. If it returns a Signal, dialogue pauses until that signal fires.

For commands that target specific nodes (like <<move mae center>>), define the method on the node's class:

Register the class as an instance command source:

Now <<move mae center>> finds the node named "mae", checks it's a Character, and calls _yarn_command_move("center") on it.

Functions let Yarn read values from your game. Register them with a name and parameter count:

Use them in Yarn expressions:

Or use the naming convention with _yarn_function_ prefix for auto-discovery.

Yarn variables work automatically. Declare them in your .yarn files and they're stored in an in-memory dictionary:

Access them from GDScript:

The runner creates a YarnInMemoryVariableStorage automatically. If you need persistence, subclass YarnVariableStorage and assign it in the inspector.

The dialogue runner emits signals for lifecycle events:

On the YarnDialogueRunner in the inspector:

  • Start Node -- which Yarn node to begin from (default: "Start")

  • Auto Start -- begin dialogue when the scene loads

  • Run Selected Option As Line -- after the player picks an option, show it as a line of dialogue before continuing

On YarnLinePresenter:

  • Typewriter Mode -- INSTANT, LETTER (character by character), or WORD

  • Characters Per Second / Words Per Second -- typewriter speed

  • Auto Advance -- automatically continue after a delay

On YarnOptionsPresenter:

  • Hide Unavailable -- hide options the player can't pick (vs showing them greyed out)

To make your own persenter UI subclass YarnDialoguePresenter:

Register it the same way:

If you're using node groups (multiple versions of the same content with when: conditions), set a saliency strategy on the runner:

Available strategies: YarnFirstSaliencyStrategy, YarnBestSaliencyStrategy, YarnRandomSaliencyStrategy, YarnBestLeastRecentlyViewedSaliencyStrategy, YarnRandomBestLeastRecentlyViewedSaliencyStrategy.

named
CharacterLabel
for the speaker name, and optionally a
Label
named
ContinueIndicator
.
  • OptionsPresenter -- add a PanelContainer, attach the YarnOptionsPresenter script. Give it a VBoxContainer child named OptionsContainer -- buttons are created automatically at runtime.

  • Auto Discover Commands -- scan the scene tree for _yarn_command_* and _yarn_function_* methods

    Use Markup -- enable BBCode rendering in the text label

    Add Your Yarn Files

    Automatic compilation

    A full release of Yarn Spinner for Godot (GDScript) will likely include ysc internally.

    Manual compilation

    Set Up the Scene

    Wire It Up

    Commands

    Instance Commands

    Functions

    Variables

    Signals

    Useful Settings

    Custom Presenters

    Node Groups and Saliency

    https://github.com/yarnspinnertool/yarnspinner-console
    Yarn Spinner for Godot (GDScript)
    Yarn Spinner for Godot (C#).
    GitHub Sponsors
    Patreon
    https://yarnspinner.dev/pre-release-feedback
    ysc compile your_project.yarnproject
    Game (Node2D)
    ├── YarnDialogueRunner (Node)
    ├── CanvasLayer
    │   ├── LinePresenter (PanelContainer)
    │   │   └── VBoxContainer
    │   │       ├── CharacterLabel (Label)
    │   │       ├── TextLabel (RichTextLabel)
    │   │       └── ContinueIndicator (Label)
    │   └── OptionsPresenter (PanelContainer)
    │       └── OptionsContainer (VBoxContainer)
    extends Node2D
    
    @onready var dialogue_runner := $YarnDialogueRunner
    @onready var line_presenter := $CanvasLayer/LinePresenter
    @onready var options_presenter := $CanvasLayer/OptionsPresenter
    
    func _ready():
        dialogue_runner.add_presenter(line_presenter)
        dialogue_runner.add_presenter(options_presenter)
        dialogue_runner.start_dialogue("Start")
    # On any node in your scene tree:
    
    func _yarn_command_shake(intensity: float) -> void:
        # <<shake 2.5>>
        # Runs instantly, dialogue continues immediately
        camera.shake(intensity)
    
    func _yarn_command_fade(duration: float) -> Signal:
        # <<fade 0.5>>
        # Returns a Signal -- dialogue waits for it to complete
        var tween = create_tween()
        tween.tween_property($Overlay, "modulate:a", 1.0, duration)
        return tween.finished
    
    func _yarn_command_wait(seconds: float) -> Signal:
        # <<wait 1.5>>
        return get_tree().create_timer(seconds).timeout
    class_name Character
    extends Node2D
    
    func _yarn_command_move(destination: String) -> Signal:
        var target := get_node("/root/Game/Waypoints/" + destination)
        var tween := create_tween()
        tween.tween_property(self, "position", target.position, 1.0)
        return tween.finished
    func _ready():
        var library := dialogue_runner.get_library()
        library.register_instance_command("move", Character)
        library.set_target_root(self)
    func _ready():
        dialogue_runner.add_function("player_health", _get_health, 0)
        dialogue_runner.add_function("has_item", _check_item, 1)
    
    func _get_health() -> float:
        return player.health
    
    func _check_item(item_name: String) -> bool:
        return inventory.has(item_name)
    <<if player_health() < 50>>
        You're not looking so good.
    <<endif>>
    
    <<if has_item("key")>>
        The door opens.
    <<endif>>
    
    You have {format("{0}", player_health())} health remaining.
    <<declare $coins = 0>>
    <<declare $player_name = "Adventurer">>
    <<declare $has_sword = false>>
    
    <<set $coins = $coins + 50>>
    Welcome, {$player_name}! You have {$coins} coins.
    var coins = dialogue_runner.variable_storage.get_value("$coins")
    dialogue_runner.variable_storage.set_value("$player_name", "Hero")
    dialogue_runner.dialogue_started.connect(func(): show_dialogue_ui())
    dialogue_runner.dialogue_completed.connect(func(): hide_dialogue_ui())
    dialogue_runner.node_started.connect(func(name): print("Entered: ", name))
    dialogue_runner.node_completed.connect(func(name): print("Left: ", name))
    dialogue_runner.unhandled_command.connect(func(text): print("Unknown: ", text))
    extends YarnDialoguePresenter
    
    func run_line(line: YarnLine) -> Variant:
        $Label.text = line.get_plain_text()
        visible = true
        # Wait for player to click
        await $Button.pressed
        visible = false
        return null
    
    func run_options(options: Array[YarnOption]) -> int:
        # Build your own UI, return the index of the selected option
        for i in options.size():
            var btn = Button.new()
            btn.text = options[i].get_plain_text()
            btn.pressed.connect(func(): selected = i)
            $Container.add_child(btn)
        await $Container.get_child(0).pressed  # Simplified
        return selected
    dialogue_runner.add_presenter(my_custom_presenter)
    dialogue_runner.saliency_strategy = YarnRandomBestLeastRecentlyViewedSaliencyStrategy.new()