敵の攻撃

攻撃方法を変えながら敵が反撃します。

Applet でプログラムを体験できます。
敵の反撃

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

プログラムの作成

  1. ANIME.java をコンパイルして ANIME.class を作成して下さい。
    ANIME Class の説明は ANIME Class でアニメーション を参照して下さい。
    SHOT.java をコンパイルして SHOT.class を作成して下さい。
    SHOT Class の説明は 爆発音と爆発のアニメーション を参照して下さい。
    Shoot_09.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_09 extends JFrame implements KeyListener
    {   //※ MyShip の領域
        int         key_t[] = { 0,0,0,0,0 };    //UP, RIGHT, DOWN, LEFT, SPACE
        Point       pos= new Point(300,440);    //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;
        //※ 敵の攻撃の領域
        int         pattern;            //攻撃のパターン
        long        enemyTime;          //攻撃の時間
    
        // Main
        public static void main(String args[])
        {   new Shoot_09();
        }
    
        // Constructor
        public Shoot_09()
        {   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");
            pattern = 0;
            enemyTime = System.currentTimeMillis()+3000;
        }
    
        //  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) && list.get(i).Sp_no==0)
                {   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()
        {   int i;
            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);
                }
            }
            // 敵の攻撃
            if (enemyTime<nowTime)
            {   enemyTime= nowTime+3000;
                switch(pattern)
                {   case 0: //横一列
                        for(i=0; i<7; i++)
                        {   shotw = new SHOT();
                            shotw.Set(1,i*110+40,50,0,8);
                            list.add(shotw);
                        }
                        break;
                    case 1: //円状に発射
                        for(i=0; i<18; i++)
                        {   shotw = new SHOT();
                            shotw.Set(2,400,240,0,0);
                            shotw.Rot(i*20,5);
                            list.add(shotw);
                        }
                        break;
                    case 2: //斜めに発射
                        for(i=0; i<7; i++)
                        {   shotw = new SHOT();
                            //shotw.Set(3,i*20+40,i*10*50,i*4,i*4+8);
                            shotw.Set(3,i*40+40,100-i*10,i*2,10-i);
                            list.add(shotw);
                        }
                        break;
                }
                pattern= (pattern+1)%3;
            }
        }
    
        // 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();
        }
    }
    
  2. 次のファイルをパスで指定したフォルダーに格納して下さい。
    ファイル 説明
    "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 にヒットすると爆発音が鳴りアニメーションが開始されます。
    敵が反撃して来ますが、MyShip との当り判定は行っていません。

プログラムの説明

  1. 前回までの説明は 爆発音と爆発のアニメーション を参照して下さい。
    前回から追加/修正した部分を中心に説明します。
    爆発音を鳴らすので javax.sound.sampled.* をインポートして下さい。
    Sound の説明は WAV を鳴らす を参照して下さい。
    pattern が敵からの攻撃パターンで、enemyTime が攻撃を仕掛ける時間です。
        import javax.sound.sampled.*;
    
        class Shoot_09 extends JFrame implements KeyListener
        {   //※ MyShip の領域
            int         key_t[] = { 0,0,0,0,0 };    //UP, RIGHT, DOWN, LEFT, SPACE
            Point       pos= new Point(300,440);    //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;
            //※ 敵の攻撃の領域
            int         pattern;            //攻撃のパターン
            long        enemyTime;          //攻撃の時間
        
  2. action() メソッドで敵の攻撃をプログラムします。
    enemyTime が攻撃の時間で、3000ミリ秒間隔で攻撃を仕掛けます。
    敵からの攻撃は三種類で「横一列, 円状に発射, 斜めに発射」です。
    攻撃の説明は 弾丸を発射する を参照して下さい。
    pattern が反撃のパターンで pattern= (pattern+1)%3; で順に切り替えています。
        // シップの移動&敵の攻撃
        public void action()
        {   int i;
            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);
                }
            }
            // 敵の攻撃
            if (enemyTime<nowTime)
            {   enemyTime= nowTime+3000;
                switch(pattern)
                {   case 0: //横一列
                        for(i=0; i<7; i++)
                        {   shotw = new SHOT();
                            shotw.Set(1,i*110+40,50,0,8);
                            list.add(shotw);
                        }
                        break;
                    case 1: //円状に発射
                        for(i=0; i<18; i++)
                        {   shotw = new SHOT();
                            shotw.Set(2,400,240,0,0);
                            shotw.Rot(i*20,5);
                            list.add(shotw);
                        }
                        break;
                    case 2: //斜めに発射
                        for(i=0; i<7; i++)
                        {   shotw = new SHOT();
                            //shotw.Set(3,i*20+40,i*10*50,i*4,i*4+8);
                            shotw.Set(3,i*40+40,100-i*10,i*2,10-i);
                            list.add(shotw);
                        }
                        break;
                }
                pattern= (pattern+1)%3;
            }
        }
        
  3. Update() メソッドで ENEMY との当り判定を行います。
    list.get(i).Sp_no==0 が MyShip から発射した弾丸です。
        // 座標の更新&弾の削除
        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) && list.get(i).Sp_no==0)
                {   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);                    // 爆発のアニメーション
        }
        
  4. あとの説明は、これまでのページを参照して下さい。
    MyShip との当り判定を設定してゲームを完成して下さい。
    ここまでの説明で簡単なシューティングゲームなら作成できるようになったでしょうか。 (^_^;)
    スコアの表示はテキスト形式でも構わないのですが、見栄えを良くするには0〜9の数字の画像を Sprite の描画と同じように組み合わせます。
    サンプルプログラムでは Enemy は一体だけですが、複数の Enemy を使うときは ArrayList に登録します。
    MyShip や Enemy がアニメーションしながら動き回ると面白いですね。
    背景のスクロールやバックミュージックもほしいですね。
    その他、状況によって装備や攻撃力をアップしたり、ライフポイント(マジックポイント)を使うことも考えられます。
    そのあたりは私よりも読者の方が詳しいのではないでしょうか。 (^_^;)
    色々工夫して面白いゲームを作成して下さい。

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