Skip to main content

Overview

Metadata allows you to attach arbitrary custom data to dialogue entries. This is useful for storing information like actor names, emotional states, camera instructions, sound effects, animations, or any other data your game needs to present dialogue correctly.

Setting Metadata

Metadata is stored as key-value pairs on each DialogueEntry:
var entry = add_text_entry("Hello, traveler!")

# Set metadata using any key-value pair
entry.set_metadata("actor", "merchant")
entry.set_metadata("emotion", "friendly")
entry.set_metadata("portrait", "res://portraits/merchant_smile.png")
entry.set_metadata("voice_pitch", 1.2)
You can use any type for keys and values - strings, numbers, objects, etc.

Getting Metadata

Retrieve metadata in your UI code when processing dialogue:
dialogue_engine.dialogue_continued.connect(func(entry: DialogueEntry) -> void:
    var text = entry.get_text()
    
    # Get metadata with optional default value
    var actor = entry.get_metadata("actor", "unknown")
    var emotion = entry.get_metadata("emotion", "neutral")
    var portrait = entry.get_metadata("portrait")
    
    # Update UI based on metadata
    $NameLabel.text = actor
    $PortraitSprite.texture = load(portrait) if portrait else null
    
    # Display the dialogue
    $DialogueLabel.text = text
)

Checking Metadata

You can check if metadata exists before retrieving it:
var entry = dialogue_engine.get_current_entry()

if entry.has_metadata("actor"):
    var actor = entry.get_metadata("actor")
    print("Actor: ", actor)
else:
    print("No actor specified")

Common Use Cases

Character Information

Store character names and portraits:
var entry1 = add_text_entry("Hello there!")
entry1.set_metadata("actor", "Knight")
entry1.set_metadata("portrait", "res://portraits/knight.png")

var entry2 = add_text_entry("Greetings, brave knight!")
entry2.set_metadata("actor", "Wizard")
entry2.set_metadata("portrait", "res://portraits/wizard.png")

Emotional States

Indicate how dialogue should be delivered:
var entry = add_text_entry("Get out of my way!")
entry.set_metadata("emotion", "angry")
entry.set_metadata("volume", 1.5) # Louder
entry.set_metadata("animation", "fist_shake")

Camera and Visual Effects

Control presentation:
var entry = add_text_entry("Look at that!")
entry.set_metadata("camera_target", "castle")
entry.set_metadata("camera_zoom", 1.5)
entry.set_metadata("screen_shake", true)

Audio Cues

Trigger sounds during dialogue:
var entry = add_text_entry("*thunder rumbles*")
entry.set_metadata("sfx", "res://sounds/thunder.ogg")
entry.set_metadata("bgm_volume", 0.5) # Lower background music

Complete Example from Demos

Here’s the handling metadata demo:
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.")
And in the UI code:
extends RichTextLabel

func _ready() -> void:
    var dialogue_engine: DialogueEngine = HandlingMetadataDialogue.new()
    dialogue_engine.dialogue_continued.connect(_print_entry)
    dialogue_engine.advance()

func _print_entry(p_dialogue_entry: DialogueEntry) -> void:
    var text: String = p_dialogue_entry.get_text()
    
    # Check for author metadata
    if p_dialogue_entry.has_metadata("author"):
        var author: String = p_dialogue_entry.get_metadata("author")
        text = author + ": " + text
    
    append_text(text + "\n")

Working with Metadata Dictionaries

You can also work with the metadata dictionary directly:
var entry = add_text_entry("Sample text")

# Get the metadata dictionary reference
var metadata = entry.get_metadata_data()

# Modify it directly
metadata["actor"] = "knight"
metadata["emotion"] = "determined"

# Or set an entire metadata dictionary at once
var character_data = {
    "actor": "wizard",
    "portrait": "res://portraits/wizard.png",
    "voice": "res://voices/wizard.ogg"
}
entry.set_metadata_data(character_data)

Sharing Metadata Between Entries

You can share the same metadata dictionary across multiple entries:
# Create a shared metadata dictionary
var knight_data = {
    "actor": "Knight",
    "portrait": "res://portraits/knight.png",
    "voice_pitch": 0.9
}

# Use it for multiple entries
var entry1 = add_text_entry("I shall defend the realm!")
entry1.set_metadata_data(knight_data)

var entry2 = add_text_entry("No evil shall pass!")
entry2.set_metadata_data(knight_data)

var entry3 = add_text_entry("For honor!")
entry3.set_metadata_data(knight_data)
When sharing metadata dictionaries, changes to the dictionary affect all entries using it.

Metadata Best Practices

Establish Conventions

Define standard metadata keys for your project:
# Define constants for metadata keys
const META_ACTOR = "actor"
const META_EMOTION = "emotion"
const META_PORTRAIT = "portrait"
const META_ANIMATION = "animation"

# Use them consistently
var entry = add_text_entry("Hello!")
entry.set_metadata(META_ACTOR, "Knight")
entry.set_metadata(META_EMOTION, "friendly")

Use Helper Functions

Create helper functions for common metadata patterns:
func add_character_dialogue(text: String, character: String, emotion: String) -> DialogueEntry:
    var entry = add_text_entry(text)
    entry.set_metadata("actor", character)
    entry.set_metadata("emotion", emotion)
    entry.set_metadata("portrait", "res://portraits/%s_%s.png" % [character.to_lower(), emotion])
    return entry

# Use it
add_character_dialogue("I'm glad you're here!", "Knight", "happy")
add_character_dialogue("We have a problem.", "Knight", "worried")

Create Character Templates

class_name CharacterData

var actor_name: String
var portrait_path: String
var voice_pitch: float

func _init(p_name: String, p_portrait: String, p_pitch: float = 1.0) -> void:
    actor_name = p_name
    portrait_path = p_portrait
    voice_pitch = p_pitch

func apply_to_entry(entry: DialogueEntry) -> void:
    entry.set_metadata("actor", actor_name)
    entry.set_metadata("portrait", portrait_path)
    entry.set_metadata("voice_pitch", voice_pitch)

# Usage
var knight = CharacterData.new("Knight", "res://portraits/knight.png", 0.9)
var wizard = CharacterData.new("Wizard", "res://portraits/wizard.png", 1.2)

func _setup() -> void:
    var entry1 = add_text_entry("Hello!")
    knight.apply_to_entry(entry1)
    
    var entry2 = add_text_entry("Greetings!")
    wizard.apply_to_entry(entry2)

Complex Metadata Examples

Animation Sequences

var entry = add_text_entry("Watch out!")
entry.set_metadata("animations", [
    {"target": "player", "animation": "dodge", "delay": 0.0},
    {"target": "enemy", "animation": "attack", "delay": 0.5},
    {"target": "camera", "animation": "shake", "delay": 0.7}
])

# Process in UI
if entry.has_metadata("animations"):
    var animations = entry.get_metadata("animations")
    for anim in animations:
        get_tree().create_timer(anim["delay"]).timeout.connect(func() -> void:
            play_animation(anim["target"], anim["animation"])
        )

Quest Tracking

var entry = add_text_entry("Thank you for helping me!")
entry.set_metadata("quest_update", {
    "quest_id": "help_farmer",
    "stage": "complete",
    "reward_gold": 100,
    "reward_xp": 50
})

# Process in UI
if entry.has_metadata("quest_update"):
    var quest_data = entry.get_metadata("quest_update")
    update_quest(quest_data["quest_id"], quest_data["stage"])
    player.add_gold(quest_data["reward_gold"])
    player.add_xp(quest_data["reward_xp"])

Conditional Formatting

var entry = add_text_entry("The hero arrived!")
entry.set_metadata("text_style", {
    "color": Color.GOLD,
    "font_size": 24,
    "bold": true,
    "center": true
})

# Apply in UI
if entry.has_metadata("text_style"):
    var style = entry.get_metadata("text_style")
    $DialogueLabel.add_theme_color_override("font_color", style["color"])
    $DialogueLabel.add_theme_font_size_override("font_size", style["font_size"])

Metadata in Exported Dialogue

Metadata is preserved when you serialize dialogue:
# Save dialogue data
var data = dialogue_engine.get_data()
var file = FileAccess.open("user://dialogue.dat", FileAccess.WRITE)
file.store_var(data)
file.close()

# Load dialogue data
file = FileAccess.open("user://dialogue.dat", FileAccess.READ)
var loaded_data = file.get_var()
file.close()

var new_engine = DialogueEngine.new()
new_engine.set_data(loaded_data)
# All metadata is preserved
Metadata values must be serializable if you plan to save/load dialogue data. Avoid storing complex objects like Nodes or Resources directly.

Debugging Metadata

Inspect metadata in debug builds:
func print_entry_metadata(entry: DialogueEntry) -> void:
    var metadata = entry.get_metadata_data()
    print("Entry %d metadata:" % entry.get_id())
    for key in metadata:
        print("  %s: %s" % [key, metadata[key]])

# Use it
dialogue_engine.entry_visited.connect(func(entry: DialogueEntry) -> void:
    if OS.is_debug_build():
        print_entry_metadata(entry)
)

Metadata vs Signals

Decide between metadata and signals based on your needs:

Use Metadata When

  • Data is specific to the dialogue entry
  • You need to inspect data before acting on it
  • Multiple systems need access to the same data
  • Data should persist with saved dialogue

Use Signals When

  • You need to trigger immediate actions
  • Logic should be decoupled from dialogue
  • Timing is critical
  • You want cleaner dialogue data
# Metadata approach
var entry = add_text_entry("The door opens.")
entry.set_metadata("door_state", "open")
entry.set_metadata("sfx", "door_open")

# Signal approach
var entry = add_text_entry("The door opens.")
dialogue_engine.dialogue_continued.connect(func(e: DialogueEntry) -> void:
    if e == entry:
        door.open()
        sfx_player.play("door_open")
)

Key Takeaways

  • Metadata is flexible - use it for any custom data your game needs
  • Establish conventions early to keep metadata consistent
  • Create helper functions to reduce boilerplate
  • Remember that metadata persists with serialized dialogue
  • Use appropriate types - keep it simple and serializable

Next Steps

Dialogue Trees

Review the basics of dialogue structure

Getting Started

Build your first dialogue system

Build docs developers (and LLMs) love