Skip to main content

Your First Dialogue

In this quickstart guide, you’ll create a simple dialogue system from scratch. We’ll start with a basic conversation, then add player choices to make it interactive.
This guide assumes you’ve already installed Dialogue Engine in your Godot project.

Creating a Simple Dialogue

1

Create a New Scene

In Godot, create a new scene with a Node as the root. Save it as my_dialogue.tscn.Add a RichTextLabel node as a child to display the dialogue text.Your scene tree should look like:
Node (my_dialogue)
└── RichTextLabel
2

Create the Dialogue Script

Create a new GDScript that extends DialogueEngine. This script will define your dialogue tree.Create my_dialogue_data.gd:
extends DialogueEngine

func _setup() -> void:
    add_text_entry("Hey...")
    add_text_entry("Have you seen the code for this sample?")
    add_text_entry("It's beautiful!")
    add_text_entry("You won't believe it!")
    add_text_entry("Press <Enter> to continue.")
The _setup() function is called automatically when the DialogueEngine is initialized. This is where you build your dialogue tree.
3

Create the Display Script

Now create a script for your scene that will display the dialogue.Attach this script to the root Node:
extends Node

@onready var text_label : RichTextLabel = $RichTextLabel
var dialogue_engine : DialogueEngine

func _ready() -> void:
    # Create the dialogue engine instance
    dialogue_engine = preload("res://my_dialogue_data.gd").new()
    
    # Connect to the dialogue_continued signal
    dialogue_engine.dialogue_continued.connect(_on_dialogue_continued)
    
    # Connect to the dialogue_finished signal
    dialogue_engine.dialogue_finished.connect(_on_dialogue_finished)
    
    # Start the dialogue
    dialogue_engine.advance()

func _on_dialogue_continued(entry: DialogueEntry) -> void:
    # Display the dialogue text
    text_label.text = entry.get_text()

func _on_dialogue_finished() -> void:
    # Handle dialogue completion
    text_label.text = "[center]Dialogue finished![/center]"

func _unhandled_input(event: InputEvent) -> void:
    # Advance dialogue on Enter or Space
    if event.is_action_pressed("ui_accept") or event.is_action_pressed("ui_select"):
        dialogue_engine.advance()
4

Run Your Scene

Run the scene (F6). You should see the first line of dialogue appear. Press Enter or Space to advance through the conversation.Congratulations! You’ve created your first dialogue with Dialogue Engine!

Understanding the Code

Let’s break down what’s happening:

The Dialogue Data Script

extends DialogueEngine

func _setup() -> void:
    add_text_entry("Hey...")
    add_text_entry("Have you seen the code for this sample?")
  • extends DialogueEngine: Your dialogue data extends the DialogueEngine class
  • _setup(): This pseudo-virtual function is called automatically during initialization
  • add_text_entry(text): Adds a new text entry to the dialogue tree

The Display Script

dialogue_engine.dialogue_continued.connect(_on_dialogue_continued)
  • dialogue_continued: Signal emitted when the dialogue advances to a text entry
  • The signal passes a DialogueEntry object containing the current entry data
dialogue_engine.advance()
  • advance(): Moves to the next entry in the dialogue tree
  • Call this whenever the player should see the next line

Adding Player Choices

Now let’s make the dialogue interactive with player choices!
1

Update the Dialogue Data

Modify my_dialogue_data.gd to include player choices:
extends DialogueEngine

enum {
    DEFAULT_BRANCH = 0,
    SLEEP_BRANCH = 1,
    WORK_BRANCH = 2,
}

func _setup() -> void:
    var entry: DialogueEntry = add_text_entry("The storm rages outside. I should...")
    
    # Add player options
    var option_sleep: int = entry.add_option("Go back to sleep.")
    var option_work: int = entry.add_option("Get back to work.")
    
    # Create responses for each choice
    var sleep_response: DialogueEntry = add_text_entry(
        "That's right, sleep is for the strong!", 
        SLEEP_BRANCH
    )
    var work_response: DialogueEntry = add_text_entry(
        "That's right, let's get back to work!", 
        WORK_BRANCH
    )
    
    # Link options to their responses
    entry.set_option_goto_id(option_sleep, sleep_response.get_id())
    entry.set_option_goto_id(option_work, work_response.get_id())
    
    # Merge branches back together
    var continue_entry: DialogueEntry = add_text_entry("Some time passes...")
    sleep_response.set_goto_id(continue_entry.get_id())
    work_response.set_goto_id(continue_entry.get_id())
    
    add_text_entry("The end.")
2

Update the Display Script

Add UI elements to display options. Update your scene to include buttons:
Node (my_dialogue)
├── RichTextLabel
└── VBoxContainer (OptionsContainer)
Update the script:
extends Node

@onready var text_label : RichTextLabel = $RichTextLabel
@onready var options_container : VBoxContainer = $OptionsContainer
var dialogue_engine : DialogueEngine
var current_entry : DialogueEntry

func _ready() -> void:
    dialogue_engine = preload("res://my_dialogue_data.gd").new()
    dialogue_engine.dialogue_continued.connect(_on_dialogue_continued)
    dialogue_engine.dialogue_finished.connect(_on_dialogue_finished)
    dialogue_engine.advance()

func _on_dialogue_continued(entry: DialogueEntry) -> void:
    current_entry = entry
    text_label.text = entry.get_text()
    
    # Clear previous options
    _clear_options()
    
    # Show options if any
    if entry.has_options():
        _display_options(entry)

func _display_options(entry: DialogueEntry) -> void:
    for option_id in entry.get_option_count():
        var button := Button.new()
        button.text = entry.get_option_text(option_id)
        button.pressed.connect(_on_option_selected.bind(option_id))
        options_container.add_child(button)

func _clear_options() -> void:
    for child in options_container.get_children():
        child.queue_free()

func _on_option_selected(option_id: int) -> void:
    # Set the chosen option
    current_entry.choose_option(option_id)
    # Clear options and advance
    _clear_options()
    dialogue_engine.advance()

func _on_dialogue_finished() -> void:
    text_label.text = "[center]Dialogue finished![/center]"
    _clear_options()

func _unhandled_input(event: InputEvent) -> void:
    # Only allow advancing if there are no options
    if current_entry and not current_entry.has_options():
        if event.is_action_pressed("ui_accept") or event.is_action_pressed("ui_select"):
            dialogue_engine.advance()
3

Test Interactive Dialogue

Run the scene again. This time you’ll see player choices appear as buttons. Click one to see the dialogue branch based on your choice!

Understanding Branching

Branch IDs

enum {
    DEFAULT_BRANCH = 0,
    SLEEP_BRANCH = 1,
    WORK_BRANCH = 2,
}
Branch IDs let you organize dialogue into separate paths. The default branch is 0.

Adding Options

var option_sleep: int = entry.add_option("Go back to sleep.")
Returns an option ID that you use to link the option to a specific entry.

Linking Options to Entries

entry.set_option_goto_id(option_sleep, sleep_response.get_id())
Connects the option to the entry that should be displayed when the player selects it.

Choosing Options

current_entry.choose_option(option_id)
dialogue_engine.advance()
First set which option was chosen, then call advance() to process it.

Key Signals Reference

# Emitted when the first entry is read
dialogue_engine.dialogue_started.connect(_on_dialogue_started)

func _on_dialogue_started() -> void:
    print("Dialogue has started!")

Rich Text Support

Dialogue Engine supports Godot’s BBCode for rich text formatting:
add_text_entry("[i]Have [b]you[/b] seen this?[/i]")
add_text_entry("[rainbow]It's beautiful![/rainbow]")
add_text_entry("[shake rate=20.0]You won't believe it![/shake]")
add_text_entry("[wave]Text with wave effect[/wave]")
Make sure your RichTextLabel has BBCode parsing enabled (bbcode_enabled = true).

Best Practices

Organize with Branches

Use branch IDs to separate different conversation paths and keep your dialogue organized.

Use Named Entries

Give important entries names with set_name() for easy reference and jumping.

Signal-Driven UI

Connect to all relevant signals to create responsive, event-driven dialogue UI.

Validate Options

Always ensure options have valid goto IDs before allowing player selection.

Next Steps

Now that you’ve created your first dialogue, explore more advanced features:

API Reference

Explore the complete DialogueEngine API documentation

Advanced Features

Learn about conditional branching, metadata, and dynamic text

Examples

Browse complete examples and common patterns

GitHub Demos

Check out the official demo scenes in the repository

Build docs developers (and LLMs) love