チュートリアルを終えて自分のゲームを作り始めるときに迷うのが、何をどの順で覚えるかです。ゲームは、動いて、当たって、数えて、終わる、の4つが1周したときに初めて遊べます。この1周を先に作れば、絵や音を足すたびに遊んで確かめられます。
順番を決めずに始めると、何から作ればいいかで手が止まったり、キャラの絵を描き直しているうちに、遊べる形になる前に止まったりします。
この記事では、1つの小部屋に機能を7段で足していく順番と、各段で読む記事を解説します。
この記事でわかること
- ゲームを遊べる形にする「動く・当たる・数える・終わる」の1周
- 最初に役割を覚えるノード5つ
- 1つの部屋に足していく7段と、各段の到達点
- 後回しでいいものと、3Dへ移る時期
動作確認環境: Godot 4.7
ゲームが遊べる形:動く・当たる・数える・終わる
画面の四角をゲームにしているのは、絵でも音でもありません。自分が動いて何かに当たり、当たった数が数えられて、決まったところで終わります。終わったら、もう一度最初から遊べます。この4つが1周すれば、四角と数字だけでもゲームです。

何から作るかは、1周に要る最小のもの、つまり床と四角とコインで決まります。絵や音は1周したあとで足します。
1つの小部屋に足していく作り方
これから作るのは、四角い床が1枚あるだけの小部屋です。床の上には、自分が1人、動かない敵が1体、コインが3枚あります。ここに7つの段で機能を足していきます。
大事なのは、毎回ゲームを作り直さないことです。今日置いた床を残したまま、明日は当たり判定を足し、その次の日に数字を出します。前に覚えたことが、そのまま次の土台になります。
シーンの分け方にも順番があります。チュートリアルの多くは、Player.tscn・Enemy.tscn・Coin.tscn を先に切り出させます。自分で作り始めるときは逆で、最初は全部を1つのシーンに置いておくほうがつまずきません。
切り出すのは、同じものをまとめて直したくなったときです。コインを3枚並べるだけなら複製で足ります。3枚の見た目を一度に変えたくなったら、そこで Coin.tscn に切り出します。
最初に覚えるノード5つ
Godotのノードは数百あります。全部を知る必要はなく、最初に役割を掴むのは5つで足ります。画面のどこに何があるかが怪しいなら、先に エディタ入門 を1本読んでおくと迷いません。
- Node2D: 部屋や物をまとめる入れ物。位置を持つ
- CharacterBody2D: 操作して動かすもの。壁にぶつかると止まる
- StaticBody2D: 動かないもの。進路をふさぐ
- CollisionShape2D: 当たる形を決める。動くもの・動かないもの・エリアのどれにも、子として付ける
- Area2D: 入ってきた相手に気づく。止めはしない

床や四角を描く ColorRect、数字を出す Label も使いますが、役割を先に掴むのはこの5つです。
この中でいちばん取り違えやすいのが、最後の2つの関係です。体でふさぐのと、エリアで気づくのは別物です。壁は通れなくするもので、コインは触れたことを知らせるものです。同じ「当たった」でも担当が違います。

壁で止まるのは CharacterBody2D と StaticBody2D、コインや敵に気づくのは Area2D です。この2系統を先に分けておくと、「当たっているのに消えない」で止まりません。
他のエンジンでいうと: Area2D は、Unity の
Collider2Dで「Is Trigger」をオンにしたもの(OnTriggerEnter2Dで気づく)、Unreal Engine の Overlap イベントに近い仕組みです。Godot では、ふさぐか気づくかをノードの種類で分けています。
当たる相手を決める Collision Layer と Mask は、新規プロジェクトの既定のままで、この記事の実践は動きます。
学ぶ順番の7段
同じ部屋に、次の順で足していきます。各段には「ここ で止めてもいい」到達点があります。画面でそれが起きていれば、次へ進んで構いません。

第1段:動く
床を囲むように壁を置き、矢印キーで上下左右に動かします。壁で止まるところまでが1段目です。横スクロールで作る場合も、ジャンプはまだ要りません。
到達点: 矢印キーでキャラが床の上を動き、壁で止まる。
見下ろしで動かすなら、CharacterBody2D の Motion Mode を Floating にします(Motion Mode)。移動を書く場所は ゲームループ の記事にあります。WASD でも動かしたくなったら、Input Map で自分の操作を作ります。
第2段:当たる
コインと敵を置きます。どちらも Area2D です。コインは触れると消え、敵は触れると何かが起きるようにします。敵は動かなくて構いません。
到達点: コインに触れると消え、敵に触れると反応がある。
Area2D が主役の段です。止まる側との違いは 2D物理ボディ にまとまっています。
第3段:数える
取ったコインの枚数を 画面に出します。変数を1つ増やして Label に書くだけです。
到達点: 画面隅の数字が、取るたびに増える。
数の持ち方は GDScript入門 にあります。あとでコインを別のシーンに分けたら、取られたことを数字の側へ知らせる方法は シグナル です。
第4段:終わる
3枚集めたらクリア、敵に当たったら失敗にして、キー1つで最初から遊べるようにします。動く・当たる・数える・終わるの1周がつながるのが、この段です。
到達点: 規定数でクリア表示が出て、失敗してもエディタに戻らずにやり直せる。
やり直しの1キーを後回しにすると、遊ぶたびにエディタへ戻って F5 を押すことになります。この手間が重なると、作っている本人が自分のゲームを遊ばなくなります。
タイトルとゲームオーバーの画面まで作るなら ゲーム進行入門、一時停止を足すなら ポーズ が次の一歩です。
第5段:見た目
四角を絵に差し替え、向きを変え、取った瞬間に何か光らせます。ここで初めて Sprite2D が出てきます。
到達点: 左右で絵が向き、コインを取ったことが目で分かる。
絵の切り替えは AnimatedSprite2DとAnimationPlayer 、被弾の白フラッシュは modulate 、追従カメラは Camera2D 。床をタイルにした くなったら TileMapLayer へ進みます。
描き直しから始めないでください。今動いているキャラのまま、演出だけを足します。絵を描き直し始めると、ゲームではなく素材フォルダが育ちます。
第6段:音
取得・被弾・クリアに音を付けます。第5段より簡単です。
到達点: 3つの場面で音が鳴り、音を切ってもゲームは成立している。
オーディオ入門 でBGMと効果音の音量を分けるところまでやれば足ります。
ここでよく踏むのが、コインに音を付けて、ノードを消す queue_free() の直前で鳴らす形です。ノードごと消えるので音も切れます。音を出す側は、消える相手ではなく残るほう(この記事の実践なら Main)に置いてください。
第7段:残す
ここは任意です。最高記録を1つだけファイルに書いて、次の起動でも残るようにします。
到達点: 閉じて開き直しても、前回の記録が出ている。
書く先は、ゲームごとに用意される保存用のフォルダ user:// です。本格的なセーブは セーブ/ロード にありますが、1つの部屋で遊ぶだけなら数値1つで十分です。
実践:動いて終わるまでを1つの部屋で 作る
見下ろしのアクションRPGの1部屋、迷路の1面、弾をよける1画面 —— ジャンルは違っても、最初に遊べる形はどれも同じ4つでできています。第1段から第4段までを、1つのシーンと1本のスクリプトで作ります。

① 置くもの
新規プロジェクトで2Dシーンを作り、main.tscn として保存して、F5 で最初に開くメインシーンに指定します。
Main (Node2D) ← main.gd
├── Floor (ColorRect)
├── Walls (StaticBody2D)
│ └── CollisionShape2D × 4
├── Player (CharacterBody2D)
│ ├── ColorRect
│ └── CollisionShape2D
├── Coins (Node2D)
│ └── Coin1 / Coin2 / Coin3 (Area2D、それぞれ ColorRect と CollisionShape2D を持つ)
├── Enemy (Area2D)
│ ├── ColorRect
│ └── CollisionShape2D
└── Hud (CanvasLayer) ← カメラが動いても、画面に貼り付いたまま描く層
├── ScoreLabel (Label)
└── MessageLabel (Label)
値はこう置きます。CollisionShape2D の Shape は最初は空なので、どれも New RectangleShape2D を選んで形を作り、Size を下の大きさに合わせます。
- Floor: Position (88, 108)、Size (976, 432)、Color は #CCCCCC(明るい灰色)
- Walls 自身は (0, 0)。その下の4つの形は 上 (576, 96) 1000 × 24、下 (576, 552) 1000 × 24、左 (76, 324) 24 × 456、右 (1076, 324) 24 × 456
- Player: (200, 324)、32 × 32、水色。Motion Mode を Floating
- Coin1 (400, 200) / Coin2 (576, 460) / Coin3 (880, 200):24 × 24、明るい青
- Enemy: (640, 324)、48 × 48、濃紺
- ScoreLabel は (88, 24)、MessageLabel は (88, 56)
見た目の ColorRect と当たる形の CollisionShape2D は、原点の位置が違います。ColorRect は左上が原点なので、同じ大きさにしたうえで、大きさの半分だけ左上へずらします。

ずらす量は、Player なら (-16, -16)、コインは (-12, -12)、Enemy は (-24, -24) です。プレイヤー・コイン・敵の CollisionShape2D は (0, 0) のままで構いません。位置を動かす形は、壁の4つだけです。
床は ColorRect だけで、当たり判定を持ちません。止めているのは Walls に付けた4つの形です。壁の形を複製で増やしたときは、Size を変える前に Shape の欄のメニューから Make Unique を選んでください。複製したままだと4つが同じ形を共有していて、1つの大きさを変えると全部が変わります。
② スクリプトを書く
Main に1本だけ付けます。プレイヤーにスクリプトは要りません。
# main.gd(Main にアタッチ)
extends Node2D
const SPEED := 240.0 # 1秒に進むピクセル数
const TARGET := 3
var score := 0
var finished := false
@onready var player: CharacterBody2D = $Player
@onready var score_label: Label = $Hud/ScoreLabel
@onready var message_label: Label = $Hud/MessageLabel
func _ready() -> void:
for child in $Coins.get_children():
var coin := child as Area2D
coin.body_entered.connect(_on_coin_touched.bind(coin))
$Enemy.body_entered.connect(_on_enemy_touched)
_update_score()
message_label.text = "矢印キーで動いて、コインを3枚集める"
func _physics_process(_delta: float) -> void:
if finished:
return
# 押している矢印キーから、長さ1までの向きを作る
var direction := Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down")
player.velocity = direction * SPEED
player.move_and_slide() # 壁で止まるのはこの1行
func _on_coin_touched(body: Node2D, coin: Area2D) -> void:
if finished or body != player:
return
coin.queue_free()
score += 1
_update_score()
if score == TARGET:
_finish("クリア")
func _on_enemy_touched(body: Node2D) -> void:
if finished or body != player:
return
_finish("敵に当たった")
func _update_score() -> void:
score_label.text = "コイン %d / %d" % [score, TARGET] # %d の位置に、右の配列の数が順に入る
func _finish(text: String) -> void:
finished = true
player.velocity = Vector2.ZERO
message_label.text = text + " Enterでやり直し"
# ボタンなどの UI が使わなかった入力だけが、ここへ届く
func _unhandled_input(event: InputEvent) -> void:
if finished and event.is_action_pressed("ui_accept"):
get_tree().reload_current_scene.call_deferred() # このフレームの処理を終えてから、シーンを最初から読み直す
ui_left や ui_accept はGodotに最初から入っているので、Input Map の設定は要りません。move_and_slide() に delta(前のフレームからの経過秒数)を掛けないのは、この関数が時間を見てくれるからです。
3枚のコインのシグナルは、エディタで1枚ずつ接続する代わりに、_ready() でまとめて接続しています。bind(coin) は、シグナルが渡す引数(触れた相手の body)の後ろに、そのコイン自身を足しておく書き方です。3枚を同じ関数につないでも、どのコインに触れたかが分かります。
body != player で相手を確かめているのは、Collision Layer と Mask を既定のまま使っているからです(Collision LayerとMask)。
③ 動かして確かめる

F5で実行して、矢印キーで動かします。見るのは3つです。
まず、床の端まで進むと止まります。床の外へ出てしまうなら、Debug メニューの Visible Collision Shapes をオンにして実行し、壁の形の位置と大きさを確かめます。4つの形が (0, 0) に集まったままになっていることがよくあります。
次に、コインに触れると消えて、左上の数字が「コイン 1 / 3」に変わります。消えないならそのコインの Shape を、数字が読めないなら2つのラベルの位置を先に確かめます。
仕上げに、3枚すべてを取ります。上の2枚を取ってから右を回って下へ向かえば、敵に触れずに集められます。3枚目で「クリア Enterでやり直し」と出てプレイヤーが止まり、Enter を押すと、コインが3枚そろった部屋から遊び直せます。途中で敵に触れても「敵に当たった」と出て、同じように Enter で戻れます。エディタに戻らずに、何度でも遊べます。
ポイントは2つです。
- 止める係と気づく係を分ける: 壁で止めているのは Walls の StaticBody2D と
player.move_and_slide()です。コインと敵が反応しているのは Area2D のbody_enteredです。同じ「当たり」でも通る道が違うので、動かないときはどちらの話か先に決めます - やり直しを最初から入れておく:
reload_current_scene()の1行があるだけで、自分のゲームを遊べるようになります。あとから足すつもりでいると、たいてい足さないまま飽きます
後回しでいいものと、3Dへ移る時期
Autoload、タイルマップ、アニメーションツリー、シェーダーは、どれも「先に覚えたほうがいい」ように見えますが、ほとんどは後で構いません。次のものは、必要になった瞬間に覚えれば間に合います。「覚えなくていい」ではなく「今の部屋で必要になった順に」という意味です。
- Autoload: スコアや音楽をシーンまたぎで持ちたくなったら
- TileMapLayer: 床を四角からタイルにしたくなったら
- AnimationTree: 歩きと走 りの切り替えが手に負えなくなったら
- カスタムリソース: 敵の種類が増えて、数値を1か所にまとめたくなったら
- NavigationAgent2D: 敵に障害物を避けさせたくなったら
- UIレイアウト: 画面サイズが変わっても崩れない配置が要るとき。Label を1つ置くだけなら第3段でできます
- シェーダー: 絵では作れない表現が要るとき
3Dへ移る時期も同じ考え方で、2Dを全部終えてからではなく、第4段で1周したら移れます。

3Dへ行くときだけは、同じ部屋に足すのではなく新しい部屋を作ります。それでも、そこまでに覚えた考え方はそのまま載ります。CharacterBody2D は CharacterBody3D、Area2D は Area3D、CollisionShape2D は CollisionShape3D に置き換わり、スコアの数え方も終わり方も変わりません。
新しく覚えるのは、向き(3Dでは y が空の方向)とカメラと見た目の作り方です。入口は Node3D入門・CharacterBody3D入門 です。
逆に、第5段の見た目まで2Dで仕上げてから移ろうとすると、たいてい移れません。絵とタイルとアニメには終わりがないか らです。
うまくいかないときは
| 症状 | 原因 | 直し方 |
|---|---|---|
| 実行した直後に「Node not found」のエラーが出る | スクリプトの $Player や $Hud/ScoreLabel と、ツリーのノード名・親子関係が違う | 名前を1文字ずつ合わせる。大文字と小文字も区別される |
| 四角を絵に替えたら、見た目と当たり判定がずれた | Sprite2D は中心が原点なのに、ColorRect 用のずらしが残っている | 絵の Sprite2D の Position を (0, 0) にする。Player 本体は動かさない |
プレイヤーを Area2D で作ったら、コインの body_entered が鳴らない | Area2D どうしの重なりは、body_entered ではなく area_entered で届く | 壁で止まってほしいプレイヤーは CharacterBody2D で作る |
| あとから足したコインだけ取れない | _ready() でつないだのは起動した時点のコインだけ。足したコインの body_entered はどこにもつながっていない | 足すときに coin.body_entered.connect(_on_coin_touched.bind(coin)) を呼ぶ |
おまけ:先に知っておくと良いこと
- 公式 チュートリアルとは作りが違う: 公式の "Your first 2D game" は、プレイヤーを Area2D で作り、見た目を早めに整えます。覚える内容は重なりますが、この記事は作り直しを減らす順を選んでいます
- 詰まったら
print()で確かめる: 動かない原因は、たいてい思っている場所と違います。デバッグ入門 の道具を持ち替える手順が早道です - 書き出して人に渡すと続く: 遊べる形にすると感想が返ってきます。エクスポート入門 は第4段で1周した時点で読む価値があります
まとめ
- 遊べるのは1周したとき: 動く・当たる・数える・終わるの1周。絵と音はそのあと
- 作るのは1つの小部屋: 毎回作り直さず、同じ部屋に足していく
- 最初の5つの役割: 体でふさぐのと、エリアで気づくのは別物
- 順番: 動く → 当たる → 数える → 終わる → 見た目 → 音 → 残す
- 分けどき、移りどき: シーンを分けるのは、同じものをまとめて直したくなったとき。3Dへ移るのは、2Dで1周したとき
まずは床と四角だけで、動いて終わるところまで作ってみてください。絵が無くても、1周すればそれはもうゲームです。あなたの最初の部屋では、プレイヤーに何を集めさせますか。