敵の反撃

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

Java Applet の実行は、次のリンクをクリックして下さい。
敵の反撃

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

プログラムの作成

  1. このページも swing の使用を止めて AWT で作成したプログラムに組み替えます。
    メモ帳などでタイプして appletshot3.java の名前で保存して下さい。
    //★ 敵の反撃    前田 稔
    //   appletviewer appletshot3.htm
    import java.applet.*;
    import java.awt.*;
    import java.io.*;
    import javax.imageio.ImageIO;
    import java.net.URL;
    import java.awt.event.*;
    import java.util.*;
    
    public class appletshot3 extends Applet implements Runnable, KeyListener
    {   // MyShip の領域
        int         key_t[] = { 0,0,0,0,0 };    //UP, RIGHT, DOWN, LEFT, SPACE
        Image       ship= null;         //Ship の画像
        int         x= 300, y= 500;     //Ship の座標
        // shot 弾丸の領域
        sprite      dangan= null;       //弾丸の切り分け
        ArrayList<shot>   list;         //弾丸の登録
        shot        shotw= new shot();  //shot のワーク
        long        nowTime,drawTime,shotTime;
        // Enemy の領域
        sprite      enemy= null;        //Enemy の画像
        // Sound &爆発の領域
        AudioClip   sound;
        sprite      bomb= null;         //爆発 の画像
        // 敵の反撃の領域
        int         pattern;            //攻撃のパターン
        long        enemyTime;          //攻撃の時間
        // Double Buffer
        Dimension   size;
        Image       back= null;
        Graphics    buffer;
    
        // Initialize
        public void init()
        {   //画像ロード
            ship = getImage(getDocumentBase(),"ship.png");
            dangan = new sprite(getCodeBase().toString() + "tama4.png",32,32);
            enemy = new sprite(getCodeBase().toString() + "ene5.png",80,96);
            bomb = new sprite(getCodeBase().toString() + "bomb.gif",128,128);
            sound = getAudioClip(getDocumentBase(),"14_5.wav");
            enemy.Set(0,30,40,4,0);             //Enemy の描画を開始
            enemy.SetRect(30,40,700,500);       //Enemy の枠
            setBackground(Color.gray);
            list = new ArrayList<shot>();
            shotTime= 0;
            pattern = 0;
            enemyTime = System.currentTimeMillis()+3000;
            size = getSize();
            back= createImage(size.width, size.height);
            if (back==null) System.out.print("createImage Error");
            drawTime= System.currentTimeMillis()+500;
            addKeyListener(this);
            requestFocus();
            Thread t = new Thread(this);
            t.start();
        }
    
       // Message Loop
        public void run()
        {   {   while(true)
                {   nowTime= System.currentTimeMillis();
                    if (drawTime<nowTime)
                    {   drawTime= nowTime+30;
                        action();
                        Update();
                        repaint();
                    }
                }
            }
        }
    
        // Paint
        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);
            if (ship!=null)                     // ENEMY の描画
                enemy.View(buffer);
            if (ship!=null)                     // MyShipの描画
                buffer.drawImage(ship,x,y,this);
            for(int i=0; i<list.size(); i++)    // 弾丸の描画
                dangan.View(buffer,list.get(i).Sp_no,(int)list.get(i).posX,(int)list.get(i).posY);
            if (bomb!=null)                     // 爆発の描画
                bomb.View(buffer);
            g.drawImage(back,0,0,this);
        }
    
        // 座標の更新&弾の削除
        public void Update()
        {   int i;
            enemy.Loop(10000);                  // ENEMY を切り替える
            enemy.Bound();                      // ENEMY が跳ね返る 
            for(i=0; i<list.size(); i++)        // 弾丸の座標を更新
            {   list.get(i).Update();
                if (enemy.Hit(list.get(i).posX,list.get(i).posY,50) && list.get(i).Sp_no==0)
                {   sound.play();               // ヒットしたとき
                    bomb.Set(0,enemy.posX,enemy.posY,0,0);
                    enemy.Set(0,30,40,4,0);
                }
            }
            for(i=list.size()-1; i>=0; i--)     // 画面から出た弾丸を削除
                if (list.get(i).Sp_no==99)  list.remove(i);
            bomb.Next(200);                     // 爆発のアニメーション
        }
    
        // シップの移動&敵の攻撃
        public void action()
        {   int i;
            if (key_t[0]==1)    y=y-4;  // UP
            if (key_t[1]==1)    x=x+4;  // RIGHT
            if (key_t[2]==1)    y=y+4;  // DOWN
            if (key_t[3]==1)    x=x-4;  // LEFT
            if (key_t[4]==1)            // Space で連射
            {   if (shotTime<nowTime)
                {   shotTime= nowTime+200;
                    shotw = new shot();
                    shotw.Set(0,x+20,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*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_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) { }
    }
    
    //★ 画像を切り分ける class sprite は前回と同じです。
    class sprite extends Applet
    {   private Image   Img;            // Sprite Image
          ・・・
    }
    
    //★ 弾丸の座標を管理する class shot は前回と同じです。
    class shot
    {   float   posX, posY;     //弾の座標
          ・・・
    }
    
  2. Java Applet を起動する HTML ファイルです。
    <html>
    <body>
    <h3>敵の反撃</h3>
      Applet のウインドウをクリックして Space キーを押すと弾丸を連射します。<br>
      <applet code=appletshot3.class width=800 height=600>
      </applet>
    </body>
    </html>
    
  3. インターネットブラウザを起動して HTML ファイルから実行して下さい。
    Applet のウインドウをクリックして Space キーを押すと MyShip から弾丸を連射します。
    弾丸が Enemy(敵)にヒットすると爆発音を鳴らしてアニメーションします。
    敵が反撃して来ますが、MyShip との当り判定は行っていません。

プログラムの説明

  1. このプログラムは 爆発音とアニメーション の続きです。
    敵の反撃は、連射で使っている ArrayList(list) を使います。
    ArrayList は配列のようにサイズの制限が無く、追加/削除/挿入を自由に行うことが出来ます。
    pattern が敵からの攻撃パターンで、enemyTime が攻撃を仕掛ける時間です。
    敵/味方の弾丸が入り乱れて描画されるので、攻撃のパターンごとに弾丸の色を変えています。
        // shot 弾丸の領域
        sprite      dangan= null;       //弾丸の切り分け
        ArrayList<shot>   list;         //弾丸の登録
        shot        shotw= new shot();  //shot のワーク
        long        nowTime,drawTime,shotTime;
        // Enemy の領域
        sprite      enemy= null;        //Enemy の画像
        // Sound &爆発の領域
        AudioClip   sound;
        sprite      bomb= null;         //爆発 の画像
        // 敵の反撃の領域
        int         pattern;            //攻撃のパターン
        long        enemyTime;          //攻撃の時間
        
  2. 敵の反撃をプログラムする action() メソッドです。
    enemyTime が攻撃の時間で、3000ミリ秒間隔で攻撃を仕掛けます。
    敵からの攻撃は三種類で「横一列, 円状に発射, 斜めに発射」で反撃します。
    pattern が反撃のパターンで pattern= (pattern+1)%3; で順に切り替えています。
        // シップの移動&敵の攻撃
        public void action()
        {   int i;
            if (key_t[0]==1)    y=y-4;  // UP
            if (key_t[1]==1)    x=x+4;  // RIGHT
            if (key_t[2]==1)    y=y+4;  // DOWN
            if (key_t[3]==1)    x=x-4;  // LEFT
            if (key_t[4]==1)            // Space で連射
            {   if (shotTime<nowTime)
                {   shotTime= nowTime+200;
                    shotw = new shot();
                    shotw.Set(0,x+20,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*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 から発射した弾丸です。
    敵の弾丸と MyShip との当り判定はプログラムしていません。
        // 座標の更新&弾の削除
        public void Update()
        {   int i;
            enemy.Loop(10000);                  // ENEMY を切り替える
            enemy.Bound();                      // ENEMY が跳ね返る 
            for(i=0; i<list.size(); i++)        // 弾丸の座標を更新
            {   list.get(i).Update();
                if (enemy.Hit(list.get(i).posX,list.get(i).posY,50) && list.get(i).Sp_no==0)
                {   sound.play();               // ヒットしたとき
                    bomb.Set(0,enemy.posX,enemy.posY,0,0);
                    enemy.Set(0,30,40,4,0);
                }
            }
            for(i=list.size()-1; i>=0; i--)     // 画面から出た弾丸を削除
                if (list.get(i).Sp_no==99)  list.remove(i);
            bomb.Next(200);                     // 爆発のアニメーション
        }
        
  4. 敵からの反撃は、連射で使っている ArrayList を兼用するので特に注意する事柄はありません。
    ★画像を切り分ける class sprite と、弾丸の座標を管理する class shot は 自機から連射 と同じです。
    あとの説明は「超初心者のプログラム入門(Java2)/Game Program/シューティングゲームに挑戦」を参照して下さい。
  5. このページはゲームプログラムの作成方法を解説するのが目的で、完成したゲームではありません。
    MyShip との当り判定を設定してゲームを完成して下さい。 (^_^;)
    スコアの表示はテキスト形式でも構わないのですが、見栄えを良くするには0〜9の数字の画像を Sprite の描画と同じように組み合わせます。
    サンプルプログラムでは描画する Enemy は一体だけですが、同時に複数の Enemy が現れるときは ArrayList に登録します。
    MyShip や Enemy がアニメーションしながら動き回ると面白いですね。
    背景のスクロールやバックミュージックもほしいですね。
    その他、状況によって装備や攻撃力をアップしたり、ライフポイント(マジックポイント)を使うことも考えられます。
    そのあたりは私よりも読者の方が詳しいのではないでしょうか。 (^_^;)
    色々工夫して面白いゲームを作成して下さい。

Java Game Program