まとめ
- 前回設計したシーン管理を実装したのでそのメモ
はじめに
実装は C++.
一部 DXライブラリの API も呼び出しているが本質とは関係ない箇所のみ.
誰かの参考になれば.
完成コード
前回設計(というかざっくりした妄想)をした記事で,
だいたい説明もしてあるのだが,
一応軽く解説を挟んだ上で完成コードを載せてしまう.
Scene.h
ユーザーはこのクラスを継承して,
シーンクラスを定義する.
例は後述する.
TO_STRING() はクラス名を文字列に置換するためのマクロ.
型情報として size_t の Type を持つ.
実際の値は型名をハッシュ化したもの.
これにより,文字列指定でのシーン切り替えを実現する.
#pragma once
#include <string>
#define TO_STRING( x ) #x
class Scene
{
// カスタム rtti 用.
public:
static const std::size_t Type;
virtual bool IsClassType( const std::size_t classType ) const;
public:
Scene();
virtual ~Scene();
// 初期化.失敗する可能性のある処理を呼ぶので戻り値は bool
virtual bool Initialize() = 0;
// 更新
virtual void Update() = 0;
// 終了.解放など.
virtual void Finalize() = 0;
private:
};
上記の実装.
Scene.cpp
#include "Scene.h"
const std::size_t Scene::Type = std::hash<std::string>()(TO_STRING(Scene));
Scene::Scene()
{
}
Scene::~Scene()
{
}
bool Scene::IsClassType( const std::size_t classType ) const
{
if ( classType == Scene::Type )
{
return true;
}
return false;
}
SceneManager.h
シーン管理クラス.
追加タイプとして
- 切り替え
- 加算
を切り替えるための enum を持つ.
全てのシーン情報を持つ m_scenes リスト.
そのリストにシーンを登録する RegisterScene().
ロード予定のシーンを持つ m_loadScenes.
アンロード予定のシーンを持つ m_unloadScenes.
実行予定のシーンを持つ m_currentScenes.
#pragma once
#include <list>
#include "Scene.h"
#include <memory>
class SceneManager
{
public:
enum AddType
{
Single,
Add,
};
static bool Initialize();
static bool Update();
static bool Finalize();
static SceneManager* GetInstance();
template< class T >
static bool RegisterScene();
static bool LoadScene( std::string sceneName, AddType type = Single );
static bool UnLoadScene( std::string sceneName);
private:
SceneManager();
~SceneManager();
std::list< std::unique_ptr<Scene> > m_scenes;
std::list< std::pair< Scene*, bool> > m_loadScenes;
std::list< Scene* > m_unloadScenes;
std::list< Scene* > m_currentScenes;
static SceneManager *m_instance;
AddType m_addType;
};
template<class T>
inline bool SceneManager::RegisterScene()
{
T* scene = new T;
if( !scene )
{
return false;
}
m_instance->m_scenes.emplace_back( (scene ) );
return false;
}
ScemeManager.cpp
上記の実装.
なにやら Update () あたりが長くなったがやってることは単純.
#include "SceneManager.h"
#include "EntityManager.h"
SceneManager *SceneManager::m_instance;
SceneManager::SceneManager()
{
}
SceneManager::~SceneManager()
{
}
bool SceneManager::Initialize()
{
m_instance = new SceneManager();
return false;
}
bool SceneManager::Update()
{
// カレントを全更新
for each( auto&& scene in m_instance->m_currentScenes )
{
scene->Update();
}
// アンロード
if( m_instance->m_unloadScenes.size() != 0 )
{
for each( auto&& scene in m_instance->m_unloadScenes )
{
// イテレート中に要素削除をするような場合には、
// 範囲for文は使用できない
for( auto it = m_instance->m_currentScenes.begin(); it != m_instance->m_currentScenes.end();)
{
if( scene == *it )
{
scene->Finalize();
it = m_instance->m_currentScenes.erase( it );
}
else
{
++it;
}
}
}
m_instance->m_unloadScenes.clear();
}
// 新しくシーンを切り替え
if( m_instance->m_loadScenes.size() != 0 )
{
// 一つのシーンを新規追加なので finalize を呼ぶ
if( m_instance->m_addType == Single )
{
for each( auto&& c in m_instance->m_currentScenes )
{
c->Finalize();
}
}
m_instance->m_currentScenes.clear();
// 初期化を呼ぶ
for each( auto&& c in m_instance->m_loadScenes )
{
m_instance->m_currentScenes.push_back( c.first );
if( c.second )
{
c.first->Initialize();
}
}
m_instance->m_loadScenes.clear();
}
return false;
}
bool SceneManager::Finalize()
{
delete m_instance;
return false;
}
SceneManager * SceneManager::GetInstance()
{
return m_instance;
}
bool SceneManager::LoadScene( std::string sceneName, AddType type )
{
for each( auto&& scene in m_instance->m_scenes )
{
if( scene->IsClassType( std::hash< std::string >()( sceneName ) ) )
{
if( type == Add )
{
for each( auto&& c in m_instance->m_currentScenes )
{
m_instance->m_loadScenes.push_back( std::pair<Scene*, bool>( c, false ) );
}
m_instance->m_loadScenes.push_back( std::pair<Scene*, bool>( scene.get(), true ) );
m_instance->m_addType = Add;
}
else if( type == Single )
{
m_instance->m_loadScenes.clear();
m_instance->m_loadScenes.push_back( std::pair<Scene*, bool>(scene.get(), true ) );
m_instance->m_addType = Single;
}
return true;
}
}
return false;
}
bool SceneManager::UnLoadScene( std::string sceneName )
{
for each( auto&& scene in m_instance->m_scenes )
{
if( scene->IsClassType( std::hash< std::string >()( sceneName ) ) )
{
m_instance->m_unloadScenes.push_back( scene.get() );
return true;
}
}
return false;
}
利用コード
ShootingScene.h
Scene を継承して ShootingScene クラスを作成.
Initialize () でリソース読み込み,
Finalize() でリソース解放.
Update () 内にシーンの処理を実装する.
#pragma once
#include <../Source/Engine/Scene.h>
class ShootingScene : public Scene
{
public :
ShootingScene();
~ShootingScene();
bool Initialize();
void Update();
void Finalize();
private:
public:
static const std::size_t Type;
virtual bool IsClassType( const std::size_t classType ) const;
};
ShootingScene.cpp
上記の実装.
説明上不要なコードも混じっているが気にしない.
#include "ShootingScene.h"
#include "GameManager.h"
#include "DxLib.h"
#include "SceneManager.h"
#include "EntityManager.h"
#include "Input.h"
#include "Enemy.h"
#include "Player.h"
#include "ResultScene.h"
#include "ResourceManager.h"
#include "EntityManager.h"
const std::size_t ShootingScene::Type = std::hash<std::string>()(TO_STRING(ShootingScene));
ShootingScene::ShootingScene()
{
}
ShootingScene::~ShootingScene()
{
}
bool ShootingScene::PreInitialize()
{
return true;
}
bool ShootingScene::Initialize()
{
ResourceManager::GetInstance()->LoadGraphics( "Resources/Enemy.png" );
ResourceManager::GetInstance()->LoadGraphics( "Resources/MyShip.png" );
ResourceManager::GetInstance()->LoadGraphics( "Resources/Shot.png" );
for( int i = 0; i < 30; i++ )
{
auto e = Instanciate<Enemy>();
e->SetPosition( GetRand(500) + 32, -40*i );
}
{
Instanciate<Player>();
}
}
void ShootingScene::Update()
{
if( GameManager::GetKillNum() >= 30 ||
GameManager::GetIsDeath() == 1 ||
GameManager::GetFrame() >= 900 )
{
SceneManager::LoadScene( "ResultScene" );
}
if( !GameManager::m_isMenuOpen )
{
if( Input::GetInstance()->GetKeyState( KEY_INPUT_SPACE ) == 1 )
{
GameManager::m_isMenuOpen = true;
SceneManager::LoadScene( "InGameMenuScene", SceneManager::Add );
}
// フレーム加算
{
int frame = GameManager::GetFrame();
++frame;
GameManager::SetFrame( frame );
}
}
else
{
if( Input::GetInstance()->GetKeyState( KEY_INPUT_SPACE ) == 1 )
{
GameManager::m_isMenuOpen = false;
SceneManager::UnLoadScene( "InGameMenuScene");
}
}
}
void ShootingScene::Finalize()
{
ResourceManager::GetInstance()->ReleaseGraphics( "Resources/Enemy.png" );
ResourceManager::GetInstance()->ReleaseGraphics( "Resources/MyShip.png" );
ResourceManager::GetInstance()->ReleaseGraphics( "Resources/Shot.png" );
EntityManager::Clear();
}
bool ShootingScene::IsClassType( const std::size_t classType ) const
{
if ( classType == ShootingScene::Type )
return true;
return false;
}
main.cpp
シーン管理関連のみ抜粋.
...
int main()
{
...
SceneManager::Initialize();
// シーンを登録
SceneManager::RegisterScene<TitleScene>();
SceneManager::RegisterScene<ShootingScene>();
SceneManager::RegisterScene<ResultScene>();
SceneManager::LoadScene( "GameMainScene" );
// メインループ
while( ProcessMessage() == 0 )
{
...
SceneManager::Update();
...
}
...
SceneManager::Finalize();
...
}
...
おわりに
シーン管理の実装の雰囲気は伝わるかな.
説明が足りない場所や
ミスがあればコメントで指摘ください.