Claude Media
Claude CodeでGodotゲーム開発を始める手順

Claude CodeでGodotゲーム開発を始める手順

GodotのGDScript実装とヘッドレステストをClaude Codeに任せる際のCLAUDE.md設定と落とし穴を解説します。

Godotのシーンとスクリプトの分担

Godotはオープンソースの2D/3Dゲームエンジンです。Claude Codeに任せやすいのはGDScriptのロジック実装で、シーンの見た目はエディタで組むほうが向いています。この線引きは、Godotのシーンの作りから来ています。

シーンはノードを親子関係で並べた木構造で、保存すると新しいノードの型のように扱えます。CharacterBody2D・Sprite2D・CollisionShape2Dを組み合わせたキャラクターを1つのシーンにして、いくつでもインスタンスを作れます。プレイヤー・敵・宝箱を個別のシーンに分けておくと、Claude Codeに渡す作業単位を「1シーンのスクリプトだけ」に絞れます。

GDScriptはGodot専用のスクリプト言語です。extendsで継承元のノードクラスを指定し、_ready()や_process(delta)といった組み込み関数にロジックを書きます。公式チュートリアルの最初のスクリプトは、スプライトを回転させながら動かす次のコードです。

extends Sprite2D
 
var speed = 400
var angular_speed = PI
 
func _process(delta):
    rotation += angular_speed * delta
    var velocity = Vector2.UP.rotated(rotation) * speed
    position += velocity * delta

_process()は描画フレームごとに呼ばれ、deltaには前フレームからの経過時間が入ります。移動量にdeltaを掛けているかどうかが、フレームレートが変わっても同じ速さで動くかの分かれ目です。Claude Codeが書いた移動コードでは、まずここを見ます。

衝突する物体を動かすコードは話が別です。物理エンジンに関わる処理には、_process()でなく_physics_process()を使うよう、スクリプトの処理方式を解説したページが案内しています。こちらは描画のフレームレートと無関係に、既定で毎秒60回呼ばれます。一方、公式の2Dゲーム教材のプレイヤー(Area2D)は、移動を_process()に書いています。CharacterBody2Dの移動を依頼するときは、どちらの関数に書くかを指示文に含めておくと、描画側に書かれる事故を避けられます。

依頼の前に整える3つの設定

Godotプロジェクトで先に用意しておくのは、CLAUDE.md・パス限定ルール・Bashの許可ルールの3つです。

CLAUDE.mdにはヘッドレス実行の形を書く

ディレクトリ構成・命名規則・実行コマンドを書いておくと、毎回の説明が要りません。

# プロジェクト概要
Godot 4 で開発する2Dアクションゲーム。
 
## ディレクトリ構成
- scenes/ シーンファイル(.tscn)
- scripts/ GDScript(.gd)
 
## 実行・検証コマンド
- 取り込み: godot --headless --import --path .
- 起動確認: godot --headless --path . --quit-after 120
 
## コーディング規約
- 変数・関数はsnake_case
- シグナル名は状態変化を表す名詞(例: health_depleted)

CLAUDE.mdは文脈として読み込まれるだけで、強制力はありません。1ファイル200行未満が目安です。長くなったら、特定のファイルを扱うときだけ読み込まれる.claude/rules/のルールへ分けます。

---
paths:
  - "scripts/**/*.gd"
---
 
# GDScriptのルール
 
- 物理ボディの移動は _physics_process に書く
- 接続するシグナルは、ノードと名前を先に確認する

pathsに合致するファイルをRead・Write・Editしたときに読み込まれる仕組みです。GDScriptの細かい規約はここへ寄せ、CLAUDE.mdには全作業に共通する内容だけを残します。書き込み禁止ディレクトリのように守らせたい制約は、CLAUDE.mdではなくPreToolUse hookやdenyルールで縛ります。

Bashの許可ルールは、deny側の書き方に落とし穴がある

GodotのCLIを都度確認なしで動かすには、.claude/settings.jsonに許可ルールを足します。エクスポートは成果物を書き出す操作なので、denyで止めておきます。

{
  "permissions": {
    "allow": [
      "Bash(godot --headless *)"
    ],
    "deny": [
      "Bash(godot --export-*)",
      "Bash(godot * --export-*)"
    ]
  }
}

denyが2行ある理由は、Bashのルールが*より前の文字列を書いたとおりに照合するためです。godot --export-release *だけを書くと、先頭がgodot --export-release で始まるコマンドしか止まりません。Godotの公式例はgodot --headless --export-release "Linux/X11" /var/builds/projectのように--headlessが先頭です。この形は1行目のdenyをすり抜け、allowのgodot --headless *に一致します。権限の評価はdeny、ask、allowの順で、最初に一致したルールが結果を決めます。2行目のdenyが入って初めて、この形が止まります。

それでも、これはセキュリティ境界ではありません。/usr/bin/godotのように絶対パスで呼ぶ形は、godotで始まるルールには一致しません。deny・askは「Claudeがよく書く形」を止める仕組みと割り切り、エクスポートそのものを任せない運用と併用します。

もう1つの注意点は-sです。godot --headless -s anything.gdも--headless *に一致するため、任意のGDScriptを確認なしで走らせられます。信頼できないリポジトリでは、allowを--headless --import *のように用途ごとの行へ分けるほうが安全です。

検証ループ — ウィンドウを開かずに結果を返させる

Claude Codeが書いたコードを検証するには、Godotをコマンドラインから動かします。次の4ステップが、1周の流れです。

手順

ヘッドレス検証の1周

  1. 1

    リソースを取り込む

    godot --headless --import --path .を実行します。--importはエディタを起動してリソースの取り込みを待ち、終わったら終了します。取り込みの結果は.godot/に溜まりますが、このフォルダは公式がバージョン管理から外すよう案内しているキャッシュです。clone直後は、先にこの手順を通します。

  2. 2

    構文だけ先に調べる

    godot --headless --check-only -s <スクリプト>を実行します。--check-onlyはスクリプトのエラーだけを調べて終了するオプションで、--script(-s)と組み合わせて使います。

  3. 3

    起動して止まることを確かめる

    godot --headless --path . --quit-after 120のように、終了までのイテレーション数を指定します。--quitは最初の1回で、--quit-afterは指定した回数で終了します。終了条件がなければ、メインシーンは動き続けます。Claude Codeがコマンドの終了を待ち続ける原因になります。

  4. 4

    ロジックをテストスクリプトで検証する

    ロジックの検証は、SceneTreeを継承したスクリプトに書きます。godot --headless -s scripts/test_runner.gdで実行し、終了コードで合否を返します。

4つ目のテストスクリプトは、公式のコマンドラインのページにあるsayhello.gdと同じ形です。-sで実行するスクリプトはSceneTreeかMainLoopを継承する必要があり、_init()の中で処理してquit()で終わります。

extends SceneTree
 
func _init():
    var ok = true
    # ここにロジックの検証を書く
    quit(0 if ok else 1)

quit()は引数に終了コードを取り、省略すると0です。失敗時に1を渡しておけば、Claude Codeはコマンドの終了コードだけで合否を判断できます。ヘッドレスはディスプレイと音声が無効なドライバで動くため、検証したいロジックは、画面や音から切り離した関数に切り出しておきます。

このループは、失敗するテストを先に書かせてから実装させる進め方とそのまま重なります。Stop hookで「テストが通るまで終わらせない」形にする手順はClaude CodeでTDDを回す手順にあり、GUIのないクラウド環境で回すならCodespaces上でDevContainer開発を始める手順の構成がヘッドレス実行と相性のいい組み合わせです。

.tscnの中身から見た、シーンの任せ方

シーンの新規作成やノードの配置は、エディタで視覚的に行う作業が中心です。ただし、.tscnはテキスト形式なので、Claude Codeが読むことも書くこともできます。ファイルは次の5つの区画で構成されます。

構造

.tscnファイルの5つの区画

  • ファイル記述子

    先頭に1つだけ置く[gd_scene format=3 uid="uid://…"]の行です。uidはシーンを識別する文字列で、ファイルを移動しても追跡できます。

  • 外部リソース

    別ファイルのリソースへの参照で、パス・型・UID・ファイル内で使うidを持ちます。本文中はExtResource("1_7bt6s")のようにidで呼びます。

  • 内部リソース

    ファイルの中に埋め込むリソースです。別の内部リソースを参照するときは、参照される側を前に書きます。

  • ノード

    [node name="…" type="…" parent="…"]の行で、親子関係を表します。

  • 接続

    シグナルとメソッドの接続を書く区画です。

テキストなので直接書かせることもできますが、手で書くと崩れやすい点が公式に載っています。内部リソースの順序は意味を持ちます。ExtResourceのidとuidは別物で、idはファイル内の参照用、uidは外部リソースの識別用です。デフォルト値と同じプロパティは、手で書いてもエディタが保存するときに消えます。

Godot 4.6より前に保存したシーンには、記述子にload_stepsという属性が付いています。この属性は今は非推奨で、あっても無視してよいと書かれています。古いシーンを手本に新しい.tscnを書かせるときは、この属性を引き継がなくて構いません。

実務では、.tscnの中身をそのまま読ませるより、ノード名と型の一覧を指示文に書き出すほうが誤解が少なくなります。ノードの追加や付け替えはエディタで行って保存し、スクリプトの実装だけを依頼する形が、差分も追いやすい分担です。

複数のシーンを並行で実装させたいときは、Taskツールでサブエージェントを並列化する方法がそのまま使えます。プレイヤーの移動・敵の生成・スコア表示のUIは依存が薄く、分担しやすい組み合わせです。

シグナルと入力処理は「名前の実在」を先に確認させる

ノード間の通信には、シグナルを使います。ノードで何かが起きたときに発せられる合図で、connect()で別のノードの関数に紐づけます。

extends Node2D
 
signal health_depleted
 
func take_damage(amount):
    health -= amount
    if health <= 0:
        health_depleted.emit()

受け取る側は_ready()の中で接続します。

func _ready():
    var timer = get_node("Timer")
    timer.timeout.connect(_on_timer_timeout)

Buttonのpressed、Timerのtimeout、Area2Dのbody_enteredのように、組み込みノードにはシグナルが最初から用意されています。実装を依頼するときは、どのノードのどのシグナルに接続するかを先に指定すると、存在しないシグナル名を書かれる事故を防げます。

入力はInputシングルトンから読みます。Input.is_action_pressed()は、指定したアクションがそのフレームで押されているかを返します。ui_left・ui_right・ui_upのような定義済みのアクション名が、プロジェクトの作成時から使えます。

func _process(delta):
    var direction = 0
    if Input.is_action_pressed("ui_left"):
        direction = -1
    if Input.is_action_pressed("ui_right"):
        direction = 1
 
    rotation += angular_speed * direction * delta
 
    var velocity = Vector2.ZERO
    if Input.is_action_pressed("ui_up"):
        velocity = Vector2.UP.rotated(rotation) * speed
    position += velocity * delta

存在しないアクション名を書かれても、実行時まで気づけません。project.godotの入力設定に実在するかを、書かせたあとに照合させます。

「Your first 2D game」の段階別・依頼の仕方

公式の「Your first 2D game」は、「Step by step」シリーズを終えた読者向けの教材です。Dodge the Creeps!という1本のゲームを、7つの段階で組み立てます。段階ごとに、Claude Codeへ渡せる範囲が変わります。

段階中身Claude Codeへの頼み方
プロジェクト設定中身プロジェクト作成・フォルダ整理Claude Codeへの頼み方エディタで行う
プレイヤーシーン中身ノード配置・スプライトアニメClaude Codeへの頼み方エディタで組む
プレイヤーのコード中身移動・アニメ切り替え・衝突判定Claude Codeへの頼み方任せやすい(ロジックで閉じる)
敵シーン中身ノード配置・敵のスクリプトClaude Codeへの頼み方配置はエディタ、スクリプトは任せる
メインシーン中身敵の生成・出現位置とタイミングClaude Codeへの頼み方コードは任せ、数値は実機で調整する
HUD中身ScoreLabel・メッセージ・スタートボタンClaude Codeへの頼み方見た目はエディタ、表示更新のコードは任せる
仕上げ中身背景・効果音・キー割り当てClaude Codeへの頼み方設定項目はエディタで行う

当たり判定や物理挙動の数値は、実機で動かして調整する作業が前提です。コードの正しさはヘッドレスのテストで確かめられても、手触りの良さは確かめられません。

CIでエクスポートするときの注意点

エクスポートをCIで回す場合は、Godotの仕様に由来する癖がいくつかあります。Claude Codeにワークフローを書かせるときも、次の点を指示文に含めます。

  • 相対パスの出力先は、カレントディレクトリでなくproject.godotのあるディレクトリが基準です
  • 出力先のディレクトリは、あらかじめ作っておく必要があります
  • GPUのない環境では、--headlessの指定が必須です。GPUのある環境でも、ウィンドウが開くのを防げます

プリセット名は、export_presets.cfgに定義した名前と一致させます。--export-release <プリセット名> <出力パス>の順で、パスにはバイナリのファイル名まで含めます。

UnityのMCP構成との違い

エディタ操作まで任せたいなら、Unityには公式のMCPサーバーがあります。Unity公式MCPサーバーをClaude Codeで使う方法は、エディタ操作をMCP経由で橋渡しする構成です。GodotはCLIとテキスト形式のファイルが中心なので、前提が異なります。

まとめ

Godotでは、シーンの組み立てをエディタ側に残し、.gdの実装とヘッドレスでの検証をClaude Codeに渡すと、役割が重なりません。許可ルールを書くときは、--headlessが先頭に来る公式のコマンド形でdenyが効くかを、一度試してから任せます。

この記事を共有:XはてブLinkedIn