Array List に混在して登録する

Array List に混在して登録した Class を統一的に管理します。
Array List には、配列と違って「型の異なるデータを混在して登録」することが出来ます。

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

プログラムの説明

  1. シューティングゲームでは、敵味方入り乱れて沢山の弾丸が飛び交います。
    今までは弾丸の種類ごとに ArrayList で管理していましたが、ここでは全ての弾丸を統一して管理する方法を説明します。
    画像を扱う Object Class も用途によって多少の違いがあります。
    今回は4種類の画像を扱う Object Class を定義してみました。
    id で ArrayList に登録した Class を識別します。
  2. 各クラスの元になる Base Object Class です。
    多少なりとも解りやすいように機能を最小限に絞って定義してみました。
    id は ArrayList に登録されたクラスを識別する領域です。
    最低限のコンストラクタと弾を移動するメソッドを定義しています。
        class  Base
        {
            public int          id;         //クラスの識別
            public Texture2D    img;        //画像の指定
            public Vector2      pos;        //弾の座標
            public Vector2      vect;       //進行方向
    
            //コンストラクタ
            public Base(Texture2D Img, Vector2 Pos, Vector2 Vect)
            {   id = 0;
                img = Img;
                pos  = Pos;
                vect = Vect;
            }
            public void Draw(SpriteBatch spBatch)
            {
                spBatch.Draw(img, pos, Color.White);
            }
    
            //弾の移動
            public bool Move()
            {
                pos = pos + vect;
                if (pos.X < -8 || pos.X > 800 || pos.Y < -8 || pos.Y > 600) return true;
                return false;
            }
        }
    
  3. Base Class を継承した Shot class です。
    コンストラクタで Base Class のコンストラクタを呼び出して、id に 1 を設定するだけです。
    C#では、継承する Class は一個しか許されず、基本クラスの Constructor を呼び出すときはキーワード base を使います。
        class  Shot : Base
        {
            public Shot(Texture2D Img, Vector2 Pos, Vector2 Vect)
                : base(Img, Pos, Vect)
            {   id = 1;
            }
        }
    
  4. Base Class を継承した Shoot class です。
    Shoot class は Shot Class と比べて id が違うだけです。
        class Shoot : Base
        {
            public Shoot(Texture2D Img, Vector2 Pos, Vector2 Vect)
                : base(Img, Pos, Vect)
            {
                id = 2;
            }
        }
    
  5. Base Class を継承した Fire class です。
    コンストラクタで Base Class のコンストラクタを呼び出して、id に 3 を設定します。
    Fire class では、自機との当たり判定をするために Hit() メソッドを定義しています。
        class  Fire : Base
        {
            public Fire(Texture2D Img, Vector2 Pos, Vector2 Vect)
                : base(Img, Pos, Vect)
            {   id = 3;
            }
            public bool Hit(Vector2 target)
            {
                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 (30 > dist) return true;
                return false;
            }
        }
    
  6. シューティングゲームの領域定義です。
    弾丸は統一して管理するので array が一個定義されているだけです。
    pos, vect はヒープ領域を節約するための作業用の領域です。
    その他の領域の説明は、関連するページを参照して下さい。
            Texture2D   tama_b;             //弾テクスチャ
            Texture2D   tama_r;             //弾テクスチャ
            Texture2D   tama_y;             //弾テクスチャ
            Texture2D   ship;               //自機テクスチャ
            Base        MyShip;             //自機の描画
            ArrayList   array;              //弾丸の ArrayList
            double      Settim;             //前回発射時刻
            Random rand = new Random();
    
            Vector2 pos;                    //弾丸の座標設定ワーク
            Vector2 vect;                   //弾丸の移動設定ワーク
    
  7. 乱数で弾丸を発射する fire_shoot() メソッドです。
            public void fire_shoot()
            {
                for (int i = 0; i < 3; i++)
                {
                    pos.X = rand.Next(800);
                    pos.Y = 10;
                    vect.X = (rand.Next(100) - 50) / 10.0f;
                    vect.Y = rand.Next(100) / 10.0f + 1.5f;
                    array.Add(new Fire(tama_r, pos, vect));
                }
            }
    
  8. 横一列に弾丸を発射する tama_shoot() メソッドです。
            public void tama_shoot()
            {
                for (int i = 0; i < 20; i++)
                {
                    pos.X = i*40;
                    pos.Y = 550;
                    vect.X = 0;
                    vect.Y = -5;
                    array.Add(new Shot(tama_b, pos, vect));
                }
            }
    
  9. 円状に弾丸を発射する Circle_shoot() メソッドです。
            public void Circle_shoot()
            {
                pos.X = 400;
                pos.Y = 300;
                for (int i=0; i<36; i++)
                {
                    Rot(i*10, 4);
                    array.Add(new Shoot(tama_y, pos, vect));
                }
            }
    
  10. ウインドウを描画する Draw() メソッドです。
    ArrayList で弾丸を統一して管理しているので、非常にシンプルになっています。
            protected override void Draw(GameTime gameTime)
            {
                graphics.GraphicsDevice.Clear(Color.CornflowerBlue);
    
                // TODO: ここに描画処理を記述します。
                spriteBatch.Begin();
                //自機を描画
                MyShip.Draw(spriteBatch);
                //弾を描画
                foreach(Base BASE in array) BASE.Draw(spriteBatch);
                spriteBatch.End();
    
                base.Draw(gameTime);
            }
    
  11. 今回のメインテーマの Update() メソッドです。
    弾丸を移動するメソッド Move() は Base Class で定義されているので、これを呼ぶだけです。
    ArrayList で弾丸を統一して管理しているので簡単です。
    自機と弾丸の当たり判定は Fire Class の弾丸に対してのみ行います。
    Fire Class は id==3 で登録されているので、この弾丸に対して Hit() メソッドを実行します。
    Hit() メソッドが (Fire) でキャストされていることに注目して下さい。
    Hit() メソッドが定義されているのは Fire Class だけです。
            protected override void Update(GameTime gameTime)
            {
                // ゲームを終了する
                if (GamePad.GetState(PlayerIndex.One).Buttons.Back ==
                    ButtonState.Pressed ||
                    Keyboard.GetState().IsKeyDown(Keys.Escape)) this.Exit();
    
                // 弾丸の移動
                for (int i = 0; i < array.Count; i++)
                {
                    bool flag = ((Base)array[i]).Move();
                    if (flag) array.RemoveAt(i);
                }
    
                // 弾丸の当たり判定(id==1)
                for (int i = 0; i < array.Count; i++)
                {   if (((Base)array[i]).id==3)
                    {
                        if (((Fire)array[i]).Hit(MyShip.pos))
                            MyShip.pos.X = 0;
                    }
                }
    
                // 弾丸を発射
                if (Settim + 600 < gameTime.TotalGameTime.TotalMilliseconds)
                {
                    Settim = gameTime.TotalGameTime.TotalMilliseconds;
                    fire_shoot();
                    tama_shoot();
                    Circle_shoot();
                }
    
                //自機の操作
                KeyboardState keyState = Keyboard.GetState();
                if (keyState.IsKeyDown(Keys.Left)) MyShip.pos.X -= 4;
                if (keyState.IsKeyDown(Keys.Right)) MyShip.pos.X += 4;
                if (keyState.IsKeyDown(Keys.Up)) MyShip.pos.Y -= 4;
                if (keyState.IsKeyDown(Keys.Down)) MyShip.pos.Y += 4;
    
                base.Update(gameTime);
            }
    
  12. その他の説明は、関連するページを参照して下さい。
    三種類の弾丸が飛び交います。
    矢印キーを操作して Fire class の弾丸と自機との当たり判定を確認して下さい。

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

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