list を使って弾丸の動きを管理する

STL(Standard Template Library) の list を使ってシューティングゲームの弾丸の動きを一元的に管理します。

前田稔(Maeda Minoru)の超初心者のプログラム入門

プログラムの説明

  1. シューティングゲームでは、敵味方入り乱れて非常に多くの弾丸が飛び交います。
    機体や砲台から弾が発射されると弾丸の描画が開始されます。
    弾丸は定められた規則で画面中を動き回り、画面の外に出ると消滅します。
    弾丸の数は、ゲームにもよりますが、百を超えることも珍しくありません。
    これらの弾丸を一個ずつ発射から消滅まで動きの設定や当たり判定をしながら管理するのは一苦労です。
    このようなときに威力を発揮するのが STL の list コンテナです。
    list コンテナはC++に標準装備されている STL の一部で、前問に続いて STL の登場です。
    ここでは list を使った弾丸管理の基本的な方法を説明し、実際のプログラムはシューティングゲームに譲ります。
  2. 新規プロジェクトから、空の Console Application を作成して、次のファイルをプロジェクトに加えて下さい。
    /*★ 弾丸の動きを管理する     前田  稔 ★*/
    #include <stdio.h>
    #include <conio.h>
    #include <list>
    using namespace std;
    
    typedef struct
    {   int     n;
        float   x,y;
        float   dx,dy;
    }   SHOT;
    SHOT    tbl[4]= {{ 0, 10.0f,  8.0f,  1.0f,  0.1f },
                     { 1, 15.0f,  3.0f,  1.0f,  0.2f },
                     { 2,  0.0f,  5.0f,  1.0f,  0.3f },
                     { 3, 13.0f,  0.0f,  1.0f,  0.4f } };
    
    int main()
    {   int     i;
        list<SHOT>  v;
        list<SHOT>::iterator p,q;
    
        for(i=0; i<4; i++)  v.push_back(tbl[i]);
        while(v.size())
        {
            for(p=v.begin(); p!=v.end(); p++)
            {   printf("%d  x=%6.2f y=%6.2f\n", p->n,p->x,p->y);
                p->x+= p->dx;
                p->y+= p->dy;
                if(p->x>20.0f)
                {   q= p;
                    p--;
                    v.erase(q);
                }
            }
            printf("\n");
            getch();
        }
        return 0;
    }
    
  3. 今回は stdio.h を使ってみました。
    conio.h は表示を確認するための getch() 関数のヘッダファイルです。
    list を使うときは list をインクルードして下さい。
    using namespace で std で宣言されたネームを使えるようにします。
        #include <stdio.h>
        #include <conio.h>
        #include <list>
        using namespace std;
        
  4. 弾丸の動きを制御する SHOT 構造体です。
    n は弾丸の番号で「登録/削除」を確認するために使っていますが、本来ならば弾の種別や動きの違いを表すフラグとして使います。
    x,y が弾の現在の座標で、今回はX座標が20を超えると消滅させています。
    ウインドウの座標は整数で表現されるのですが、DirectX などで使われる座標の計算は、細かい動きにも対応できるように float タイプが使われるようになってきました。
    dx,dy が弾の動きを制御するX座標とY座標のレンダリングサイクル(更新時間)当たりの移動量です。
    誘導弾などの動きを設定するときは、この値を変化させて下さい。
        typedef struct
        {   int     n;
            float   x,y;
            float   dx,dy;
        }   SHOT;
        
  5. このプログラムは list の基本的な使い方がテーマなので、弾丸は4発だけ登録しています。
    もっと大量の弾丸を登録して試してみて下さい。
        SHOT    tbl[4]= {{ 0, 10.0f,  8.0f,  1.0f,  0.1f },
                         { 1, 15.0f,  3.0f,  1.0f,  0.2f },
                         { 2,  0.0f,  5.0f,  1.0f,  0.3f },
                         { 3, 13.0f,  0.0f,  1.0f,  0.4f } };
        
  6. 弾丸を list に登録するソースコードです。
            for(i=0; i<4; i++)  v.push_back(tbl[i]);
        
  7. 全ての弾丸が無くなるまでループします。
    list の領域はメモリ上で連続しているとは限らないので、v.begin() から v.end() でループします。
    現在登録されている弾丸の番号と座標を printf() で表示します。
        while(v.size())
        {
            for(p=v.begin(); p!=v.end(); p++)
            {   printf("%d  x=%6.2f y=%6.2f\n", p->n,p->x,p->y);
        
  8. X,Y座標を dx,dy で更新します。
    X座標が 20.0f を超えると弾丸を list から削除します。
    このとき list が途切れることが無いようにリンクを設定して下さい。
    p をそのまま削除するとリンクがとぎれてしまいます。
                p->x+= p->dx;
                p->y+= p->dy;
                if(p->x>20.0f)
                {   q= p;
                    p--;
                    v.erase(q);
                }
            }
        
  9. 表示の確認です。
            printf("\n");
            getch();
        }
        

【演習】

もっと大量の弾丸を登録して試してみて下さい。

超初心者のプログラム入門(C/C++)