Sprite Class でアニメーション

Sprite Class を使って画像を切り分けてアニメーションします。

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

プログラムの作成

  1. 画像を切り分けて描画する Sprite Object Class です。
    SpriteJF.java の名前で保存して下さい。
    //★ JFrame を継承して画像を切り分ける Sprite Class    前田 稔
    import java.awt.*;
    import javax.swing.*;
    
    class SpriteJF extends JFrame 
    {   private Image   img;
        private int Height,Width,Hnum,Wnum;
        int     frameNum;
    
        // Constructor
        SpriteJF(String filename, int wn, int hn)
        {   Wnum= wn;
            Hnum= hn;
            Width=Height= 32;
            frameNum = Wnum*Hnum;
            img= getToolkit().getImage(filename);
            while(img.getHeight(null)==-1);
            Width= img.getWidth(null)/Wnum;
            Height= img.getHeight(null)/Hnum;
        }
    
        SpriteJF()
        {   this("Bijin.jpg",4,2);
        }
    
        SpriteJF(String x)
        {   this(x,1,1);
        }
    
        public void view(Graphics g, int n, int dx, int dy)
        {   int sx, sy;
            if (n >= frameNum)  return;
            sx = (n % Wnum) * Width;
            sy = (n / Wnum) * Height;
            if (img != null)
            {   g.drawImage(img,dx,dy,dx+Width,dy+Height,sx,sy,sx+Width,sy+Height,this);
            }
        }
    }
    
  2. SpriteJF.java をコンパイルして SpriteJF.class を作成して下さい。
    main() メソッドが定義されていないので、次のメッセージが表示されますが無視して下さい。
        Exception in thread "main" java.lang.NoSuchMethodError: main
         -- Press any key to exit (Input "c" to continue) --
        
  3. SpriteJF.class を使って、アニメーションをする MainJF.java です。
    SpriteJF.class と同じフォルダーに格納して下さい。
    //★ SpriteJF Class を使って美人のアニメーション    前田 稔
    import java.awt.*;
    import javax.swing.*;
    import java.awt.event.*;
    
    class MainJF extends JFrame implements ActionListener
    {   SpriteJF    spr;
        Timer       time;
        int         currFrame;
    
        // Main()
        public  static void main(String args[])
        {   new MainJF();
        }
    
        // Constructor
        MainJF(String filename, int fwn, int fhn)
        {   super("Animation");
            spr = new SpriteJF(filename, fwn, fhn);
            currFrame = 0;
            time = new Timer(200, this);
            time.start();
            setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            setSize(200, 200);
            setVisible(true);
        }
        MainJF()
        {   this("Bijin.jpg",4,2);
        }
        MainJF(String x)
        {   this(x,1,1);
        }
    
        public void paint(Graphics g)
        {   spr.view(g, currFrame, 50, 60);
        }
    
        public void actionPerformed(ActionEvent evt)
        {   Object source = evt.getSource();
            if (source == time)
            {   if (isShowing())
                {   currFrame = (currFrame+1) % spr.frameNum;
                    repaint();
                }
            }
        }
    }
    
  4. 画像ファイル(bijin.jpg)をプログラムと同じフォルダーに格納して下さい。
    このファイルには「幅=96, 高さ=96」の Sprite が横に4枚、縦に2段でならんでいます。
    MainJF.java をコンパイルして実行して下さい。
    Windows の画面に美人の画像がアニメーションされたら完成です。

プログラムの説明

Sprite Class を使って画像を切り分けてアニメーションします。
  1. Swing(スィング)を使ったプログラムの基本的な説明は Swing を使って、色を設定して線を描く を参照して下さい。
    SpriteJF spr は Sprite Class の定義です。
    time はタイマの Object です。
    currFrame はタイマ切り替えで表示する画像の番号です。
        import java.awt.*;
        import javax.swing.*;
        import java.awt.event.*;
    
        class MainJF extends JFrame implements ActionListener
        {   SpriteJF    spr;
            Timer       time;
            int         currFrame;
        
  2. MainJF では3種類の Constructor を用意しています。
    画像ファイル名と横と縦の Sprite 枚数を指定した Constructor と
    何もパラメータが指定されなかった場合と
    画像ファイルの名前だけが指定された場合です。
    new SpriteJF() で Sprite Class をインスタンス化します。
    currFrame は表示する画像の番号で、最初は0番目に設定します。
    new Timer(200, this) でタイマをインスタンス化します。
    time.start() でタイマを開始します。
        // Constructor
        MainJF(String filename, int fwn, int fhn)
        {   super("Animation");
            spr = new SpriteJF(filename, fwn, fhn);
            currFrame = 0;
            time = new Timer(200, this);
            time.start();
            setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            setSize(200, 200);
            setVisible(true);
        }
        MainJF()
        {   this("Bijin.jpg",4,2);
        }
        MainJF(String x)
        {   this(x,1,1);
        }
        
  3. タイマ割り込みのメソッドです。
    割り込みが自分に対するものかを if (source == time) で確かめます。
    isShowing() で画面が表示中かを確かめます。
    (currFrame+1) % spr.frameNum; で表示する画像を切り替えて repaint() で画像を描画するメソッドを呼びます。
        public void actionPerformed(ActionEvent evt)
        {   Object source = evt.getSource();
            if (source == time)
            {   if (isShowing())
                {   currFrame = (currFrame+1) % spr.frameNum;
                    repaint();
                }
            }
        }
        
  4. 画像を描画するメソッドです。
    spr.view(g,currFrame,50,60) で Sprite Class を使ってフレームを描画します。
    50,60 は画像を表示する Windows の左上座標です。
        public void paint(Graphics g)
        {   spr.view(g, currFrame, 50, 60);
        }
        

Sprite Object Class

  1. Sprite Object Class(SpriteJF.java) の説明です。
    基本的な説明は Image(画像)の描画 を参照して下さい。
  2. class SpriteJF は JFrame を継承しています。
    JFrame は getToolkit().getImage() の呼び出しと描画のときに必要になるようです。
        import java.awt.*;
        import javax.swing.*;
    
        class SpriteJF extends JFrame
        {
        
  3. コンストラクタで横と縦の枚数を受け取るので、画像サイズから Sprite の幅と高さを計算します。
    このとき getToolkit().getImage() はスレッドで実行されるので、画像を入力するのに多少時間がかかります。
    img.getHeight(null) で画像の高さを取得するのですが、入力が完了していない時は -1 になっています。
    そこで while(img.getHeight(null)==-1) で入力が終了するのを待ち合わせます。
  4. SpriteJF class でも三種類の Constructor を用意しました。
    パラメータを省略した場合と、ファイル名だけを指定した場合と全てを指定した場合の Constructor です。
        // Constructor
        SpriteJF(String filename, int wn, int hn)
        {   Wnum= wn;
              ・・・
        }
        SpriteJF()
        {   this("Bijin.jpg",4,2);
        }
    
        SpriteJF(String x)
        {   this(x,1,1);
        }
        

【NOTE】

  1. 画像入力には getToolkit().getImage() が良く使われるのですが、このメソッドはスレッドで画像ファイルの入力を開始するとすぐに次へ進みます。
    そこで「画像入力の完了」を待って画像サイズから Sprite の幅と高さを計算します。
  2. しかし while(img.getHeight(null)==-1) で入力の終了を待ち合わせるのは、いかにも品がありません。
    そこで MediaTracker で待ち合わせる方法を紹介します。
        // Constructor
        SpriteJF(String filename, int wn, int hn)
        {   Wnum= wn;
            Hnum= hn;
            Width=Height= 32;
            frameNum = Wnum*Hnum;
            img= getToolkit().getImage(filename);
            // MediaTracker で待ち合わせ
            MediaTracker tracker = new MediaTracker(new Component(){});
            tracker.addImage(img, 0);
            try
            {   tracker.waitForID(0);  }
            catch(InterruptedException e){}
            Width= img.getWidth(null)/Wnum;
            Height= img.getHeight(null)/Hnum;
            if (img.getHeight(null)==-1)    System.out.println("Image File Error" + filename);
        }
        
  3. 今回のように「画像入力の完了」を待ち合わせる場合は、getToolkit().getImage() よりも ImageIO.read() を使う方法がお勧めです。
    詳細は Image(画像)の描画 を参照して下さい。

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