【Godot】シンプルなダイアログシステムの作り方(テキスト、選択肢、分岐)

作成: 2025-12-08

Godot Engineで基本的なダイアログシステムを自作する方法を解説します。テキスト表示、タイプライター効果、選択肢による分岐を実装します。

導入:なぜダイアログシステムは重要なのか

ゲームにおいて、キャラクターとの会話は世界観を伝え、プレイヤーを物語に引き込むための 最も重要な要素の一つ です。特にRPGやアドベンチャーゲームでは、ダイアログシステムがゲーム体験の質を大きく左右します。

本記事では、初心者から中級者の方を対象に、テキスト表示、選択肢の提示、そして会話の分岐という、ダイアログシステムの核となる機能をシンプルに実装する方法を解説します。

1. ダイアログシステムの核となる概念

シンプルなダイアログシステムを構築するために必要な要素は、主に以下の3つです。

  1. UIノード: テキストを表示するためのLabel、選択肢のためのButton、それらを配置するためのControlノード群。
  2. ダイアログデータ: 会話の内容、話者、選択肢、そして次の会話への遷移情報などを保持するデータ構造。
  3. 制御スクリプト: データを読み込み、UIを操作し、プレイヤーの入力に応じて会話を進めるGDScript。

最もシンプルで拡張性の高いダイアログデータ構造は、GDScriptの 配列と辞書 を組み合わせたものです。

const DIALOGUE = [
    {
        "id": 1,
        "speaker": "村人A",
        "text": "ようこそ、旅人さん。この村に来たのは初めてかい?",
        "next_id": 2
    },
    {
        "id": 2,
        "speaker": "村人A",
        "text": "実は困ったことがあってね。君に頼みがあるんだが...",
        "choices": [
            {"text": "話を聞く", "next_id": 3},
            {"text": "今は忙しい", "next_id": 4}
        ]
    }
]

2. テキスト表示の実装とタイプライター効果

ダイアログのテキストは、単に一瞬で表示されるよりも、一文字ずつ表示される タイプライター効果 を加えることで、よりゲームらしい演出になります。

UIのセットアップ

Controlノードをルートとするシーンを作成し、以下のノードを配置します。

  • Control (ルートノード)
    • PanelContainer (背景)
      • VBoxContainer
        • Label (話者名表示用)
        • Label (ダイアログテキスト表示用、dialogue_text)
        • HBoxContainer (選択肢ボタン配置用、choice_container)

制御スクリプト(DialogueSystem.gd)

extends Control

@onready var dialogue_text: Label = $PanelContainer/VBoxContainer/DialogueText
@onready var speaker_name: Label = $PanelContainer/VBoxContainer/SpeakerName
@onready var choice_container: HBoxContainer = $PanelContainer/VBoxContainer/ChoiceContainer

const DIALOGUE_DATA = preload("res://dialogue_data.gd").DIALOGUE
var current_dialogue_index: int = 0
var is_typing: bool = false
var full_text: String = ""
const TYPING_SPEED: float = 0.05

@onready var typing_timer: Timer = Timer.new()

func _ready():
    typing_timer.timeout.connect(_on_typing_timer_timeout)
    add_child(typing_timer)
    start_dialogue(1)

func start_dialogue(id: int):
    var dialogue_entry = get_dialogue_by_id(id)
    if dialogue_entry == null:
        queue_free()
        return

    for child in choice_container.get_children():
        child.queue_free()

    current_dialogue_index = id
    speaker_name.text = dialogue_entry.get("speaker", "???")
    full_text = dialogue_entry.text
    dialogue_text.text = ""
    dialogue_text.visible_characters = 0
    is_typing = true
    typing_timer.start(TYPING_SPEED)

func _on_typing_timer_timeout():
    if dialogue_text.visible_characters < full_text.length():
        dialogue_text.visible_characters += 1
    else:
        typing_timer.stop()
        is_typing = false

        var dialogue_entry = get_dialogue_by_id(current_dialogue_index)
        if dialogue_entry.has("choices"):
            display_choices(dialogue_entry.choices)

func get_dialogue_by_id(id: int) -> Dictionary:
    for entry in DIALOGUE_DATA:
        if entry.id == id:
            return entry
    return null

3. 選択肢と会話の分岐の実装

会話に選択肢を導入することで、プレイヤーの行動が物語に影響を与える 分岐 を実装できます。

選択肢ボタンの動的生成

func display_choices(choices: Array):
    for child in choice_container.get_children():
        child.queue_free()

    for choice in choices:
        var button = Button.new()
        button.text = choice.text
        button.pressed.connect(func(): _on_choice_button_pressed(choice.next_id))
        choice_container.add_child(button)

func _on_choice_button_pressed(next_id: int):
    for child in choice_container.get_children():
        child.queue_free()

    start_dialogue(next_id)

4. 実践的な使用例:ゲームシーンへの組み込み

このDialogueSystemシーンを、メインのゲームシーンにインスタンス化して使用します。

# MainScene.gd
var dialogue_scene = preload("res://DialogueSystem.tscn")
var is_dialogue_active: bool = false

func _on_npc_interact():
    if not is_dialogue_active:
        var dialogue_instance = dialogue_scene.instantiate()
        add_child(dialogue_instance)
        is_dialogue_active = true
        dialogue_instance.tree_exited.connect(_on_dialogue_finished)
        dialogue_instance.start_dialogue(1)

func _on_dialogue_finished():
    is_dialogue_active = false

まとめ

本記事で紹介したシンプルなダイアログシステムは、Godot Engineの基本的なUIノードとGDScriptの機能だけで、 テキスト表示、タイプライター効果、選択肢による分岐 という核となる機能をすべて実現しています。

このシステムを基盤として、以下のような拡張を行うことで、よりリッチな会話体験を提供できます。

  • データ管理の外部化: DIALOGUE_DATA をJSONやCSVファイルとして外部化
  • 演出の追加: 話者ごとのポートレート表示、会話中のSE/BGM変更
  • 条件分岐: プレイヤーのステータスやゲーム内のフラグに応じた動的な変更

補足:Dialogicアドオンという選択肢

本記事ではダイアログシステムを自作する方法を解説しましたが、より高機能なダイアログシステムを効率的に構築したい場合は、Dialogic というアドオンも有力な選択肢です。

Dialogicは、Godot Engine用のビジュアルダイアログエディタで、以下のような特徴があります。

  • ビジュアルエディタ: コードを書かずにノードベースのエディタで会話フローを設計可能
  • 豊富な機能: キャラクター管理、タイムライン、条件分岐、変数管理などが標準搭載
  • コミュニティサポート: 活発なコミュニティとドキュメントが充実

自作システムは学習目的完全なカスタマイズが必要な場合に最適ですが、大規模プロジェクト非プログラマーがダイアログを編集する必要がある場合は、Dialogicの導入を検討してみてください。Godot Asset Libraryから無料でインストールできます。