Posts Issued on May 11, 2020

BSVの設計トライアル (21)

posted by sakurai on May 11, 2020 #254

ゲームFSMのアルゴリズム

トライアルの結果、BSVによるゲームFSMが完成しました。過去記事のステートベースのサウンドステートマシンと異なり、ステート分解をしていないため、rule文を一切使用していません。全てbsc(Bluespec Compiler)の、StmtFSMライブラリにステート管理を任せました。

基本的にはCで記述するようにゲームが記述できることが分かりました。例えば、弾の移動及び衝突判定、衝突処理(爆発マーク)、爆発マーク消去等のアルゴリズムを考えると、自弾、敵弾共にアルゴリズムは共通で、疑似コードで書けば、

    if (弾爆発タイマ >= 1) {   // 弾爆発中
        弾爆発タイマ++;
        if (弾爆発タイマ == MAX) {
            弾削除;            // 論理的な消去
            弾爆発マーク消去;   // 物理的な消去
            弾爆発タイマ停止;
        }
    } else {
        if (弾が出ていない and 弾生成条件) {
            弾生成処理;
            弾発射音;     // 自弾のみ
        }
        if (弾存在) {
            衝突判定;
            if (対象物) {  // 自弾の場合はインベーダ及びUFO、敵弾の場合は自機
                弾削除;          // 論理的な消去
                対象物ステート <= 爆発;
                対象物爆発タイマ <= 0;
            } else if (上下ハズレ || ベース || 弾) { // 弾:自弾の場合は敵弾、敵弾の場合は自弾
                弾マーク消去;
                弾爆発マーク;
                弾爆発タイマ <= 1;
            } else {        // 衝突していない場合
                弾を進める;
            }
        }
    }

一方、対象物は、

    if (対象物ステート == 爆発) {
        if (対象物爆発タイマ==0) {
            対象物爆発タイマ <= 1;
            対象物爆発音;
            対象物爆発マーク;
        } else {
            対象物爆発タイマ++;
            if (対象物爆発タイマ == MAX) {
               対象物削除;          // 論理的な消去
               対象物爆発マーク消去; // 物理的な消去
            }
        }
    }

のようになりますが、StmtFSMを使うと、このようなシーケンスをクロック毎のステートに分解しなくて記述できます。

インベーダのタイミング

某所で質問があったので、タイミングについて解説します。基本の1 tickは1/60秒で、その中で、インベーダ1匹、敵弾全弾、自機、自弾、UFO、スコア等の処理を行います。以下は実際のBSVのメインループのコードです。

     while (game_flag) seq // メインループ
        for (noy <= 0; noy < `Inv_TateS; noy <= noy + 1) seq  // インベーダの行処理
           for (nox <= 0; nox < `Inv_YokoS; nox <= nox + 1) seq // インベーダの列処理
              if (inv_s[nox][noy]) seq // インベーダが生きてれば
                 ivader;      // インベーダ処理
                 gun;         // 自機処理
                 bullet;      // 自弾処理
                 for (idx <= 0; idx < extend(max); idx <= idx + 1) seq
                    invBullet(idx);  // 敵弾全弾処理
                 endseq
                 ufo;         // UFO処理
                 scores;      // スコア表示
                 endJudge;    // 終了判定
                 counter <= counter + 1;  // tickカウンタ++
                 wait_timer;  // インナーループを1/60secにするウエイト
              endseq
           endseq
        endseq
     endseq
     gameOver;  // ゲームオーバー表示

1tick=1/60secの間に、インベーダ1匹(2ピクセル移動)の処理に対して、自機(1ピクセル移動)、敵弾(1ピクセル移動)、自弾(4ピクセル移動)の処理が行われます。インベーダは初期に55匹存在するので、1/55倍のスピードで始まりますが、最終的に1倍のスピードになります。従って、インベーダを倒すたびにインベーダ全体は速くなり、一方その他の速度は変わらないわけです。

FPGAでの実装では1 tick内にインベーダ全体を移動することは可能ですし、そのような実装も見ますが、ゲーム性が変わってしまいます。具体的には、インベーダ全体の速度が次第に速くならなかったり、後ろのインベーダを撃つことができなくなります。

例えば、インベーダゲームのレインボーは、後ろのインベーダを撃つことにより出現します。インベーダは残りが一匹になると左へは2ピクセルずつ右には3ピクセルずつ移動します。下2段のインベーダは、左右2ピクセルまでの移動では跡が残らない図形になっていますが、3ピクセルだと跡が消えずに残ります。もちろん今回の実装でもレインボーを体験できます。

ゲームFSMの完成

図254.1は、BSVで再設計したゲームFSMにより動作する、インベーダーゲームの動画です。過去記事に書いたように、サウンドが4ch同時発声と高品質になりました。

図%%.1
図254.1 ゲームFSMの完成

左矢前のブログ 次のブログ右矢