弾丸を一括管理

シューティングゲームで飛び交う弾丸を一括管理します。
C言語(Windows)でも 弾丸を一括して管理 を作成しています。

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

弾丸を一括管理

  1. 弾丸を一括管理する SHOOT Class を使ったソースコードです。
    @"c:\data\test\tama4.png" が 32*32 の色違いの弾丸が4枚並んだ画像です。
    /***********************************/
    /*★ SHOOT で一括管理    前田 稔 ★*/
    /***********************************/
    using System;
    using System.Drawing;
    using System.Windows.Forms;
    using System.Collections;
    using System.Runtime.InteropServices;
    
    //☆ 弾丸を一括管理するクラス
    public class SHOOT
    {
        class SHOT
        {
            public  int N;          //弾の種類
            public  float X, Y;     //弾の座標
            public  float DX, DY;   //弾の移動量
    
            // Constructor
            public SHOT(int n, float x, float y, float dx, float dy)
            {
                N = n;
                X = x;
                Y = y;
                DX = dx;
                DY = dy;
            }
        }
    
        public  Bitmap  Bmp;        //弾丸の画像
        public  Point   Size;       //Sprite の幅と高さ
        public  int     Wn;         //横の枚数
        public  ArrayList array;    //弾丸を登録
    
        // Constructor
        public SHOOT(string file, int w, int h)
        {
            try
            {   Bmp = new Bitmap(file);  }
            catch
            {
                MessageBox.Show("イメージが取得できません", "Error");
                return;
            }
            Size = new Point(w, h);
            Wn = Bmp.Width / w;
            array = new ArrayList();
        }
    
        // 弾丸を一個登録する(弾の種別/発射座標/方向)
        public void Add(int n, float x, float y, float dx, float dy)
        {   array.Add(new SHOT(n,x,y,dx,dy));  }
    
        // 弾丸を円状に登録する(弾の種別/発射座標)
        public void Circle(int n, float x, float y)
        {
            float dx, dy;
            for(int i=0; i<10; i++)
            {   dx = (float)(Math.Sin(i*36.0/180.0*Math.PI));
                dy = (float)(Math.Cos(i*36.0/180.0*Math.PI));
                array.Add(new SHOT(n,x,y,dx*2f,dy*2f));
            }
        }
    
        // 弾丸を並べて登録する(弾の種別/発射基点/方向/弾丸の数/間隔)
        public void Line(int n, float x, float y, float dx, float dy, int num, float px, float py)
        {
            float wx, wy;
            wx= x;
            wy= y;
            for(int i=0; i<num; i++)
            {
                array.Add(new SHOT(n,wx,wy,dx*2f,dy*2f));
                wx+= px;
                wy+= py;
            }
        }
    
        // 弾丸の描画
        public void View(Graphics g)
        {
            foreach(SHOT wk in array)
            {
                if (Bmp==null)  return;
                Rectangle des = new Rectangle((int)wk.X, (int)wk.Y, (int)Size.X, (int)Size.Y);
                Rectangle sou = new Rectangle((wk.N%Wn)*Size.X,(wk.N/Wn)*Size.Y,Size.X,Size.Y);
                g.DrawImage(Bmp,des,sou,GraphicsUnit.Pixel);
            }
        }
    
        // 画像全体を描画
        public void View(Graphics g, int x, int y)
        {   if (Bmp!=null)  g.DrawImage(Bmp,x,y);   }
    
        // 座標の移動(Rectangle を超えると削除)
        public void Move(Rectangle rect)
        {
            SHOT wk; 
            for(int i=0; i<array.Count; i++)
            {   wk = (SHOT)array[i];
                wk.X += wk.DX;
                wk.Y += wk.DY;
                if (wk.X<rect.Left || wk.X>rect.Right || wk.Y<rect.Top  || wk.Y>rect.Bottom)
                {   array.RemoveAt(i);
                    i--;
                }
            }
        }
    
        // 当たり判定
        public bool Hit(float targetx, float targety, float len)
        {   float   dist,wx,wy;
            foreach(SHOT wk in array)
            {
                wx = wk.X - targetx;
                wy = wk.Y - targety;
                dist = (float)Math.Sqrt((double)(wx * wx + wy * wy));
                if (dist<len)   return true;
            }
            return false;
        }
    }
    
    public class MyForm : Form
    {
        [DllImport("User32.dll")]
        static extern short GetAsyncKeyState(int vKey);
    
        SHOOT   App;
        private Timer timer1;
        private System.ComponentModel.IContainer components;
        Rectangle rect = new Rectangle(20,20,540,380);
    
        // Constructor
        public MyForm()
        {
            // timer1
            components = new System.ComponentModel.Container();
            timer1 = new System.Windows.Forms.Timer(this.components);
            SuspendLayout();
            timer1.Interval = 15;
            timer1.Tick += new System.EventHandler(this.timer1_Tick);
            // MyForm
            BackColor = SystemColors.AppWorkspace;
            this.Width = 640;
            this.Height = 480;
            SetStyle(ControlStyles.DoubleBuffer, true);
            SetStyle(ControlStyles.UserPaint, true);
            SetStyle(ControlStyles.AllPaintingInWmPaint, true);
            Paint += new PaintEventHandler(MyHandler);
            App = new SHOOT(@"c:\data\test\tama4.png",32,32);
            timer1.Start();
        }
    
        // 画像を描画
        private void MyHandler(object sender, PaintEventArgs e)
        {
            Graphics g = e.Graphics;
            App.View(g,50,50);
            App.View(g);
        }
    
        // タイマー割り込み
        private void timer1_Tick(object sender, EventArgs e)
        {
            short   kw;
            if (GetAsyncKeyState(0x1B)!=0)  Application.Exit();
            App.Move(rect);
            kw = GetAsyncKeyState(0x20);    //空白
            if (kw<0 && (kw&1)!=0)  App.Add(0,300,400,0,-1);
            kw = GetAsyncKeyState(0x0D);    //Enter
            if (kw<0 && (kw&1)!=0)  App.Circle(1,300,200);
            kw = GetAsyncKeyState(0x26);    //上
            if (kw<0 && (kw&1)!=0)  App.Line(2,80,400,0,-1,8,60,0);
            kw = GetAsyncKeyState(0x25);    //左
            if (kw<0 && (kw&1)!=0)  App.Line(3,80,300,0.2f,-1,8,60,10);
            kw = GetAsyncKeyState(0x27);    //右
            if (kw<0 && (kw&1)!=0)  App.Line(3,80,400,-0.6f,-1,8,60,-30);
            Invalidate();
        }
    }
    
    class form01
    {
        public static void Main()
        {
            MyForm mf = new MyForm();
            Application.Run(mf);
        }
    }
    
  2. SHOT が一個の弾丸を制御するクラスですが、これを取り込んで一括管理する SHOOT クラスを使います。
    SHOT クラスや基本的な弾丸の描画は 弾丸を発射 を参照して下さい。
    今回は色違いの4種類の弾丸を切り分けて使います。
    N は弾丸の番号です。
            public  int N;          //弾の種類
            public  float X, Y;     //弾の座標
            public  float DX, DY;   //弾の移動量
        
  3. 弾丸を一括管理する SHOOT Class です。
    Bmp は弾丸の画像で、Size は弾丸(Sprite)の幅と高さです。
    ArrayList もクラスの中で定義します。
        public  Bitmap  Bmp;        //弾丸の画像
        public  Point   Size;       //Sprite の幅と高さ
        public  int     Wn;         //横の枚数
        public  ArrayList array;    //弾丸を登録
        
  4. 弾丸を登録するメソッドを用意しました。
    Add() は一個の弾丸を登録します。
    Circle() は円状に弾丸を登録します。
    Line() は直線状に弾丸を登録します。
  5. View() は ArrayList に登録されている全ての弾丸を描画するメソッドです。
    Move() は弾丸の移動と、矩形の外に出た弾丸を削除するメソッドです。
    今回は使いませんが、Hit() で弾丸とターゲットの当たり判定をします。
  6. キーの種類によって、幾通りかの方法で弾丸を発射します。

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