爆発音と爆発のアニメーション

弾丸が ENEMY(敵)にヒットすると爆発音を鳴らして爆発のアニメーションを行います。
弾丸を一括管理する SHOT Object Class を作成します。

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

プログラムの作成

  1. 弾丸を一括管理する SHOT Object Class です。
    メモ帳などでタイプして SHOT.java の名前で保存して下さい。
    //★ 弾丸を管理するクラス SHOT Object Class    前田 稔
    import java.awt.*;
    import javax.swing.*;
    import java.io.*;
    import java.awt.Image;
    import javax.imageio.ImageIO;
    import java.awt.geom.Point2D;
    
    class SHOT extends JFrame
    {   static  Image   Img;    // 弾の画像
        static  Dimension   size= new Dimension();    // Sprite Size
        static  Dimension   num= new Dimension(1,1);  // Sprite 並び数
        static  Rectangle   rect= new Rectangle(0,0,800,600);// Game Window
    
        Point2D.Float   pos;    // Sprite の座標
        Point2D.Float   vect;   // Sprite の移動量
        byte            Flag;   // 弾フラグ
        int             Sp_no;  // Sprite の番号
    
        // Constructor
        public SHOT()
        {   pos= new Point2D.Float();
            vect= new Point2D.Float();
            Sp_no= 0;
            Flag= 0;
        }
    
        // Sprite 番号と座標の設定(描画開始)
        public void Set(int n,float xp,float yp,float xv,float yv)
        {   Sp_no= n;
            pos.setLocation(xp,yp);
            vect.setLocation(xv,yv);
            Flag= 1;
        }
    
        // Update method
        public void Update()
        {   if (Flag==0)    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)     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;
        }
    
        // Sprite View
        public void View(Graphics g)
        {   int sx, sy;
            sx = (Sp_no%num.width) * size.width;
            sy = (Sp_no/num.width) * size.height;
            if (Img != null)
            {   g.drawImage(Img,(int)pos.x,(int)pos.y,(int)pos.x+size.width,(int)pos.y+size.height,
                            sx,sy,sx+size.width,sy+size.height,this);
            }
        }
    
        // Load Image
        static public void LoadImage(String filename, int ws, int hs)
        {   size.setSize(ws,hs);
            num.setSize(1,1);
            //画像ロード
            File infile = new File(filename);
            Img = loadImage(infile);
            num.width= Img.getWidth(null)/size.width;
            num.height= Img.getHeight(null)/size.height;
            if (num.width<1 || num.height<1)   System.out.println("Image File Error" + filename);
        }
    
        // Load Image
        static public Image loadImage(File f)
        {   try
            {   Image img = ImageIO.read(f);
                return img;
            }
            catch (IOException e)
            {   throw new RuntimeException(e);  }
        }
    
        // Game Window の設定
        static public void SetRect(int x, int y, int w, int h)
        {   rect.setBounds(x,y,w,h);
        }
    }
    
  2. SHOT.java をコンパイルして SHOT.class を作成して下さい。
    爆発のアニメーションや Enemy を描画する ANIME.java をコンパイルして ANIME.class を作成して下さい。
    ANIME.java の説明は ANIME Class でアニメーション を参照して下さい。
    ANIME.class と SHOT.class を使って、弾丸がヒットすると爆発音を鳴らして爆発のアニメーションを行います。
  3. Shoot_08.java の名前で ANIME.class と SHOT.class と同じフォルダーに格納して下さい。
    //★ 爆発音と爆発のアニメーション    前田 稔
    import java.awt.*;
    import javax.swing.*;
    import java.awt.event.*;
    import java.util.*;
    import java.io.*;
    import java.io.File;
    import java.io.IOException;
    import javax.sound.sampled.*;
    
    class Shoot_08 extends JFrame implements KeyListener
    {   // MyShip の領域
        int         key_t[] = { 0,0,0,0,0 };    //UP, RIGHT, DOWN, LEFT, SPACE
        Point       pos= new Point(300,380);    //Ship の座標
        Image       ship;               //Ship の画像
        // SHOT 弾丸の領域
        ArrayList<SHOT>   list;         //弾の登録
        SHOT        shotw;              //SHOT のワーク
        long        nowTime,drawTime,shotTime;
        // Enemy の領域
        ANIME       enemy = null;
        // Double Buffer
        Dimension   size;
        Image       back;
        Graphics    buffer;
        // Sound &爆発の領域
        Bomb        bomb;
        ANIME       anime = null;
    
        // Main
        public static void main(String args[])
        {   new Shoot_08();
        }
    
        // Constructor
        public Shoot_08()
        {   super("Shooting");
            ship = getToolkit().getImage("c:\\data\\test\\ship.png");
            SHOT.LoadImage("c:\\data\\test\\tama4.png",32,32);
            enemy = new ANIME("c:\\data\\test\\ene5.png",80,96);
            enemy.Set(0,30,40,4,0);         // Enemy の描画を開始
            enemy.SetRect(30,40,700,500);   // Enemy の枠
            anime = new ANIME("c:\\data\\test\\Bomb.gif",128,128);
            bomb = new Bomb("C:\\DATA\\Test\\14_5.wav");
            list = new ArrayList<SHOT>();   // 弾丸を登録する List の初期化
            SHOT.SetRect(40,40,700,500);    // 弾丸の枠
            shotTime= 0;                    // 連射の初期化
            addKeyListener(this);
            ThreadClass threadcls = new ThreadClass();
            Thread thread = new Thread(threadcls);
            thread.start();
            setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            setSize(800, 600);
            setLocation(200,200);
            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;
                        action();
                        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);
            enemy.View(buffer);                 // ENEMY の描画
            if (ship!=null)                     // MyShip の描画
            {   buffer.drawImage(ship,pos.x,pos.y,this);  }
            for(int i=0; i<list.size(); i++)    // 弾丸の描画
                list.get(i).View(buffer);
            if (anime!=null)                    // 爆発の描画
                anime.View(buffer);
            g.drawImage(back,0,0,this);
        }
    
        // 座標の更新&弾の削除
        public void Update()
        {   enemy.Loop(10000);                  // ENEMY を切り替える
            enemy.Bound();                      // ENEMY が跳ね返る 
            for(int i=0; i<list.size(); i++)
            {   list.get(i).Update();           // 弾丸の座標を更新
                if (enemy.Hit(list.get(i).pos.x,list.get(i).pos.y,50))
                {   bomb.Play();                // ヒットしたとき
                    anime.Set(0,enemy.pos.x,enemy.pos.y,0,0);
                    enemy.Set(0,30,40,4,0);
                }
            }
            for(int i=list.size()-1; i>=0; i--) // ゲーム画面から出た弾丸を削除
                if (list.get(i).Flag==0)    list.remove(i);
            anime.Next(200);                    // 爆発のアニメーション
        }
    
        // シップの移動
        public void action()
        {   if (key_t[0]==1)    pos.y-= 4;  // UP
            if (key_t[1]==1)    pos.x+= 4;  // RIGHT
            if (key_t[2]==1)    pos.y+= 4;  // DOWN
            if (key_t[3]==1)    pos.x-= 4;  // LEFT
            if (key_t[4]==1)                // Space で連射
            {   if (shotTime<nowTime)
                {   shotTime= nowTime+200;
                    shotw = new SHOT();
                    shotw.Set(0,pos.x+20,pos.y,0,-8);
                    list.add(shotw);
                }
            }
        }
    
        // KeyEvent Listener
        public void keyPressed(KeyEvent e)
        {   switch(e.getKeyCode( ))
            {   case KeyEvent.VK_ESCAPE: System.exit(0);  break;
                case KeyEvent.VK_UP :   key_t[0]= 1;  break;
                case KeyEvent.VK_RIGHT: key_t[1]= 1;  break;
                case KeyEvent.VK_DOWN : key_t[2]= 1;  break;
                case KeyEvent.VK_LEFT : key_t[3]= 1;  break;
                case KeyEvent.VK_SPACE: key_t[4]= 1;  break;
            }
        }
        public void keyReleased(KeyEvent e)
        {   switch(e.getKeyCode( ))
            {   case KeyEvent.VK_UP :   key_t[0]= 0;  break;
                case KeyEvent.VK_RIGHT: key_t[1]= 0;  break;
                case KeyEvent.VK_DOWN : key_t[2]= 0;  break;
                case KeyEvent.VK_LEFT : key_t[3]= 0;  break;
                case KeyEvent.VK_SPACE: key_t[4]= 0;  break;
            }
        }
        public void keyTyped(KeyEvent e) { }
    }
    
    //★ Bomb Object Class
    class Bomb
    {
        File    soundFile;
        Clip    clip;
        AudioInputStream audioInputStream;
        AudioFormat     audioFormat;
        DataLine.Info   info;
    
        // Constructor
        Bomb(String file)
        {   try
            {   soundFile = new File(file);
                audioInputStream = AudioSystem.getAudioInputStream(soundFile);
                audioFormat = audioInputStream.getFormat();
                info = new DataLine.Info(Clip.class, audioFormat);
                clip = (Clip)AudioSystem.getLine(info);
                clip.open(audioInputStream);
            }
            catch (UnsupportedAudioFileException e)
            {   e.printStackTrace();  }
            catch (IOException e)
            {   e.printStackTrace();  }
            catch (LineUnavailableException e)
            {   e.printStackTrace();  }
        }
    
        // Play Bomb
        void Play()
        {   clip.setFramePosition(0);
            clip.start();
        }
    }
    
  4. 次のファイルをパスで指定したフォルダーに格納して下さい。
    ファイル 説明
    "c:\\data\\test\\ship.png" 80*80 の MySHip の画像です
    "c:\\data\\test\\tama4.png" 32*32 の Sprite が横に4枚並んだ弾丸の画像です
    "c:\\data\\test\\ene5.png" 80*96 の Sprite が横に5枚並んだ Enemy の画像です
    "c:\\data\\test\\Bomb.gif" 128*128 の Sprite が4段4列に並んだ爆発の画像です
    "C:\\DATA\\Test\\14_5.wav" 爆発の音です
    Space キーを押すと MyShip から弾丸が連射されます。
    弾丸が ENEMY にヒットすると爆発音が鳴りアニメーションが開始されます。

Object Class の説明

  1. SHOT Class は、敵・味方入り乱れて発射する弾丸を一括管理するために作成したクラスです。
    Shot Class からレベルアップした機能を重点的に説明します。
    Shot Class の説明は 弾丸を発射する から「Shot Class の説明」を参照して下さい。
    SHOT Class の中で弾丸の Image 領域を定義しました。
    弾丸のイメージは、敵・味方が一目で解るように色違いの画像を4枚並べたものを使っています。
    SHOT.LoadImage("c:\\data\\test\\tama4.png",32,32);

    どの弾丸の画像を使うかを Sp_no で指定します。
    SHOT Class で最も注目していただきたい所は、static で宣言された領域とメソッドです。
    static で宣言された領域は SHOT Class 全体で一個しか存在せず、生成された全 Class で共通に参照されます。
    つまり Img, size, num, rect などの領域は Class 全体で一個しか必要無いのです。
    逆に pos, vect, ... などは個々の弾丸ごとに別個の領域が必要です。
        class SHOT extends JFrame
        {   static  Image   Img;    // 弾の画像
            static  Dimension   size= new Dimension();    // Sprite Size
            static  Dimension   num= new Dimension(1,1);  // Sprite 並び数
            static  Rectangle   rect= new Rectangle(0,0,800,600);// Game Window
    
            Point2D.Float   pos;    // Sprite の座標
            Point2D.Float   vect;   // Sprite の移動量
            byte            Flag;   // 弾フラグ
            int             Sp_no;  // Sprite の番号
        
  2. Constructor で pos と vect をインスタンス化して下さい。
    Flag= 0; で非表示に設定しています。
    弾丸を発射するときは Set() メソッドを使って下さい。
        // Constructor
        public SHOT()
        {   pos= new Point2D.Float();
            vect= new Point2D.Float();
            Sp_no= 0;
            Flag= 0;
        }
        
  3. LoadImage() に付いて補足しておきます。
    メソッドで行っている処理は今までと変わりませんが、static で宣言されています。
    static で宣言されたメソッドはインスタンス化しなくても呼び出すことが出来ます。
        // Load Image
        static public void LoadImage(String filename, int ws, int hs)
        {   size.setSize(ws,hs);
            num.setSize(1,1);
            //画像ロード
            File infile = new File(filename);
            Img = loadImage(infile);
            num.width= Img.getWidth(null)/size.width;
            num.height= Img.getHeight(null)/size.height;
            if (num.width<1 || num.height<1)   System.out.println("Image File Error" + filename);
        }
        

Main Program の説明

  1. 前回までの説明は ENEMY(敵)が出現 を参照して下さい。
    爆発音を鳴らすので javax.sound.sampled.* をインポートして下さい。
    Sound の説明は WAV を鳴らす を参照して下さい。
    ArrayList を使って弾丸を登録します。
    ANIME enemy で Enemy を定義します。
    Bomb bomb が爆発音の定義です。
    ANIME anime で爆発のアニメーションを定義します。
    size, back, buffer は描画の「チラツキ」を抑えるための Back Buffer です。
        import javax.sound.sampled.*;
    
        class Shoot_08 extends JFrame implements KeyListener
        {   // MyShip の領域
            int         key_t[] = { 0,0,0,0,0 };    //UP, RIGHT, DOWN, LEFT, SPACE
            Point       pos= new Point(300,380);    //Ship の座標
            Image       ship;               //Ship の画像
            // SHOT 弾丸の領域
            ArrayList<SHOT>   list;         //弾の登録
            SHOT        shotw;              //SHOT のワーク
            long        nowTime,drawTime,shotTime;
            // Enemy の領域
            ANIME       enemy = null;
            // Sound &爆発の領域
            Bomb        bomb;
            ANIME       anime = null;
            // Double Buffer
            Dimension   size;
            Image       back;
            Graphics    buffer;
        
  2. Constructor から SHOT.LoadImage() で弾丸の画像をロードします。
    SHOT Class はインスタンス化されていないのですが static で定義されているので呼び出す事が出来ます。
    あとの説明は、これまでのページを参照して下さい。
        // Constructor
        public Shoot_08()
        {   super("Shooting");
            ship = getToolkit().getImage("c:\\data\\test\\ship.png");
            SHOT.LoadImage("c:\\data\\test\\tama4.png",32,32);
            enemy = new ANIME("c:\\data\\test\\ene5.png",80,96);
            enemy.Set(0,30,40,4,0);         // Enemy の描画を開始
            enemy.SetRect(30,40,700,500);   // Enemy の枠
            anime = new ANIME("c:\\data\\test\\Bomb.gif",128,128);
            bomb = new Bomb("C:\\DATA\\Test\\14_5.wav");
            list = new ArrayList<SHOT>();   // 弾丸を登録する List の初期化
            SHOT.SetRect(40,40,700,500);    // 弾丸の枠
            shotTime= 0;                    // 連射の初期化
            addKeyListener(this);
            ThreadClass threadcls = new ThreadClass();
            Thread thread = new Thread(threadcls);
            thread.start();
            setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            setSize(800, 600);
            setLocation(200,200);
            setBackground(Color.gray);
            setVisible(true);
            size = getSize();
            back= createImage(size.width, size.height);
            if (back==null) System.out.print("createImage Error");
        }
        
  3. Update() メソッドではゲーム画面に表示されている全ての弾丸と ENEMY との当り判定を行います。
    ENEMY にヒットしたときは爆発音を鳴らしてアニメーションを開始します。
    enemy.Set(0,30,40,4,0) で ENEMY の座標を左端に戻します。
    あとの説明は、これまでのページを参照して下さい。
        // 座標の更新&弾の削除
        public void Update()
        {   enemy.Loop(10000);                  // ENEMY を切り替える
            enemy.Bound();                      // ENEMY が跳ね返る 
            for(int i=0; i<list.size(); i++)
            {   list.get(i).Update();           // 弾丸の座標を更新
                if (enemy.Hit(list.get(i).pos.x,list.get(i).pos.y,50))
                {   bomb.Play();                // ヒットしたとき
                    anime.Set(0,enemy.pos.x,enemy.pos.y,0,0);
                    enemy.Set(0,30,40,4,0);
                }
            }
            for(int i=list.size()-1; i>=0; i--) // ゲーム画面から出た弾丸を削除
                if (list.get(i).Flag==0)    list.remove(i);
            anime.Next(200);                    // 爆発のアニメーション
        }
        

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