はじめてのGodot 第8回(最終回): 音とリスタートで「ゲーム」に仕上げよう


前回までで、ゲームの中身は完成しました。最終回は音・スタート画面・リトライを付けて、人に渡せる「完成品」に仕上げます。

今回のゴール

スタート画面からプレイ、ゲームオーバーを経てリトライで再び遊べるまでの一連の流れ

  • ゲームオーバーで効果音(爆発音)が鳴る
  • スタートボタンを押してからゲームが始まる
  • リトライボタンで何度でも遊べる

音素材を用意する

Kenneyには効果音素材もあります(CC0)。「Sci-fi Sounds」や「Impact Sounds」あたりから、爆発音っぽいもの(.ogg.wav)をひとつダウンロードして、ファイルシステムにドラッグ&ドロップしてください。

爆発音を鳴らす

音を鳴らすのも、もちろんノードです。AudioStreamPlayer を使います。

  1. Main の子に AudioStreamPlayer を追加し、名前を ExplosionSound に。Streamに爆発音を設定(Autoplayはオフのまま)
  2. main.gd_on_player_died() に1行追加:
func _on_player_died():
	playing = false
	$SpawnTimer.stop()
	%ScoreLabel.hide()
	$ExplosionSound.play()
	%GameOverLabel.text = "GAME OVER\nSCORE: %.1f" % score
	%GameOverLabel.show()

play() で好きなタイミングで再生できます。

スタート画面を作る

今はゲームが問答無用で始まります。「ボタンを押したら開始」に変えましょう。

  1. HUD の子に Label を追加 → 名前 TitleLabel、Textにゲームタイトル(METEOR DODGE など)、画面中央上寄りに大きく配置
  2. HUD の子に Button を追加 → 名前 StartButton、Textを スタート に、画面中央に配置
  3. どちらも右クリック→「固有名でアクセス」で % を付ける
  4. SpawnTimerAutostart をオフに戻す(開始はボタンが合図になるため)
  5. StartButtonpressed() シグナル(ボタンが押された)を Main に接続

main.gd を仕上げます。これが完成版の全文です。

extends Node2D

const METEOR_SCENE = preload("res://meteor.tscn")

var score = 0.0
var playing = false

func _ready():
	$SpawnTimer.timeout.connect(_on_spawn_timer_timeout)

func _process(delta):
	if playing:
		score += delta
		%ScoreLabel.text = "SCORE: %.1f" % score

func _on_spawn_timer_timeout():
	var meteor = METEOR_SCENE.instantiate()
	meteor.position = Vector2(randf_range(20, 460), -50)
	meteor.speed = randf_range(200, 500)
	add_child(meteor)

func _on_start_button_pressed():
	%TitleLabel.hide()
	%StartButton.hide()
	playing = true
	$SpawnTimer.start()

func _on_player_died():
	playing = false
	$SpawnTimer.stop()
	%ScoreLabel.hide()
	$ExplosionSound.play()
	%GameOverLabel.text = "GAME OVER\nSCORE: %.1f" % score
	%GameOverLabel.show()
	%StartButton.text = "リトライ"
	%StartButton.show()

ポイントは2つ。playing の初期値が false になったこと(スタート前はスコアが進まない)。そしてゲームオーバー時にスタートボタンを「リトライ」に変えて再表示していることです。

リトライを実装する

「リトライ」を押したときも _on_start_button_pressed() が呼ばれますが、死んだ宇宙船や降ってきた隕石が残ったままです。全部リセットする一番簡単な方法は、シーンを丸ごと読み込み直すことです。

_on_start_button_pressed() を少しだけ加工します。

func _on_start_button_pressed():
	if %StartButton.text == "リトライ":
		get_tree().reload_current_scene()
		return
	%TitleLabel.hide()
	%StartButton.hide()
	playing = true
	$SpawnTimer.start()

get_tree().reload_current_scene() は「現在のシーンを最初から読み込み直す」命令です。ゲームを実行した直後の状態に完全に戻ります。return は「この関数をここで終わる」という命令です。

リロード後はタイトル画面に戻るので、もう一度スタートを押せば2周目です。「リトライで即ゲーム再開」にしたい人は、改造課題としてぜひ挑戦してみてください(ヒント: 起動直後かどうかをフラグで区別します)。

完成!

実行してみてください。タイトル → スタート → プレイ → 爆発音とともにGAME OVER → リトライ。ゲームが1本、完成しました。

第1回の自分を思い出してください。シーンとノードという言葉も知らなかったところから、あなたはここまで来ました。

連載で学んだことの全リスト

  • Godotの構造: シーン(部品)とノード(1機能)、シーンの量産(instantiate/add_child)、グループ
  • GDScript: 変数とフラグ、if 文、関数定義、文字列とフォーマット、_ready_processdelta
  • ゲームの基本要素: 入力(Input.get_vector)、当たり判定(Area2D)、時間管理(Timer)、UI(CanvasLayer)、音(AudioStreamPlayer)
  • Godot流の設計: シグナルで「叫ぶ側」と「聞く側」を分ける、%固有名、queue_free で後片付け

卒業課題 — 自分の手で改造しよう

ここからが本当のスタートです。お題を置いておきます。上から順に難しくなります。

  1. 難易度を調整する: スポーン間隔・速度・判定サイズを自分の好みに
  2. 時間とともに難しくする: スコアに応じてWait Timeを短くしてみよう
  3. ハイスコアを覚える: 変数をもうひとつ足すだけでできます
  4. 隕石を撃てるようにする: 弾も「量産する部品」です。第4〜5回の知識で作れます
  5. アイテムを追加する: 取るとスコアが増える星など。第6回のグループ判定が活きます

次のステップ

  • 完成したゲームはブラウザで動く形に書き出して公開できます。Godotの「HTML5書き出し」を使えば、itch.ioなどに置いて誰でも遊べる形にできます
  • 今回はタイトルもゲームも1つのシーンに収めましたが、規模が大きくなるとタイトル画面を独立したシーンにして change_scene_to_file() で切り替えるのが一般的です(シーン遷移も今後のTips記事で扱います)
  • 連載で駆け足だったトピック(シグナル設計、%固有名、物理ボディの種類など)は、今後Tips記事として深掘りしていきます

完走おつかれさまでした。あなたはもう「Godotでゲームが作れる人」です。