2Dゲームを作成しよう!


Godot Engine バージョン4.3

このチュートリアルでは、Godot 4を使用してシンプルな2Dゲームをゼロから作成する方法を学びます。プレイヤーが動くキャラクターを操作し、敵を避けながらスコアを稼ぐ基本的なゲームを目指します。



チュートリアルゲームを遊びたい方はこちらから!



ステップ1:プロジェクトの準備


Godot 4のインストール Godotの公式サイトからGodot 4をダウンロードしてインストールします。
詳しくはこちらから確認できます。

新しいプロジェクトを作成 Godotを起動し、「Create」をクリックしてプロジェクトを作成します。適当な名前を付け、2Dゲーム用のフォルダを指定します。





ステップ2:プレイヤーキャラクターを作成


シーンの設定
  • Area2Dをメインノード(名前をPlayer)にします。
  • Area2Dの子コードとしてAnimatedSprite2DCollisionShape2Dを追加ます。
下記の画像のように設定してください。


AnimatedSprite2Dの設定 AnimatedSprite2Dの詳しい説明はこちらからご確認ください。
ゴドチューの素材を使いたい場合はこちらからダウンロードしてください。
  • idlerunの二つのアニメーションを追加します。
  • 各アニメーションに動かしたいフレームをドラッグ&ドロップしてください。
  • 各アニメーションにFPSを設定します
下記の画像のように設定してください。


スクリプトを追加 Sprite2DにGDScriptをアタッチし、プレイヤーの移動ロジックを記述します。
extends Area2D

# プレイヤーが敵や障害物に衝突したときに発信されるシグナル
signal hit

# プレイヤーの移動速度
@export var move_speed = 400

# 画面サイズを格納する変数
var screen_size

# ゲーム開始時の初期化処理
func _ready():
# ビューポートのサイズを取得して screen_size に保存
screen_size = get_viewport_rect().size

# フレームごとの処理
func _process(delta):
# プレイヤーの移動方向を示すベクトル (初期値はゼロ)
var velocity = Vector2.ZERO
# 入力に応じて移動方向を設定
if Input.is_action_pressed("move_up"):
velocity.y -= 1 # 上方向
if Input.is_action_pressed("move_down"):
velocity.y += 1 # 下方向
if Input.is_action_pressed("move_left"):
velocity.x -= 1 # 左方向
if Input.is_action_pressed("move_right"):
velocity.x += 1 # 右方向
# 移動方向が設定されている場合、速度を計算
if velocity.length() > 0:
# ベクトルを正規化し、速度を掛けて移動速度を設定
velocity = velocity.normalized() * move_speed
# 移動アニメーションを再生
$AnimatedSprite2D.play("run")
else:
# 移動していない場合、待機アニメーションを再生
$AnimatedSprite2D.play("idle")
# 現在の位置に移動方向と速度を掛けた移動量を加算
position += velocity * delta
# プレイヤーが画面外に出ないように位置を制限
position = position.clamp(Vector2.ZERO, screen_size)
# 移動方向に応じてスプライトを反転
if velocity.x != 0:
$AnimatedSprite2D.flip_v = false # 縦方向の反転を無効化
$AnimatedSprite2D.flip_h = velocity.x < 0 # 左に移動する場合スプライトを反転

# プレイヤーを初期状態に戻す処理
func start():
show() # プレイヤーを表示
$CollisionShape2D.disabled = false # 衝突判定を有効化

# プレイヤーが別の物体と衝突した際の処理
func _on_body_entered(body):
hide() # プレイヤーを非表示にする
hit.emit() # "hit" シグナルを発信
$CollisionShape2D.set_deferred("disabled", true) # 衝突判定を無効化

_on_body_enteredをシグナルを設定してプレイヤーが別の物体と衝突した際の処理をできるようにします。


入力マッピングの設定 Godotの「Project Settings」で、「Input Map」から move_up, move_down, move_left, move_right を確認します。




ステップ3:敵を作成


シーンの設定
  • Rigibody2Dをメインノード(名前をEnemy)にします。
  • Rigibody2Dの子コードとしてAnimatedSprite2D, CollisionShape2DVisibleOnScreenNotifier2Dを追加ます。
下記の画像のように設定してください。

AnimatedSprite2Dの設定 AnimatedSprite2Dの詳しい説明はこちらからご確認ください。
バググの素材を使いたい場合はこちらからダウンロードしてください。
  • walkのアニメーションを追加します。
  • アニメーションに動かしたいフレームをドラッグ&ドロップしてください。
  • アニメーションにFPSを設定します
下記の画像のように設定してください。


敵の動きのロジック 敵を制御するスクリプトをアタッチします。
extends RigidBody2D

# ノードが準備完了時に呼び出される
func _ready():
# AnimatedSprite2Dノードのアニメーションを再生
$AnimatedSprite2D.play()

# 毎フレーム呼び出される
func _process(delta):
pass # 現在は特に処理を実行しない

# オブジェクトが画面外に出たときに呼び出される
func _on_visible_on_screen_notifier_2d_screen_exited():
# オブジェクトを削除してメモリを解放
queue_free()

# 敵の向きを切り替える関数
# flip: Vector2 値で、x方向の向きに基づいてスプライトを反転
func flip_enemy(flip):
if (flip.x < 0): # x方向の値が負の場合
$AnimatedSprite2D.flip_v = false # 縦方向の反転を無効化
$AnimatedSprite2D.flip_h = true # 横方向の反転を有効化

敵が画面外に出た場合は削除する処理をします
  • VisibleOnScreenNotifier2Dのシグナルscreen_exited()をスクリプトの_on_visible_on_screen_notifier_2d_screen_exitedと設定します




ステップ4:タイトル画面やゲームオーバー画面を管理するUI作成


シーンの設定
  • CanvasLayerをメインノード(名前をCanvas)にします。
  • CanvasLayerの子コードとしてタイトル用のLabel, スコア用のLabelとスタートやリスタートのButtonを追加ます。
  • タイトル用のLabelは画面の中央に設定して好きなタイトルのテキストにしてください。
  • スコア用のLabelは画面の上の方に設定して、初期スコアとしてにします。
  • スタートやリスタートのButtonはタイトルの下に設定します。
下記の画像のように設定してください。


Canvasにスクリプトを設定します。
extends CanvasLayer

# ゲーム開始を通知するカスタム信号
signal start_game

# ノードの初期化時に呼び出される
func _ready():
# スコアの表示を隠す
$Score.hide()

# 毎フレーム呼び出される
func _process(delta):
pass # 現在は特に処理を実行しない

# スタートボタンが押されたときに呼び出される
func _on_start_button_pressed():
# スコアを表示
$Score.show()
# タイトルを隠す
$Title.hide()
# スタートボタンを隠す
$StartButton.hide()
# ゲーム開始の信号を送信
start_game.emit()

# スコアを更新する関数
# score: 更新するスコアの値
func update_score(score):
# スコアのテキストを更新
$Score.text = str(score)

# ゲームオーバー画面を表示する関数
func game_show_over():
# タイトルを「GAME OVER」に変更して表示
$Title.text = "GAME OVER"
$Title.show()
# スタートボタンを「RESTART」に変更して表示
$StartButton.text = "RESTART"
$StartButton.show()

このスクリプトの使用場面

  • タイトル画面やゲームオーバー画面を管理するUIコントローラーとして機能します。
  • スタートボタンの動作、スコア表示の更新、ゲームオーバー画面の切り替えをシンプルに実装します。
  • プレイヤーのアクションに応じて信号や表示を管理し、ゲーム全体の流れを制御します。



ステップ5:メインシーンの作成


シーンの設定
  • Node2Dをメインノード(名前をMain)にします。
  • Node2Dの子コードとしてColorRectを追加してBackgroundに名前を変更します。名前通りに背景の色になります。好きな色に変更してください。
  • プレイヤーを追加します。プレイヤーシーンをドラッグ&ドロップします。
  • 次にTimerを二つ追加します。一つはEnemyTimerに名前を変更します。敵の生成を管理するタイマーになります。Wait Time0.5sにします。もう一つはスコア用のタイマーになります。名前はScoreTimerです。一秒づつスコア用のLabelを更新するためになります。
  • Marker2Dを追加します。これはプレイヤーを初期位置に設定するためのものになります。プレイヤーのスタートする好きな位置に配置して名前をPlayerPositionにします。
  • 次に先ほど作ったタイトル画面やゲームオーバー画面を管理するUIをドラッグ&ドロップします。
  • 敵をランダムの場所から生成するためにPath2Dを追加します。名前はEnemyPathにします。子としてPathFollow2Dを追加します。名前はEnemySpawnLocationにします。
下記の画像のように設定してください。


敵の生成の作成

敵の生成のためにPath2D(EnemyPath)を追加したと思います。それを使っていきます。
Path2Dを選択すると、エディタの上部にいくつかの新しいボタンが表示されます:



中央のアイコン([点を空きスペースに追加])を選択し、表示されているコーナーをクリックしてポイントを追加してパスを描画します。ポイントをグリッドにスナップするには、[グリッドスナップを使う]が選択されていることを確認します。このオプションは、[選択Nodeをロック]ボタンの左側にあり、「交差する線と磁石」のアイコンで表示されています。



重要:時計回りにパスを描画します。そうしないと、モブは内側ではなく外側を向いて発生します!

画像にポイント 4 を配置した後、「カーブを閉じる」ボタンをクリックすると、カーブが完成します。
パスが定義されたので、EnemyPathの子として追加したPathFollow2D(EnemySpawnLocation)は自動的に回転し、パスの移動に従うので、パスに沿ってランダムな位置と方向を選択できます。

Mainにスクリプトを設定します。
extends Node2D

# 敵キャラクターのシーンを定義
@export var enemy_object: PackedScene
# 敵の速度を設定
@export var enemy_speed = 400

# スコアの初期値
var score = 0

# ノードの初期化時に呼び出される
func _ready():
# プレイヤーとタイマーを非表示・停止
$Player.hide()
$ScoreTimer.stop()
$EnemyTimer.stop()

# ゲームオーバー時の処理
func game_over():
# スコアと敵生成のタイマーを停止
$ScoreTimer.stop()
$EnemyTimer.stop()
# GUI にゲームオーバー画面を表示
$GUI.game_show_over()
# 敵キャラクターをすべて削除
get_tree().call_group("enemies", "queue_free")

# 新しいゲームを開始する処理
func new_game():
# スコアをリセット
score = 0
# プレイヤーの初期化
$Player.start()
# スコアと敵生成タイマーを開始
$ScoreTimer.start()
$EnemyTimer.start()
# プレイヤーを初期位置に設定
$Player.position = $PlayerPosition.position
# スコアを更新
$GUI.update_score(score)

# スコアタイマーがタイムアウトした際に呼び出される処理
func _on_score_timer_timeout():
# スコアを増加
score += 1
# GUI にスコアを更新
$GUI.update_score(score)

# 敵生成タイマーがタイムアウトした際に呼び出される処理
func _on_enemy_timer_timeout():
# 敵キャラクターを生成
var enemy = enemy_object.instantiate()
# 敵生成位置をランダムに設定
var enemy_spawn_location = get_node("EnemyPath/EnemySpawnLocation")
enemy_spawn_location.progress_ratio = randf()
# 敵の移動方向をランダム化
var direction = enemy_spawn_location.rotation + PI / 2
enemy.position = enemy_spawn_location.position
direction += randf_range(-PI / 4, PI / 4)
# 敵の速度を設定し、方向に応じて回転させる
var velocity = Vector2(randf_range(150.0, 250.0), 0.0)
var linear = velocity.rotated(direction)
enemy.linear_velocity = linear
enemy.flip_enemy(linear) # 敵の向きを速度に合わせて変更
# 敵をゲームシーンに追加
add_child(enemy)

このスクリプトの使用場面

  • ゲームの全体的なロジック管理を担当。
  • スコアの管理、敵キャラクターの生成と削除、ゲームオーバーや新しいゲームの開始などを処理します。
  • 敵のランダムな挙動とスコアシステムを統合し、ゲームプレイを楽しくします。



ステップ6:ゲームをテストして完成!


最後にゲームを実行して動作を確認します。チュートリアルを通じて、2Dゲームの基本が完成しました!

これでゲームが完成です!完成したゲームを遊びたい方はこちらから!

完成したプロジェクトのダウンロードはこちらから!


このチュートリアルをもとに、プレイヤーの強化や新しい敵の追加など、さまざまな機能を拡張してみてください。質問があればぜひQ&Aページで相談してください!
最終更新日: 2025/03/27 01:56

コメント