Space キーで弾を連射



Space キーを押し続けると機関銃のように弾を連射します。

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

プログラムの説明

  1. 背景画像のスクロールには IMAGE Object Class を使います。
    弾の発射は SPRITE Object Class を継承した SHOOT Object Class にメンバー関数を追加します。
    ID_TIMER は背景画像をスクロールするタイマのIDで、ID_TIMER1 は弾を発射するタイマのIDです。
    Shoot が SHOOT Object Class で、Img が IMAGE Object Class の定義です。
    g_Pos は背景画像のオフセットです。
    rec は弾を描画する範囲の設定で、rec からはみ出た弾は削除されます。
        #include    "Shoot.h"
        #include    "Image.h"
        #define     SAFE_DELETE(p)  { if (p) { delete (p);     (p)=NULL; } }
        #define     SAFE_RELEASE(p) { if (p) { (p)->Release(); (p)=NULL; } }
        #define     ID_TIMER    (WM_APP + 0)
        #define     ID_TIMER1   (WM_APP + 1)
    
        SHOOT*      Shoot= NULL;
        IMAGE*      Img= NULL;
        WORD        g_Pos= 0;       //Back Scroll の位置
        RECT        rec= { 0,0,600,440 };
        
  2. WM_CREATE: で Object をインスタンス化して、背景の画像と弾の画像を入力します。
    背景画像のスクロールと弾を発射するタイマの間隔を設定します。
            case WM_CREATE:
                Img= new IMAGE(hWnd);
                Img->LoadFile("星空.jpg");
                Shoot= new SHOOT(hWnd);
                Shoot->Load("tama.bmp",2,1);
                SetTimer(hWnd,ID_TIMER,30,NULL);
                SetTimer(hWnd,ID_TIMER1,120,NULL);
                break;
        
  3. WM_PAINT: では g_Pos(背景画像のオフセット)を使って背景をスクロールします。
    前のフレームで描画されていた弾の画像は、背景の描画によって消去されます。
    Shot->View() で Object に登録されている弾を描画します。
    Shot->Move() で Object に登録されている弾の座標を移動します。
    一番目の引数がX座標の移動量で、二番目の引数がY座標の移動量です。
    移動の結果、rec の領域から出た弾は自動的に削除されます。
            case WM_PAINT:
                hdc= BeginPaint(hWnd,&ps);
                Bmp->Show(0,0,640-g_Pos,480,g_Pos,0);
                Bmp->Show(640-g_Pos,0,g_Pos,480,0,0);
                Shoot->View(1);
                Shoot->Move(12,0,rec);
                EndPaint(hWnd, &ps);
                break;
        
  4. WM_TIMER: には二種類のタイマがポストされます。
    ID_TIMER1 が弾を発射するタイマのIDで、Space キーが押されているときに弾を連射します。
    Shot->Count() は、現在描画中の弾の個数で最大100発まで登録できるのですが、今回は10発で抑えています。
    Shot->Set() が初期座標を指定して弾を登録するメソッドです。
    Set() で登録された弾は Move() で移動します。
    ID_TIMER が背景画像をスクロールするタイマのIDです。
            case WM_TIMER:
                if (wParam == ID_TIMER1)
                {   if (GetKeyState(VK_SPACE)<0 && Shoot->Count()<10)
                    {   Shoot->Set(20,200);  }
                }
                if (wParam == ID_TIMER)
                {   g_Pos= (g_Pos+1)%640;
                    InvalidateRect(hWnd,NULL,FALSE);    //クリアしない
                }
                break;
        
  5. WM_DESTROY: ではタイマを止めて画像のハンドルを開放して下さい。
            case WM_DESTROY:
                KillTimer(hWnd,ID_TIMER);
                KillTimer(hWnd,ID_TIMER1);
                SAFE_DELETE(Img);
                SAFE_DELETE(Shoot);
                PostQuitMessage(0);
                return 0L;
        

SHOOT Object Class

ここでは簡単に SHOOT Object Class に付いて説明します。
  1. SHOOT Object Class には「同心円状に弾を描画する機能」と「最大100発の弾を描画する機能」が用意されています。
  2. 同心円状に弾を描画するメソッドです。
    Shot->View(1,320,220,len);
    ソースコード説明
    View() 同心円状に弾を描画するメソッド
    1 Sprite(弾)の番号
    320,220 描画の中心座標
    len 中心からの広がり(半径)
  3. 最大100発の弾を描画する機能です。
    この機能を使うと沢山の弾の描画や移動が関数を呼ぶだけですみ、「弾の連射」や「一列発射」が簡単にできるようになります。
  4. 最大100発の弾は、それそれにフラグと座標を持っています。
    flag が 1 の弾は、現在画面に描画中の弾です。
        struct
        {   WORD    flag;           //弾の描画フラグ
            long    x,y;            //弾の座標
        }   T[100];
        
  5. 登録された最大100発の弾を描画するメソッドです。
    flag が 1 の弾を x,y の座標に描画します。
    1 は登録した弾の画像(Sprite)の番号です。
    Shot->View(1);
  6. 弾を登録するメソッドです。
    T[100] から空いている領域を見つけて弾を登録します。
    x,y は弾を発射する座標です。
    WORD Set(WORD x,WORD y);
  7. 登録されている全ての弾をリセットします。
    void Reset();
  8. 登録されている弾の個数を調べます。
    WORD Count();
  9. 登録されている弾の座標を移動します。
    rec から外に出た弾のフラグをリセットして削除します。
    void Move(WORD xdt,WORD ydt,RECT rec);

【オプション】

  1. 今回は100発の領域を固定的に確保しましたが、一般的には動的に割り当てます。
    動的にメモリを割り当てる方法は new でメモリ領域の割り当て を参照して下さい。
  2. 「連射弾/円形弾/誘導弾/渦巻き弾」など、弾の飛び方は一定ではありません。
    また雑魚の敵機も弾丸と同じように制御することが可能です。
    これらをまとめて制御する構造体を定義してみました。
        typedef struct
        {   int     n;                  //弾(画像)の種別
            float   x,y;                //現在の弾の座標
            float   dx,dy;              //弾の移動量
        }   SHOT;
        
  3. x,y は、弾を描画する現在の座標です。
    弾の移動量(dx,dy) は、タイマ割り込みで毎回 x,y に加える値です。
  4. 弾丸の進行方向が決まっている連射弾/円形弾などは dx,dy を設定すれば、あとは x,y に加えるだけです。
    誘導弾などはタイマ割り込みで dx,dy の値を再計算します。
    渦巻き弾などは回転軸の中心座標と回転角度と広がり係数から x,y の値を直接再計算します。

超初心者の方のために全ソースコードを掲載します。 (^_^;)

ファイル名 ファイルの説明
shot.h ソースプログラム
shot.cpp ソースプログラム

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