第07回 ゲームの状態と終了条件
2 / 4 状態遷移の集約と終了条件の判定
このステップの目的
状態変数の直接書き換えを避け、ChangeState()に遷移処理を集約して安全な画面切り替えを実装します。
状態変更を ChangeState() に集約する
各所でstate = State::GameOver;やstate = State::Playing;と直接書き換えると、次の問題が起きます。
ゲームオーバー時のBGM再生やクリア時のスコア保存など、状態が変わる際の処理を、各所のif文の後に重複して書く必要があります。
予期しない状態遷移が起きたときも、どこで状態が変わったのかを特定しにくくなります。
そこで、状態の変更はすべてChangeState()を通して行います。
Game.cppcpp
void Game::ChangeState(State nextState)
{
state = nextState;
}変更箇所を一つにまとめると、第09回で音声を追加するときも制御しやすくなります。この関数に、タイトルに入ったらタイトルBGM、ゲームオーバーに入ったらゲームオーバーSEを再生する処理を追加できます。
プレイ中の終了条件判定
プレイ中の更新処理(UpdatePlaying)の末尾で、勝敗の終了条件を判定してChangeState()を呼び出します。
Game.cppcpp
void Game::UpdatePlaying(float deltaTime)
{
timeLeft -= deltaTime;
if (timeLeft < 0.0f)
timeLeft = 0.0f;
player.Update(input, deltaTime);
playerShots.Update(deltaTime);
enemies.Update(deltaTime);
enemyShots.Update(deltaTime);
player.TryShoot(input, playerShots);
ResolveCollisions();
if (!player.IsAlive())
{
ChangeState(State::GameOver);
return;
}
if (score >= tuning::game::ClearScore || timeLeft <= 0.0f)
{
ChangeState(State::GameClear);
return;
}
// 発射時点のプレイヤー位置を、すべての敵が狙う位置として共有する
Vector2 playerPosition = player.GetHitbox().GetCenter();
for (Enemy& enemy : enemies.GetAll())
{
enemy.TryShoot(enemyShots, playerPosition);
}
}判定順序によるゲームルールの確定
HPが0かどうかを、クリア条件より先に判定する点が重要です。
同じフレームで、最後の弾が敵に当たって30点に達し、敵弾にも当たってHPが0になる相打ちが起きた場合、どちらを優先するかを決めます。
ゲームオーバー判定を先に行い、成立したらreturnで関数を抜けます。これで、クリア条件を満たしても自機が撃破されていればゲームオーバーになる、というルールになります。
チェックリスト
- 状態変更がすべて
ChangeState()を経由して行われている - プレイヤーの
!IsAlive()によってゲームオーバーへ遷移する - スコア条件または時間切れによってゲームクリアへ遷移する
- ゲームオーバー判定がクリア判定より先に評価されている理由を説明できる