というわけでさっそく実装テストしてみる。
ついでに状態管理の方法もいっしょにまとめる。
質問掲示板はあまり見ないのだけど最近見ていたら、
状態の切り替えに四苦八苦している人が結構いらっしゃるので自分なりの方法を一応書いてみます。
まずファイル読み込み、
ちょっと重いファイルをたくさんロードしてみます
// 画像リソース管理
struct GraphicResource {
private :
int m_handle;
int m_initialized;
const char* m_filepath;
public :
GraphicResource(){
m_handle = 0;
m_initialized = false;
m_filepath = NULL;
}
~GraphicResource(){
APP_ASSERT( !m_initialized );
}
// ロード処理
void load( const char* filepath ){
if( !filepath ) return;
SetUseASyncLoadFlag( TRUE ) // 非同期読み込みON
m_filepath = filepath; // ファイルパスを指定して
m_handle = LoadGraph( m_filepath ); // ロード開始
}
// ロードの同期をとる
bool loadSync(){
int result = CheckHandleASyncLoad( m_handle );
if( result == 1 ) return false; // 1は読み込み中らしい
if( result == 0 ){ // 0が返ってきたら正常終了
m_initialized = true;
return true;
}
// それ以外の値が返ってきたら異常である
APP_PRINT( "LoadGraph Failed [m_handle %d][m_filepath %s] \n" , m_handle , m_filepath );
return false;
}
// 終了処理
void finalize(){
if( m_initialized ){
m_initialized = false;
DeleteGraph( m_handle );
}
}
// 画像ハンドル取得
const int& getHandle(){
return m_handle;
}
};
という関数で非同期フラグを立てたり、
読み込み完了通知を知ることができるようです
すごい!
loadSync()はロードが完了するまでfalseを投げる関数。
完了したらtrueを投げます。
そして次にこれを振り回すシーンを作ります。
私がよく書くシーンのライフサイクルはこんな感じです。
// シーン管理
class Scene {
private :
unsigned int m_frametime;
int m_state;
int m_statenext;
bool m_isfirst;
public :
enum {
STATE_NONE = 0 ,
STATE_INITIALIZE ,
STATE_LOAD ,
STATE_SYNC ,
STATE_SETUP ,
STATE_PROCESS ,
STATE_NUM
};
Scene(){
m_frametime = 0;
m_state = STATE_NONE;
m_statenext = STATE_INITIALIZE;
m_isfirst = false;
}
virtual ~Scene(){
}
void changeState( int statenext ){
m_statenext = statenext;
}
virtual void update(){
if( m_statenext != m_state ){
m_state = m_statenext;
m_isfirst = true;
m_frametime = 0;
}
switch ( m_state ){
case STATE_INITIALIZE :
initialize();
changeState( STATE_LOAD );
break;
case STATE_LOAD :
load();
changeState( STATE_SYNC );
break;
case STATE_SYNC :
if( !sync() ) break;
changeState( STATE_SETUP );
break;
case STATE_SETUP :
setup();
changeState( STATE_PROCESS );
break;
case STATE_PROCESS :
process();
break;
}
m_isfirst = false;
m_frametime++;
}
virtual void initialize() {}
virtual void load(){}
virtual bool sync(){}
virtual void setup(){}
virtual void process(){}
};
virtual void initialize() {}
virtual void load(){}
virtual bool sync(){}
virtual void setup(){}
virtual void process(){}
は継承先での実装となる。
状態の切り替えはm_stateとm_statenextからコントロールする。
m_statenextに新しい状態がやってきた場合、変化タイミングとみなして初回フラグを立てる。
各ステートは切り替わり時の初回タイミングを手に入れることができます。
いわゆるトリガーですが、これを手に入れるタイミングってなかなかわかりづらいですよね。
initialize,load,setupは一回しか呼ばれない。それぞれのタイミングで必要な初期設定を行う
syncは読み込み同期が完了したと認識するまで動かない
processは更新処理、ゲームループです。
これらの処理は継承先で実装、基底のことは継承先は知らなくてもいいようにします。
// 画像読み込み処理サンプル
class MainScene : public Scene {
private :
GraphicResource m_gRes_1;
GraphicResource m_gRes_2;
GraphicResource m_gRes_3;
GraphicResource m_gRes_4;
public :
~MainScene(){
m_gRes_1.finalize();
m_gRes_2.finalize();
m_gRes_3.finalize();
m_gRes_4.finalize();
}
virtual void initialize(){
}
virtual void load(){
m_gRes_1.load( "img/01.png" );
m_gRes_2.load( "img/02.png" );
m_gRes_3.load( "img/03.png" );
m_gRes_4.load( "img/04.png" );
}
virtual bool sync(){
if( !m_gRes_1.loadSync() ) return false;
if( !m_gRes_2.loadSync() ) return false;
if( !m_gRes_3.loadSync() ) return false;
if( !m_gRes_4.loadSync() ) return false;
return true;
}
virtual void setup(){
}
virtual void process(){
DrawGraph( 0 , 0 , m_gRes_1.getHandle() , TRUE );
}
};
挙動を確認しています。
メインには大したことは書いていないので割愛。
とりあえず状態管理と非同期処理と同期をとる方法を書いてみる。