Skip to main content
The Dialogue Engine includes a powerful built-in debugger that automatically visualizes your dialogue structure as an interactive graph, making it easy to understand and debug complex branching conversations.

Overview

The debugger provides:
  • Automatic graph generation of your entire dialogue tree
  • Real-time visualization as dialogue is added or modified
  • Live highlighting showing which entries have been visited
  • Branch ID display to understand dialogue organization
  • Connection visualization showing all gotos and branches
  • Entry details including text, options, conditions, and metadata

Accessing the Debugger

1

Run your game from the Godot editor

The debugger only works when running from the Godot editor (not standalone builds):
if EngineDebugger.is_active():
    print("Debugger is available!")
2

Open the Debugger panel

Go to DebuggerDialogueEngine in the bottom panel of the Godot editor while your game is running.
3

Select a DialogueEngine instance

If you have multiple DialogueEngine instances, select the one you want to visualize from the list on the left.

Understanding the Graph

Node Structure

Each dialogue entry appears as a node showing:
  • Title: Entry name (if set) and entry ID
  • Branch ID: Which branch this entry belongs to
  • Text: The dialogue text content
  • Format: Dynamic formatting data (if any)
  • Goto: Top-level goto connection (if set)
  • Options: Player choices (if any)
  • Condition: Condition callable (if conditional entry)
  • Metadata: Custom metadata attached to the entry

Connection Types

ConnectionVisualMeaning
GotoSingle lineExplicit jump to another entry
Default flowSingle lineAutomatic progression to next entry in same branch
OptionMultiple linesEach option leads to different entry
ConditionTwo linesTrue/false branches

Node Colors

The debugger uses colors to show dialogue state:
ColorMeaning
Default (gray)Entry not yet visited
BlueEntry was previously visited
GreenCurrent entry (last visited)
RedDialogue was canceled at this entry
The debugger updates in real-time as you advance through dialogue, making it easy to trace the conversation flow.

Naming Entries for Easier Debugging

Give your entries meaningful names to make the graph more readable:
func _setup() -> void:
    var intro: DialogueEntry = add_text_entry("Welcome to the village!")
    intro.set_name("Village_Intro")
    
    var merchant_greeting: DialogueEntry = add_text_entry("Hello, traveler!")
    merchant_greeting.set_name("Merchant_Greeting")
    
    var quest_offer: DialogueEntry = add_text_entry("I have a quest for you.")
    quest_offer.set_name("Quest_Offer")
Named entries display as: "Village_Intro - ID: 0" instead of just "DialogueEntry ID: 0".

Setting the Engine Name

When you have multiple DialogueEngine instances, set names to identify them:
extends DialogueEngine

func _setup() -> void:
    set_name("Main Quest Dialogue")
    # ... add entries ...
The name appears in the debugger’s engine selection list.

Identifying Unreachable Dialogue

The debugger highlights disconnected entries - dialogue that can never be reached:
func _setup() -> void:
    var entry_1: DialogueEntry = add_text_entry("This is reachable")
    var entry_2: DialogueEntry = add_text_entry("This is also reachable")
    
    # This entry is disconnected - no goto points to it
    var orphan: DialogueEntry = add_text_entry("This is unreachable!", 1)
Disconnected nodes appear as isolated nodes in the graph, making it easy to spot unused or forgotten dialogue.
Unreachable entries are not necessarily bugs - you might be building dialogue incrementally or keeping alternate versions for testing.

Debugging Branching Dialogue

Visualizing Branch IDs

Each node displays its branch ID, helping you understand the logical organization:
enum Branch { MAIN = 0, QUEST_A = 1, QUEST_B = 2 }

func _setup() -> void:
    add_text_entry("Main dialogue", Branch.MAIN)
    add_text_entry("Quest A dialogue", Branch.QUEST_A)
    add_text_entry("Quest B dialogue", Branch.QUEST_B)
In the debugger, you’ll see entries grouped visually by their branch ID.

Following Goto Connections

Goto connections appear as arrows between nodes:
var start: DialogueEntry = add_text_entry("Start")
var middle: DialogueEntry = add_text_entry("Middle")
var end: DialogueEntry = add_text_entry("End")

start.set_goto_id(end.get_id())  # Arrow from Start to End
The debugger shows this as: Start → End (skipping Middle).

Debugging Options and Conditions

Options Display

Entries with options show all available choices:
var choice: DialogueEntry = add_text_entry("What will you do?")
choice.add_option("Fight")   # Option 0
choice.add_option("Run")     # Option 1
The graph shows:
DialogueEntry ID: 0
─────────────────
Text: "What will you do?"
Option 0: "Fight"
Option 1: "Run"

Condition Display

Conditional entries show the condition callable:
func __has_key() -> bool:
    return inventory.has("key")

var condition: DialogueEntry = add_conditional_entry(__has_key)
The graph shows:
DialogueEntry ID: 5
─────────────────
Condition: Callable(__has_key)
 → True: Entry 6
 → False: Entry 7

Real-Time Visualization

Dynamic Dialogue

The debugger updates as you add entries dynamically:
var counter: int = 0

func _setup() -> void:
    add_text_entry("Starting count...")
    dialogue_about_to_finish.connect(__add_next_number)

func __add_next_number() -> void:
    counter += 1
    add_text_entry(str(counter))
    # New nodes appear in the debugger automatically!
Watch the graph grow as the dialogue progresses.

Live Progress Tracking

As you advance through dialogue:
  1. Previously visited entries turn blue
  2. The current entry turns green
  3. When dialogue finishes, the last entry stays blue
  4. If dialogue is canceled, the last entry turns red

Debugging Tips

Run through your dialogue and watch the highlighting to ensure it follows the expected path:
# Expected flow: 0 → 2 → 3
# If you see 0 → 1 → 2, your gotos are wrong!
Look for isolated nodes or groups of nodes with no incoming connections. These indicate unreachable dialogue.
Ensure each option has an outgoing arrow. Missing arrows mean you forgot to set the option’s goto:
var option_id: int = entry.add_option("Choice")
# Don't forget this! ↓
entry.set_option_goto_id(option_id, target.get_id())
Before writing code, sketch your dialogue flow on paper, then use the debugger to verify it matches your design.
Use the debugger to ensure all branches can reach the end, or that infinite loops behave as intended.

Debugger API

The DialogueEngine automatically communicates with the debugger when active:
if EngineDebugger.is_active():
    # Debugger messages are sent automatically:
    # - dialogue_engine:register_engine
    # - dialogue_engine:sync_entry
    # - dialogue_engine:dialogue_started
    # - dialogue_engine:entry_visited
    # - dialogue_engine:dialogue_finished
    # - dialogue_engine:dialogue_canceled

Deregistering from Debugger

If you want to hide a DialogueEngine from the debugger:
func _setup() -> void:
    # ... setup dialogue ...
    
    # Remove from debugger
    deregister()
Deregistering prevents the debugger from displaying or updating this DialogueEngine instance.

Limitations

  • The debugger only works in editor debug builds, not in exported games
  • Very large dialogue trees (1000+ entries) may slow down the graph rendering
  • The debugger shows the structure but doesn’t execute code - use print() statements to debug logic

Common Debugging Scenarios

”My dialogue gets stuck”

  1. Check the debugger for the red (canceled) node
  2. Look for:
    • Missing option gotos
    • Invalid goto IDs
    • Conditions that always return the same value

”Some dialogue never appears”

  1. Look for disconnected nodes in the graph
  2. Verify all branches have gotos pointing to them
  3. Check branch IDs match between entries and gotos

”Dialogue jumps to the wrong place”

  1. Follow the arrows in the graph
  2. Verify goto IDs point to the correct entries
  3. Check that you’re not confusing entry IDs with option IDs

Next Steps

Build docs developers (and LLMs) love