前回までのシーン遷移を図にすると、↓のようになります。
タイトルシーンなどもっと付け加えられるシーンもあると思います。
しかし状況によっては、↓のようなシーンを作らなければならないケースも出てきます。
ゲーム中に「メニュー」を出し、「メニュー」での処理が終わったら「ゲームに戻る」と言うイメージです。
これまでのシーンの遷移では、シーンが変われば前のシーンは削除され、共用するデータ以外は残らないよう作ってあります。
上の例で説明すると、「ゲームシーン」の途中で「メニューシーン」に移行した場合、「ゲームシーン」独自の変数などは全て削除され、新しく「メニューシーン」が実行されます。
再度「メニューシーン」から「ゲームシーン」へ移行した時も、新しく「ゲームシーン」が作られて実行されます。
「ゲームシーン」独自の変数などは、ここで再度初期化されてしまいますので、ゲームを途中まで進めていた場合など最初に戻ってしまうなどの不都合な状況が発生します。
これを防ぐには「ゲームシーン」で必要なデータを、全て共用データにするしかありません。
しかし、共用データが肥大する事はあまり得策とは言えません・・・
そこで「サブシーン」と言う機能を使う事にします。
まずはフォントを追加しますので、「GameBase.h」を開き、定数を追加します。
//-----------------------------------------------------------------------------
//ゲーム中で使用するテクスチャ、BGM、SE、フォントのパス付ファイル名を書きます。
//-----------------------------------------------------------------------------
namespace KeyString
{
const std::string FONT_GOTHIC = "MS ゴシック";
}
|
「SceneGame.cpp」を開き、「Start関数」で追加、「Exit関数」で解放を追加します。
//=============================================================================
// シーンの実行時に1度だけ呼び出される開始処理関数
//=============================================================================
void SceneClear::Start()
{
m_pEngine->AddFont(FONT_GOTHIC, 20);
}
|
//=============================================================================
// シーンの終了時に呼び出される終了処理関数
//=============================================================================
void SceneClear::Exit()
{
m_pEngine->ReleaseFont(FONT_GOTHIC);
}
|
次に「SceneGame.h」を開き、privateに変数を1つ追加します。
private: int counter; }; |
「SceneGame.cpp」を開き、「Start関数」で初期化します。
//============================================================================= // シーンの実行時に1度だけ呼び出される開始処理関数 //============================================================================= void SceneGame::Start() { m_pEngine->AddFont(FONT_GOTHIC, 20); counter = 0; } |
「Update関数」でcounterをインクリメントします。
//=============================================================================
// シーンの実行時に繰り返し呼び出される更新処理関数
//=============================================================================
void SceneGame::Update()
{
counter++;
}
|
「Draw関数」でcounterの中身を表示します。
//=============================================================================
// シーンの実行時に繰り返し呼び出される描画処理関数
//=============================================================================
void SceneGame::Draw()
{
m_pEngine->DrawPrintf(0, 0, FONT_GOTHIC, D3DCOLOR_XRGB(255, 255, 255), "Counter = %d", counter);
}
|
<実行結果 クライアント領域のみ>
カウンタが増加するのが分かります。
サブシーンとして「メニューシーン」を作ります。
何か画像を表示するのではなく、文字で「メニュー画面」と表示するだけにします。
手順は前回、前々回と書いていますので、箇条書きで説明しますから皆さんでシーンを追加してください。
1.プロジェクトフォルダの「Sourceフォルダ」→「Sceneフォルダ」を開く。 2.「_SceneSourceフォルダ」を複製する。 3.フォルダ名を「SceneMenu」に変更する。 4.「SceneMenuフォルダ」を開き、「SceneMenu.h」「SceneMenu.cpp」に名前を変更する。 5.Visual Studioにて「Sceneフィルタ」で右クリック、「追加」→「新しいフィルタ」を選択する。 6.フィルタ名を「SceneMenu」に変更する。 7.「SceneMenuフィルタ」で右クリック、「追加」→「既存の項目」を選択する。 8.「SceneMenuフォルダ」を開き、「SceneMenu.h」「SceneMenu.cpp」を選択する。 |
最終的に↓のようになっていれば良いです。
「SceneMenu.h」「SceneMenu.cpp」の内容も変更しておいてください。
SceneMenu.h
//**************************************************************************** // // メニューシーン // //***************************************************************************** #pragma once #include "..\\..\\GameBase.h" #include "..\\Scene\\Scene.h" class SceneMenu : public Scene { public: //============================================================================= // コンストラクタ // 引 数:Engine* エンジンクラスのアドレス //============================================================================= SceneMenu(Engine *pEngine); //============================================================================= // デストラクタ //============================================================================= ~SceneMenu(); //============================================================================= // シーンの実行時に1度だけ呼び出される開始処理関数 //============================================================================= void Start(); //============================================================================= // シーンの実行時に繰り返し呼び出される更新処理関数 //============================================================================= void Update(); //============================================================================= // シーンの実行時に繰り返し呼び出される描画処理関数 //============================================================================= void Draw(); //============================================================================= // シーンの終了時に呼び出される終了処理関数 //============================================================================= void Exit(); private: //============================================================================= // このシーンでのみ使用するデータを宣言してください。 //============================================================================= }; |
SceneMenu.cpp
//**************************************************************************** // // メニューシーン // //***************************************************************************** #define _USING_V110_SDK71_ 1 #include "SceneMenu.h" using namespace KeyString; using namespace InputKey; //============================================================================= // コンストラクタ // 引 数:Engine* エンジンクラスのアドレス //============================================================================= SceneMenu::SceneMenu(Engine *pEngine) : Scene(pEngine) { } //============================================================================= // デストラクタ //============================================================================= SceneMenu::~SceneMenu() { Exit(); } //============================================================================= // シーンの実行時に1度だけ呼び出される開始処理関数 //============================================================================= void SceneMenu::Start() { } //============================================================================= // シーンの実行時に繰り返し呼び出される更新処理関数 //============================================================================= void SceneMenu::Update() { } //============================================================================= // シーンの実行時に繰り返し呼び出される描画処理関数 //============================================================================= void SceneMenu::Draw() { } //============================================================================= // シーンの終了時に呼び出される終了処理関数 //============================================================================= void SceneMenu::Exit() { } |
「SceneMenu.cpp」の「Draw関数」で文字を表示します。
//=============================================================================
// シーンの実行時に繰り返し呼び出される描画処理関数
//=============================================================================
void SceneMenu::Draw()
{
m_pEngine->DrawPrintf(0, 0, FONT_GOTHIC, D3DCOLOR_XRGB(255, 255, 255), "メニュー画面");
}
|
さらにシーン遷移のための準備も行います。
「GameData.h」を開き、シーン定数を追加します。
//----------------------------------------------------------------------------- // シーンを追加した際に定数を追加します。 //----------------------------------------------------------------------------- enum { SCENE_INIT, SCENE_GAME, SCENE_MENU, SCENE_EXIT, }; |
「Game.h」を開き、ヘッダファイルをインクルードします。
//----------------------------------------------------------------------------- //シーンクラスのヘッダファイルをインクルードします。 //----------------------------------------------------------------------------- #include "Scene\\Scene\Scene.h" #include "Scene\\SceneInit\\SceneInit.h" #include "Scene\\SceneGame\\SceneGame.h" #include "Scene\\SceneMenu\\SceneMenu.h" |
「Game.cpp」を開き、シーンの切り替えコードを追加します。
//----------------------------------------------------------------------------- // シーン切り替え //----------------------------------------------------------------------------- bool Game::ChangeScene() { switch (Scene::m_nowSceneData.m_scene) { //↓ここに追加 case Common::SCENE_GAME: m_pNowScene = new SceneGame(m_pEngine); break; case Common::SCENE_MENU: m_pNowScene = new SceneMenu(m_pEngine); break; case Common::SCENE_EXIT: return false; } return true; } |
これで準備OKです。
まずは「サブシーン」としてではなく、通常のシーンとして遷移させてみます。
「SceneGame.cpp」を開き、「Update関数」に次のコードを追加します。
//============================================================================= // シーンの実行時に繰り返し呼び出される更新処理関数 //============================================================================= void SceneGame::Update() { if (m_pEngine->GetKeyStateSync(DIK_M)) { m_nowSceneData.Set(Common::SCENE_MENU, false, NULL); } counter++; } |
この状態で1回動かしてみましょう。
ゲームシーンで「Mキー」を押してメニューシーンに移行します。
<実行結果>
止まってしまいました・・・
エラーメッセージには「フォントが見つかりません」と書いてあります。
上に書いた通り、通常のシーン遷移では、元のシーンはいったん終了し削除されます。
「SceneGame.cpp」の「Exit関数」でフォントは解放されていますので、シーンが変わると使えません。
そこで、一時的にフォントの解放を無効にしましょう。
//============================================================================= // シーンの終了時に呼び出される終了処理関数 //============================================================================= void SceneGame::Exit() { #if 0 m_pEngine->ReleaseFont(FONT_GOTHIC); #endif } |
もう1度実行してみてください。
<実行結果 クライアント領域のみ>
ちゃんとシーンが移行されました。
では、元のゲームシーンに戻るようにします。
「SceneMenu.cpp」の「Update関数」にコードを追加しましょう。
//=============================================================================
// シーンの実行時に繰り返し呼び出される更新処理関数
//=============================================================================
void SceneMenu::Update()
{
if (m_pEngine->GetKeyStateSync(DIK_M)) {
m_nowSceneData.Set(Common::SCENE_GAME, false, NULL);
}
}
|
メニューシーンで「Mキー」を押すと、ゲームシーンに戻るようにしました。
実行するとどうなるか試してください。
<実行結果 クライアント領域のみ>
最初のゲームシーン
メニューシーン
メニューシーンからゲームシーンへ切り替え
ゲームシーンに戻った時に、counterは最初から数を数え始めています。
ゲームシーンは「リセット」されたと言う事です。
では、サブシーンとして遷移させると、どのように違う結果になるかを試します。
「SceneGame.cpp」を開き「Update関数」を書き換えます。
//============================================================================= // シーンの実行時に繰り返し呼び出される更新処理関数 //============================================================================= void SceneGame::Update() { if (m_pEngine->GetKeyStateSync(DIK_M)) { m_nowSceneData.Set(Common::SCENE_MENU, true, this); } counter++; } |
シーンを切り替える際に使うm_nowSceneData.Set関数の第2引数を「true」にすると「サブシーン」として実行される事になります。
第3引数は戻ってくるシーンのアドレスを渡しますので、自分自身(ゲームシーン)のアドレス「this」を指定します。
これで「ゲームシーン」は実行途中の状態が保管され、「メニューシーン」がサブシーンとして呼ばれます。
「メニューシーン」を終了して「ゲームシーン」に戻ると続きを実行します。
実際に試してみましょう。
<実行結果 クライアント領域のみ>
最初のゲームシーン
メニューシーン
メニューシーンからゲームシーンへ切り替え
「ゲームシーン」に戻ると、最初のシーンの続きを数え始めます。
下の図のように「サブシーン」が呼び出されたシーン以外に戻る事は避けてください。
下のように「サブシーン」から「サブシーン」を呼び出す事も可能です。
ただし、「ゲームシーン」独自の変数などは「メニューシーン」には渡されません。
シーンを超えて共有するデータは「GameData.h」で構造体にまとめてください。