Skip to main content
This example demonstrates how to use metadata to attach custom data to dialogue entries. Metadata allows you to store information like character names, emotions, animations, voice lines, and any other custom data your game needs.

Overview

In this example, you’ll learn how to:
  • Set metadata on dialogue entries
  • Retrieve metadata in UI code
  • Use metadata for displaying character names
  • Store complex data structures in metadata
  • Handle missing metadata gracefully

Basic Metadata Example

Let’s start with a simple example that adds an author name to dialogue:
extends DialogueEngine

func _setup() -> void:
    # Use DialogueEntry.set_metadata for data that must be available through DialogueEngine.dialogue_continued signal.
    # The metadata handling per DialogueEntry must be implemented by the user.
    add_text_entry("[i]We won! Let's goooo!![/i]").set_metadata("author", "Gary")
    add_text_entry("Press <Enter> or <Space> to exit.")
set_metadata() returns the DialogueEntry, allowing you to chain it directly after add_text_entry().

Displaying Metadata in UI

Here’s how to read and display the metadata:
extends VBoxContainer

@export var dialogue_gdscript: GDScript = null
var dialogue_engine: DialogueEngine = null

func _ready() -> void:
    dialogue_engine = dialogue_gdscript.new()
    dialogue_engine.dialogue_started.connect(__on_dialogue_started)
    dialogue_engine.dialogue_continued.connect(__on_dialogue_continued)
    dialogue_engine.dialogue_finished.connect(__on_dialogue_finished)
    dialogue_engine.dialogue_canceled.connect(__on_dialogue_canceled)

func _input(p_input_event: InputEvent) -> void:
    if p_input_event.is_action_pressed(&"ui_accept"):
        dialogue_engine.advance()

func __on_dialogue_started() -> void:
    print("Dialogue Started!")

func __on_dialogue_continued(p_dialogue_entry: DialogueEntry) -> void:
    var label: RichTextLabel = RichTextLabel.new()
    label.set_use_bbcode(true)
    label.set_fit_content(true)
    if p_dialogue_entry.has_metadata("author"):
        var author: String = p_dialogue_entry.get_metadata("author")
        label.set_text("  > " + author + ": " + p_dialogue_entry.get_text())
    else:
        label.set_text("  > " + p_dialogue_entry.get_text())
    add_child(label)

func __on_dialogue_finished() -> void:
    print("Dialogue Finished! Exiting...")
    get_tree().quit()

func __on_dialogue_canceled() -> void:
    print("Dialogue Canceled! Exiting...")
    get_tree().quit()

Metadata API

Setting Metadata

# Set a single metadata key-value pair
var entry: DialogueEntry = add_text_entry("Hello!")
entry.set_metadata("character", "Alice")

# Chain metadata calls
add_text_entry("Hi there!").set_metadata("character", "Bob").set_metadata("emotion", "happy")

Checking for Metadata

if p_dialogue_entry.has_metadata("character"):
    var character = p_dialogue_entry.get_metadata("character")
Always check if metadata exists with has_metadata() before calling get_metadata() to avoid errors.

Getting Metadata with Default Value

# Returns default value if key doesn't exist
var character = p_dialogue_entry.get_metadata("character", "Unknown")
var emotion = p_dialogue_entry.get_metadata("emotion", "neutral")

Advanced: Dynamic Text with Metadata

Here’s a more advanced example that uses metadata for getting player input:
extends DialogueEngine

var player_name: String # will be set by the UI code

func _setup() -> void:
    add_text_entry("Welcome adventurer. May I know you name?").set_metadata(&"get_player_name", "The UI code will act accordingly and inject player_name into DialogueEngine.")
    add_text_entry("The legendary %s!? Please, follow me this way. I will personally show you our guild.").set_format([get.bind("player_name")], DialogueEntry.FORMAT_OPERATOR)
    add_text_entry("Press <Enter> or <Space> to exit.").set_name("Exit")
This example uses set_format() with a callable to dynamically insert the player name. The metadata get_player_name signals to the UI that it should prompt for the player’s name.

Common Metadata Patterns

Character Information

add_text_entry("Hello there!") \
    .set_metadata("character", "Alice") \
    .set_metadata("portrait", "res://portraits/alice.png") \
    .set_metadata("emotion", "happy")
In your UI:
func __on_dialogue_continued(entry: DialogueEntry) -> void:
    if entry.has_metadata("character"):
        character_name_label.text = entry.get_metadata("character")
    
    if entry.has_metadata("portrait"):
        var portrait_path = entry.get_metadata("portrait")
        character_portrait.texture = load(portrait_path)
    
    if entry.has_metadata("emotion"):
        var emotion = entry.get_metadata("emotion")
        apply_emotion_effect(emotion)
    
    dialogue_text_label.text = entry.get_text()

Voice and Audio

add_text_entry("I've been waiting for you!") \
    .set_metadata("voice_line", "res://audio/npc_greeting.ogg") \
    .set_metadata("voice_pitch", 1.2)
In your UI:
func __on_dialogue_continued(entry: DialogueEntry) -> void:
    if entry.has_metadata("voice_line"):
        var audio_path = entry.get_metadata("voice_line")
        var pitch = entry.get_metadata("voice_pitch", 1.0)
        play_voice_line(audio_path, pitch)

Animations and Effects

add_text_entry("*gasps*") \
    .set_metadata("animation", "shocked") \
    .set_metadata("camera_shake", true) \
    .set_metadata("sfx", "gasp")
In your UI:
func __on_dialogue_continued(entry: DialogueEntry) -> void:
    if entry.has_metadata("animation"):
        var anim = entry.get_metadata("animation")
        character_animator.play(anim)
    
    if entry.get_metadata("camera_shake", false):
        camera_shake_effect.start()
    
    if entry.has_metadata("sfx"):
        var sfx = entry.get_metadata("sfx")
        audio_player.stream = load("res://sfx/" + sfx + ".ogg")
        audio_player.play()

Quest and Game State

add_text_entry("Thank you for helping me!") \
    .set_metadata("complete_quest", "help_villager") \
    .set_metadata("give_reward", {"gold": 100, "xp": 50})
In your UI:
func __on_dialogue_continued(entry: DialogueEntry) -> void:
    if entry.has_metadata("complete_quest"):
        var quest_id = entry.get_metadata("complete_quest")
        quest_manager.complete_quest(quest_id)
    
    if entry.has_metadata("give_reward"):
        var rewards = entry.get_metadata("give_reward")
        if rewards.has("gold"):
            player.gold += rewards["gold"]
        if rewards.has("xp"):
            player.add_xp(rewards["xp"])

Hidden Options

Metadata can control UI behavior, like hiding certain options:
func _setup() -> void:
    var entry: DialogueEntry = add_text_entry("The storm rages right outside the window. I should...")
    var option_id_1: int = entry.add_option("Wait for storm to finish.")
    var option_id_2: int = entry.add_option("Go back to sleep.")
    var option_id_3: int = entry.add_option("Get back to work.")
    var option_id_4: int = entry.add_option("Hidden option -- this should not be shown on the UI")
    entry.set_metadata("dont_show_options", [option_id_4])
    entry.set_metadata("auto_choose", option_id_4)
In your UI:
func __on_dialogue_continued(entry: DialogueEntry) -> void:
    if entry.has_options():
        var dont_show: Array = entry.get_metadata("dont_show_options", [])
        for option_id in range(entry.get_option_count()):
            if option_id in dont_show:
                continue
            # Show this option button
            create_option_button(option_id)

Metadata Data Types

Metadata values can be any Godot type:
# String
entry.set_metadata("character", "Alice")

# Number
entry.set_metadata("volume", 0.8)

# Boolean
entry.set_metadata("is_important", true)

# Array
entry.set_metadata("tags", ["main_quest", "emotional", "branch_point"])

# Dictionary
entry.set_metadata("rewards", {"gold": 100, "items": ["potion", "sword"]})

# Callable
entry.set_metadata("on_show", func() -> void: print("Dialogue shown!"))

# Resource
entry.set_metadata("portrait", preload("res://portraits/character.png"))

Key Takeaways

Metadata is flexible - Store any data type: strings, numbers, arrays, dictionaries, callables, resources.
Always check existence - Use has_metadata() before get_metadata() or provide a default value.
Chain metadata calls - set_metadata() returns the DialogueEntry for easy chaining.
Metadata is per-entry - Each DialogueEntry has its own metadata storage independent of others.
UI interprets metadata - The DialogueEngine stores metadata but doesn’t interpret it. Your UI code decides what to do with it.

Next Steps

Animations

Use metadata to trigger character animations during dialogue

Timed Options

Create time-limited choices with metadata

Build docs developers (and LLMs) love