INDIE INDEX

Claude CodeのGDScriptを読む|変数・条件・関数・シグナル【AIでゲーム開発】

公開 2026-09-24更新 2026-09-24Claude Code

Claude Code が作った Godot 4 のコイン集めの3つのスクリプトは、変数・条件・関数・シグナルの4つの語で、どの行が何を決めているかを追えました。コインの必要数を決める1行を5から3に書き換えると、3枚目で「CLEAR」が出ました。画面の文字を出す部分を別のファイルに分けてもらうと、取り込みを済ませた写しでは分ける前と同じ動きになり、取り込みの前の写しでは読み込みのエラーが出ました。

このページの目次
  1. この試しで読んだ3つのファイル
  2. 変数:値に名前を付けた行
  3. 条件:「〜なら」で分かれる行
  4. 関数:まとまった手順に名前を付けた行
  5. シグナル:コインが「取られた」と知らせる行
  6. Claude Code に1ファイルを分けてもらう頼み方
  7. 分けたあとに読み込めるか・動きが変わっていないかを確かめる
  8. 情報源

この試しで読んだ3つのファイル

Claude Code に作ってもらった Godot Engine 4 の小さな2Dゲームを例にします。左右キーで動いて上キーでジャンプするプレイヤーが、画面に置いたコイン5枚を集めるゲームです。コインに触れるとコインが消えてスコアが1増え、5枚集めると画面に「CLEAR」と出ます。

スクリプト(GDScript のファイル)は3つあり、それぞれがシーンの中のノード1つに付いています。

ファイル 付いているノード 行数
scripts/Player.gd Player.tscn の Player(CharacterBody2D) 19
scripts/Coin.gd Coin.tscn の Coin(Area2D) 11
scripts/Main.gd Main.tscn の Main(Node2D) 23

どのファイルも1行目は extends で始まり、そのスクリプトを付けるノードの種類を書いています。Player.gd の1行目は次のとおりです。

extends CharacterBody2D

このページでは、この3つのファイルに出てきた行だけを例にして、変数・条件・関数・シグナルの4つを順に読みます。文法を頭から順に並べることはしません。

変数:値に名前を付けた行

Main.gd の上のほうには、次の行があります。

const TOTAL_COINS := 5

var score := 0

@onready var score_label: Label = $CanvasLayer/ScoreLabel
@onready var clear_label: Label = $CanvasLayer/ClearLabel
  • var で始まる行は変数です。ゲームの途中で値が変わります。この作例のスコアは 0 から始まり、コインを1枚取るたびに1増えます。
  • const で始まる行は定数です。ゲームが動いている間は値を変えられません。この作例では、「CLEAR」に要るコインの数が 5 になっています。
  • 「:=」は、右に書いた値から型(整数か文字列か など)を決める書き方です。名前のあとに「: Label」のように型をじかに書く形もあります。
  • 「$」で始まるところは、シーンの中のノードを道の形で取り出す書き方です。行の頭の「@onready」は、ノードの準備ができてから値を入れる印です。

Player.gd では、横に動く速さとジャンプの強さが定数になっています。

const SPEED = 200.0
const JUMP_VELOCITY = -400.0

数を変えたいときに書き換える場所は、こうした定数の行です。Main.gd の写しで、コインの数の 5 を 3 に書き換え、画面なしの Godot で動かしました。

const TOTAL_COINS := 3

3枚目を取ったところで「CLEAR」が出ました。4枚目・5枚目でもスコアは増え続け、最後は「Score: 5」でした。

条件:「〜なら」で分かれる行

if のあとに書いた式が成り立つときだけ、その下の字下げした行が動きます。else の下の行は、成り立たないときに動きます。GDScript は字下げで、どこまでが if の中かを決めます。

Player.gd の、プレイヤーを動かす関数の中には、次の条件があります。

	if not is_on_floor():
		velocity += get_gravity() * delta

	if Input.is_action_just_pressed("ui_up") and is_on_floor():
		velocity.y = JUMP_VELOCITY

	var direction := Input.get_axis("ui_left", "ui_right")
	if direction:
		velocity.x = direction * SPEED
	else:
		velocity.x = move_toward(velocity.x, 0, SPEED)
  • 1つ目は、床の上にいない(not)ときだけ、下向きの速さを足す条件です。
  • 2つ目は、上キーを押した瞬間で、しかも(and)床の上にいるときだけ、縦の速さをジャンプの強さにする条件です。
  • 3つ目は、左右キーが押されているときは横の速さを決め、押されていないとき(else)は横の速さを 0 に近づける条件です。関数の中で var を使った変数は、その関数の中だけで使われます。

画面なしの Godot で右キーを押したままにすると、横の速さは 200 になり、離すと 0 に戻りました。床の上で上キーを押すと、縦の速さは -400 になりました。

Coin.gd と Main.gd にも、条件が1つずつあります。

if body.is_in_group("player"):
if score >= TOTAL_COINS:

Coin.gd のほうは、触れたものが player というグループに入っているときだけ、コインを取った扱いにする条件です。Main.gd のほうは、スコアが「CLEAR」に要る数以上になったときだけ、「CLEAR」の文字を見えるようにする条件です。

関数:まとまった手順に名前を付けた行

func で始まる行から、その下の字下げが続くところまでが、関数1つです。この作例の関数は、Godot が決まったときに呼ぶものと、スクリプトの中で名前を付けて呼ぶものの2種類に分かれます。

func _ready() -> void:
func _physics_process(delta: float) -> void:
  • 前者は、ノードの準備ができたときに1回呼ばれる関数です。Main.gd と Coin.gd にあります。
  • 後者は、物理の更新のたびに呼ばれる関数で、この回数は初めの設定では1秒に60回です。delta には、前に呼ばれてからの秒数が入ります。Player.gd の動きは、すべてこの中に書かれています。
  • 「-> void」は、この関数が値を返さないことを表します。

Main.gd には、名前を付けて呼ぶ関数があります。

func _update_score_label() -> void:
	score_label.text = "Score: %d" % score

この関数は、スコアの文字を書き換えるだけの関数です。「%d」のところにスコアの数が入り、「Score: 3」のような文字になります。スコアの文字の形を変えたいときに書き換える場所は、この関数の中です。

シグナル:コインが「取られた」と知らせる行

シグナルは、ノードが「何かが起きた」と知らせる仕組みです。知らせを受けたい側がつないでおくと、知らせが出たときに、つないだ関数が呼ばれます。Coin.gd は次の11行です。

extends Area2D

signal collected

func _ready() -> void:
	body_entered.connect(_on_body_entered)

func _on_body_entered(body: Node2D) -> void:
	if body.is_in_group("player"):
		collected.emit()
		queue_free()
  • 「signal collected」は、このコインが出す知らせに名前を付けた行です。
  • 「collected.emit()」は、その知らせを出す行です。
  • 6行目でつないでいるシグナルは、Coin.gd の中では決めていないもので、Area2D のノードが元から持っています。ほかの物体がコインの範囲に入ると出るシグナルで、ここでは Coin.gd の中の、その下の関数につないでいます。
  • 知らせを出したあとの行で、コインは消えます。

知らせを受ける側は Main.gd です。

	for coin in get_tree().get_nodes_in_group("coins"):
		coin.collected.connect(_on_coin_collected)

coins というグループに入っているコインを1枚ずつ取り出し、そのコインの知らせを、スコアを1増やす関数につないでいます。プレイヤーがコインに触れると、Coin.gd が知らせを出し、Main.gd のスコアが1増える、という順につながっています。

このつなぎ方は、コインが coins のグループに入っていることが前提です。最初にできた版の写しで試すと、コインは消えるのに、スコアは「Score: 0」のままでした。グループに入っているコインを数えると 0 枚でした。Main.tscn の中で、コインをグループに入れる書き方(groups=["coins"])が、コインのノードの見出しの行([node …] の行)の外に1行で置かれていました。見出しの行の中に入れた版では、グループのコインは5枚になり、スコアは1枚ごとに1ずつ増えました。

Claude Code に1ファイルを分けてもらう頼み方

Main.gd は、コインの数を数えることと、画面の文字(スコアと「CLEAR」)を出すことの両方を、1つのファイルで書いていました。画面の文字を出す部分だけを別のファイルに分けるよう、Claude Code に次の文を1回だけ渡しました。分けるファイルと仕事・変えない動き・分けること以外は直さないこと・返してほしいことを、1文ずつ書いています。

Claude CodeにGDScriptの1ファイルを分けてもらった文

作業フォルダは D:\Claude_Game_Site\drafts\W-142-A\read-gdscript.play\ だけです。このフォルダの外は読まない・書かないでください。

この Godot 4 のプロジェクトの scripts/Main.gd は、コインを取った数を数えることと、画面の文字(ScoreLabel のスコアと ClearLabel の「CLEAR」)を出すことを、1つのファイルで両方やっています。 画面の文字を出す部分だけを、別のスクリプトのファイルに分けてください。ゲームの動き(コインに触れると消えてスコアが1増え、5枚で「CLEAR」と出る)は変えないでください。分けること以外の直しはしないでください。 Godot のエディタは開けないので、ファイル(.tscn・.gd)を直接書いてください。

できたら、新しく作ったファイルと書き換えたファイルの一覧、それぞれのスクリプトがどのシーンのどのノードに付いているか、Main.gd に残った仕事と新しいファイルに移った仕事を、短く返してください。

Claude Code は、フォルダの中のファイルを読み、書き換え、コマンドを動かすツールです。頼んでから返事が来るまでは43秒でした。Claude Code は、scripts/Hud.gd を新しく作り、Main.gd と Main.tscn を書き換えた、と返しました。Hud.gd は、文字を出す2つのノードの親(CanvasLayer)に付いています。分けたあとの Hud.gd は次の14行です。

class_name Hud
extends CanvasLayer

@onready var score_label: Label = $ScoreLabel
@onready var clear_label: Label = $ClearLabel

func _ready() -> void:
	clear_label.visible = false

func update_score(score: int) -> void:
	score_label.text = "Score: %d" % score

func show_clear() -> void:
	clear_label.visible = true

1行目は、このスクリプトに Hud という名前を付け、ほかのスクリプトから型の名前として使えるようにする行です。分けたあとの Main.gd は、次のように Hud.gd の関数を呼ぶ形に変わりました。

@onready var hud: Hud = $CanvasLayer

func _ready() -> void:
	hud.update_score(score)
	for coin in get_tree().get_nodes_in_group("coins"):
		coin.collected.connect(_on_coin_collected)

func _on_coin_collected() -> void:
	score += 1
	hud.update_score(score)
	if score >= TOTAL_COINS:
		hud.show_clear()

コインを数えることと「CLEAR」にするかどうかを決めることは Main.gd に残り、文字を書き換えることは Hud.gd に移りました。Player.gd と Coin.gd は変わっていませんでした。

分けたあとに読み込めるか・動きが変わっていないかを確かめる

分ける前と分けたあとのプロジェクトの写しを、画面なしの Godot 4.7.2 で、同じ台本で動かしました。台本は、プレイヤーをコインの位置に1枚ずつ置き、スコアの文字と「CLEAR」が見えているかを記録します。右キーと上キーの押下も送りました。どちらの写しも、先に次のコマンドで取り込み(エディタを起動し、取り込みを済ませて閉じる)を1回走らせています。

Godot_v4.7.2-stable_win64_console.exe --headless --path <プロジェクトの写し> --import
見たこと 分ける前 分けたあと
coins のグループに入っているコイン 5枚 5枚
1枚目から4枚目を取ったあとのスコアの文字 Score: 1 から Score: 4 Score: 1 から Score: 4
5枚目を取ったあと Score: 5・「CLEAR」が見える Score: 5・「CLEAR」が見える
右キーを押したままにした30回の物理の更新での横の移動 約100 約100
床の上で上キーを押したあとの縦の速さ -400 -400
読み込みのエラー なし なし

分けたあとのプロジェクトで、プレイヤーを5枚のコインの位置に順に置いたあとの画面。左上に Score: 5、中央に CLEAR の文字が出ている

取り込みを1回も走らせていない写しでは、結果が違いました。分けたあとの版をそのまま動かすと、次のエラーが出て Main.gd が読み込めず、コインは消えてもスコアは「Score: 0」のままでした。

SCRIPT ERROR: Parse Error: Could not find type "Hud" in the current scope.
   at: GDScript::reload (res://scripts/Main.gd:7)
ERROR: Failed to load script "res://scripts/Main.gd" with error "Parse error".

エラーの行の Main.gd:7 は、分けたあとの Main.gd で Hud という型の名前を使っている行です。Hud という名前は、Hud.gd の1行目で付けたものです。同じ写しで取り込みを1回走らせたあとは、このエラーは出ず、上の表のとおりに動きました。分ける前の版(型の名前を付ける行が無い)は、取り込みを走らせていない写しでも、スコアが1ずつ増えて5枚目で「CLEAR」が見えました。

エディタの画面を開いて実行ボタンから遊ぶことは、この試しでは確かめきれませんでした。

情報源

Claude Code のほかのページ

このページに出た言葉