Skip to content

Dialogue

ss-gameforge-dialogue provides a dialogue orchestration system decoupled from its visual presentation. Configure the content and behavior as a DialogueResource, display it with a DialogueView node, and style it with a DialogueTheme resource.

  1. Download ss-gameforge-dialogue from the releases page.
  2. Copy addons/ss-gameforge-dialogue/ into your project’s res://addons/.
  3. Enable ss-gameforge-dialogue in Project Settings → Plugins.
ClassDescription
DialogueResourceHolds dialogue lines, timing, and behavior settings.
DialogueViewUI node that renders and animates the dialogue.
DialogueThemeVisual theme resource for the dialogue box.

Create a new DialogueResource via New Resource in the inspector.

PropertyTypeDefaultDescription
dialoguesArray[String][]Lines of dialogue (text or translation keys).
use_translationbooltrueIf true, values are used as translation keys via tr().
auto_startboolfalseStart playing automatically when the view is ready.
allow_skipbooltrueAllow the player to skip the typewriter animation.
advance_modeAdvanceModeHYBRIDHow the player advances through lines.
text_speedfloat0.075Seconds per character in the typewriter effect.
time_to_startfloat0.25Delay before the first line starts.
hold_after_linefloat0.75Pause after a line finishes before auto-advancing.
open_timefloat0.55Open animation duration.
open_transitionTween.TransitionTypeTRANS_ELASTICEasing type for the open animation.
close_timefloat0.20Close animation duration.
enum AdvanceMode {
AUTO, # Lines advance automatically after hold_after_line
MANUAL, # Waits for ui_accept input
HYBRID, # Either input or timer advances
}

Add a DialogueView node to your scene (or instance the provided scene).

PropertyTypeDescription
resourceDialogueResourceDialogue to play. Can be set before calling play().
dialogue_themeDialogueThemeVisual theme.
skip_actionStringNameInput action that advances dialogue (default: "ui_accept").
signal dialogue_started
signal line_changed(index: int) # Fires when a new line begins (0-based)
signal dialogue_finished
func play(res: DialogueResource) -> void # Start a dialogue sequence
func stop() -> void # Cancel immediately
func apply_theme() -> void # Re-apply theme after runtime changes

Create via New ResourceDialogueTheme.

PropertyTypeDescription
background_colorColorBox fill color.
border_colorColorBox border color.
border_widthintBorder width in pixels.
corner_radiusfloatCorner radius.
paddingVector2Horizontal and vertical padding.
font_colorColorText color.
font_sizeintFont size.
fontFontCustom font. Leave null to use the project default.
position_marginVector2Distance from screen edges.

Right-click in the FileSystem → New ResourceDialogueResource.

Set dialogues to your lines:

["Welcome, traveler.", "The path ahead is dangerous.", "Are you ready?"]

Set advance_mode to HYBRID (player input or timer advances).

Instance addons/ss-gameforge-dialogue/scenes/dialogue_view.tscn, or add a Control node and attach a DialogueView script.

@onready var dialogue_view: DialogueView = $DialogueView
func _ready() -> void:
dialogue_view.dialogue_finished.connect(_on_dialogue_done)
dialogue_view.play(preload("res://dialogues/intro.tres"))
func _on_dialogue_done() -> void:
print("Dialogue finished")

Dialogue lines support Godot BBCode tags. The typewriter effect correctly counts only visible characters:

"The [b]dragon[/b] awakens..."
"Your health is [color=green]full[/color]."
"[wave]Loading...[/wave]"

The built-in BBCodeParser utility handles visible character counting so skipping and advance timing work correctly with formatted text.

With use_translation = true, each string in dialogues is treated as a translation key:

# In the DialogueResource
dialogues = ["INTRO_LINE_1", "INTRO_LINE_2"]
use_translation = true

The view calls tr("INTRO_LINE_1") automatically before displaying each line.

extends Area2D
@export var dialogue_resource: DialogueResource
@onready var dialogue_view: DialogueView = $DialogueView
var _player_inside := false
func _ready() -> void:
body_entered.connect(_on_body_entered)
body_exited.connect(_on_body_exited)
dialogue_view.dialogue_finished.connect(_on_dialogue_finished)
func _on_body_entered(body: Node) -> void:
if body.is_in_group("player"):
_player_inside = true
func _unhandled_input(event: InputEvent) -> void:
if _player_inside and event.is_action_pressed("ui_accept"):
dialogue_view.play(dialogue_resource)
func _on_body_exited(body: Node) -> void:
if body.is_in_group("player"):
_player_inside = false
dialogue_view.stop()
func _on_dialogue_finished() -> void:
print("NPC dialogue done")