本文へ移動
cccskills
無料GitHub で公開

dialogue-manager

Use when using the Dialogue Manager addon — .dialogue files with titles, responses, conditions and mutations, runtime balloons, and C# support

インストール方法を見る

含まれるファイル(1)

  • SKILL.md13.2 KB

SKILL.md(原文)

インストールする前に、エージェントに与えられる指示の中身を確認できます。

Dialogue Manager

Related skills: dialogue-system for hand-rolled dialogue data structures, localization for translating lines, popochiu for full adventure-game workflows.

Addon: Dialogue Manager · version v3.10.4 · Godot 4.6 · MIT · source: https://github.com/nathanhoad/godot_dialogue_manager · GDScript with official C# support.


1. When to use Dialogue Manager

ApproachBest for
dialogue-system skill (hand-rolled Resource data)Full control over data shape, no addon dependency, small dialogue trees
Dialogue ManagerScript-like .dialogue text format, branching responses, conditions/mutations, translation pipeline, visual editor tab, official C# wrapper
Full adventure-game frameworkPoint-and-click games needing rooms/inventory/actors bundled with dialogue — Dialogue Manager only owns the dialogue layer

Choose Dialogue Manager when writers want to author branching dialogue as readable script text (not Inspector-edited Resources) and you want built-in conditions, mutations, random lines, and a translation workflow (CSV or PO) for free. If you need dialogue as strongly-typed Resources you fully own, use dialogue-system instead. Dialogue Manager only handles dialogue — it is not a full adventure/quest framework, so a larger point-and-click framework would still own rooms, inventory, and actors around it.


2. Install & setup

  1. Godot AssetLib → search "Dialogue Manager" → Download. Or copy addons/dialogue_manager/ from the GitHub repo into res://addons/dialogue_manager/.
  2. Project → Project Settings → Plugins → enable "Dialogue Manager". This adds a Dialogue tab to the bottom editor panel and registers the DialogueManager autoload automatically — no manual autoload step needed.
  3. addons/dialogue_manager/plugin.cfg:
[plugin]
name="Dialogue Manager"
description="A powerful nonlinear dialogue system"
author="Nathan Hoad"
version="3.10.4"
script="plugin.gd"
  1. C# projects need no extra NuGet package — using DialogueManagerRuntime; is enough (§4).
  2. Settings live at Project Settings → General → Dialogue Manager: notably State Autoload Shortcuts (autoload names usable in dialogue without a prefix) and Balloon Path (the scene show_dialogue_balloon() opens — leave empty to use the built-in example balloon).

3. .dialogue syntax

Open/create a .dialogue file from the Dialogue editor tab. Lines are Character: text or bare text (narrator). Godot's RichTextLabel BBCode works, plus extras: [[A|B|C]] (random inline pick), [wait=N] / [wait="ui_accept"] (pause typing), [speed=N], [next=auto] (auto-advance).

Titles and jumps — ~ name marks a title; => name jumps to it; => END ends the flow; => END! force-ends past any pending jump-and-returns; =>< name jumps and returns here once that branch hits END:

~ start
Nathan: Well?
- First one
- Another one => another_title
- Start again => start
=> END

~ another_title
Nathan: Another one?
=> END

Responses (- ) nest by indentation and can carry a condition in [...] — put any jump last:

Nathan: How many projects have you started and not finished?
- Just a couple
	Nathan: That's not so bad.
- A lot [if SomeGlobal.some_property == true]
	Nathan: Maybe you should finish one before another.
- Another one [if SomeGlobal.some_method()] => another_title

Conditions — if / elif / else, boolean and/or/(), and match/while blocks:

if SomeGlobal.some_property >= 10
    Nathan: That property is >= 10.
elif SomeGlobal.some_other_property == "some value"
    Nathan: Or we might be in here.
else
    Nathan: If neither are true, I'll say this.

Inline conditions: Nathan: I have done this [if already_done]once again[/if], with an optional [else]. Null-safe member access uses ?.: if some_node_reference?.name == "SomeNode".

Mutations — set assigns state, do calls a method or emits a signal; both can run inline ([do wave()], suppress the implicit await with [do! wave()]):

if SomeGlobal.has_met_nathan == false
    do SomeGlobal.animate("Nathan", "Wave")
    Nathan: Hi, I'm Nathan.
    set SomeGlobal.has_met_nathan = true

Built-in mutations: do wait(float), do debug(...). Emit a signal from dialogue with do SomeGlobal.some_signal.emit("arg").

Locals vs extra game states — set locals.asked = true creates a per-conversation temp variable (a convention implemented by the example balloon, not a Dialogue Manager core feature). Objects passed in the extra_game_states array are referenced directly by name and their mutations persist after the conversation ends — pass instances, not classes (GameStateClass.new(), never the bare class).

Randomised lines — prefix with % (equal weight) or %N (relative weight); a blank line separates random groups:

Nathan: I will say this.
%3 Nathan: This line has a 60% chance of being picked
%2 Nathan: This line has a 40% chance of being picked

Variables and tags — {{SomeGlobal.some_property}} interpolates state into text (also usable as a character name). [#happy, #mood=calm] attaches tags, readable via line.get_tag_value("mood").


4. Runtime API & example balloon

Load a DialogueResource (.dialogue file) and either let DialogueManager open a balloon for you, or pull lines manually with await DialogueManager.get_next_dialogue_line(resource, title). When there is no next line it returns null — check falsy in GDScript (if not line: / while line:) and line != null in C#. (The tag's API.md prose says "empty dictionary {}", but the v3.10.4 source returns null on every end-of-dialogue path — a line == {} check would never fire.)

GDScript

# npc.gd
extends Node2D

@export var dialogue_resource: DialogueResource

func _on_interact() -> void:
    # Opens the configured balloon (Settings → Balloon Path), or the built-in example balloon.
    DialogueManager.show_dialogue_balloon(dialogue_resource, "start")

func _manual_walk() -> void:
    # Manual traversal — build a totally custom balloon around this loop.
    var line: DialogueLine = await DialogueManager.get_next_dialogue_line(dialogue_resource, "start")
    while line:
        print("%s: %s" % [line.character, line.text])
        if line.responses.is_empty():
            line = await DialogueManager.get_next_dialogue_line(dialogue_resource, line.next_id)
        else:
            var chosen: DialogueResponse = line.responses[0]  # replace with real UI selection
            line = await DialogueManager.get_next_dialogue_line(dialogue_resource, chosen.next_id)

C#

// Npc.cs
using Godot;
using DialogueManagerRuntime;

public partial class Npc : Node2D
{
    [Export] public Resource DialogueResource;

    private void OnInteract()
    {
        DialogueManager.ShowDialogueBalloon(DialogueResource, "start");
    }

    private async void ManualWalk()
    {
        var line = await DialogueManager.GetNextDialogueLine(DialogueResource, "start");
        while (line != null)
        {
            GD.Print($"{line.Character}: {line.Text}");
            if (line.Responses.Count == 0)
            {
                line = await DialogueManager.GetNextDialogueLine(DialogueResource, line.NextId);
            }
            else
            {
                var chosen = line.Responses[0]; // replace with real UI selection
                line = await DialogueManager.GetNextDialogueLine(DialogueResource, chosen.NextId);
            }
        }
    }
}

Other DialogueManager methods: show_dialogue_balloon_scene(balloon_scene, resource, title) (open a specific balloon scene), show_example_dialogue_balloon(resource, title) (force the built-in balloon), create_resource_from_text(text) (compile a .dialogue string at runtime — fails on syntax errors). C# names are identical PascalCase: ShowDialogueBalloonScene, ShowExampleDialogueBalloon, CreateResourceFromText.

get_next_dialogue_line takes a mutation_behaviour param — GDScript DMConstants.MutationBehaviour, C# MutationBehaviour (both: Wait default, DoNotWait, Skip). The enum lives on DMConstants, not on the DialogueManager autoload. Leave it Wait unless you know otherwise; the example balloon only supports Wait.


5. Signals & typed access

DialogueLine fields: id, next_id, character, text, tags (PackedStringArray), translation_key, responses (Array[DialogueResponse]), concurrent_lines. DialogueResponse adds is_allowed: bool and condition_as_text: String on top of the same id/next_id/character/text/ tags/translation_key fields.

GDScript

func _ready() -> void:
    DialogueManager.dialogue_started.connect(_on_dialogue_started)
    DialogueManager.dialogue_ended.connect(_on_dialogue_ended)
    DialogueManager.got_dialogue.connect(_on_got_dialogue)
    DialogueManager.mutated.connect(_on_mutated)

func _on_dialogue_started(resource: DialogueResource) -> void:
    pass

func _on_dialogue_ended(resource: DialogueResource) -> void:
    pass

func _on_got_dialogue(line: DialogueLine) -> void:
    print(line.character, ": ", line.text, " tags=", line.tags)

func _on_mutated(mutation: Dictionary) -> void:
    pass  # fires before a `do`/inline mutation runs (not `set` lines)

C#

using DialogueManagerRuntime;

public override void _Ready()
{
    DialogueManager.DialogueStarted += (Resource resource) => { };
    DialogueManager.DialogueEnded += (Resource resource) => { };
    DialogueManager.GotDialogue += (DialogueLine line) =>
    {
        GD.Print($"{line.Character}: {line.Text} tags={line.Tags}");
    };
    DialogueManager.Mutated += (Godot.Collections.Dictionary mutation) => { };
}

⚠️ These C# events are bridged to the underlying Godot signals lazily, on the first access to DialogueManager.Instance. A project that only subscribes here and then drives dialogue from GDScript never touches Instance, so the handlers never fire. Touch the API from C# at least once (e.g. _ = DialogueManager.Instance;, or run the dialogue via GetNextDialogueLine/ShowDialogueBalloon from C#) to wire them up.

The built-in responses menu node only exposes response_selected as a Godot signal, so connect it with Connect + Callable instead of a C# event handler:

responsesMenu.Connect("response_selected", Callable.From((DialogueResponse response) =>
{
    // advance using response.NextId
}));

passed_title(title) (GDScript) / DialogueManager.PassedTitle += (string title) => { } (C#) fires every time a ~ title marker is crossed — useful for analytics or save-point bookmarking.


6. Translations

By default all dialogue/response text is run through Godot's tr(). The DialogueManager.translation_source property picks the backend — its enum type is GDScript DMConstants.TranslationSource / C# TranslationSource (None, CSV, PO, Guess default), not DialogueManager.TranslationSource. Guess inspects your locale project settings for a PO file and falls back to CSV.

Static per-line IDs (Nathan: Hi! I'm Nathan. [ID:HI_IM_NATHAN]) give a stable translation_key for matching voiced lines and CSV/PO round-trips, instead of keying off the literal text. .dialogue files auto-register in the POT Generation list; a ## comment line before a dialogue line becomes a #. TRANSLATORS: note in the exported PO/POT. Export/import CSV from the editor's Translations menu (Dialogue tab); re-import matches by static ID when present, otherwise by literal text.

See the localization skill for TranslationServer, locale switching, and RTL — Dialogue Manager only produces the translation keys and CSV/PO export; wiring locale changes into the running game is localization's job.


Implementation checklist

  • Dialogue Manager plugin enabled in Project Settings → Plugins (registers the DialogueManager autoload automatically)
  • .dialogue resources loaded via load()/preload(), not parsed by hand
  • Every get_next_dialogue_line / GetNextDialogueLine call is awaited
  • Manual traversal loops detect dialogue end via the null return (if not line: in GDScript, line != null in C#) — never compare against {}
  • Response lines put [if condition] before any => target jump, never after
  • extra_game_states entries are instances (GameStateClass.new()), not bare classes
  • C# state properties that dialogue reads/writes carry [Export]
  • C# code uses using DialogueManagerRuntime; and PascalCase members (ShowDialogueBalloon, GetNextDialogueLine, DialogueStarted, GotDialogue)
  • Custom balloons override Balloon Path in Settings rather than forking show_dialogue_balloon call sites
  • Static translation IDs ([ID:KEY]) added to any line needing voice-over or stable CSV/PO keys
  • Inline do/await mutations use do! when the caller shouldn't block on them

レビュー

まだレビューはありません。使ってみた感想をお寄せください。

同じリポジトリのスキル

概要と使いどころ

Use when working with 2D-specific systems — TileMaps, parallax scrolling, 2D lights and shadows, canvas layers, particles 2D, custom drawing, and 2D meshes in Godot 4.3+

日本語の概要は準備中です。原文の説明を表示しています。

jame581/GodotPrompter8152026年10月12日 更新

Use when working with 3D-specific systems — materials, lighting, shadows, environment, global illumination, fog, LOD, occlusion culling, and decals in Godot 4.3+

日本語の概要は準備中です。原文の説明を表示しています。

jame581/GodotPrompter8152026年10月12日 更新

Use when building character abilities — Resource-based abilities with cost/cooldown/cast, buffs/debuffs, stat modifiers, gameplay tags, and HUD binding

日本語の概要は準備中です。原文の説明を表示しています。

jame581/GodotPrompter8152026年10月12日 更新

Use when creating Godot editor plugins — EditorPlugin, @tool scripts, custom inspectors, and dock panels

日本語の概要は準備中です。原文の説明を表示しています。

jame581/GodotPrompter8152026年10月12日 更新

Use when implementing AI movement — NavigationAgent2D/3D, steering behaviors, behavior trees, and patrol patterns

日本語の概要は準備中です。原文の説明を表示しています。

jame581/GodotPrompter8152026年10月12日 更新

Use when implementing animations — AnimationPlayer, AnimationTree, blend trees, state machines, sprite animation, and code-driven animation

日本語の概要は準備中です。原文の説明を表示しています。

jame581/GodotPrompter8152026年10月12日 更新

jame581 のスキルをすべて見る

このスキルの問題を報告する