まとめ
- Unity っぽくシーン管理するための設計(というかまだ妄想)をしたのでそのメモ
そもそもシーン管理とは
要するに,
タイトル画面 → ゲーム画面 → リザルト画面
のような感じで,
各画面をシーンと呼び,
画面遷移を行うこと.
をシーン管理と呼ぶ.*1
本によっては「シーケンス」という呼ばれ方もしているが,
一般的には シーン と呼ばれることが多いように思う.
普通にやるだけなら switch で分岐すれば十分なのだけど,
大規模なゲームには複雑なシーン管理がつきもの*2.
そうなると switch では役不足.
あときれいな設計にしたいのがプログラマの性.
汎用エンジンではどのように解決しているかをまずは知る.
実現したいこと
Unity でのシーン管理
Unity では,各シーンがファイル化されている.
スクリプト上から 別のシーンを読み込む時は以下のようにする.
SceneManager.LoadScene("Hoge");
※文字列でシーンの名前を指定して,
LoadScene する.
また,シーンの追加読み込みに対応していて,
SceneManager.LoadScene("Hoge", LoadSceneMode.Additive);
とする.
これはメニューを別シーンで表現したり,
広大なマップの追加読み込みに使用したりする*3.
また,当然シーンのアンロードもできて,以下のようにする.
SceneManager.UnloadSceneAsync("Hoge");
忘れてはいけないのが,
スクリプト上からシーンを切り替えたいときには,
ビルド設定のシーン一覧に
あらかじめ呼び出し予定のシーンを
登録しておく必要がある.
実現したいこと
Unity のようなインターフェースで,
シーンの切り替えを実現したい.
つまり,
SceneManager::LoadScene("Hoge");
でシーン切り替え,
SceneManager::UnLoadScene("Hoge");
でシーンの解放,
SceneManager::LoadScene("Hoge", SceneManager::LoadSceneMode::Additive);
でシーンの追加読み込み.
を実現したい.
シーンのファイル化は今の所考えていないが,
コードのスクリプトコンポーネント化は行いたいので,
シーンの指定は文字列での指定としたい.
※リフレクションのような機能は使用しないため.
ざっくりした設計というか妄想
シーンの基底クラスとシーン管理クラスは必要そう.
- Scene
- SceneManager*4
Scene クラス
各シーンの基底となるクラス.
コイツを継承して,各 シーンクラスを実装する.
純粋仮想関数
Update()
を持ち,各シーンでの更新処理を行う.
SceneManager クラス
インターフェース
- LoadScene()
- UnLoadScene()
- Update()
を持つ.
また,内部に現在実行すべき Scene を持ち,
その Scene の Update() を呼ぶことで,
各シーンの更新処理を一手に担う.
追加読み込み用の enum
- Single,
- Additive
も持つ.
ざっくりした使用方針
Scene を継承したシーンクラスを定義する.
例えば,
class TitleScene : public Scene { void Initialize(); void Update(); void Finalize(); };
のようなイメージ.
これの Initialize(), Finalize() でリソース管理,
Update() 内で各シーンの更新処理を行う.
例えば,
void TitleScene::Update() { //エンターを押したら ShootingScene シーンに遷移する. if( CheckHitKey( KEY_INPUT_RETURN ) == 1 ) { SceneManager::LoadScene( "ShootingScene" ); } }
みたいな感じ.
これで各シーンが自身が必要なリソースも管理できるし,
追加読み込みによってシーンを階層構造とする必要もないしで,
設計がスッキリしそう.
ユーザーは,
SceneManager::Update() をゲームのメインループの中で呼ぶ.
SceneManager は,
現在実行すべきシーンの Update() 全てを呼ぶ.
全ての シーン は Scene を継承しているので問題なく可能.
実行すべき Scene は リストで持つのが妥当.
※ Unity のように複数シーンを同時に実行する可能性があるので.
つまり,こんな感じ
for each( auto&& scene in m_currentScenes ) { scene->Update(); }
おわりに
絶賛実装中.
実装できたら実装編を書く.