ANIME Class でアニメーション

爆発のアニメーションや Enemy を描画する ANIME Class を作成します。

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

プログラムの作成

  1. 爆発のアニメーションや Enemy を描画する ANIME Class です。
    メモ帳などでタイプして ANIME.java の名前で保存して下さい。
    //★ ANIME Object Class(JFrame を継承)    前田 稔
    import java.awt.*;
    import javax.swing.*;
    import java.awt.geom.Point2D;
    
    class ANIME extends JFrame
    {   Image               Img;        // Anime Image
        private Dimension   size;       // Sprite Size
        private Dimension   num;        // Sprite 並び数
        private int         frameNum;   // num.width*num.height
        int                 Sp_no;      // Sprite の番号
        Point2D.Float       pos;        // Sprite の座標
        Point2D.Float       vect;       // Sprite の移動量
        Rectangle           rect;       // Game Window
        long                chgTime;    // Sprite の切替速度
    
        // Constructor
        ANIME(String filename, int ws, int hs)
        {   size= new Dimension(ws,hs);
            num= new Dimension(1,1);
            pos= new Point2D.Float();
            vect= new Point2D.Float();
            rect= new Rectangle(0,0,240,240);
            //画像ロード
            MediaTracker mediaT = new MediaTracker(this);
            Img = getToolkit().getImage(filename);
            mediaT.addImage(Img,0);
            //ロード待ち
            try  {   mediaT.waitForAll();  }
            catch(InterruptedException e) { }
            num.width= Img.getWidth(null)/size.width;
            num.height= Img.getHeight(null)/size.height;
            frameNum = num.width*num.height;
            if (num.width<1 || num.height<1)   System.out.println("Image File Error" + filename);
            Sp_no= 99;
            chgTime= 0;
        }
    
        // Sprite View
        public void View(Graphics g, int n, int dx, int dy)
        {   int sx, sy;
            if (n >= frameNum)  return;
            sx = (n%num.width) * size.width;
            sy = (n/num.width) * size.height;
            if (Img != null)
            {   g.drawImage(Img,dx,dy,dx+size.width,dy+size.height,
                            sx,sy,sx+size.width,sy+size.height,this);  }
        }
        public void View(Graphics g)
        {   View(g,Sp_no,(int)pos.x,(int)pos.y);
        }
    
        // 次の画像に切り替える
        public void Next(long Tim)
        {   if (Sp_no < frameNum)
            {   long  nowTime = System.currentTimeMillis();
                if (chgTime+Tim < nowTime)
                {   chgTime= nowTime;
                    Sp_no++;
                }
            }
            else Sp_no = 99;
        }
        public void Loop(long Tim)
        {   long  nowTime = System.currentTimeMillis();
            if (Sp_no < frameNum && chgTime+Tim < nowTime)
            {   chgTime= nowTime;
                Sp_no = (Sp_no+1) % frameNum;
            }
        }
    
        // Sprite 番号と座標の設定
        public void Set(int num,float xp,float yp,float xv,float yv)
        {   Sp_no= num;
            pos.setLocation(xp,yp);
            vect.setLocation(xv,yv);
        }
    
        // Game Window の設定
        // Update(), Bound() を使うときは設定する
        public void SetRect(int x, int y, int w, int h)
        {   rect.setBounds(x,y,w,h);
        }
    
        // Update method
        // 枠とvectを設定すること
        public void Update()
        {   if (Sp_no==99)  return;
            pos.x+= vect.x;
            pos.y+= vect.y;
            if (pos.x>(rect.x+rect.width) || pos.x<rect.x ||
                pos.y>(rect.y+rect.height) || pos.y<rect.y) Sp_no= 99;
        }
    
        // Bound method
        // 枠とvectを設定すること
        public void Bound()
        {   if (Sp_no==99)  return;
            pos.x+= vect.x;
            pos.y+= vect.y;
            if (pos.x>(rect.x+rect.width) || pos.x<rect.x)      vect.x= 0-vect.x;
            if (pos.y>(rect.y+rect.height) || pos.y<rect.y)     vect.y= 0-vect.y;
        }
    
        //当たり判定
        public boolean Hit(float targetx, float targety, float len)
        {   float   dist,wx,wy;
            if (Sp_no==99)  return false;
            wx = pos.x - targetx;
            wy = pos.y - targety;
            dist = (float)Math.sqrt((double)(wx * wx + wy * wy));
            if (dist<len)   return true;
            return false;
        }
    }
    
  2. ANIME.java をコンパイルして ANIME.class を作成して下さい。
    main() メソッドが定義されていないので、次のメッセージが表示されますが無視して下さい。
        Exception in thread "main" java.lang.NoSuchMethodError: main
         -- Press any key to exit (Input "c" to continue) --
        
  3. ANIME.class を使って、爆発のアニメーションを行う Shoot_06.java です。
    ANIME.class と同じフォルダーに格納して下さい。
    //★ ANIME Class で爆発のアニメーション    前田 稔
    import java.awt.*;
    import javax.swing.*;
    import java.awt.event.*;
    
    class Shoot_06 extends JFrame implements KeyListener
    {   ANIME       anime = null;
        Dimension   size;
        Image       back = null;
        Graphics    buffer = null;
    
        // Main
        public static void main(String args[])
        {   new Shoot_06();
        }
    
        // Constructor
        public Shoot_06()
        {   super("Image View");
            anime = new ANIME("c:\\data\\test\\Bomb.gif",128,128);
            anime.Set(0,40,50);         //アニメーションの開始
            addKeyListener(this);
            ThreadClass threadcls = new ThreadClass();
            Thread thread = new Thread(threadcls);
            thread.start();
            setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            setSize(200, 200);
            setBackground(Color.gray);
            setLocation(300,300);
            setVisible(true);
            size = getSize();
            back= createImage(size.width, size.height);
            if (back==null) System.out.println("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 (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;
            if (anime==null)    return;
            buffer.setColor(getBackground());
            buffer.fillRect(0, 0, size.width, size.height);
            anime.View(buffer);
            g.drawImage(back,0,0,this);
        }
    
        // 更新処理
        public void Update()
        {   anime.Next(200);  }
        //{   anime.Loop(200);  }
    
        // KeyEvent Listener
        public void keyPressed(KeyEvent e)
        {   switch(e.getKeyCode( ))
            {   case KeyEvent.VK_ESCAPE: System.exit(0); break;
                case KeyEvent.VK_SPACE:
                    anime.Set(0,40,50,0,0); //アニメーションの開始
                    break;
            }
        }
        public void keyReleased(KeyEvent e) { }
        public void keyTyped(KeyEvent e) { }
    }
    
  4. 画像ファイル("c:\\data\\test\\bomb.gif")をパスで指定したフォルダーに格納して下さい。
    bomb.gif は 128*128 の Sprite が4段4列に並んだ爆発の画像です。
    Space キーを押すと爆発のアニメーションが表示されます。

ANIME Class の説明

  1. ANIME Class はシューティングゲームの爆発や Enemy(敵)を描画する目的で作成した Class です。
    画像の切り分けは CARD Class で画像を切り分ける を参照して下さい。
    座標の管理は 弾丸を発射する の Shot Class を参照して下さい。
  2. size は Sprite 一枚分のサイズの領域です。
    num は横方向と縦方向に Sprite が何枚並んでいるかを記憶する領域です。
    Sp_no は描画する Sprite の番号で、99 の時は描画 OFF のフラグとして使います。
    rect は Game Window を設定する矩形領域です。
    chgTime はアニメーション画像を切り替えるタイミングを制御する領域です。
    他の領域の説明は、これまでのページを参照して下さい。
        class ANIME extends JFrame
        {   Image               Img;        // Anime Image
            private Dimension   size;       // Sprite Size
            private Dimension   num;        // Sprite 並び数
            private int         frameNum;   // num.width*num.height
            int                 Sp_no;      // Sprite の番号
            Point2D.Float       pos;        // Sprite の座標
            Point2D.Float       vect;       // Sprite の移動量
            Rectangle           rect;       // Game Window
            long                chgTime;    // Sprite の切替速度
        
  3. Constructor で画像を入力して領域を初期化します。
    パラメータで一枚分のサイズを受け取るので、画像のサイズから横と縦の枚数を計算します。
    Sp_no が描画フラグも兼ねていて、99 は OFF(描画しない)状態です。
        // Constructor
        ANIME(String filename, int ws, int hs)
        {   size= new Dimension(ws,hs);
            num= new Dimension(1,1);
            pos= new Point2D.Float();
            vect= new Point2D.Float();
            rect= new Rectangle(0,0,240,240);
            //画像ロード
            MediaTracker mediaT = new MediaTracker(this);
            Img = getToolkit().getImage(filename);
            mediaT.addImage(Img,0);
            //ロード待ち
            try  {   mediaT.waitForAll();  }
            catch(InterruptedException e) { }
            num.width= Img.getWidth(null)/size.width;
            num.height= Img.getHeight(null)/size.height;
            frameNum = num.width*num.height;
            if (num.width<1 || num.height<1)   System.out.println("Image File Error" + filename);
            Sp_no= 99;
            chgTime= 0;
        }
        
  4. View() メソッドは二本用意しています。
    画像(Sprite)の番号と座標を直接指定するメソッドと Class 内に持っている領域を使うメソッドです。
        // Sprite View
        public void View(Graphics g, int n, int dx, int dy)
        {   int sx, sy;
            if (n >= frameNum)  return;
            sx = (n%num.width) * size.width;
            sy = (n/num.width) * size.height;
            if (Img != null)
            {   g.drawImage(Img,dx,dy,dx+size.width,dy+size.height,
                            sx,sy,sx+size.width,sy+size.height,this);  }
        }
        public void View(Graphics g)
        {   View(g,Sp_no,(int)pos.x,(int)pos.y);
        }
        
  5. Sprite の番号を Tim(ミリ秒) 間隔で切り替えるメソッドです。
    Next() メソッドは、0から始めて frameNum-1 になると描画フラグを OFF にします。
    このメソッドは爆発のアニメーションなどに使用します。
    Loop() メソッドは「0〜frameNum-1」を何度でも繰り返します。
    このメソッドは自機や Enemy がアニメーションしながら動き回るときに使用します。
        // 次の画像に切り替える
        public void Next(long Tim)
        {   if (Sp_no < frameNum)
            {   long  nowTime = System.currentTimeMillis();
                if (chgTime+Tim < nowTime)
                {   chgTime= nowTime;
                    Sp_no++;
                }
            }
            else Sp_no = 99;
        }
        public void Loop(long Tim)
        {   long  nowTime = System.currentTimeMillis();
            if (Sp_no < frameNum && chgTime+Tim < nowTime)
            {   chgTime= nowTime;
                Sp_no = (Sp_no+1) % frameNum;
            }
        }
        
  6. Sprite 番号と座標を設定するメソッドです。
    Sp_no は最初 99(OFF) に設定されているので、このメソッドで描画を開始します。
        // Sprite 番号と座標の設定
        public void Set(int num,float xp,float yp,float xv,float yv)
        {   Sp_no= num;
            pos.setLocation(xp,yp);
            vect.setLocation(xv,yv);
        }
        
  7. Game Window(ゲーム画面)の矩形領域を設定するメソッドです。
    Update(), Bound() を使うときは事前に設定して下さい。
        // Game Window の設定
        public void SetRect(int x, int y, int w, int h)
        {   rect.setBounds(x,y,w,h);
        }
        
  8. 座標を更新する Update() メソッドです。
    Game Window(ゲーム画面)の外に出るとフラグを OFF にします。
        // Update method
        public void Update()
        {   if (Sp_no==99)  return;
            pos.x+= vect.x;
            pos.y+= vect.y;
            if (pos.x>(rect.x+rect.width) || pos.x<rect.x ||
                pos.y>(rect.y+rect.height) || pos.y<rect.y) Sp_no= 99;
        }
        
  9. Game Window(ゲーム画面)の枠で跳ね返るメソッドです。
    このメソッドを使うと Enemy がゲーム画面で上下左右に動きまわります。
        // Bound method
        public void Bound()
        {   if (Sp_no==99)  return;
            pos.x+= vect.x;
            pos.y+= vect.y;
            if (pos.x>(rect.x+rect.width) || pos.x<rect.x)      vect.x= 0-vect.x;
            if (pos.y>(rect.y+rect.height) || pos.y<rect.y)     vect.y= 0-vect.y;
        }
        
  10. 当たり判定を行う Hit() メソッドです。
    Enemy の座標と弾丸(ターゲット)の座標の直線距離を計算して len より短ければヒットと判定します。
    実際のゲームではプレイした感覚で len の値を決めて下さい。
    Enemy とターゲットの当り判定は、画像の左上座標で判定しています。
    場合によっては画像サイズを考慮して中央で判定する必要があるかも知れません。
        //当たり判定
        public boolean Hit(float targetx, float targety, float len)
        {   float   dist,wx,wy;
            if (Sp_no==99)  return false;
            wx = pos.x - targetx;
            wy = pos.y - targety;
            dist = (float)Math.sqrt((double)(wx * wx + wy * wy));
            if (dist<len)   return true;
            return false;
        }
        

Main Program の説明

  1. ANIME anime = null; で ANIME Class を定義します。
    size, back, buffer は描画の「チラツキ」を抑えるための Back Buffer です。
        class Shoot_06 extends JFrame implements KeyListener
        {   ANIME       anime = null;
            Dimension   size;
            Image       back = null;
            Graphics    buffer = null;
        
  2. Constructor で ANIME Class をインスタンス化します。
    あとの説明は、これまでのページを参照して下さい。
        // Constructor
        public Shoot_06()
        {   super("Image View");
            anime = new ANIME("c:\\data\\test\\Bomb.gif",128,128);
            anime.Set(0,40,50);         //アニメーションの開始
            addKeyListener(this);
            ThreadClass threadcls = new ThreadClass();
            Thread thread = new Thread(threadcls);
            thread.start();
            setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            setSize(200, 200);
            setBackground(Color.gray);
            setLocation(300,300);
            setVisible(true);
            size = getSize();
            back= createImage(size.width, size.height);
            if (back==null) System.out.println("createImage Error");
        }
        
  3. paint() メソッドでは ANIME Class 内に持っている領域(座標と画像番号)を使って描画しています。
        // Paint Method
        public void paint(Graphics g)
        {   if (back==null)     return;
            buffer= back.getGraphics();
            if (buffer==null)   return;
            if (anime==null)    return;
            buffer.setColor(getBackground());
            buffer.fillRect(0, 0, size.width, size.height);
            anime.View(buffer);
            g.drawImage(back,0,0,this);
        }
        
  4. KeyEvent Listener では、Space キーが押されると anime.Set(0,40,50,0,0); で爆発のアニメーションを開始します。
        // KeyEvent Listener
        public void keyPressed(KeyEvent e)
        {   switch(e.getKeyCode( ))
            {   case KeyEvent.VK_ESCAPE: System.exit(0); break;
                case KeyEvent.VK_SPACE:
                    anime.Set(0,40,50,0,0); //アニメーションの開始
                    break;
            }
        }
        
  5. 画像を更新する Update() メソッドです。
    anime.Next(200) で 200 o秒ごとに画像を切り替えて爆発のアニメーションを描画します。
        // 更新処理
        public void Update()
        {   anime.Next(200);  }
        

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