当たり判定と爆発

弾丸の当たり判定と爆発のアニメーションです。

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

プログラムの説明

  1. 定期的に乱数を使って20発の弾丸を発射します。
    自機を矢印キーで操作して、弾丸を避けて下さい。
    自機が弾丸に当たると爆発のアニメーションが始まります。
  2. ship は自機のテクスチャです。
    tama は弾丸のテクスチャです。
    bomb は爆発のテクスチャで、50*50 の画像が横方向に8枚並んでいます。
    jikipos は自機の座標で、矢印キーで操作します。
    pos は20発分の弾丸の座標です。
    vect は20発分の弾丸の移動量(移動の方向)です。
    flag は20発分の弾丸のフラグです。
    rand は乱数の設定です。
            Texture2D   ship;           //自機テクスチャ
            Texture2D   tama;           //弾テクスチャ
            Texture2D   bomb;           //爆発テクスチャ
    
            public Vector2      jikipos = new Vector2(300,400); //自機の座標
            public Vector2[]    pos = new Vector2[20];          //弾の座標
            public Vector2[]    vect = new Vector2[20];         //弾の移動
            public bool[]       flag = new bool[20];            //弾フラグ
            Random              rand = new Random();
        
  3. 爆発のアニメーションを設定する領域です。
    pat は bomb に格納されている8枚の画像の爆発パターンです。
    単に8枚の画像を順番に切り替えるだけでは、能がありません。
    sp_no で pat[] の何番目の画像を描画するかを制御します。
    settim は爆発アニメーションの画像を切り替えるタイミングを調べる領域です。
            int[]       pat =           //爆発パターン
            { 0, 0, 1, 2, 2, 1, 0, 0, 0, 1, 2, 3, 4, 4, 5, 6, 7, 99 };
            int         sp_no = 99;     //Sprite Number
            double      settim = 0;     //切り替え時間
        
  4. LoadContent() でテクスチャを入力します。
            protected override void LoadContent()
            {
                // Create a new SpriteBatch, which can be used to draw textures.
                spriteBatch = new SpriteBatch(GraphicsDevice);
    
                // TODO: use this.Content to load your game content here
                ship = Content.Load<Texture2D>("ship");
                tama = Content.Load<Texture2D>("tama");
                bomb = Content.Load<Texture2D>("bomb8");
            }
        
  5. 当たり判定と爆発アニメーションを制御する Update() メソッドです。
    sp_no に 0 が設定されたときにアニメーションが始まり、終了すると 99 が設定されます。
    pat[sp_no] が爆発アニメーションの画像番号です。
    「settim+100」の 100 がアニメーションの切り替え速度です。
            protected override void Update(GameTime gameTime)
            {
                int     i;
    
                //プログラムの終了
                if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed  ||
                    Keyboard.GetState().IsKeyDown(Keys.Escape)) this.Exit();
    
                //爆発アニメーション
                if (sp_no<99)
                {
                    if (settim+100 < gameTime.TotalGameTime.TotalMilliseconds)
                    {   settim = gameTime.TotalGameTime.TotalMilliseconds;
                        sp_no++;
                        if (pat[sp_no]==99)     sp_no= 99;
                    }
                }
        
  6. Update() メソッドの続きです。
    flag[i] が true の弾丸が、現在画面に描画されている弾丸です。
    pos[i] に vect[i] を加えて、弾丸の座標を更新します。
    弾丸の座標がウインドウの外に出ると flag[] をリセットします。
    Hit(jikipos,pos[i]) で自機の座標と弾丸の座標を比較して、ヒットを判定します。
    ヒットしたとき弾丸の flag[] をリセットして、sp_no に 0 を設定してアニメーションを開始します。
                //弾丸の移動と当たり判定
                for (i=0; i<20; i++)
                {   if (flag[i])
                    {
                        pos[i] += vect[i];
                        if (pos[i].X < 0 || pos[i].X > 800 || pos[i].Y < 0 || pos[i].Y > 600) flag[i] = false;
                    }
                    if (flag[i] && Hit(jikipos,pos[i]))
                    {
                        flag[i] = false;
                        sp_no= 0;       //爆発の開始
                    }
                }
        
  7. Update() メソッドの続きです。
    描画中の弾丸が画面から消えると、自動的に次の20発の弾丸を乱数を使って発射します。
    pos[i].X, pos[i].Y が発射する弾丸の座標で、vect[i].X, vect[i].Y が移動の方向と速度です。
    flag[] をセットして描画を開始します。
    上下左右のキーで自機を操作します。
                //弾丸の発射
                for(i=0; i<20 && flag[i]==false; i++);
                if (i==20)              //次の発射
                {   for(i=0; i<20; i++)
                    {   pos[i].X= rand.Next(800);
                        pos[i].Y= 10;
                        vect[i].X= (rand.Next(100)-50) / 10.0f;
                        vect[i].Y= rand.Next(100) / 10.0f + 1.5f;
                        flag[i]= true;
                    }
                }
    
                //自機の操作
                KeyboardState keyState = Keyboard.GetState();
                if (keyState.IsKeyDown(Keys.Left))  jikipos.X -= 4;
                if (keyState.IsKeyDown(Keys.Right)) jikipos.X += 4;
                if (keyState.IsKeyDown(Keys.Up))    jikipos.Y -= 4;
                if (keyState.IsKeyDown(Keys.Down))  jikipos.Y += 4;
    
                base.Update(gameTime);
            }
        
  8. Draw() メソッドでウインドウを描画します。
    jikipos の座標に自機を描画します。
    20発分の flag[] を調べてセットされている弾丸を描画します。
    sp_no が 99 で無いとき、pat[sp_no] の爆発テクスチャを描画します。
    テクスチャの一枚分のサイズは 50*50 なので、自機のサイズに合わせて引き伸ばして描画しています。
            protected override void Draw(GameTime gameTime)
            {
                graphics.GraphicsDevice.Clear(Color.CornflowerBlue);
    
                // TODO: Add your drawing code here
                spriteBatch.Begin(SpriteBlendMode.AlphaBlend);
                spriteBatch.Draw(ship, jikipos, Color.White);   //自機の描画
                for (int i = 0; i < 20; i++)                    //弾丸の描画
                    if (flag[i])    spriteBatch.Draw(tama, pos[i], Color.White);
                if (sp_no < 99)                                 //爆発の描画
                    spriteBatch.Draw(bomb, new Rectangle((int)jikipos.X - 50, (int)jikipos.Y - 50, 180, 180),
                                           new Rectangle(pat[sp_no] * 50, 0, 50, 50), Color.White);
                spriteBatch.End();
    
                base.Draw(gameTime);
            }
        
  9. 自機の座標と弾丸の座標を比較して、当たり判定を行う Hit() メソッドです。
    target が自機の座標で、pos が弾丸の座標です。
    target と pos の直線距離が 50 以下に接近すると true を返します。
    当たり判定のきつさは 50 で調整して下さい。
    また target と pos の座標は、画像の左上なので必要に応じて中央に寄せて下さい。
            public bool Hit(Vector2 target, Vector2 pos)
            {
                float dist;
                float wx;
                float wy;
    
                wx = pos.X - target.X;
                wy = pos.Y - target.Y;
                dist = (float)Math.Sqrt((double)(wx * wx + wy * wy));
                if (dist < 50)  return true;
                return false;
            }
        
  10. メニューの [デバッグ] から [デバッグ開始] を選択すればコンパイルされ、エラーが無ければ実行されます。
    自機を矢印キーで操作して、弾丸を避けて下さい。

【NOTE】

次のソースコードを見て下さい。
爆発の画像を bomb テクスチャから切り出して描画しています。
spriteBatch.Draw(bomb, new Rectangle((int)jikipos.X - 50, (int)jikipos.Y - 50, 180, 180),
                       new Rectangle(pat[sp_no] * 50, 0, 50, 50), Color.White);

一見何の問題も無いようですが、毎回 new Rectangle() で Object が生成されて、使い終わった領域が溜まって行きます。
そしてヒープ領域を使い切ると Garbage Collection が起動してゲームが中断してしまいます。
このようなときは class Game1 の最初で一度だけ領域を割り当てて、繰り返し同じ領域を使うようにしましょう。
Rectangle des = new Rectangle(0, 0, 180, 180);
Rectangle sou = new Rectangle(0, 0, 50, 50);
Garbage Collection の説明は ガベージコレクションを起動 を参照して下さい。

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

超初心者のプログラム入門(XNA(C#) game program)