Array List で連射する

Space キーを押し続けると Array List で連射します。

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

プログラムの作成

  1. メモ帳などでタイプして Shoot_04.java の名前で保存して下さい。
    //★ Array List で連射    前田 稔
    import java.awt.*;
    import javax.swing.*;
    import java.awt.event.*;
    import java.awt.geom.Point2D;
    import java.util.*;
    
    class Shoot_04 extends JFrame implements KeyListener
    {   // Shot
        ArrayList<Shot>   list;
        Image       img;
        Shot        shotw;
        long        nowTime,drawTime,shotTime;
        // Double Buffer
        Dimension   size;
        Image       back;
        Graphics    buffer;
    
        // Main
        public static void main(String args[])
        {   new Shoot_04();
        }
    
        // Constructor
        public Shoot_04()
        {   super("Shot Class");
            img = getToolkit().getImage("c:\\data\\test\\Tama_R.png");
            list = new ArrayList<Shot>();
            shotTime= 0;
            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()
            {   nowTime= System.currentTimeMillis();
                drawTime= nowTime+500;
                while(true)
                {   nowTime= System.currentTimeMillis();
                    if (drawTime<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; i<list.size(); i++)
                 buffer.drawImage(img,(int)list.get(i).pos.x,(int)list.get(i).pos.y,this);
            g.drawImage(back,0,0,this);
        }
    
        // 座標の更新&弾の削除
        public void Update()
        {   for(int i=0; i<list.size(); i++)    list.get(i).Update();
            for(int i=list.size()-1; i>=0; i--)
                if (list.get(i).flag==0)    list.remove(i);
        }
    
        // KeyEvent Listener(SPACE キーで連射)
        public void keyPressed(KeyEvent e)
        {   switch(e.getKeyCode( ))
            {   case KeyEvent.VK_ESCAPE: System.exit(0); break;
                case KeyEvent.VK_SPACE:
                    nowTime= System.currentTimeMillis();
                    if (shotTime<nowTime)
                    {   shotTime= nowTime+100;
                        shotw = new Shot();
                        shotw.Set(20,200,8,0);
                        list.add(shotw);
                    }
                    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.x<0 || pos.y>480 || pos.y<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. 前回までの説明は 弾丸を発射する を参照して下さい。
    ArrayList を使うとサイズの制限が無くなり、要素の「追加/挿入/削除」が簡単に出来るようになります。
    ArrayList の説明は ArrayList で String を使う を参照して下さい。
  2. ArrayList を使うときは java.util.* をインポートして下さい。
    配列に代えて ArrayList<Shot> list; に Shot Class(弾丸の座標を管理するクラス)を登録します。
    nowTime, drawTime, shotTime は時刻を保存する領域です。
    size, back, buffer は描画の「チラツキ」を抑えるための Back Buffer です。
    Back Buffer の説明は Double Buffer を使う を参照して下さい。
        import java.awt.*;
        import javax.swing.*;
        import java.awt.event.*;
        import java.awt.geom.Point2D;
        import java.util.*;
    
        class Shoot_04 extends JFrame implements KeyListener
        {   // Shot
            ArrayList<Shot>   list;
            Image       img;
            Shot        shotw;
            long        nowTime,drawTime,shotTime;
            // Double Buffer
            Dimension   size;
            Image       back;
            Graphics    buffer;
        
  3. Constructor で弾丸の画像を読み込みます。
    new ArrayList<Shot>() で ArrayList を初期化します。
    このとき配列と違ってサイズを指定する必要はありません。
    キーを検出するために KeyListener を設定します。
    Runnable でスレッドを起動します。
    スレッドの説明は Runnable を使う を参照して下さい。
    Back Buffer を作成します。
        // Constructor
        public Shoot_04()
        {   super("Shot Class");
            img = getToolkit().getImage("c:\\data\\test\\Tama_R.png");
            list = new ArrayList<Shot>();
            shotTime= 0;
            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");
        }
        
  4. paint() メソッドでは BackBuffer にイメージを作成してから一挙に FrontBuffer に転送します。
    BackBuffer の準備が整っていることを確認します。
    fillRect() で画面をクリアして、list に登録されている弾丸を描画します。
        // 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<list.size(); i++)
                 buffer.drawImage(img,(int)list.get(i).pos.x,(int)list.get(i).pos.y,this);
            g.drawImage(back,0,0,this);
        }
        
  5. 弾丸の座標を更新する Update() メソッドです。
    Shot Class の Update() メソッドを呼び出します。
    この時、ウインドウの外に出た弾丸の flag がリセットされるので list から削除します。
    先頭から削除して行くとインデックスがずれるので、後部から削除して行きます。
    ArrayList の要素の削除は ArrayList で String を使う を参照して下さい。
        // 座標の更新&弾の削除
        public void Update()
        {   for(int i=0; i<list.size(); i++)    list.get(i).Update();
            for(int i=list.size()-1; i>=0; i--)
                if (list.get(i).flag==0)    list.remove(i);
        }
        
  6. Space キーで弾丸を発射する KeyEvent Listener です。
    shotTime で連射の間隔を調整します。
    弾丸は shotw = new Shot() で Shot Class を生成して、list.add(shotw) で登録します。
    このとき一発目と二発目の間にタイムラグが生じます。
    タイムラグの説明は Timer でキーの状態を調べる を参照して下さい。
        // KeyEvent Listener(SPACE キーで連射)
        public void keyPressed(KeyEvent e)
        {   switch(e.getKeyCode( ))
            {   case KeyEvent.VK_ESCAPE: System.exit(0); break;
                case KeyEvent.VK_SPACE:
                    nowTime= System.currentTimeMillis();
                    if (shotTime<nowTime)
                    {   shotTime= nowTime+100;
                        shotw = new Shot();
                        shotw.Set(20,200,8,0);
                        list.add(shotw);
                    }
                    break;
            }
        }
        
  7. スレッドで起動する ThreadClass と Shot Class は前回と同じです。

【課題】

タイムラグが生じないように弾丸を発射して下さい。

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