mcpField
D
dialogue-system

dialogue-system

github.com/jame581開発889165 回インストール4 日前に更新
リポジトリ

dialogue-system について

Godot 4.3+向けの会話システム実装ガイドです。GDScriptとC#の例で、会話データ、分岐ロジック、UI表示を扱います。

主な機能

  1. 01DialogueLineとDialogueDataのリソース
  2. 02会話シグナルを持つシングルトン管理
  3. 03条件付き選択肢と分岐
  4. 04会話UIとタイプライター効果
  5. 05JSON読み込みと変数補間

活用シーン

  1. 01NPCの分岐会話の作成
  2. 02ゲームUIで台詞と選択肢を表示
  3. 03JSONから会話テキストを読み込み

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/jame581/godotprompter --skill dialogue-system

インストール後、Claude Code で/dialogue-systemを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

references/
branching-and-conditions.md
dialogue-manager.md
external-formats.md
ui-presentation.md
variable-interpolation.md

詳細

Godot 4.3+ の会話システム

すべての例は、非推奨 API を使わずに Godot 4.3+ を対象としています。まず GDScript を示し、次に C# を示します。

関連スキル: 会話データを Resource として扱う resource-pattern、Control ノードのレイアウトに関する godot-ui、会話のフロー管理に関する state-machine、会話状態の永続化に関する save-load、高機能な会話アドオンに関する dialogue-manager、アドベンチャーゲーム向けの popochiu。


1. アーキテクチャの概要

┌─────────────────────────────────────────────────────────┐
│                        UI Layer                         │
│   DialogueUI (Control)                                  │
│     ├─ Label (speaker_name)                             │
│     ├─ TextureRect (portrait)                           │
│     ├─ RichTextLabel (dialogue_text, typewriter effect) │
│     └─ VBoxContainer (choice_container)                 │
│           └─ Button × N (choice buttons)                │
│                                                         │
│   Connects to: line_displayed, choice_presented signals │
└───────────────────────┬─────────────────────────────────┘
                        │ drives UI via signals
┌───────────────────────▼─────────────────────────────────┐
│              DialogueManager (Autoload / Node)           │
│   start_dialogue(dialogue_data)                         │
│   advance()  → next line or end                         │
│   choose(choice_index)                                  │
│   current_line: DialogueLine (read-only)                │
│                                                         │
│   signals: dialogue_started                             │
│             line_displayed(line)                        │
│             choice_presented(choices)                   │
│             dialogue_ended                              │
└───────────────────────┬─────────────────────────────────┘
                        │ reads
┌───────────────────────▼─────────────────────────────────┐
│                   Data Layer (Resources)                 │
│   DialogueData (Resource)                               │
│     lines: Dictionary  ← id → DialogueLine              │
│     start_line_id: String                               │
│                                                         │
│   DialogueLine (Resource)                               │
│     speaker, text, choices, next_line_id, condition     │
└─────────────────────────────────────────────────────────┘

2. DialogueLine Resource

DialogueLine は、会話の1つの区切りに関するすべてのデータを保持します。Choices は型指定のない Dictionary の Array なので、別のクラスを用意しなくても、各エントリーに text、next_line_id、省略可能な condition を含められます。

GDScript
# dialogue_line.gd
class_name DialogueLine
extends Resource

## Display name shown in the UI speaker box.
@export var speaker: String = ""

## The body text. Supports BBCode and variable placeholders: {player_name}.
@export_multiline var text: String = ""

## When non-empty, overrides next_line_id. Each Dictionary must have:
##   "text"        : String   — label on the choice button
##   "next_line_id": String   — line to jump to when chosen
##   "condition"   : String   — (optional) expression; omit or "" to always show
@export var choices: Array = []

## ID of the next DialogueLine. Ignored when choices is non-empty.
@export var next_line_id: String = ""

## Optional condition expression evaluated before displaying this line.
## If the expression returns false the manager skips to next_line_id.
## Example: "GameState.has_item('key')"
@export var condition: String = ""
C#
// DialogueLine.cs
using Godot;
using Godot.Collections;

[GlobalClass]
public partial class DialogueLine : Resource
{
    /// <summary>Display name shown in the speaker box.</summary>
    [Export] public string Speaker     { get; set; } = "";

    /// <summary>Body text. Supports BBCode and {variable} placeholders.</summary>
    [Export(PropertyHint.MultilineText)]
    public string Text                 { get; set; } = "";

    /// <summary>
    /// When non-empty, overrides NextLineId. Each Dictionary entry must contain:
    ///   "text"         : string  — choice button label
    ///   "next_line_id" : string  — line to jump to
    ///   "condition"    : string  — (optional) expression; omit or "" to always show
    /// </summary>
    [Export] public Array Choices      { get; set; } = new();

    /// <summary>ID of the next DialogueLine. Ignored when Choices is non-empty.</summary>
    [Export] public string NextLineId  { get; set; } = "";

    /// <summary>
    /// Optional condition expression. Evaluated before displaying this line.
    /// Example: "GameState.HasItem(\"key\")"
    /// </summary>
    [Export] public string Condition   { get; set; } = "";
}

3. DialogueData Resource

DialogueData は、文字列 ID をキーとするすべての行の Dictionary を保持するコンテナー Resource です。.tres ファイルとして作成すると、インスペクターで NPC に割り当てられます。

GDScript
# dialogue_data.gd
class_name DialogueData
extends Resource

## Dictionary mapping line ID strings to DialogueLine resources.
## Example: { "intro": <DialogueLine>, "ask_quest": <DialogueLine> }
@export var lines: Dictionary = {}

## ID of the first line to display when dialogue starts.
@export var start_line_id: String = ""


## Convenience accessor — returns null for unknown IDs.
func get_line(id: String) -> DialogueLine:
    return lines.get(id, null)
C#
// DialogueData.cs
using Godot;
using Godot.Collections;

[GlobalClass]
public partial class DialogueData : Resource
{
    /// <summary>Maps line ID strings to DialogueLine resources.</summary>
    [Export] public Dictionary Lines        { get; set; } = new();

    /// <summary>ID of the first line to display when dialogue starts.</summary>
    [Export] public string StartLineId      { get; set; } = "";

    /// <summary>Returns the DialogueLine for id, or null if not found.</summary>
    public DialogueLine GetLine(string id)
    {
        if (Lines.ContainsKey(id))
            return Lines[id].As<DialogueLine>();
        return null;
    }
}

インスペクターで lines にエントリーを追加し、文字列キーと DialogueLine リソースの値を持つ Dictionary を設定します。または、JSON からプログラムで読み込むこともできます(7章を参照)。


4. DialogueManager

シングルトンのオートロードがアクティブな DialogueData を管理し、現在の行を追跡します。start_dialogue(data) はデータを設定して最初の行を発行し、advance() は次に進み、choose(choice_index) は選択肢を選びます。シグナル:dialogue_started、line_displayed(line)、choice_presented(choices)、dialogue_ended。

GDScript と C# による完全なマネージャー(行の移動、選択肢の処理、条件評価フック、シグナル)については、references/dialogue-manager.md を参照してください。


5. 分岐と条件

選択肢は DialogueLine.choices(Dictionary の Array)に格納されます。各選択肢には text、next_line_id、省略可能な condition があります。条件は Expression クラスで評価される式文字列で、プロジェクトの状態(例:GameState オートロード)は名前付き入力として渡します。

選択肢処理と条件評価器の完全な実装(GDScript + C#)、および Expression の入力に関するセキュリティ上の注意点については、references/branching-and-conditions.md を参照してください。


6. 会話 UI

行の本文用の RichTextLabel(BBCode を有効化)、話者名用の Label、選択肢ボタン用の VBoxContainer を備えた CanvasLayer を使用します。Timer で制御する RichTextLabel.visible_characters により、タイプライター効果を実現します。UI は DialogueManager のシグナルを購読します。

シーンツリーの断片、タイプライター効果の実装方法、選択肢ボタンの生成、GDScript と C# による完全な接続方法については、references/ui-presentation.md を参照してください。


7. 外部フォーマット

デザイナーが編集しやすいよう、JSON から会話を読み込みます。読み込み時に JSON のキーを DialogueLine のプロパティに対応付けます。または、ノードグラフエディターを使える Dialogic アドオンを統合します(コミュニティ標準)。

JSON ローダーの実装方法と Dialogic の統合に関する注意事項については、references/external-formats.md を参照してください。


8. 変数の埋め込み

会話テキストでは、{player_name} 形式のプレースホルダーを使用できます。簡単なテンプレート処理で解決します。GDScript では text.format(vars) を使い、C# ではプレースホルダーごとに string.Replace を使います。

GDScript と C# の埋め込み用ヘルパーについては、references/variable-interpolation.md を参照してください。


9. 実装チェックリスト

  • DialogueLine と DialogueData は Resource を継承し、インスペクター連携のために [GlobalClass](C#)を指定します
  • DialogueManager は Autoload として登録され、すべてのシーンで単一のインスタンスを共有します
  • start_dialogue() は dialogue_data にアクセスする前に、null でないことをアサートします
  • advance() は、選択肢が保留中の場合に呼び出されないようガードします
  • choose() は、生の choices 配列ではなく、フィルター済みの表示可能な選択肢リストを操作します
  • 条件文字列では、安定した Autoload メソッド名のみを参照します。シーン固有のノードは参照しないでください
  • _evaluate_condition() は GameState を Expression の入力名として登録します(parse(cond, ["GameState"])、execute([GameState]))。Expression は Autoload 名を解決しません
  • タイプライターのタイマーでは、BBCode との互換性のため、フレームごとの文字列スライスではなく visible_characters を使用します
  • タイプライターの動作中に ui_accept を押すと全文を表示し、もう一度押すと次の行に進みます
  • 新しい選択肢ボタンを作成する前に、既存のボタンを解放(queue_free)します。古い子ノードを蓄積させないでください
  • JSON ローダーはファイルを開く処理と解析処理を個別に検証し、それぞれの失敗について明確なエラーメッセージを出力します
  • 変数の補間は _interpolate() ヘルパーに一元化し、シグナルハンドラーに分散させません
  • next_line_id = "" は会話の終了を示します。空文字列以外の特殊なセンチネル文字列は使用しません
  • push_error() のすべてのメッセージに、ログを追跡しやすいようクラス名とメソッド名を含めます

コメント