While Dialogue Manager includes an example balloon, you’ll want to create custom balloons that match your game’s art style and UI design. This guide covers copying the example balloon, customizing it, and implementing your own balloon logic.
class_name DialogueManagerExampleBalloon extends CanvasLayer## The dialogue resource@export var dialogue_resource: DialogueResource## Start from a given label@export var start_from_label: String = ""## Auto start the dialogue@export var auto_start: bool = false## Block other input while dialogue is shown@export var will_block_other_input: bool = true## The action to advance dialogue@export var next_action: StringName = &"ui_accept"## The action to skip typing@export var skip_action: StringName = &"ui_cancel"
# In the scene tree, add a Panel node as a child of Balloonvar panel = Panel.new()panel.name = "Background"# Create a custom stylevar style = StyleBoxFlat.new()style.bg_color = Color(0.1, 0.1, 0.15, 0.95)style.border_width_all = 2style.border_color = Color(0.8, 0.7, 0.5)style.corner_radius_all = 8panel.add_theme_stylebox_override("panel", style)
func show_responses(responses: Array) -> void: # Clear previous response buttons for child in responses_menu.get_children(): child.queue_free() # Create a button for each response for response in responses: if response.is_allowed: # Check conditions var button = Button.new() button.text = response.text button.pressed.connect(func(): on_response_selected(response)) responses_menu.add_child(button) responses_menu.show() # Focus the first button if responses_menu.get_child_count() > 0: responses_menu.get_child(0).grab_focus()func on_response_selected(response: DialogueResponse) -> void: responses_menu.hide() next(response.next_id)
@export var normal_theme: Theme@export var thought_theme: Theme@export var shout_theme: Themefunc apply_dialogue_line() -> void: # Check tags for style if current_line.has_tag("thought"): apply_theme(thought_theme) elif current_line.has_tag("shout"): apply_theme(shout_theme) else: apply_theme(normal_theme) # ... rest of the logic ...
In your dialogue:
Nathan: I wonder what that is... #thoughtNathan: WATCH OUT! #shout
Add a visual indicator when waiting for player input:
@onready var continue_indicator: Control = %ContinueIndicatorfunc _process(_delta: float) -> void: # Show indicator when ready to advance continue_indicator.visible = ( not dialogue_label.is_typing and current_line != null and current_line.responses.size() == 0 )
Ensure dialogue takes priority over other game input:
@export var will_block_other_input: bool = truefunc _unhandled_input(_event: InputEvent) -> void: if will_block_other_input and is_visible_in_tree(): get_viewport().set_input_as_handled()