8-4 CGameクラスの基本ルーチン

ここではCGameクラスの基本的なルーチンについて説明します。
以前説明した内容もありますがここではより詳しく説明しています。

■コンストラクタ

コンストラクタではゲームで使用する値を初期化します。
ここではステート値を初期値に設定し、その他の各変数は規定の値へセットします。

/////////////////////////////////////////////////////////////////////////
// コンストラクタ
/////////////////////////////////////////////////////////////////////////
CGame::CGame()
{
    bLostDevice     = FALSE;

    dwState         = G_INIT;
    fScrMulti       = 1.0f;
    llStartTime     = 0;
    llGlobalFreq    = 0;
    dElapsedTime    = 0;
    iImageID        = 0;
    ZeroMemory( &iStartNum,sizeof(iStartNum) );

    ZeroMemory( &bOnKey,sizeof(bOnKey) );
    iScratchStatus  = 0;
    ZeroMemory( &iFlashIndex,sizeof(iFlashIndex) );
    ZeroMemory( &iFlashCount,sizeof(iFlashCount) );
    ZeroMemory( &iBackKeyCount,sizeof(iBackKeyCount) );
}


■デストラクタ

デストラクタではロードした画像やサウンドを全てクリアするための関数を呼び出していますが、
実はこれらの関数を明示的に呼ばなくても問題ありません。

これはデストラクタからリターンする手前でCGameクラス内に定義した
各クラスのデストラクタも自動的に呼ばれるためです。

それぞれのクラスライブラリのデストラクタで自分自身の終了処理を行っているので、
あえてCGameのデストラクタで終了を呼び出さなくても、各クラス個別に勝手に終了してくれます。

/////////////////////////////////////////////////////////////////////////
// デストラクタ
/////////////////////////////////////////////////////////////////////////
CGame::~CGame()
{
    Clear();
}

※Clearの中身は後ほど説明します

■初期化&ゲーム形成

ここではゲームの全体的な初期化を行います。

まずはウインドウの作成、次にDirect3DやDirectSound、そしてDirectInputの初期化を行います。
これらの初期化が完了したら最後にマウスカーソルを消してゲームの処理を開始します。

///////////////////////////////////////////////////////
// 初期化&ゲーム形成
///////////////////////////////////////////////////////
BOOL CGame::Init( HINSTANCE hinst )
{
    // ウインドウ生成
    win.SetWindowStyle( WS_POPUP );                 // 枠無しウインドウ
    win.SetIcon( MAKEINTRESOURCEW(IDI_ICON1) );     // アイコン設定
    if( !win.Create(hinst,L"BMSPlayerSample") ) {
        DEBUG( "Window create error\n" );
        return FALSE;
    }
    ImmAssociateContext( win.hWnd, NULL );          // IMEを出さないようにする

    // Direct3D生成
    // フルスクリーンの640x480の32bitカラーにセットする。
    // ※2つ目の引数をFALSEにするとウインドウモードに出来る
    if( !dd.Create(win.hWnd,TRUE,640,480,32,0,TRUE) ) {
        DEBUG( "Direct3D create error\n" );
        return FALSE;
    }

    // DirectSound生成
    if( !ds.Create(win.hWnd) ) {
        DEBUG( "DirectAudio create error\n" );
        return FALSE;
    }

    // DirectInput生成
    if( !di.Create(win.hWnd,win.hInstance) ){
        DEBUG( "DirectInput生成失敗\n" );
        return FALSE;
    }

    // キーボードを使う
    if( !di.CreateKeyboard() ) {
        DEBUG( "キーボード使用不可\n" );
//      return FALSE;               // キーボードが使用できなくても起動可能とする
    }

    // ジョイスティックを使う
    if( !di.CreateJoystick() ) {
        DEBUG( "ジョイスティック使用不可\n" );
//      return FALSE;               // ジョイスティックが使用できなくても起動可能とする
    }

    // マウスカーソルOFF
    win.ShowCursor( FALSE );

    return TRUE;
}


この関数が完了すると画面はフルスクリーンとして起動します。
もしウインドウモードにしたい場合は上記の
赤字をFALSEにして下さい。
※詳しくはCDDPro90クラスのマニュアルを参照

ウィンドウ生成前にSetIconにてアプリアイコンをセットしていますが、
このアイコンはプロジェクトのリソースとして登録されている必要があります。
もしアイコンが無い場合はSetIcon部分をコメント化して下さい。

■ロード済みデータの開放

Clearではロードしたテクスチャやサウンドを明示的に開放しています。

本来は明示的に開放する関数は必要はありませんが、
ここではメモリの効率化を行うため明示的に開放出来る関数を用意しました。

///////////////////////////////////////////////////////
// ロード済みデータの全開放
///////////////////////////////////////////////////////
BOOL CGame::Clear( void )
{
    ds.Clear();
    dd.Clear();
    bms.Clear();
    return TRUE;
}


DirectSoundとDirect3Dの各クラスのClearでは根幹となるオブジェクトは開放されません。
これはあくまでもロードした画像やサウンドのみを開放するだけとなります。

■公開関数Runの定義

この関数はいわゆるプログラムのメインループを行います。
そしてゲームの処理はすべてこの関数が管理することになります。

これはコーディングページで説明した内容そのものですが、
ここではこれをクラス内で実行しているだけです。

/////////////////////////////////////////////////////////////////////////
// ゲームメインルーチン
/////////////////////////////////////////////////////////////////////////
BOOL CGame::Run( HINSTANCE hinst )
{
    // ゲームメインループ
    MSG msg;
    BOOL bLoop=TRUE;
    while( bLoop ) {
        if( PeekMessage(&msg,NULL,0,0,PM_REMOVE) ) {
            if( msg.message==WM_QUIT ) {
                bLoop = FALSE;
                DEBUG( "WM_QUIT\n" );
                break;
            }
            TranslateMessage( &msg );
            DispatchMessage( &msg );
        }

        // メインゲーム処理分け
        switch( dwState )
        {
        case G_INIT:
            // 初期化
            if( !Init(hinst) ) {
                dwState = G_END;
                break;
            }
            dwState = G_MAININIT;
            break;

        case G_MAININIT:
            // メインゲーム初期化
            if( !GameInit() ) {
                dwState = G_END;
                break;
            }
            dwState = G_MAIN;
            break;

        case G_MAIN:
            // メインゲーム
            switch( GameRun(FALSE) )
            {
            case -1:
                // ESCAPE
                dwState = G_END;
                break;
            case 1:
                // ゲーム終了
                dwState = G_END;
                break;
            }
            break;

        case G_END:
            // 終了処理
            Clear();
            bLoop = FALSE;
            break;

        default:
            // 未定義のステート
            DEBUG( "異常終了\n" );
            return FALSE;
        }

        Sleep( 3 );
    }

    // マウスカーソルON
    win.ShowCursor( TRUE );
    win.Delete();

    // プログラム正常終了
    return TRUE;
}


この関数の動作について詳しく説明すると、まずはプログラムを起動した直後はステートが
G_INITの状態になっているため、最初のループでInitが実行されることになります。

この時エラーがあればステートをG_ENDとして終了処理を行うようにしますが、
成功だった場合はG_MAININITステートとなります。

G_MAININITとなった場合は次のループでGameInitを呼び出してゲームの初期化を行います。
これに失敗した場合はステートをG_ENDに設定してゲームを終了させますが、
成功した場合はG_MAINに設定します。

ステートがG_MAINの時はGameRunを呼び出してゲーム処理を行いますが、
これがエラーもしくは終了を返した時点でG_ENDステートに設定します。
つまりG_MAINステートがゲームのメインステートとなり、
曲終了などでゲームが終わるまでこのステートが続きます。

最後にG_ENDステートだった場合は明示的に開放処理を行い、
同時にbLoop変数をFALSEにセットすることでこれでwhileループから脱出し、
最後にマウスカーソルを表示してRunからリターンすることでアプリが終了します。


例えば曲が終了したあと再びもう一度ゲームを始めたい場合、
GameRunが終了を返した時にステートをG_ENDではなくG_MAININITに設定することで、
再びゲームの初期化が行われれるのでもう一度遊ぶことが出来るようになります。
※ここではGameRunについてはまだ詳しく説明しませんが、
 この関数は正常ループ時は0、ESCキーが押された場合は-1、曲が終了したら1を返します


このサンプルでは毎フレーム3ミリ秒のウエイトを置いていますが、
もし処理が間に合わない場合など画面がカクカクしたように見える場合は、
このSleep値を下げたりSleep自体をコメント化するなどして対応下さい。


ひとまずこれでゲームの根幹部分は整ったので、次は実際にメインゲーム部分について説明していきます。