8-7 GameInitルーチン

ここではメインゲームで使用する画像のロードやBMSの解析、
サウンドのロード処理などを行いゲーム開始直前までの処理を行います。

■ゲーム画像

ゲーム用の画像はアルファ付きの32bitTGAファイルとして用意します。
また画像1枚の中にゲームに使用する全ての画像を四角形区切りで格納しています。

ここではサンプルとして以下のような画像を使用します。

RGB
ALPHA


■画像のロード

まずはゲーム内で使用する画像の読み込みを行います。
ここではCDDPro90クラスのAddTextureを使って画像をロードします。

///////////////////////////////////////////////////////
// メインゲーム初期化
///////////////////////////////////////////////////////
BOOL CGame::GameInit( void )
{
    // 画像ロード
    if( !dd.AddTexture(0,"FILES\\BM.TGA") ) {
        DEBUG( "BM.TGA load error\n" );
        return FALSE;
    }


CDDPro90クラスのAddTextureは指定のインデックスに画像ファイルを割り当てます。

既にそのインデックスが使用されている場合は、内部でいったん開放してから新しいものが割り当てらるため、
明示的に開放する必要はありません。
詳しくはCDDPro90のリファレンスを確認してください。



■画像の切り抜き

次にこの画像から描画に使用する領域を切り抜きます。
切り抜きはだいたい以下のようなイメージとなります。

    // 背景
    dd.SetPutRange( 0,0,0,0,128,480 );      // 背景
    dd.SetPutRange( 1,0,224,0,256,256 );    // センターアニメーション

    // 小節ライン
    dd.SetPutRange( 2,0,292,258,126,2 );
 
    // キーを押したときの後ろの光
    dd.SetPutRange( 10,0,130,0,28,419 );    // 白
    dd.SetPutRange( 11,0,160,0,24,419 );    // 青
    dd.SetPutRange( 12,0,186,0,35,419 );    // 赤

    // オブジェ
    dd.SetPutRange( 15,0,130,422,28,6 );    // 白
    dd.SetPutRange( 16,0,160,422,24,6 );    // 青
    dd.SetPutRange( 17,0,186,422,35,6 );    // 赤

    // フラッシュ
    dd.SetPutRange( 20,0,224,258,60,50 );   // 白
    dd.SetPutRange( 21,0,224,308,52,50 );   // 青
    dd.SetPutRange( 22,0,224,358,68,50 );   // 赤

今回は全て1枚のビットマップから切抜きを行っているため、
2番目の引数は全て0となっています。

もし別な画像をさらにAddTextureでロードしている場合は、
ロードした際のインデックス番号を指定することで、いくらでも画像を追加することが出来ます。


■BMSデータのロード

これでゲーム上で表示するデータが一通りそろったので次はBMSファイルをロードします。

    // BMSロード
    if( !bms.Load("SAMPLE\\SAMPLE.BMS") ) {
        DEBUG( "SAMPLE\\SAMPLE.BMS error\n" );
        return FALSE;
    }


ここでは固定でSAMPLEフォルダ内のSAMPLE.BMSをロードしていますが、
このファイル名を変数として用意し、その変数にロードしたいファイル名を与えるようにすれば、
例えば曲選択画面で選択した曲をロードさせるといったことが出来ます。


■サウンドデータのロード

BMSのロードに成功したらこのクラスには解析済みの全情報が記録されているので、
次にこの曲が使用しているサウンドファイルとBGAファイルをロードします。

まずはWAVの読み込みです。

    // サウンドデータのロード
    int i;
    char buf[MAX_PATH];
    for( i=0;i<BMS_MAXBUFFER;i++ ) {
        // WAVファイル名が存在するなら
        if( strlen(bms.GetWavFile(i))>0 ) {
            // テクスチャファイル名の生成
            sprintf( buf,"SAMPLE\\%s",bms.GetWavFile(i) );
            // サウンドロード
            ds.AddSound( i,buf );
            // 再生してキャッシング
            ds.SetVolume( i,0 );        // 音量を0にする
            ds.Play( i );
            Sleep(1);
            ds.Stop( i );
            ds.SetVolume( i,1 );        // 音量を戻す
        }
    }


BMSデータ内で使用されるWAVの最大定義数は256個(新仕様では1296個)となります。
つまり0x00~0xFFまでの値(新仕様では00~ZZ)となりますので、
このプログラムでは256個分のインデックス値を調べ、
そのインデックスのファイル名が空文字で無いかをGetWavFileを使用してチェックし、
存在する場合はCDSPro81クラスのAddSoundを使用師弟ロードします。

ただし、BMSデータ内のファイル名にはフォルダ名などは含まれないため、
そのままではEXEファイルと同じフォルダからロードしようとしてしまいます。

これを避けるためにBMSファイルのあったフォルダの中を指定するように、
sprintfを使ってSAMPLEフォルダ内のファイル名を生成しています。

さらにここではおまじないとして一度再生を行っていますが、
サウンドデバイスによっては一度再生されないと実際にメモリにロードされないことがあるためで、
もしこれを行っていないとゲーム中に始めてそのサウンドが鳴る際、
その瞬間にロードが発生して画面が処理落ちしてしまうことがあります。

ちなみにここでは音量を下げてから再生を行っていますが、
もし音量を下げないと一瞬音が聞こえてしまうことがあります。


サウンドのロードが完了すればあとは再生したいインデックスに対してPlayを呼ぶだけなので、
例えば「#00101:FF」のようにBGMの再生をBMSに定義した場合、サウンド0xFFを再生する、
つまりPlay( 0xFF )を呼び出すことでそのサウンドを再生することが出来ます。

■画像データのロード

次にBGA画像ですがこれもサウンドのロードとほぼ同様の処理を行います。

    // 画像データのロード
    dd.DrawBegin( FALSE );              // 描画キャッシュ用に描画を開始する(クリアはせずに前回描画された状態を残す)
    for( i=0;i<256;i++ ) {
        // 画像ファイル名が存在するなら
        if( strlen(bms.GetBmpFile(i))>0 ) {
            // 画像ファイル名の生成
            sprintf( buf,"SAMPLE\\%s",bms.GetBmpFile(i) );
            // テクスチャロード
            dd.AddTexture( i+256,buf );
            // テクスチャクラスを取得
            CDDTexPro90 *tex = dd.GetTexClass(i+256);
            // 画像のサイズで切り抜き
            dd.SetPutRange( i+256,i+256,0,0,tex->GetWidth(),tex->GetHeight() );
            // 描画キャッシュ
            dd.SetPutStatus( i+256,0.0f );      // 透明にする
            dd.Put( i+256,0,0 );
            dd.SetPutStatus( i+256,1.0f );      // 元に戻す
        }
    }
    dd.DrawEnd();                       // 描画キャッシュ終了(透明で描画したので実際には前回の描画画像のまま)


画像ファイル名が存在するかはGetBmpFileが空文字かどうかで判断します。

また画像ファイル名もサウンドと同じくフォルダ名は含まれないため、
sprintf関数を使いフォルダ名付きのファイル名を生成しています。

ここで気をつけることは画像を追加する際にインデックス番号に+256をしているところです。
これは単純にゲーム用のシステム画像(BM.TGA)をインデックス0にロードしているため、
インデックスが被らないようにしているためです。

またCDDPro90ではロードした画像からさらに領域を切り抜いて表示する仕様のため、
ここでは同時にロードした画像のサイズで切り抜きを行っています。
※ロードしたテクスチャのサイズはGetTexClassにてCDDTexPro90を取得して、
 その中のGetWidthとGetHeight関数から取得出来ます

実は画像もサウンドと同じく表示を行わないと実際にロードされないことがあり、
ゲーム中に始めて表示される際にロードが発生してしまい、
一瞬ゲームがカクついたりといった現象が発生することがあります。

そこでここではまずCDDPro90のDrawBeginとDrawEndで囲って描画状態としておき、
画像をロードしたらそれを即座に描画することで確実にロードされるようにしています。


■サウンドデバイスのアクティブ化

最近のノートPCなどは省電力機能が充実しており、音が鳴っていないと
自動的にサウンドデバイスの電力を切ってしまう物があります。

この状態でサウンドを再生しようとすると、サウンドデバイスが有効になるまで再生が行われないため、
一瞬遅れて再生されたり音の最初の部分が再生されなくなるといった現象が起こります。

そしてこの問題はDirectSoundを使っているかは関係なく、音が鳴っていなければ自動的に電力を切ってしまうため、
ゲーム中に音を鳴らそうとすると音が鳴らなかったり、またBGMがズレたりして譜面と曲が同期しなくなったりします。

この問題を避けるためここでは裏技として無音のサウンドをロードし、
これをループ再生することで常にサウンドデバイスが活性化状態になるようにします。

    // 無音サウンドのロード
    ds.AddSound( CDSPRO_MAXSOUND-1,"FILES\\NULL.WAV" );
    // ループ再生してデバイスを活性化
    ds.Play( CDSPRO_MAXSOUND-1,TRUE );


このサンプルでは「サウンド数の最大値-1」のIDにサウンドをロードしています。

■変数の初期化

次にゲームに使用する変数を初期化します。

    // ゲーム用変数の初期化
    fScrMulti       = 1.0f;
    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) );

    // マシンの周波数を取得
    LARGE_INTEGER freq;
    QueryPerformanceFrequency( &freq );
    llGlobalFreq = freq.QuadPart;

    // 現在の時間を開始時間とする
    LARGE_INTEGER li;
    QueryPerformanceCounter( &li );
    llStartTime = li.QuadPart;


fScrMultiは譜面のスクロールスピードの倍率となり、ここではデフォルト値として1倍に設定しています。
ゲーム中はテンキーの「+」と「-」でこの値を変化させることで、リアルタイムに譜面のスクロール速度が変化します。

iImageIDは現在表示中のBGA画像のIDが保存されるもので、
ゲーム中は04チャンネルに指定された画像のIDに更新されていきます。
このサンプルでは0の場合はまだBGAが設定されていないものとしてデフォルトのBGAを表示する仕様としていますが、
例えば0をミス画像の表示に使いたいなどある場合は、各自で-1を未指定とするなど仕様を決めてみてください。

それ以外の各変数については後ほど説明するのでここでは気にしないで下さい。

全ての変数を初期化したらゲームの開始時間を記録するため、現在の時間をllStartTimeに入れています。
ついでにこのカウント値を時間に変換するために必要な、マシンの1秒間のクロック数もllGlobalFreqに記録しておきます。

■タイマー初期化

最後に60フレーム制御を行うためのタイマーを開始させてGameInitを終了します。

    // タイマー初期化
    tm.Start( 60 );

    return TRUE;
}



この関数から抜けたらステートをST_GAMERUNにセットすることで、

ゲームのメインループ処理に入ることになります。