弾丸を発射する

Space キーで弾丸を発射します。

前田稔の超初心者のプログラム入門

プログラムの作成

  1. メモ帳などでタイプして Shoot_03.java の名前で保存して下さい。
    //★ 弾丸を「縦/横/広がるように/矢印/円状」に発射    前田 稔
    import java.awt.*;
    import javax.swing.*;
    import java.awt.event.*;
    import java.awt.geom.Point2D;
    
    class Shoot_03 extends JFrame implements KeyListener
    {   Image       img;
        Shot[]      shot;
        Dimension   size;
        Image       back;
        Graphics    buffer;
    
        // Main
        public static void main(String args[])
        {   new Shoot_03();
        }
    
        // Constructor
        public Shoot_03()
        {   super("Shot Class");
            img = getToolkit().getImage("c:\\data\\test\\Tama_R.png");
            shot = new Shot[11];
            for(int i=0; i<shot.length; i++) shot[i] = new Shot();
            addKeyListener(this);
            ThreadClass threadcls = new ThreadClass();
            Thread thread = new Thread(threadcls);
            thread.start();
            setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            setSize(640, 480);
            setBackground(Color.gray);
            setVisible(true);
            size = getSize();
            back= createImage(size.width, size.height);
            if (back==null) System.out.print("createImage Error");
        }
    
        //  Runnable Class
        class ThreadClass implements Runnable
        {
            public void run()
            {   long    nowTime,drawTime;
                nowTime= System.currentTimeMillis();
                drawTime= nowTime+500;
                while(true)
                {   nowTime= System.currentTimeMillis();
                    if (drawTimei<nowTime)
                    {   drawTime= nowTime+30;
                        Update();
                        repaint();
                    }
                }
            }
        }
    
        // Paint Method
        public void paint(Graphics g)
        {   if (back==null)     return;
            buffer= back.getGraphics();
            if (buffer==null)   return;
            size = getSize();
            buffer.setColor(getBackground());
            buffer.fillRect(0, 0, size.width, size.height);
            for(int i=0; ii<shot.length; i++)
                if (shot[i].flag==1)    buffer.drawImage(img,(int)shot[i].pos.x,(int)shot[i].pos.y,this);
            g.drawImage(back,0,0,this);
        }
    
        // 座標の更新
        public void Update()
        {   for(int i=0; ii<shot.length; i++) shot[i].Update();
        }
    
        // KeyEvent Listener
        public void keyPressed(KeyEvent e)
        {   int i;
            switch(e.getKeyCode( ))
            {   case KeyEvent.VK_ESCAPE: System.exit(0); break;
                case KeyEvent.VK_SPACE:
                    for(i=0; ii<shot.length && shot[i].flag==0; i++);
                    if (ii<shot.length)   break;
                    for(i=0; ii<shot.length; i++) shot[i].Set(20,i*30+60,4,0);             //縦に並べる
                    //for(i=0; ii<shot.length; i++) shot[i].Set(20,i*10+160,4,(i-5)*0.3f);   //広がるように
                    //for(i=0; ii<5; i++)  shot[i].Set(i*30,200,4,0);                        //横に並べる
                    //for(i=0; ii<7; i++)  shot[i].Set((3-Math.abs(i-3))*30,i*50+100,4,0);   //矢印状
                    /*for(i=0; ii<10; i++)
                    {   shot[i].Set(300,240,0,0);
                        shot[i].Rot(i*36,5);        //円状に移動
                    }*/
                    break;
            }
        }
        public void keyReleased(KeyEvent e) { }
        public void keyTyped(KeyEvent e) { }
    }
    
    // 弾丸の座標を管理するクラス
    class Shot
    {   Point2D.Float   pos;    //弾の座標
        Point2D.Float   vect;   //弾の移動
        byte    flag;           //弾フラグ
    
        // Constructor
        public Shot()
        {   pos= new Point2D.Float();
            vect= new Point2D.Float();
            flag= 0;
        }
    
        // Set method
        public void Set(float xp,float yp,float xv,float yv)
        {   pos.x= xp;
            pos.y= yp;
            vect.x= xv;
            vect.y= yv;
            flag= 1;
        }
    
        // Update method
        public void Update()
        {   if (flag==0)    return;
            pos.x+= vect.x;
            pos.y+= vect.y;
            if (pos.x>640 || pos.xi<0 || pos.y>480 || pos.yi<0)   flag= 0;
        }
    
        // 円状に弾の移動を設定
        public void Rot(float rt, float len)
        {   vect.x = (float)(Math.sin(rt / 180 * 3.14)) * len;
            vect.y = (float)(Math.cos(rt / 180 * 3.14)) * len;
        }
    }
    
  2. 画像ファイル("c:\\data\\test\\Tama_R.png")をパスで指定したフォルダーに格納して下さい。
    Tama_R.png は 32*32 の弾丸の画像ですが、テストするときは適当なサイズであれば何でも構いません。

プログラムの説明

  1. 前回までの説明は キーでシップを操作 を参照して下さい。
    Image img は弾丸の画像です。
    Shot[] shot で弾丸の座標を管理する Shot Class の配列を定義します。
    size, back, buffer は描画の「チラツキ」を抑えるための Back Buffer です。
    Back Buffer の説明は Double Buffer を使う を参照して下さい。
        Image       img;
        Shot[]      shot;
        Dimension   size;
        Image       back;
        Graphics    buffer;
        
  2. Constructor で弾丸の画像を読み込みます。
    new Shot[11] で最大11発分の Shot Class の領域を確保します。
    Class の配列を使うときは、各要素を new Shot() でインスタンス化して下さい。
    キーを検出するために KeyListener を設定します。
    Runnable でスレッドを起動します。
    ウインドウの背景を gray に設定します。
    画面の「チラツキ」を抑えるために Back Buffer を作成します。
        // Constructor
        public Shoot_03()
        {   super("Shot Class");
            img = getToolkit().getImage("c:\\data\\test\\Tama_R.png");
            shot = new Shot[11];
            for(int i=0; i<shot.length; i++) shot[i] = new Shot();
            addKeyListener(this);
            ThreadClass threadcls = new ThreadClass();
            Thread thread = new Thread(threadcls);
            thread.start();
            setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            setSize(640, 480);
            setBackground(Color.gray);
            setVisible(true);
            size = getSize();
            back= createImage(size.width, size.height);
            if (back==null) System.out.print("createImage Error");
        }
        
  3. スレッドで起動する ThreadClass です。
    30 _秒ごとに Update() メソッドを呼んで repaint() で描画しています。
    能力の低いマシンではこの値を大きく(FPS を小さく)設定して下さい。
    FPS の説明は Thread で FPS を制御 または Runnable で FPS を制御 を参照して下さい。
        //  Runnable Class
        class ThreadClass implements Runnable
        {
            public void run()
            {   long    nowTime,drawTime;
                nowTime= System.currentTimeMillis();
                drawTime= nowTime+500;
                while(true)
                {   nowTime= System.currentTimeMillis();
                    if (drawTime<nowTime)
                    {   drawTime= nowTime+30;
                        Update();
                        repaint();
                    }
                }
            }
        }
        
  4. paint() メソッドでは BackBuffer にイメージを作成してから一挙に FrontBuffer に転送します。
    BackBuffer の準備が整っていることを確認します。
    fillRect() で画面をクリアして、shot[] 配列に登録されている弾丸を描画します。
        // Paint Method
        public void paint(Graphics g)
        {   if (back==null)     return;
            buffer= back.getGraphics();
            if (buffer==null)   return;
            size = getSize();
            buffer.setColor(getBackground());
            buffer.fillRect(0, 0, size.width, size.height);
            for(int i=0; i<shot.length; i++)
                if (shot[i].flag==1)    buffer.drawImage(img,(int)shot[i].pos.x,(int)shot[i].pos.y,this);
            g.drawImage(back,0,0,this);
        }
        
  5. 弾丸の座標を更新する Update() メソッドです。
    Shot Class の Update() メソッドを呼び出します。
        // 座標の更新
        public void Update()
        {   for(int i=0; i<shot.length; i++) shot[i].Update();
        }
        
  6. Space キーで弾丸を発射する KeyEvent Listener です。
    shot[] 配列に登録されている弾丸が全て画面上から消えたことを確認して、次の弾丸を発射します。
    上から順に、縦に並べて発射/弾丸が広がるように移動/横に並べて発射/矢印状に発射/円状に発射です。
    順にコメントを外して確かめて下さい。
        // KeyEvent Listener
        public void keyPressed(KeyEvent e)
        {   int i;
            switch(e.getKeyCode( ))
            {   case KeyEvent.VK_ESCAPE: System.exit(0); break;
                case KeyEvent.VK_SPACE:
                    for(i=0; i<shot.length && shot[i].flag==0; i++);
                    if (i<shot.length)   break;
                    for(i=0; i<shot.length; i++) shot[i].Set(20,i*30+60,4,0);             //縦に並べる
                    //for(i=0; i<shot.length; i++) shot[i].Set(20,i*10+160,4,(i-5)*0.3f);   //広がるように
                    //for(i=0; i<5; i++)  shot[i].Set(i*30,200,4,0);                        //横に並べる
                    //for(i=0; i<7; i++)  shot[i].Set((3-Math.abs(i-3))*30,i*50+100,4,0);   //矢印状
                    /*for(i=0; i<10; i++)
                    {   shot[i].Set(300,240,0,0);
                        shot[i].Rot(i*36,5);        //円状に移動
                    }*/
                    break;
            }
        }
        

Shot Class の説明

  1. 弾丸一発ごとに座標を管理する Shot Object Class です。
    pos が現在の座標で、vect がその移動量です。
    ウインドウの座標は整数値で指定するのですが、座標の計算には float を使っています。
    flag は描画フラグで、1:描画する/0:描画しないです。
        // 弾丸の座標を管理するクラス
        class Shot
        {   Point2D.Float   pos;    //弾の座標
            Point2D.Float   vect;   //弾の移動
            byte    flag;           //弾フラグ
        
  2. Constructor で Class の領域を初期化します。
    Point2D.Float をインスタンス化して下さい。
        // Constructor
        public Shot()
        {   pos= new Point2D.Float();
            vect= new Point2D.Float();
            flag= 0;
        }
        
  3. Set() メソッドで弾丸を登録します。
        // Set method
        public void Set(float xp,float yp,float xv,float yv)
        {   pos.x= xp;
            pos.y= yp;
            vect.x= xv;
            vect.y= yv;
            flag= 1;
        }
        
  4. Update() メソッドで弾丸の座標を更新します。
    ウインドウの外に出た弾丸の flag をリセットします。
        // Update method
        public void Update()
        {   if (flag==0)    return;
            pos.x+= vect.x;
            pos.y+= vect.y;
            if (posx>640 || posx<0 || posy>480 || posy<0)   flag= 0;
        }
        
  5. Rot() メソッドは弾丸を円状に発射するときに使います。
        // 円状に弾の移動を設定
        public void Rot(float rt, float len)
        {   vect.x = (float)(Math.sin(rt / 180 * 3.14)) * len;
            vect.y = (float)(Math.cos(rt / 180 * 3.14)) * len;
        }
        

超初心者のプログラム入門(Java2)