ことれいのもり

ゲームの画面や状態を切り替えるには?C++で学ぶ状態遷移と有限オートマトン

はじめに

ゲームやアプリのシステムに「今はこの状態で、次はこの状態」といった流れ(状態遷移)があります。

例えば、タイトル画面→ゲーム画面→結果画面という一連の切り替わり。

あるいは、コマンドバトルにおける「戦闘開始→味方のターン→敵のターン→...→バトル終了」といった進行も同じです。

こうした「状態の流れ」をプログラムで表現するにはどうしたらいいでしょうか。


実はこの仕組みは、数学では有限オートマトン(Final State Machine)と呼ばれたり、プログラミングの世界ではステート構造といった形でよく使われています。

名前は難しそうに聞こえるかもしれませんが、基本的にenumとswitch文で簡単に実現できます。


この記事ではそうした「状態を持った処理の書き方」について、はじめてこの考え方を知る人に向けて、概念の理解から具体的な実装までを解説します。

初めてこの考えを知った方でも分かるように説明するので、安心してください。


より深く知りたい人のために、次回の記事では、この仕組みを使って、実際のRPGのコマンドバトルを構築する方法を紹介する予定です。

「ステート構造」あるいは「有限オートマトン」とは?

「今はタイトル画面」「今は味方の攻撃ターン」、こうした今はどんな状態か? という情報のことを状態(State)と呼びます。

そして、状態がある条件の下で別の状態に変化することを状態遷移といいます。


例えばコマンドバトルでは、「味方ターン→敵ターン」と変化していきますよね。

このとき、「味方の攻撃が終了したら」という条件があり、これを満たすと別の状態「敵ターン」へ遷移します。

これはまさに状態遷移です。


こうしたいくつかの状態の中を、ルールに従って移動する仕組みを、数学では有限オートマトンと呼びます。

なんだか難しそうですが、実際には、

状態を列挙型で定義して、switch文で状態ごとの処理を書くだけというシンプルな構造で実現できます。

状態遷移図とは?

状態遷移図とは、「どの状態からどの状態に移れるか?」を矢印と線で視覚的に表現した図です。

数学の有限オートマトン等でよく使われます。

状態の流れを図として描くことで、「今どの状態にいて、どこへ遷移できるのか?」が明確になります。


次の画像は、タイトル画面→ゲーム画面→結果画面の流れを状態遷移図にしたものです。


状態遷移図


①最初はタイトル画面からはじまります。

②ボタンを押すとゲーム画面へ遷移します。

③ゲーム画面です。

④ゲームが終わると結果画面へ遷移します。

⑤結果画面では結果を表示します。

⑥結果画面でボタンを押すとタイトル画面へ遷移します。


このような図で表すことで、流れがすぐに理解できます。

状態をコードに落とし込む方法

状態の考え方が分かったところで、次はそれをどうやってプログラムに落とし込むか見ていきましょう。

今回はC++を例に解説しますが、他の言語でも同じ考え方が使えます。


まず、状態は「名前付きの値」として扱います。

C++では、enum classの列挙型を使って実装します。(他の言語の場合でも、「列挙型」と調べると同じような構文があります)


enum class GameState
{
    Title,  // タイトル画面
    Game,   // ゲーム画面
    Result  // 結果画面
};
GameState currentState;  // 現在のステート(状態)


このように状態を定義しておけば、現在の状態を変数に持たせるだけで、「今どの状態か」を明確に制御できます。

それぞれに応じた処理は、switch文で書くと分かりやすいです。


int main()
{
    switch (currentState)
    {
    case GameState::Title:
        // タイトル画面の処理
        break;
    case GameState::Game:
        // ゲーム中の処理
        break;
    case GameState::Result:
        // 結果画面の処理
        break;
    }
}


例えば現在がタイトルシーンの場合、switch文では「case GameState::Title内」の処理を毎回実行します。

状態を切り替えたいときは、関数を使って状態を変更できるようにしておくと便利です。


void ChangeState(GameState state)
{
    currentState = state;
}


この構造は、「有限オートマトン(Final State Machine)」と呼ばれる状態遷移の中でも最もシンプルな実装方法です。

難しい理屈を知らなくてもenumとswitchがあれば、状態管理の仕組みが実現できる、ということです。


コードの全体像はこちらです。


enum class GameState
{
    Title,  // タイトル画面
    Game,   // ゲーム画面
    Result  // 結果画面
};
GameState currentState = GameState::Title;;  // 現在のステート(状態)

void ChangeState(GameState state)
{
    currentState = state;
}

int main()
{
    switch (currentstate)
    {
    case GameState::Title:
        // タイトル画面の処理
        break;
    case GameState::Game:
        // ゲーム中の処理
        break;
    case GameState::Result:
        // 結果画面の処理
        break;
    }
}

おわりに

今回は「状態」という考え方をプログラムでどう扱うかについて紹介しました。

状態をenumで管理し、それに応じた処理をswitch文で切り替えることで、簡単に実装できることが分かっていただけたと思っています。


次回の記事では、今回の構造を使って、RPGのコマンドバトルの流れを状態遷移で制御する方法を紹介します。

概念は分かったけど、自分で実装するにはどう考えたら良いかわからない、という人におすすめの記事です。

よければ、ご覧ください。