8-11 コンピュータプレイ

スクロールが出来たところでこれでゲームが動いているよう見えるようになりましたが、
ここではこのスクロールに対して、コンピュータ(COM)が自動的に演奏するような処理を考えます。

これはデモ画面と言われるもので、アーケードなどではアドバタイズ画面などということがあります。
例えばタイトル画面がしばらく表示され一定時間経つと、実際にゲームをプレイしているような画面に切り替わって、
このゲームはこういうゲームですよと見せるような状態です。

ここではそのアドバタイズ画面に必要なCOMプレイルーチンについて説明します。
※厳密にはアドバタイズ画面では音は鳴りませんが、ここではあくまでもオートプレイということで実装します


■COMプレイとは

COMプレイとは人間がプレイするものを真似しないといけないため、
何か難しそうなイメージがあるかもしれません。

しかし、よくよく考えてみるとCOMでは常に完璧な演奏をします。
ワザと間違ったりなんてすることはありません。
ということは別に人間がキーを押す部分をマネる必要はありません。
別途完璧に演奏するルーチンを作ってしまえば良いのです。


ではどのようにすればCOMプレイが出来るのかというと、
実は以前作成したBGMチャンネルの再生処理を応用するだけです。
また、これを応用したオブジェの表示処理がほぼそのまま使えます。

どういうことかというと、まずスクロールするといつかは判定バーまでオブジェが来ますが、
判定バーに来たときというのはオブジェのBMSカウント値(lTime)から
現在のBMSカウント値(now_count)を引いた値がちょうど0になった時であり、
この時にキーを押したことにすれば完璧な判定になります。

ただしこのゲームは60フレームで動作することが前提のため、
実際には計算結果がぴったりと0になることはありません。
そのためこの場合は0以下かどうかで判定するようにします。

詳しく説明すると例えば1フレーム前はまだ判定バーの手前だったとして、
次のフレームで判定バーを過ぎた場合にCOMがプレイすれば、
あたかも判定バーの上に重なった時にプレイしたように見えます。


※判定バーにぴったりと重ならず少し下にズレたとしてもヒットと見なします

ちなみにCOMがプレイしたというのは、つまりそのオブジェの音を鳴らすことです。
この処理はBGMの再生処理とほとんど同じで、チャンネル番号だけオブジェの番号に変えるだけです。
※これを応用したのがオブジェの描画処理なので、これらの処理が分かっていれば何も難しくはありません

またここではまだ説明しませんが、鳴らしたときにさらに爆発エフェクトや
判定時に表示される文言(GREATなど)を表示させれば、結果的に人間がプレイしたのと同じ効果となります。

■COM判定

COM判定をするためにはオブジェの情報が必要です。
以前のBGM再生処理ではBGMチャンネルは1つしか無かったため、
そのチャンネルだけ処理するだけで済みましたが、
今度はオブジェチャンネル全ての判定を行う必要があるため、
オブジェの描画処理で行ったことと同じく、全チャンネルに対してチェックするようにします。


以下はBGMの再生処理を全オブジェチャンネルに対応させ、さらに最適化も行ってあるプログラムとなります。

    /////////////////////////////////////////////////////////////////////////////////////
    // コンピュータプレイ
    /////////////////////////////////////////////////////////////////////////////////////
    for( j=0;j<6;j++ ) {
        for( i=iStartNum[index[j]+0x11+0x20];i<bms.GetObjeNum(0x11+index[j]);i++ ) {
            LPBMSDATA b = bms.GetObje( 0x11+index[j],i );
            if( now_count<b->lTime )
                break;
            if( b->bFlag ) {
                if( now_count>=b->lTime ) {
                    b->bFlag = FALSE;
                    ds.Reset( b->lData );
                    ds.Play( b->lData );
                    iStartNum[index[j]+0x11+0x20] = i + 1;
                }
            }
        }
    }


ここでは全てのチャンネルをチェックする際に、
オブジェの表示順を変えるためのインデックスリストを参照していますが、
これはオブジェの描画処理を流用したためでここでは特に描画は関係無いため、
実際にはインデックスリストを参照する必要はありません。(jをそのまま加算してもOK)

なお、ここで使用している最適化変数のインデックスは赤字のようにさらに「+0x20」していますが、
これはオブジェの描画処理にて既に「index[j]+0x11」を使用しているためで、
それとかち合わないようにするためです。

もし同じ配列を使ってしまった場合、描画処理の方で先に判定バーを過ぎた時、
つまりforの開始位置が更新されてしまうとそのオブジェはもう判定されなくなってしまうため、
そうなるとそのオブジェは二度と鳴らなくなってしまいます。
これを避けるために敢えてここでは使用していない配列を指定するようにしています。

ちなみに特に+0x20でなくても未使用の領域であれば問題ありませんが、
+0x10の場合はプレイヤー2のオブジェとかぶってしまうためここでは避けるようにしています。
※プレイヤー2用の処理はこのサンプルには入っていません


これでCOMプレイの処理は完成ですが、実はこれで聞くだけ専用の
BMSプレイヤー(というかビューワー)は完成しています。

ということで次はせっかくCOMプレイが出来るようになったので、
ユーザープレイの前に先に弾いたときのエフェクトを入れてみます。