Skip to main content
DialogueEngine emits signals at key points during dialogue execution, allowing you to respond to dialogue events in your game. Connect to these signals to update your UI, play audio, trigger animations, and more.

Signal Reference

dialogue_started

signal dialogue_started
Emitted when the first DialogueEntry is read after calling advance() for the first time.
This signal is emitted only once at the beginning of the dialogue, before any entries are processed.
When it emits:
  • The first time advance() is called on a DialogueEngine instance
  • After the dialogue has been reset and advance() is called again
Use cases:
  • Show the dialogue UI panel
  • Play dialogue start sound effects
  • Initialize dialogue state
  • Log dialogue start for analytics
Example:
var dialogue_engine = DialogueEngine.new()
dialogue_engine.dialogue_started.connect(__on_dialogue_started)

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

dialogue_continued

signal dialogue_continued(p_dialogue_entry: DialogueEntry)
Emitted when advance() visits a DialogueEntry that has text. This is the primary signal you’ll use to display dialogue text to the player. Parameters:
  • p_dialogue_entry (DialogueEntry): The current dialogue entry with text content
When it emits:
  • Each time advance() processes a text-based entry
  • Does not emit for conditional entries (those without text)
Use cases:
  • Display dialogue text in your UI
  • Update character portraits
  • Play voice lines
  • Show character names from metadata
Example:
func __on_dialogue_continued(p_dialogue_entry: DialogueEntry) -> void:
    var text = p_dialogue_entry.get_text()
    dialogue_label.text = text
    
    # Check for metadata
    if p_dialogue_entry.has_metadata("character"):
        var character = p_dialogue_entry.get_metadata("character")
        character_name_label.text = character

entry_visited

signal entry_visited(p_dialogue_entry: DialogueEntry)
Emitted when advance() visits any DialogueEntry, whether it has text or just a condition. This signal fires for every entry processed, unlike dialogue_continued which only fires for text entries. Parameters:
  • p_dialogue_entry (DialogueEntry): The dialogue entry being visited
When it emits:
  • Every time advance() processes an entry
  • For both text entries and conditional entries
Use cases:
  • Track dialogue flow for debugging
  • Update dialogue history
  • Trigger events based on specific entries
  • Log analytics data
Use entry_visited when you need to track all dialogue flow, including conditional branches. Use dialogue_continued when you only care about text entries.
Example:
func __on_entry_visited(p_dialogue_entry: DialogueEntry) -> void:
    var entry_id = p_dialogue_entry.get_id()
    print("Visited entry: ", entry_id)
    
    # Track visited entries for achievements
    visited_entries.append(entry_id)

dialogue_about_to_finish

signal dialogue_about_to_finish
Emitted when the dialogue is about to finish, specifically when advance() is called and the internal read needle is at the last DialogueEntry. When it emits:
  • Just before dialogue_finished is emitted
  • When advance() is processing the final entry
Use cases:
  • Prepare UI for dialogue end
  • Start fade-out animations
  • Queue up post-dialogue events
  • Save dialogue completion state
Example:
func __on_dialogue_about_to_finish() -> void:
    print("Dialogue ending soon...")
    # Start fade out animation
    var tween = create_tween()
    tween.tween_property(dialogue_panel, "modulate:a", 0.0, 0.5)

dialogue_finished

signal dialogue_finished
Emitted when the dialogue completes successfully after all entries have been processed. When it emits:
  • After the last DialogueEntry has been processed
  • When there are no more entries to read
  • The dialogue can be restarted after this signal
Use cases:
  • Hide the dialogue UI
  • Resume gameplay
  • Trigger post-dialogue events
  • Update quest states
  • Enable player movement
After dialogue_finished is emitted, calling advance() again will restart the dialogue from the beginning.
Example:
func __on_dialogue_finished() -> void:
    print("Dialogue Finished!")
    dialogue_panel.hide()
    player.enable_movement()
    
    # Mark quest as complete
    quest_manager.complete_quest("talk_to_npc")

dialogue_canceled

signal dialogue_canceled
Emitted when the dialogue is canceled due to an error or manual reset. When it emits:
  • When reset() is called while dialogue is active (started but not finished)
  • When an invalid goto ID is encountered
  • When an invalid option choice is detected
  • When a conditional entry has an invalid branch
Use cases:
  • Handle dialogue errors gracefully
  • Clean up dialogue state
  • Log errors for debugging
  • Show error messages to developers
This signal indicates an error condition. Make sure to handle it properly to avoid leaving your UI in an inconsistent state.
Example:
func __on_dialogue_canceled() -> void:
    print("Dialogue Canceled!")
    dialogue_panel.hide()
    
    # Reset dialogue state
    current_dialogue = null
    
    # In debug mode, show error
    if OS.is_debug_build():
        show_error_notification("Dialogue was canceled due to an error")

Complete Example

Here’s a complete example showing how to connect to all dialogue signals:
extends Control

var dialogue_engine: DialogueEngine

func _ready() -> void:
    dialogue_engine = DialogueEngine.new()
    
    # Connect all signals
    dialogue_engine.dialogue_started.connect(__on_dialogue_started)
    dialogue_engine.dialogue_continued.connect(__on_dialogue_continued)
    dialogue_engine.entry_visited.connect(__on_entry_visited)
    dialogue_engine.dialogue_about_to_finish.connect(__on_dialogue_about_to_finish)
    dialogue_engine.dialogue_finished.connect(__on_dialogue_finished)
    dialogue_engine.dialogue_canceled.connect(__on_dialogue_canceled)
    
    # Build dialogue
    dialogue_engine.add_text_entry("Hello!")
    dialogue_engine.add_text_entry("How are you?")
    dialogue_engine.add_text_entry("Goodbye!")

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

func __on_dialogue_started() -> void:
    print("Dialogue started")
    $DialoguePanel.show()

func __on_dialogue_continued(entry: DialogueEntry) -> void:
    print("Text: ", entry.get_text())
    $DialoguePanel/Label.text = entry.get_text()

func __on_entry_visited(entry: DialogueEntry) -> void:
    print("Visited entry ID: ", entry.get_id())

func __on_dialogue_about_to_finish() -> void:
    print("Dialogue about to finish")

func __on_dialogue_finished() -> void:
    print("Dialogue finished")
    $DialoguePanel.hide()

func __on_dialogue_canceled() -> void:
    print("Dialogue canceled")
    $DialoguePanel.hide()

Signal Flow Diagram

Typical signal emission order:
1. dialogue_started (first advance() call only)
2. entry_visited (for each entry)
3. dialogue_continued (for text entries only)
4. ... repeat 2-3 for all entries ...
5. dialogue_about_to_finish (when processing last entry)
6. dialogue_finished (after all entries processed)
If an error occurs:
1. dialogue_started
2. entry_visited / dialogue_continued
3. ... error occurs ...
4. dialogue_canceled (instead of finished)

Best Practices

Always connect to dialogue_continued - This is the primary signal for displaying dialogue text.
Handle dialogue_canceled - Even if you don’t expect errors, handle this signal to prevent UI issues.
Use entry_visited for tracking - If you need to track all dialogue flow (including conditionals), use this instead of dialogue_continued.
Clean up on finish/cancel - Always hide UI and clean up state in both dialogue_finished and dialogue_canceled handlers.

Build docs developers (and LLMs) love