Appearance
第23回 トラップとゲーム進行
2 / 4 ゲーム状態と遷移を集約する
このステップの目的
タイトル・プレイ中・クリア・ゲームオーバーの4状態を GameState 列挙型で表現し、状態に応じた更新処理の切り替えと、遷移に伴う初期化や音響制御を ChangeState() に集約する方法を学びます。
状態ごとの更新責務の分離
ゲームの進行には、「入力待ち」「アクションプレイ」「結果表示」といった異なる局面が存在します。これらを秩序正しく制御するため、状態列挙型 GameState を定義します。
cpp
enum class GameState
{
Title,
Playing,
GameClear,
GameOver
};毎フレーム呼び出される Game::Update() では、現在の状態に応じて処理を分岐させます。
cpp
void Game::Update(float deltaTime)
{
UpdateDebugToggle();
switch (state_)
{
case GameState::Title:
if (input_.IsPressed(Key::Jump))
ChangeState(GameState::Playing);
break;
case GameState::Playing:
UpdatePlaying(deltaTime);
break;
case GameState::GameClear:
case GameState::GameOver:
if (input_.IsPressed(Key::Jump))
ChangeState(GameState::Playing);
else if (input_.IsPressed(Key::BackToTitle))
ChangeState(GameState::Title);
break;
}
}結果画面(クリアやゲームオーバー)の表示中は、UpdatePlaying() が呼ばれないため、プレイヤーや敵の物理運動が停止します。これにより、ゲームオーバー画面の裏でキャラクターが奈落へ落下し続けたり、敵が動き回って予期せぬ判定を起こしたりするのを防ぎます。
状態遷移と副作用の集約
ゲーム状態が切り替わる際には、単に状態変数を書き換えるだけでなく、多くの準備処理や終了処理(副作用)が連動して発生します。
Playingへの遷移: ステージの再配置(ResetStage)、メインBGMの再生開始GameClearへの遷移: BGMの停止、クリア効果音の再生GameOverへの遷移: BGMの停止、ゲームオーバー効果音の再生Titleへの遷移: タイトルBGMの再生開始
cpp
void Game::ChangeState(GameState nextState)
{
state_ = nextState;
switch (state_)
{
case GameState::Title:
sounds_.PlayBgm(SoundId::TitleBgm);
break;
case GameState::Playing:
ResetStage();
sounds_.PlayBgm(SoundId::PlayingBgm);
break;
case GameState::GameClear:
sounds_.StopBgm();
sounds_.PlaySe(SoundId::Goal);
break;
case GameState::GameOver:
sounds_.StopBgm();
sounds_.PlaySe(SoundId::GameOver);
break;
}
}散発的な状態変更による不具合の防止
もし「トゲに触れた判定」「敵にぶつかった判定」「落下判定」の各箇所で個別に state_ = GameState::GameOver; sounds_.StopBgm(); などを記述していると、修正や機能追加のたびに「BGMを止め忘れた」「効果音を鳴らし忘れた」という実装漏れが発生しやすくなります。
すべての状態変更を ChangeState(nextState) という唯一の関数を経由させるルールにすることで、どの経路から遷移しても必ず正しい後処理と初期化が保証されます。
チェックリスト
-
GameState列挙型を用いてタイトル、プレイ中、結果画面の更新処理を分離できる - 結果表示中にキャラクターの更新を停止させ、画面裏での不要な処理を防ぐ理由を説明できる
- 状態の書き換え、ステージリセット、BGM・SEの切り替えを
ChangeState()に集約する利点を理解できる