シーンの遷移(Stateパターン)
シーンクラス
まず基底となるISceneクラスを定義します。
/** * シーン基底クラス。 * コイツをインプリするのだ! */ class IScene { public: IScene(); virtual ~IScene(); virtual void Render() = 0; // シーンの描画 };
virtual void Render() = 0;
っていうのが、仮想関数の宣言で、
この宣言によりISceneは仮想クラスになります。
そしてこれを継承して、各シーンクラスを実装します。
/** * タイトルシーン(タイトル画面) */ class CSceneTitle : IScene { public: CSceneTitle(); ~CSceneTitle(); void Render(); };
シーンを使うクラス
そして、このシーンを利用するCGameクラスを定義します。
/** * ゲーム管理クラス。 */ class CGame { private: static CGame m_singleton; static IScene *m_pScene; // シーンオブジェクト CGame(); ~CGame(); public: // シーン列挙型 enum __SCENE { TITLE, // タイトル画面 MODE_SELECT, // モードセレクト画面 GAME_MAIN, // ゲームメイン画面 RESULT, // 結果画面 }; static void ChangeScene(__SCENE scene); // シーンの遷移 static void Render(); // シーンの描画 };
CGameはゲームを管理するオブジェクトで、
1つだけ存在すればよいのでシングルトンにし、
スタティック関数のみで構成します。
CGame::Render()は、保持しているシーンオブジェクトのRender()を呼びます。
void CGame::Render() { m_pScene->Render(); }
シーン遷移のCGame::ChangeScene()は、例えば以下のように実装します。
void CGame::ChangeScene(__SCENE scene) { static CSceneTitle title; static CSceneModeSelect mode_select; static CSceneGameMain game_main; static CSceneResult result; switch(scene) { case TITLE: m_pScene = &title; break; case MODE_SELECT: m_pScene = &mode_select; break; case GAME_MAIN: m_pScene = &game_main; break; default: //case RESULT: m_pScene = &result; break; } }
そうすると、シーンを遷移するときは、
// タイトル画面に遷移 CGame::ChangeScene(CGame::TITLE);
といった感じで、
いつでもシーンオブジェクトを入れ替えられますね。
このような設計を、
「状態」(ここではシーン)の変化により、
「状態」をあらわすクラスをごっそり入れ替えることから、
Stateパターンと呼びます。
補足
ゲームの規模によっては、シーンの遷移が複雑になることがあります。
今回のような方法は、シーンをjump,jumpするようなものでgoto的なフローになりがちです。
そこで、遷移が複雑になる場合には、
「シーンをスタックに積む」
という決まりを作ると、管理がやりやすくなります。
例)
>
↓タイトルをPush
>タイトル
↓セーブデータセレクトをPush
>タイトル>セーブデータセレクト
↓セーブデータセレクトをPopして、セーブポイントの街と教会をPush
>タイトル>街>教会
↓教会から出る
>タイトル>街
↓フィールドマップに移動
>タイトル>フィールドマップ
↓ダンジョンに潜る
>タイトル>フィールドマップ>ダンジョン
↓戦闘シーン突入
>タイトル>フィールドマップ>ダンジョン>戦闘