Object Class File

ArrayList に適した Object Class を作成して、ファイル名を分けて組み込みます。
Enemy から「縦一列/横一列/矢印状/広がるように発射/円状に発射/乱数で発射」などの多彩な攻撃を仕掛けて来ます。
自機からの弾丸を発射して、敵機との当たり判定を行います。
配列を使った連射は 弾丸を連射 を参照して下さい。
Image Class を使った連射は Image & Array で弾丸を連射 を参照して下さい。

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

Shot Object Class の説明

  1. Shot Class は既に説明した Image Class と大きな違いはありませんが、ArrayList に対応するように 次の点を変更しています。
    ArrayList の詳細は 螺旋状に弾を発射 を参照して下さい。
    1. ArrayList を使って追加/削除を繰り返すと、ガベージコレクションでゲームが一時中断することがあります。 (^_^;)
      そこで Shot Class 全体で共通に使える領域を static で宣言して、メモリを節約しています。
    2. コンストラクタで座標(pos)や進行方向(vect)を設定出来るように、パラメータの個数や形式を増やしました。
    3. Move() メソッドで、ウインドウの外に出たセルを削除出来るように bool 値を返します。
    4. 同様に Next() メソッドでもセルを削除出来るように bool 値を返します。
  2. Shot Object Class は Shot.cs の名前で作成して、[プロジェクト][既存項目の追加]からプロジェクトに組み込んで下さい。
    namespace の指定をプロジェクトで統一する以外には、特に気をつけることはありません。
    //----------------------------------------------------------------------------
    //  ショットクラスの定義
    //----------------------------------------------------------------------------
    using System;
    using System.Collections.Generic;
    using Microsoft.Xna.Framework;
    using Microsoft.Xna.Framework.Audio;
    using Microsoft.Xna.Framework.Content;
    using Microsoft.Xna.Framework.GamerServices;
    using Microsoft.Xna.Framework.Graphics;
    using Microsoft.Xna.Framework.Input;
    using Microsoft.Xna.Framework.Net;
    using Microsoft.Xna.Framework.Storage;
    
    namespace WindowsGame1
    {
        public class Shot
        {
            public static Texture2D img;        //画像の指定
            public static int       xn,yn;      //横と縦の枚数
            public static int       xs,ys;      //横と縦のサイズ
            public int              sp_no;      //Image No
            public Vector2          pos;        //弾の座標
            public Vector2          vect;       //進行方向
            private double          tim;        //アニメタイム
    
            //Shot Object Class を定義して下さい
                   ・・・
        }
    }
    

Char Object Class の説明

  1. Char Class は Shot Class のサブセットみたいなもので、static 宣言を使わなければ共用出来るのですが (^_^;)
    今回は static の使い方も目的の一つなので、別に定義しています。
  2. Char Object Class は Char.cs の名前で作成して[プロジェクト][既存項目の追加]からプロジェクトに組み込んで下さい。
    namespace の指定をプロジェクトで統一する以外には、特に気をつけることはありません。
    //----------------------------------------------------------------------------
    //  キャラクタクラス
    //----------------------------------------------------------------------------
    using System;
    using System.Collections.Generic;
    using Microsoft.Xna.Framework;
    using Microsoft.Xna.Framework.Audio;
    using Microsoft.Xna.Framework.Content;
    using Microsoft.Xna.Framework.GamerServices;
    using Microsoft.Xna.Framework.Graphics;
    using Microsoft.Xna.Framework.Input;
    using Microsoft.Xna.Framework.Net;
    using Microsoft.Xna.Framework.Storage;
    
    namespace WindowsGame1
    {
        public class Char
        {
            public Texture2D img;        //画像の指定
            public int       xn,yn;      //横と縦の枚数
            public int       xs,ys;      //横と縦のサイズ
            public int       sp_no;      //Image No
            public Vector2   pos;        //描画の座標
            public Vector2   vect;       //進行方向
    
            //Char Object Class を定義して下さい
                   ・・・
        }
    }
    

Game1.cs の説明

  1. Enemy から「縦一列/横一列/矢印状/広がるように発射/円状に発射/乱数で発射」などの多彩な攻撃を仕掛けて来ます。
    Game1 Class では Shot Object Class と Char Object Class を使います。
  2. このプログラムでは、次の画像を使っています。
    読者がプログラムを作成するときは、適当な画像を調達して来て下さい。
    画像 説明
    ship4.png 色違いの4種類の自機を横に並べた画像
    tama4.png 4種類の弾丸を横に並べ画像
    Ene5.png 5体の Enemy を横に並べ画像


  3. Enemy から発射される弾丸の色を、時間の経過と共に変化させてみました。
    また Enemy を撃墜すると Enemy の画像が切り替わります。
  4. ArrayList を使うときは System.Collections; を追加して下さい。
    using System;
    using System.Collections;               //Arrayを使うのに必要
    using System.Collections.Generic;
            :
    
  5. Game1.cs を編集して、弾丸を発射するプログラムを作成します。
    class Game1 の最初に、次の領域を宣言して下さい。
    myship, enemy, tama はプログラムで使用するテクスチャ画像です。
    ship, teki は自機と敵機の Char Class です。
    ShotArray は自機から発射する弾丸を管理する ArrayList です。
    FireArray は敵機から発射する弾丸を管理する ArrayList です。
    settim は自機から発射する弾丸の連射のタイミングを調整する領域です。
    fire_n は敵機の攻撃パターンを制御する領域です。
        public class Game1 : Microsoft.Xna.Framework.Game
        {
            GraphicsDeviceManager graphics;     //グラフィックス
            SpriteBatch spriteBatch;            //スプライト
    
            //-----テクスチャ変数------------------------------------------
            public Texture2D myship;            //自機テクスチャ
            public Texture2D enemy;             //敵機テクスチャ
            public Texture2D tama;              //弾テクスチャ
    
            //-----画像クラスとArrayList--------------------------------------
            Char        ship;                   //自機の画像クラス
            Char        teki;                   //敵機の画像クラス
            ArrayList   ShotArray;              //ショットの ArrayList
            ArrayList   FireArray;              //ファイアの ArrayList
    
            //-----制御領域--------------------------------------
            double      settim = 0;             //弾の設定時間
            int         Fire_n = 0;             //攻撃の種類
            Random      rand = new Random();
        
  6. Initialize() メソッドで ArrayList を初期化します。
            protected override void Initialize()
            {
                // TODO: Add your initialization logic here
                base.Window.Title = "XNA Game Studio";
                ShotArray = new ArrayList();    //ArrayList を生成
                FireArray = new ArrayList();    //ArrayList を生成
    
                base.Initialize();
            }
        
  7. LoadContent() メソッドで Texture2D に画像をロードします。
    ship と teki の Char Class を生成します。
            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
                myship = Texture2D.FromFile(graphics.GraphicsDevice, "c:\\data\\test\\ship4.png");
                enemy = Texture2D.FromFile(graphics.GraphicsDevice, "c:\\data\\test\\Ene5.png");
                tama = Texture2D.FromFile(graphics.GraphicsDevice, "c:\\data\\test\\tama4.png");
                //自機と敵機の設定
                ship = new Char(myship, new Vector2(400, 600), new Vector2(0, 0), 4, 1);
                teki = new Char(enemy, new Vector2(0, 100), new Vector2(2, 0), 5, 1);
            }
        
  8. Update() メソッドで弾丸の発射と移動と当たり判定を行います。
    teki_move() で Enemy を移動します。
    jiki_move() で自機をキーで操作します。
    tama_shoot() で自機から弾丸を連射します。
            protected override void Update(GameTime gameTime)
            {   bool    flag;
                int     i;
    
                // ゲームを終了する
                if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed  ||
                    Keyboard.GetState().IsKeyDown(Keys.Escape)) this.Exit();
    
                // ゲームの処理はここから書く。
                teki_move();
                jiki_move();
                // 弾丸の発射
                tama_shoot(gameTime);
        
  9. 自機から連射した弾丸の当たり判定です。
    ((Shot)ShotArray[i]).Move() で弾丸を移動します。
    ((Shot)ShotArray[i]).Hit(teki.pos) で敵機との当たり判定をします。
    ヒットしたときX座標を左端に戻して、teki.Loop() で Enemy の画像を切り替えます。
    ArrayList からの削除はループの最後に行って下さい。
    Move() の直後(ウインドウの外に出た)タイミングで削除すると、アクセス違反を起こす危険性があります。
                // 弾丸の移動と当たり判定
                for(i=0; i<ShotArray.Count; i++)
                {
                    flag = ((Shot)ShotArray[i]).Move(new Rectangle(0, 0, 1200, 800));
                    if (((Shot)ShotArray[i]).Hit(teki.pos))
                    {   teki.pos.X = 0;
                        flag = true;
                        teki.Loop();
                    }
                    if (flag)   ShotArray.RemoveAt(i);
                }
        
  10. fire_shoot() で敵が多彩な攻撃を仕掛けます。
    ((Shot)FireArray[i]).Loop() で時間の経過と共に弾丸の色を変えています。
    弾丸がウインドウの外に出ると ArrayList から削除します。
    敵の弾丸と自機の当たり判定は行っていません。
                // 敵の攻撃
                fire_shoot(gameTime);
                // 敵の弾の移動
                for(i=0; i<FireArray.Count; i++)
                {
                    ((Shot)FireArray[i]).Loop(gameTime, 500);
                    if (((Shot)FireArray[i]).Move(new Rectangle(0, 0, 1200, 800)))
                        FireArray.RemoveAt(i);
                }
    
                base.Update(gameTime);
            }
        
  11. Draw() メソッドに画像を描画するコードを追加します。
    Char Class で自機と敵機を描画します。
    AllayList に登録されている自機と敵機の弾丸を描画します。
            protected override void Draw(GameTime gameTime)
            {
                graphics.GraphicsDevice.Clear(Color.CornflowerBlue);
    
                // TODO: ここに描画処理を記述します。
                spriteBatch.Begin();                    //描画開始
                ship.Draw(spriteBatch);                 //自機描画
                teki.Draw(spriteBatch);                 //敵機描画
                //弾を描画
                foreach (Shot SHOT in ShotArray)    SHOT.Draw(spriteBatch);
                foreach (Shot SHOT in FireArray)    SHOT.Draw(spriteBatch);
                spriteBatch.End();
    
                base.Draw(gameTime);
            }
        
  12. 敵機は画面上部で左右に往復運動をします。
            public void teki_move( )
            {
                teki.pos += teki.vect;
                if (teki.pos.X > 1150)
                {   teki.vect.X = -2;
                }
                if (teki.pos.X < 0)
                {   teki.vect.X = 2;
                }
            }
        
  13. 自機を矢印キーで操作します。
    Vector2.Clamp() で移動範囲を制限します。
            public void jiki_move ( )
            {
                if (Keyboard.GetState().IsKeyDown(Keys.Left))   ship.pos.X -= 4;
                if (Keyboard.GetState().IsKeyDown(Keys.Right))  ship.pos.X += 4;
                if (Keyboard.GetState().IsKeyDown(Keys.Up))     ship.pos.Y -= 3;
                if (Keyboard.GetState().IsKeyDown(Keys.Down))   ship.pos.Y += 3;
                ship.pos = Vector2.Clamp(ship.pos, new Vector2(0,400), new Vector2(1150,750));
            }
        
  14. Space キーで自機から弾丸を連射します。
    4,1,2 の2は画像の番号(色違いの弾丸)です。
            public void tama_shoot(GameTime gameTime)
            {   if (Keyboard.GetState().IsKeyDown(Keys.Space))
                {
                    if (settim + 300 < gameTime.TotalGameTime.TotalMilliseconds)
                    {
                        if (20>ShotArray.Count)
                        {   settim = gameTime.TotalGameTime.TotalMilliseconds;
                            ShotArray.Add(new Shot(tama, ship.pos, new Vector2(0, -4),4,1,2));
                        }
                    }
                }
            }
        
  15. 敵機からの多彩な攻撃を仕掛けます。
    if (0 < FireArray.Count) で前回発射した弾丸がウインドウの外に消えたときに、次の攻撃を仕掛けます。
    fire_n が攻撃の種類です。
    「縦一列/横一列/矢印状/広がるように発射/円状に発射/乱数で発射」を順に繰り返します。
            public void fire_shoot(GameTime gameTime)
            {   int     i;
    
                if (0 < FireArray.Count)    return;
                switch (Fire_n)
                {
                    case 0:     //縦列発射
                        for (i = 0; i < 5; i++)
                        {
                            FireArray.Add(new Shot(tama, new Vector2(teki.pos.X, teki.pos.Y + i * 60),
                                new Vector2(0, 4), 4, 1));
                        }
                        break;
                    case 1:     //横列発射
                        for (i = 0; i < 5; i++)
                        {
                            FireArray.Add(new Shot(tama, new Vector2(teki.pos.X + (2 - i) * 60, teki.pos.Y),
                                new Vector2(0, 4), 4, 1));
                        }
                        break;
                    case 2:     //矢印発射
                        for (i = 0; i < 5; i++)
                        {
                            FireArray.Add(new Shot(tama, new Vector2(teki.pos.X + (2 - i) * 60,
                                teki.pos.Y + 40 - Math.Abs(2 - i) * 30), new Vector2(0, 4), 4, 1));
                        }
                        break;
                    case 3:     //放射状発射
                        for (i = 0; i < 5; i++)
                        {
                            FireArray.Add(new Shot(tama, teki.pos, new Vector2((2 - i) * 1.5f, 4), 4, 1));
                        }
                        break;
                    case 4:     //円形発射
                        for (i = 0; i < 18; i++)
                        {
                            FireArray.Add(new Shot(tama, teki.pos, new Vector2(0, 4), 4, 1));
                            ((Shot)FireArray[i]).Rot(i*20, 5);
                        }
                        break;
                    case 5:     //乱数発射
                        for (i = 0; i < 20; i++)
                        {
                            FireArray.Add(new Shot(tama, teki.pos, new Vector2((float)(rand.Next(100)-50)/10.0f,
                                (float)(rand.Next(100)) / 10.0f + 1.5f), 4, 1));
                        }
                        break;
    
                }
                Fire_n = (Fire_n + 1) % 6;
            }
        

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

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