My dialog system in Godot

2025-09-25


Over the years I managed to maintain a simple dialog system in Godot, which uses yield (or await for Godot 4). Ever feel like making an epic RPG with deep storylines? Unless you only do environmental storytelling, it's great to have dialogs. Or dialogues. However you spell it.

But first, a word about why maybe your game doesn't need all that.

Do you really need modal dialogs?

By modal dialogs I mean text which stops the gameplay and waits for the player to press a button.

Instead there are very good reasons to do non-modal, positional, "background dialogs". I do it often with jam games, and primarily with Ace of Rope:

Drawbacks:

So yeah, modal dialogs are still useful.

A simple modal dialog

Here is a simple example which doesn't even have text animation or delay support. I have scenes inherited from Areas called Interactables that prompt for buttons to start the dialogue and emit a signal. Then I can connect it to a signal callback in the current level's scene:

func _on_NPC_dialog_started():
	await Globals.say("I am the first line...")
	await Globals.say("And the second line!")
	unlock_door()

The "say" function is in a "Globals" singleton where I put all the other global stuff of the game:

signal _enter
onready var dialog: Label

func is_cutscene() -> bool:
	return dialog.visible

func say(text: String) -> GDScriptFunctionState:
	dialog.show()
	dialog.text = text
	var state: GDScriptFunctionState = await _enter
	dialog.hide()
	$NextDialogSound.play()
	return state

func _unhandled_input(event: InputEvent):
	if event.is_action_pressed("ui_accept"):
		_enter.emit()
		get_viewport().set_input_as_handled()

Using is_cutscene, all objects in the game can check if a cutscene (here: a dialog line) is ongoing. For example, the player and enemies stop moving. It doesn't go all the way to pausing the game either: you could keep an animation playing, and even move your actors around for the cutscene.

Can be extended if you want some of the characters to have portraits, speech bubbles to place above them, or thinking bubbles when they don't talk out loud. I do that for Uproar in Bug Parliament.

More complicated dialog systems

I recommend Nathan Hoad's Dialogue Manager.

Bonus: a quick and dirty localization system

Godot has all the necessary tools for localization but what about no-engine games?

Personally, since I self-translate my small games in only 2 languages, I check a boolean ternarily and I hardcode everything in a switch statement. Behold my device:

typedef enum { TITLE, PLAY, STORY1... } line_t;

char *tr(line_t line) {
	bool fr = globals.is_french; // or whatever
	switch (line) {
	case TITLE: return fr ? "Mon jeu" : "My game";
	case PLAY: return fr ? "Jouer" : "Play";
	case STORY_1: return fr ? "Il était une fois..." : "Once upon a time...";
	...
	}
}

It works fine. I hope your eyes still work after reading this code.

On that, see ya in the next one!