第07回 ゲームの状態と終了条件
1 / 4 ゲーム状態の定義と処理の分離
このステップの目的
画面の種類を列挙型Stateで定義し、現在の状態に応じて実行する更新・描画処理を切り替えられるようにします。
状態を enum class で型安全に定義する
画面の種類を0や1などの整数、または"Title"や"Playing"などの文字列で管理すると、不正な値や入力ミスによるバグが起こる危険があります。
C++では、値を限定できるスコープ付き列挙型(enum class)で状態を定義するのが標準的です。
Game.hcpp
class Game
{
public:
// ゲームの各画面状態を表す列挙型
enum class State
{
Title, // タイトル画面
Playing, // ゲームプレイ中
GameClear, // クリア結果画面
GameOver // ゲームオーバー画面
};
private:
// 現在のゲーム状態(初期状態はタイトル画面)
State state = State::Title;
};この定義では、コンパイラが型の整合性を確認します。意図しない数値の代入を防げます。
状態ごとの更新と描画の分離
これまでGame::Update()とGame::Draw()に書いていたプレイ中の処理を、それぞれUpdatePlaying()とDrawPlaying()へ移します。
Game.hcpp
private:
void UpdatePlaying(float deltaTime);
void DrawPlaying() const;
void UpdateTitle();
void DrawTitle() const;
void UpdateResult();
void DrawResult() const;メインのUpdate()とDraw()では、現在のstateに応じてswitch文で関数を呼び分けます。
Game.cppcpp
void Game::Update(float deltaTime)
{
switch (state)
{
case State::Title:
UpdateTitle();
break;
case State::Playing:
UpdatePlaying(deltaTime);
break;
case State::GameClear:
case State::GameOver:
UpdateResult();
break;
}
}Game.cppcpp
void Game::Draw() const
{
switch (state)
{
case State::Title:
DrawTitle();
break;
case State::Playing:
DrawPlaying();
break;
case State::GameClear:
case State::GameOver:
DrawPlaying();
DrawResult();
break;
}
}状態に応じた処理の完全な独立
この構造により、各画面の役割が明確に分離されます。
状態(State) | 更新処理(Update)の役割 | 描画処理(Draw)の役割 | 待ち受ける主な入力 |
|---|---|---|---|
Title | 開始入力の確認 | タイトルロゴや操作案内の描画 | 決定キーでゲーム開始 |
Playing | 自機、弾、敵、命中判定、制限時間 | 宇宙空間、自機、敵、弾、HUD | 移動キー、射撃キー |
GameClear | 結果画面の入力判定 | プレイ画面を背景にしたクリア文字 | リトライ、タイトルへ戻る |
GameOver | 結果画面の入力判定 | プレイ画面を背景にしたゲームオーバー文字 | リトライ、タイトルへ戻る |
タイトル画面や結果画面ではUpdatePlaying()を呼びません。そのため、背後で敵が出現したり、自機が動いたりすることはありません。
チェックリスト
- ゲームの画面状態が
enum class Stateとして定義されている - プレイ中の処理が
UpdatePlaying()とDrawPlaying()へ抽出されている -
Game::Update()とGame::Draw()のswitch文によって、現在の状態に応じた処理のみが実行されている - タイトル画面でプレイ中の敵や弾が更新されない構造になっていることを説明できる