java坦克大战(2.0)

坦克大战(2.0)

在前面的坦克大战1.0已经完成了移动 绘制我方/敌方坦克。
接下来会运用到前面学习的多线程的基础,来实现发射子弹的效果

发射子弹

想要坦克发射子弹,可以用以下思路

按J发射子弹
将子弹变成一个线程,让子弹的x,y值不停变换,画板重绘,当子弹到达边界时就将线程销毁

步骤如下:

子弹类

  1. 创建子弹类shoot,定义对应的x,y,发射的方向,移动的速度,实现Runnable接口
  2. 在run方法中根据坦克的朝向,改变子弹的x或者y,且当子弹达到边界时就销毁

子弹类(线程)定义好了自然要启动,而子弹是从坦克发送出来的,所以启动线程就在坦克类中定义

坦克类

  1. 定义一个子弹类的属性,但先不赋值,定义一个发射子弹的方法fire(),用于确定子弹发射的初始位置,根据坦克的位置创建不同的子弹类的实例
  2. 创建完子弹后,启动子弹线程

监听器

  1. 在按下键盘做出操作的方法,新建一个当按下J的时候就调用fire方法

画板类绘制子弹
1.因为子弹类是只有在按J之后才创建的,所以不能直接在paint绘制,而是需要加if判断子弹类不是空,且子弹线程是正在运行的,才绘制
2.又因为paint现在是只有在移动的时候才会重新绘制,但是子弹要求是一直在动的,所以需要将MyPanel画板类变成一个线程,run方法中一直不断重绘,才能让子弹动起来,启动放在画框类

//shoot类
public class shoot implements Runnable{
    int x;//子弹的x坐标
    int y;//子弹的y坐标
    int direct;//子弹的方向
    int speed = 2;//子弹的速度
    boolean lief = true;//子弹线程的状态
    public shoot(int x, int y, int direct) {
        this.x = x;
        this.y = y;
        this.direct = direct;
    }

    @Override
    public void run() {
        while (lief){
            //因为要让子弹移动的别太快,所以休眠一会
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //根据方向,开始移动子弹
            switch (direct){
                case 0://当坦克朝上时
                    y-=speed;
                    break;
                case 1://当坦克朝右时
                    x+=speed;
                    break;
                case 2://当坦克朝下时
                    y+=speed;
                    break;
                case 3://当坦克朝左边时
                    x-=speed;
                    break;
            }
            //当子弹到达边界时
            if (!(x<500&&x>0 && y<600&& y>0)){
                lief = false;
                break;
            }
        }
    }
}
//Tank类
public class Tank {
    int x;
    int y;
    int direct;
    public Tank(int x, int y, int direct) {
        this.x = x;
        this.y = y;
        this.direct = direct;
    }
    shoot shoot;//定义一个子弹属性
    public void fire(){
        switch (direct){//根据此时坦克的朝向创建对应的子弹发送初始位置
            case 0://当坦克朝上时
                System.out.println("上");
                shoot = new shoot(x+20,y-6,direct);
                break;
            case 1://当坦克朝右时
                shoot = new shoot(x+60,y+24,direct);
                break;
            case 2://当坦克朝下时
                System.out.println("下");
                shoot = new shoot(x+21,y+60,direct);
                break;
            case 3://当坦克朝左边时
                System.out.println("左");
                shoot = new shoot(x-8,y+23,direct);
                break;
        }
        //创建完子弹,启动子弹
        new Thread(shoot).start();
    }
}
//MyPanel类
public class MyPanel extends Panel implements KeyListener,Runnable{
    int speek = 1;
    Tank tank;
    Vector<EnemyTanks> enemyTanks = new Vector<>();
    public MyPanel(){
        tank = new Tank(20,460,0);
        for (int i = 0; i < 3; i++) {
            EnemyTanks enemyTank = new EnemyTanks(100*(i+1),0,2);
            enemyTanks.add(enemyTank);
        }
    }
   @Override
    public void paint(Graphics g) {
        super.paint(g);
       System.out.println("调用paint");
       g.fillRect(0,0,500,600);
       //画我方坦克
       drawTank(tank.x,tank.y,g,tank.direct,0);
       for (int i = 0; i < enemyTanks.size(); i++) {
           EnemyTanks e = enemyTanks.get(i);
           //画敌方坦克
           drawTank(e.x,e.y,g,e.direct,1);
       }
       //画子弹
       if (tank.shoot!= null && tank.shoot.lief!=false){
           g.setColor(Color.PINK);
           g.drawRect(tank.shoot.x,tank.shoot.y,3,3);
       }
    }
    /**
    @param x 坦克的起始横坐标
    @param y 坦克的起始纵坐标
    @param g 画笔
    @param direct 根据方向绘制不同朝向的坦克
    @param type  根据0或者1 绘制不同颜色的坦克
    */
    public void drawTank(int x,int y,Graphics g,int direct,int type){
        switch (type){
            case 0:
                g.setColor(Color.PINK);//自己的坦克就粉色
                break;
            case 1:
                g.setColor(Color.RED);//敌人的坦克就红色
                break;
        }
            switch (direct){//0上,1右,2下,3左
                case 0://0表示绘制方向朝上的坦克
                    g.fill3DRect(x,y,10,50,false);
                    g.fill3DRect(x+10,y+5,25,40,false);
                    g.fill3DRect(x+35,y,10,50,false);
                    g.fillOval(x+10,y+15,25,25);
                    g.drawLine(x+22,y-5,x+22,y+25);
                    break;
                case 1://1表示绘制方向朝右的坦克
                    g.fill3DRect(x,y+7,50,10,false);
                    g.fill3DRect(x+5,y+14,40,25,false);
                    g.fill3DRect(x,y+37,50,10,false);
                    g.fillOval(x+10,y+15,25,25);
                    g.drawLine(x+25,y+25,x+58,y+25);
                    break;
                case 2://2表示绘制方向朝下的坦克
                    g.fill3DRect(x,y,10,50,false);
                    g.fill3DRect(x+10,y+5,25,40,false);
                    g.fill3DRect(x+35,y,10,50,false);
                    g.fillOval(x+10,y+12,25,25);
                    g.drawLine(x+22,y+55,x+22,y+25);
                    break;
                case 3://3表示绘制方向朝左的坦克
                    g.fill3DRect(x,y+7,50,10,false);
                    g.fill3DRect(x+5,y+14,40,25,false);
                    g.fill3DRect(x,y+37,50,10,false);
                    g.fillOval(x+10,y+15,25,25);
                    g.drawLine(x+25,y+25,x-8,y+25);
                    break;
                default://其他情况暂不考虑
                    break;
            }
    }

    @Override //字符输出时,该方法触发
    public void keyTyped(KeyEvent e) {

    }

    @Override//有按键按下时,该方法触发
    public void keyPressed(KeyEvent e) {
        if (e.getKeyCode() == KeyEvent.VK_W){//前进
            moveUp();
            tank.direct = 0;
        }else if(e.getKeyCode() == KeyEvent.VK_S){
            moveDown();
            tank.direct = 2;
        }else if(e.getKeyCode() == KeyEvent.VK_A){
            moveLeft();
            tank.direct = 3;
        }
        else if(e.getKeyCode() == KeyEvent.VK_D){
            moveRight();
            tank.direct = 1;
        }else if(e.getKeyCode() == 32){
            System.out.println("氮气加速~");
            speek +=5;
        }else{
            System.out.println(e.getKeyCode());
        }
        if (e.getKeyCode() == KeyEvent.VK_J){
            tank.fire();
        }
        repaint();
    }

    @Override//有按键松开时,该方法触发
    public void keyReleased(KeyEvent e) {

    }
    public void moveUp(){
        tank.y-=speek;
    }
    public void moveLeft(){
        tank.x-=speek;
    }
    public void moveRight(){
        tank.x+=speek;
    }
    public void moveDown(){
        tank.y+=speek;
    }

    @Override
    public void run() {
        while (true){
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            this.repaint();
        }
    }
}
//Window类
public class Window extends JFrame {
    MyPanel m = null;
    public Window(){
        m  = new MyPanel();
        this.add(m);
        new Thread(m).start();
        this.setSize(500,600);
        this.addKeyListener(m);//让JFram监听m的事件
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.setVisible(true);
    }

    public static void main(String[] args) {
        new Window();
    }
}

让敌方坦克发射子弹

思路在EnemyTanks类使用Vector类创建一个集合,用于存放子弹
然后在MyPanel类初始化敌方坦克的敌方,每创建一个敌方坦克就初始化一个子弹对象,同时启动这个子弹
接着到绘制敌方坦克的敌方,每绘制一个敌人坦克,就绘制一个敌方坦克的子弹

//MyPanel
public class MyPanel extends Panel implements KeyListener,Runnable{
    int speek = 1;
    Tank tank;
    Vector<EnemyTanks> enemyTanks = new Vector<>();
    public MyPanel(){
        //初始化我方坦克
        tank = new Tank(20,460,0);
        //初始化敌方坦克
        for (int i = 0; i < 3; i++) {
            EnemyTanks enemyTank = new EnemyTanks(100*(i+1),0,2);
            //初始化敌方坦克子弹
            shoot shoot = new shoot(enemyTank.x,enemyTank.y,enemyTank.direct);
            //将子弹放入敌方坦克的shoots集合进行管理
            enemyTank.shoots.add(shoot);
            //启动敌方坦克的子弹
            new Thread(shoot).start();
            enemyTanks.add(enemyTank);
        }
    }
   @Override
    public void paint(Graphics g) {
        super.paint(g);
       System.out.println("调用paint");
       g.fillRect(0,0,500,600);
       //画我方坦克
       drawTank(tank.x,tank.y,g,tank.direct,0);
       //画敌方坦克
       for (int i = 0; i < enemyTanks.size(); i++) {
           EnemyTanks e = enemyTanks.get(i);
           drawTank(e.x,e.y,g,e.direct,1);
           //画出敌方坦克子弹
           for (int j = 0; j < e.shoots.size(); j++) {
               shoot s = e.shoots.get(j);
               if (s.lief){
                   g.setColor(Color.RED);
                   g.drawRect(s.x+20,s.y+60,3,3);
               }else {
                   e.shoots.remove(s);
               }
           }
       }
       //画子弹
       if (tank.shoot!= null && tank.shoot.lief!=false){
           g.setColor(Color.PINK);
           g.drawRect(tank.shoot.x,tank.shoot.y,3,3);
       }
    }
    /**
    @param x 坦克的起始横坐标
    @param y 坦克的起始纵坐标
    @param g 画笔
    @param direct 根据方向绘制不同朝向的坦克
    @param type  根据0或者1 绘制不同颜色的坦克
    */
    public void drawTank(int x,int y,Graphics g,int direct,int type){
        switch (type){
            case 0:
                g.setColor(Color.PINK);//自己的坦克就粉色
                break;
            case 1:
                g.setColor(Color.RED);//敌人的坦克就红色
                break;
        }
            switch (direct){//0上,1右,2下,3左
                case 0://0表示绘制方向朝上的坦克
                    g.fill3DRect(x,y,10,50,false);
                    g.fill3DRect(x+10,y+5,25,40,false);
                    g.fill3DRect(x+35,y,10,50,false);
                    g.fillOval(x+10,y+15,25,25);
                    g.drawLine(x+22,y-5,x+22,y+25);
                    break;
                case 1://1表示绘制方向朝右的坦克
                    g.fill3DRect(x,y+7,50,10,false);
                    g.fill3DRect(x+5,y+14,40,25,false);
                    g.fill3DRect(x,y+37,50,10,false);
                    g.fillOval(x+10,y+15,25,25);
                    g.drawLine(x+25,y+25,x+58,y+25);
                    break;
                case 2://2表示绘制方向朝下的坦克
                    g.fill3DRect(x,y,10,50,false);
                    g.fill3DRect(x+10,y+5,25,40,false);
                    g.fill3DRect(x+35,y,10,50,false);
                    g.fillOval(x+10,y+12,25,25);
                    g.drawLine(x+22,y+55,x+22,y+25);
                    break;
                case 3://3表示绘制方向朝左的坦克
                    g.fill3DRect(x,y+7,50,10,false);
                    g.fill3DRect(x+5,y+14,40,25,false);
                    g.fill3DRect(x,y+37,50,10,false);
                    g.fillOval(x+10,y+15,25,25);
                    g.drawLine(x+25,y+25,x-8,y+25);
                    break;
                default://其他情况暂不考虑
                    break;
            }
    }

    @Override //字符输出时,该方法触发
    public void keyTyped(KeyEvent e) {

    }

    @Override//有按键按下时,该方法触发
    public void keyPressed(KeyEvent e) {
        if (e.getKeyCode() == KeyEvent.VK_W){//前进
            moveUp();
            tank.direct = 0;
        }else if(e.getKeyCode() == KeyEvent.VK_S){
            moveDown();
            tank.direct = 2;
        }else if(e.getKeyCode() == KeyEvent.VK_A){
            moveLeft();
            tank.direct = 3;
        }
        else if(e.getKeyCode() == KeyEvent.VK_D){
            moveRight();
            tank.direct = 1;
        }else if(e.getKeyCode() == 32){
            System.out.println("氮气加速~");
            speek +=5;
        }else{
            System.out.println(e.getKeyCode());
        }
        if (e.getKeyCode() == KeyEvent.VK_J){
            tank.fire();
        }
        repaint();
    }

    @Override//有按键松开时,该方法触发
    public void keyReleased(KeyEvent e) {

    }
    public void moveUp(){
        tank.y-=speek;
    }
    public void moveLeft(){
        tank.x-=speek;
    }
    public void moveRight(){
        tank.x+=speek;
    }
    public void moveDown(){
        tank.y+=speek;
    }

    @Override
    public void run() {
        while (true){
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            this.repaint();
        }
    }
}

当子弹击中敌方坦克,敌人就消失

判断子弹是否击中坦克,肯定是看子弹有没有进入到坦克的x和y的范围内。
根据这个思路可以想到,坦克几乎是有两种形态,上下 和 左右,这两类坦克范围的x和y是有不同的,所以根据这个特性,写出两个x和y的范围,加以判断即可。是否击中可以判断了,让被被击中坦克消失直接将该坦克从集合中删除即可。

根据上面的思路,可以将这个思路封装到一个方法中,再加上一个条件,先在敌人坦克类加一个boolean属性Live。如果子弹击中敌人坦克,就把敌人坦克的live改成false,同时也把打出的子弹改成false。

那么由于我们不知道什么时候子弹会击中敌人的坦克,所以需要在MyPanel类的run方法中调用此方法,且我们不知道到底会击中哪个坦克,所以需要便利敌人坦克的集合。

根据上面的判断,当我方子弹击中敌人坦克时,敌人的坦克的live会变成false。如果这时在MyPanel类绘制敌人坦克的时候,加一个判断,只有当敌人坦克的live是true的时候才进行绘制。那么一旦子弹击中敌人坦克,在下一次的重绘中就不会绘制被击中的坦克,以此就可以实现敌人坦克消失的效果。


//MyPanel类
public class MyPanel extends Panel implements KeyListener,Runnable{
    int speek = 1;
    Tank tank;
    Vector<EnemyTanks> enemyTanks = new Vector<>();
    public MyPanel(){
        //初始化我方坦克
        tank = new Tank(20,460,0);
        //初始化敌方坦克
        for (int i = 0; i < 3; i++) {
            EnemyTanks enemyTank = new EnemyTanks(100*(i+1),0,2);
            //初始化敌方坦克子弹
            shoot shoot = new shoot(enemyTank.x,enemyTank.y,enemyTank.direct);
            //将子弹放入敌方坦克的shoots集合进行管理
            enemyTank.shoots.add(shoot);
            //启动敌方坦克的子弹
            new Thread(shoot).start();
            enemyTanks.add(enemyTank);
        }
    }
   @Override
    public void paint(Graphics g) {
        super.paint(g);
       System.out.println("调用paint");
       g.fillRect(0,0,500,600);
       //画我方坦克
       drawTank(tank.x,tank.y,g,tank.direct,0);
       //画敌方坦克
       for (int i = 0; i < enemyTanks.size(); i++) {
           EnemyTanks e = enemyTanks.get(i);
           if (e.live) {//如果敌方坦克的live不是false才进行绘制
               drawTank(e.x, e.y, g, e.direct, 1);
               //画出敌方坦克子弹
               for (int j = 0; j < e.shoots.size(); j++) {
                   shoot s = e.shoots.get(j);
                   if (s.lief) {
                       g.setColor(Color.RED);
                       g.drawRect(s.x + 20, s.y + 60, 3, 3);
                   } else {
                       e.shoots.remove(s);
                   }
               }
           }
       }
       //画子弹
       if (tank.shoot!= null && tank.shoot.lief!=false){
           g.setColor(Color.PINK);
           g.drawRect(tank.shoot.x,tank.shoot.y,3,3);
       }
    }
    /**
    @param x 坦克的起始横坐标
    @param y 坦克的起始纵坐标
    @param g 画笔
    @param direct 根据方向绘制不同朝向的坦克
    @param type  根据0或者1 绘制不同颜色的坦克
    */
    public void drawTank(int x,int y,Graphics g,int direct,int type){
        switch (type){
            case 0:
                g.setColor(Color.PINK);//自己的坦克就粉色
                break;
            case 1:
                g.setColor(Color.RED);//敌人的坦克就红色
                break;
        }
            switch (direct){//0上,1右,2下,3左
                case 0://0表示绘制方向朝上的坦克
                    g.fill3DRect(x,y,10,50,false);
                    g.fill3DRect(x+10,y+5,25,40,false);
                    g.fill3DRect(x+35,y,10,50,false);
                    g.fillOval(x+10,y+15,25,25);
                    g.drawLine(x+22,y-5,x+22,y+25);
                    break;
                case 1://1表示绘制方向朝右的坦克
                    g.fill3DRect(x,y+7,50,10,false);
                    g.fill3DRect(x+5,y+14,40,25,false);
                    g.fill3DRect(x,y+37,50,10,false);
                    g.fillOval(x+10,y+15,25,25);
                    g.drawLine(x+25,y+25,x+58,y+25);
                    break;
                case 2://2表示绘制方向朝下的坦克
                    g.fill3DRect(x,y,10,50,false);
                    g.fill3DRect(x+10,y+5,25,40,false);
                    g.fill3DRect(x+35,y,10,50,false);
                    g.fillOval(x+10,y+12,25,25);
                    g.drawLine(x+22,y+55,x+22,y+25);
                    break;
                case 3://3表示绘制方向朝左的坦克
                    g.fill3DRect(x,y+7,50,10,false);
                    g.fill3DRect(x+5,y+14,40,25,false);
                    g.fill3DRect(x,y+37,50,10,false);
                    g.fillOval(x+10,y+15,25,25);
                    g.drawLine(x+25,y+25,x-8,y+25);
                    break;
                default://其他情况暂不考虑
                    break;
            }
    }

    @Override //字符输出时,该方法触发
    public void keyTyped(KeyEvent e) {

    }

    @Override//有按键按下时,该方法触发
    public void keyPressed(KeyEvent e) {
        if (e.getKeyCode() == KeyEvent.VK_W){//前进
            moveUp();
            tank.direct = 0;
        }else if(e.getKeyCode() == KeyEvent.VK_S){
            moveDown();
            tank.direct = 2;
        }else if(e.getKeyCode() == KeyEvent.VK_A){
            moveLeft();
            tank.direct = 3;
        }
        else if(e.getKeyCode() == KeyEvent.VK_D){
            moveRight();
            tank.direct = 1;
        }else if(e.getKeyCode() == 32){
            System.out.println("氮气加速~");
            speek +=5;
        }else{
            System.out.println(e.getKeyCode());
        }
        if (e.getKeyCode() == KeyEvent.VK_J){
            tank.fire();
        }
        repaint();
    }

    @Override//有按键松开时,该方法触发
    public void keyReleased(KeyEvent e) {

    }
    public void moveUp(){
        tank.y-=speek;
    }
    public void moveLeft(){
        tank.x-=speek;
    }
    public void moveRight(){
        tank.x+=speek;
    }
    public void moveDown(){
        tank.y+=speek;
    }
    //此方法判断我方坦克子弹是否击中敌人坦克
    public static void himEnemyTank(shoot s , EnemyTanks e ){
        //上下和左右的x,y是相同的,所以写两个判断即可

        switch (e.direct){
            case 0:
            case 2:
                if (s.x>e.x&&s.x< e.x+40
                && s.y>e.y&&s.y<e.y+60){//如果此条件成立就代表,地方在朝上或者朝下的方向被击中了
                    e.live = false;
                    s.lief = false;
                }
                break;
            case 1:
            case 3:
                if (s.x>e.x&&s.x< e.x+60
                        && s.y>e.y&&s.y<e.y+40){//如果此条件成立就代表,地方在朝左或者朝右的方向被击中了
                    e.live = false;
                    s.lief = false;
                }
        }
    }

    @Override
    public void run() {
        while (true){
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if (tank.shoot!=null && tank.shoot.lief != false){//如果我方坦克的子弹创建了,且线程未结束就对每个敌人坦克进行判断
                for (int i = 0; i < enemyTanks.size(); i++) {
                    EnemyTanks e = this.enemyTanks.get(i);
                    himEnemyTank(tank.shoot,e);
                }
            }
            this.repaint();
        }
    }
}

除此之外还可以在MyPanel类的run方法中再加一个判断,如果敌人坦克的live不是true的时候,就将此坦克从集合中删除,以防消失后还在吃子弹,因为上面只是不绘制被击中的坦克,实际上他还是存在的。所以再加一个判断,从集合中删除掉,就不会吃子弹了。以此实现真正的死亡

for (int i = 0; i < enemyTanks.size(); i++) {//如果敌人坦克被击中,那么就把此坦克从集合中删除
                    if (!(enemyTanks.get(i).live)){
                        enemyTanks.remove(i);
                    }
                }

坦克死亡爆炸效果

首先可以将爆炸效果写成一个类 boom
再分析
爆炸效果其实就是几张图片快速切换重绘,要实现这个效果需分析几点。
爆炸效果肯定是坦克的当前位置,所以也得有x和y。
再因为爆炸效果是几张图片不断重绘,所以可以添加一个int值作为生命周期,不同的生命周期绘制不同的图片
再写一个生命周期不断–的方法,当生命周期为0时爆炸效果结束。

boom类定义好了后,在MyPanel类定义一个爆炸的集合,用于保存爆炸效果。
再定义三张图片,分别是爆炸开始到结束的效果。
然后在判断是否击中坦克的方法中,每当判断击中坦克时,就new 一个boom类,将被击中的坦克x和y传入。
然后在paint方法中,判断如果booms集合不为null就代表有坦克被击中了,需要执行爆炸效果。
接着在boom的生命周期的不同节点,绘制不同的图片,以此爆炸效果就完成了。

//boom类
public class boom {
    int x;
    int y;
    int duration = 9;//爆炸效果生命周期
    boolean live = true;

    public boom(int x, int y) {
        this.x = x;
        this.y = y;
    }
    public void DownDur(){
        if (duration>0){
            duration--;
        }else{
            live = false;
        }
    }
}
//MyPanel类
public class MyPanel extends Panel implements KeyListener,Runnable{
    int speek = 1;
    Tank tank;
    Vector<EnemyTanks> enemyTanks = new Vector<>();//用于保存敌人坦克
    Vector<boom>booms = new Vector<>();//用于保存要爆炸的地方
    Image image1 = Toolkit.getDefaultToolkit().getImage(MyPanel.class.getResource("/b1.png"));//用于保存爆炸效果的图片
    Image image2 = Toolkit.getDefaultToolkit().getImage(MyPanel.class.getResource("/b2.png"));
    Image image3 = Toolkit.getDefaultToolkit().getImage(MyPanel.class.getResource("/b3.png"));
    public MyPanel(){
        //初始化我方坦克
        tank = new Tank(20,460,0);
        //初始化敌方坦克
        for (int i = 0; i < 3; i++) {
            EnemyTanks enemyTank = new EnemyTanks(100*(i+1),0,2);
            //初始化敌方坦克子弹
            shoot shoot = new shoot(enemyTank.x,enemyTank.y,enemyTank.direct);
            //将子弹放入敌方坦克的shoots集合进行管理
            enemyTank.shoots.add(shoot);
            //启动敌方坦克的子弹
            new Thread(shoot).start();
            enemyTanks.add(enemyTank);
        }
    }
   @Override
    public void paint(Graphics g) {
        super.paint(g);
       System.out.println("调用paint");
       g.fillRect(0,0,500,600);
       //画我方坦克
       drawTank(tank.x,tank.y,g,tank.direct,0);
       //画敌方坦克
       for (int i = 0; i < enemyTanks.size(); i++) {
           EnemyTanks e = enemyTanks.get(i);
           if (e.live) {//如果敌方坦克的live不是false才进行绘制
               drawTank(e.x, e.y, g, e.direct, 1);
               //画出敌方坦克子弹
               for (int j = 0; j < e.shoots.size(); j++) {
                   shoot s = e.shoots.get(j);
                   if (s.lief) {
                       g.setColor(Color.RED);
                       g.drawRect(s.x + 20, s.y + 60, 3, 3);
                   } else {
                       e.shoots.remove(s);
                   }
               }
           }
       }
       //画子弹
       if (tank.shoot!= null && tank.shoot.lief!=false){
           g.setColor(Color.PINK);
           g.drawRect(tank.shoot.x,tank.shoot.y,3,3);
       }
       //当爆炸效果集合不是null时,说明有坦克被击中了,得开始绘制爆炸效果了
       if (booms!=null){
           try {
               Thread.sleep(50);//因为第一次会额外调用一次paint,所以最好休眠一下,不然第一次爆炸不显示
           } catch (InterruptedException e) {
               e.printStackTrace();
           }
           for (int i = 0; i < booms.size(); i++) {
               boom boom = booms.get(i);
               if (boom.duration>=7){
                   g.drawImage(image1,boom.x,boom.y,60,60,this);
               }else if(boom.duration>=5){
                   g.drawImage(image2,boom.x,boom.y,60,60,this);
               }
               else if(boom.duration>=3){
                   g.drawImage(image3,boom.x,boom.y,60,60,this);
               }
               boom.DownDur();
               if (boom.duration == 0){
                   booms.remove(boom);
               }
           }
       }
    }
    /**
    @param x 坦克的起始横坐标
    @param y 坦克的起始纵坐标
    @param g 画笔
    @param direct 根据方向绘制不同朝向的坦克
    @param type  根据0或者1 绘制不同颜色的坦克
    */
    public void drawTank(int x,int y,Graphics g,int direct,int type){
        switch (type){
            case 0:
                g.setColor(Color.PINK);//自己的坦克就粉色
                break;
            case 1:
                g.setColor(Color.RED);//敌人的坦克就红色
                break;
        }
            switch (direct){//0上,1右,2下,3左
                case 0://0表示绘制方向朝上的坦克
                    g.fill3DRect(x,y,10,50,false);
                    g.fill3DRect(x+10,y+5,25,40,false);
                    g.fill3DRect(x+35,y,10,50,false);
                    g.fillOval(x+10,y+15,25,25);
                    g.drawLine(x+22,y-5,x+22,y+25);
                    break;
                case 1://1表示绘制方向朝右的坦克
                    g.fill3DRect(x,y+7,50,10,false);
                    g.fill3DRect(x+5,y+14,40,25,false);
                    g.fill3DRect(x,y+37,50,10,false);
                    g.fillOval(x+10,y+15,25,25);
                    g.drawLine(x+25,y+25,x+58,y+25);
                    break;
                case 2://2表示绘制方向朝下的坦克
                    g.fill3DRect(x,y,10,50,false);
                    g.fill3DRect(x+10,y+5,25,40,false);
                    g.fill3DRect(x+35,y,10,50,false);
                    g.fillOval(x+10,y+12,25,25);
                    g.drawLine(x+22,y+55,x+22,y+25);
                    break;
                case 3://3表示绘制方向朝左的坦克
                    g.fill3DRect(x,y+7,50,10,false);
                    g.fill3DRect(x+5,y+14,40,25,false);
                    g.fill3DRect(x,y+37,50,10,false);
                    g.fillOval(x+10,y+15,25,25);
                    g.drawLine(x+25,y+25,x-8,y+25);
                    break;
                default://其他情况暂不考虑
                    break;
            }
    }

    @Override //字符输出时,该方法触发
    public void keyTyped(KeyEvent e) {

    }

    @Override//有按键按下时,该方法触发
    public void keyPressed(KeyEvent e) {
        if (e.getKeyCode() == KeyEvent.VK_W){//前进
            moveUp();
            tank.direct = 0;
        }else if(e.getKeyCode() == KeyEvent.VK_S){
            moveDown();
            tank.direct = 2;
        }else if(e.getKeyCode() == KeyEvent.VK_A){
            moveLeft();
            tank.direct = 3;
        }
        else if(e.getKeyCode() == KeyEvent.VK_D){
            moveRight();
            tank.direct = 1;
        }else if(e.getKeyCode() == 32){
            System.out.println("氮气加速~");
            speek +=5;
        }else{
            System.out.println(e.getKeyCode());
        }
        if (e.getKeyCode() == KeyEvent.VK_J){
            tank.fire();
        }
        repaint();
    }

    @Override//有按键松开时,该方法触发
    public void keyReleased(KeyEvent e) {

    }
    public void moveUp(){
        tank.y-=speek;
    }

让敌方坦克动起来

首先分析这个需求:
坦克动起来得改变x和y的值,且需要不断的改变
根据上面的分析,可以使用以下方法:
将敌人坦克类EnemyTanks做成一个线程,然后再run方法中使用switch语句根据坦克的朝向改变x和y的值,再使用 Math. random方法随机反馈0~3之间的数字。不断的循环就可以实现让坦克动起来.
可以在初始化创建敌人坦克后,马上就启动线程

//EnemyTanks类
public class EnemyTanks extends Tank implements Runnable{
    boolean live = true;
    Vector<shoot> shoots = new Vector<>();
    public EnemyTanks(int x, int y, int direct) {
        super(x, y, direct);
    }

    @Override
    public void run() {
        while (live){
            for (int i = 0; i < 30; i++) {
                try {
                    Thread.sleep(50);//睡眠一会防止,还没动几步就换方向了
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                switch (direct){
                    case 0://上
                        y-=2;
                        break;
                    case 1://右
                        x+=2;
                        break;
                    case 2://下
                        y+=2;
                        break;
                    case 3://左
                        x-=2;
                        break;
                }
            }
            direct = (int)(Math.random()*4);//随机调整方向
        }
    }
}

控制坦克的移动范围

在上面的制作中,由于没有限制坦克的x和y最高和最低值是多少,所以坦克是可以出界的,而子弹又是不可以出界的,所以就会有漏洞,下面将限制坦克的x和y最高和最低值。

代码提现也很简单,只需要在改变x和y的时候加一个if判断,确定没有超出才改变,否则不改变。这个思路也可以拿来做障碍物

public class EnemyTanks extends Tank implements Runnable{
    boolean live = true;
    Vector<shoot> shoots = new Vector<>();
    public EnemyTanks(int x, int y, int direct) {
        super(x, y, direct);
    }

    @Override
    public void run() {
        while (live){
            for (int i = 0; i < 30; i++) {
                try {
                    Thread.sleep(50);//睡眠一会防止,还没动几步就换方向了
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                switch (direct){
                    case 0://if (y >= 0) {
                            y -= 2;
                        }
                        break;
                    case 1://if ( x+60 < 500) {
                            x += 2;
                        }
                        break;
                    case 2://if (y+50 < 600) {
                            y += 2;
                        }
                        break;
                    case 3://if (x >= 0 ) {
                            x -= 2;
                        }
                        break;
                }
            }
            direct = (int)(Math.random()*4);//随机调整方向
        }
    }
}

发射多颗子弹

在前面的制作中,只实现了,发射一颗子弹,下面要实现,可以发射多颗子弹。
要实现发射多颗子弹,先将前面的发射子弹的问题解决一下。
前面坦克发射子弹时,当发射子弹后,子弹还未达到边界时,再按J发射,那么之前的子弹对象就会被滞空,从而去绘制新的子弹。
那么我们先调整成,当,前一颗子弹还未到达边界时,或到达边界了且线程live是false时,才可以创建下一个子弹线程。
实现这个功能,只需要在创建子弹对象的时候加一个判断,tank.shoot == null || tank.shoot ==false

if (e.getKeyCode() == KeyEvent.VK_J){
            if(tank.shoot == null || !tank.shoot.lief) {
                tank.fire();
            }
        }

解决了上面的问题,再来思考怎么发射多颗子弹。
发射子弹其实就是创建一个子弹对象,不断的重绘它的位置,然后判断是否进入了敌人坦克的范围
要发射多颗子弹,可以创建一个子弹集合,每当按下J时,就创建一个子弹对象,且加入到集合中
再绘制的时候改成遍历子弹集合,挨个绘制。判断是否击中敌人时也遍历集合,挨个判断。

//Tank类 我方坦克类
public class Tank {
    int x;
    int y;
    int direct;
    //创建一个子弹集合,用于保存多个子弹
    Vector<shoot> shoots = new Vector<>();
    public Tank(int x, int y, int direct) {
        this.x = x;
        this.y = y;
        this.direct = direct;
    }
    shoot shoot;//定义一个子弹属性
    public void fire(){
        switch (direct){//根据此时坦克的朝向创建对应的子弹发送初始位置
            case 0://当坦克朝上时
                System.out.println("上");
                shoot = new shoot(x+20,y-6,direct);
                break;
            case 1://当坦克朝右时
                shoot = new shoot(x+60,y+24,direct);
                break;
            case 2://当坦克朝下时
                System.out.println("下");
                shoot = new shoot(x+21,y+60,direct);
                break;
            case 3://当坦克朝左边时
                System.out.println("左");
                shoot = new shoot(x-8,y+23,direct);
                break;
        }
        //将刚创建的子弹加入到子弹集合中
        shoots.add(shoot);
        //创建完子弹,启动子弹
        new Thread(shoot).start();
    }
}
//MyPanel类
public class MyPanel extends Panel implements KeyListener,Runnable{
    int speek = 1;
    Tank tank;
    Vector<EnemyTanks> enemyTanks = new Vector<>();//用于保存敌人坦克
    Vector<boom>booms = new Vector<>();//用于保存要爆炸的地方
    Image image1 = Toolkit.getDefaultToolkit().getImage(MyPanel.class.getResource("/b1.png"));//用于保存爆炸效果的图片
    Image image2 = Toolkit.getDefaultToolkit().getImage(MyPanel.class.getResource("/b2.png"));
    Image image3 = Toolkit.getDefaultToolkit().getImage(MyPanel.class.getResource("/b3.png"));
    public MyPanel(){
        //初始化我方坦克
        tank = new Tank(20,460,0);
        //初始化敌方坦克
        for (int i = 0; i < 3; i++) {
            EnemyTanks enemyTank = new EnemyTanks(100*(i+1),0,2);
            //启动敌人坦克线程
            new Thread(enemyTank).start();
            //初始化敌方坦克子弹
            shoot shoot = new shoot(enemyTank.x,enemyTank.y,enemyTank.direct);
            //将子弹放入敌方坦克的shoots集合进行管理
            enemyTank.shoots.add(shoot);
            //启动敌方坦克的子弹
            new Thread(shoot).start();
            enemyTanks.add(enemyTank);
        }
    }
   @Override
    public void paint(Graphics g) {
        super.paint(g);
       System.out.println("调用paint");
       g.fillRect(0,0,500,600);
       //画我方坦克
       drawTank(tank.x,tank.y,g,tank.direct,0);
       //画敌方坦克
       for (int i = 0; i < enemyTanks.size(); i++) {
           EnemyTanks e = enemyTanks.get(i);
           if (e.live) {//如果敌方坦克的live不是false才进行绘制
               drawTank(e.x, e.y, g, e.direct, 1);
               //画出敌方坦克子弹
               for (int j = 0; j < e.shoots.size(); j++) {
                   shoot s = e.shoots.get(j);
                   if (s.lief) {
                       g.setColor(Color.RED);
                       g.drawRect(s.x + 20, s.y + 60, 3, 3);
                   } else {
                       e.shoots.remove(s);
                   }
               }
           }
       }
       //画我方坦克的子弹
       for (int i = 0; i < tank.shoots.size(); i++) {
           shoot s = tank.shoots.get(i);
           if (s!= null && s.lief){
               g.setColor(Color.PINK);
               g.drawRect(s.x,s.y,3,3);
           }else if(!s.lief){
               tank.shoots.remove(s);//当该子弹线程消亡后就把此对象从集合中删除
           }
       }
       //当爆炸效果集合不是null时,说明有坦克被击中了,得开始绘制爆炸效果了
       if (booms!=null){
           try {
               Thread.sleep(50);//因为第一次会额外调用一次paint,所以最好休眠一下,不然第一次爆炸不显示
           } catch (InterruptedException e) {
               e.printStackTrace();
           }
           for (int i = 0; i < booms.size(); i++) {
               boom boom = booms.get(i);
               if (boom.duration>=7){
                   g.drawImage(image1,boom.x,boom.y,60,60,this);
               }else if(boom.duration>=5){
                   g.drawImage(image2,boom.x,boom.y,60,60,this);
               }
               else if(boom.duration>=3){
                   g.drawImage(image3,boom.x,boom.y,60,60,this);
               }
               boom.DownDur();
               if (boom.duration == 0){
                   booms.remove(boom);
               }
           }
       }
    }
    /**
    @param x 坦克的起始横坐标
    @param y 坦克的起始纵坐标
    @param g 画笔
    @param direct 根据方向绘制不同朝向的坦克
    @param type  根据0或者1 绘制不同颜色的坦克
    */
    public void drawTank(int x,int y,Graphics g,int direct,int type){
        switch (type){
            case 0:
                g.setColor(Color.PINK);//自己的坦克就粉色
                break;
            case 1:
                g.setColor(Color.RED);//敌人的坦克就红色
                break;
        }
            switch (direct){//0上,1右,2下,3左
                case 0://0表示绘制方向朝上的坦克
                    g.fill3DRect(x,y,10,50,false);
                    g.fill3DRect(x+10,y+5,25,40,false);
                    g.fill3DRect(x+35,y,10,50,false);
                    g.fillOval(x+10,y+15,25,25);
                    g.drawLine(x+22,y-5,x+22,y+25);
                    break;
                case 1://1表示绘制方向朝右的坦克
                    g.fill3DRect(x,y+7,50,10,false);
                    g.fill3DRect(x+5,y+14,40,25,false);
                    g.fill3DRect(x,y+37,50,10,false);
                    g.fillOval(x+10,y+15,25,25);
                    g.drawLine(x+25,y+25,x+58,y+25);
                    break;
                case 2://2表示绘制方向朝下的坦克
                    g.fill3DRect(x,y,10,50,false);
                    g.fill3DRect(x+10,y+5,25,40,false);
                    g.fill3DRect(x+35,y,10,50,false);
                    g.fillOval(x+10,y+12,25,25);
                    g.drawLine(x+22,y+55,x+22,y+25);
                    break;
                case 3://3表示绘制方向朝左的坦克
                    g.fill3DRect(x,y+7,50,10,false);
                    g.fill3DRect(x+5,y+14,40,25,false);
                    g.fill3DRect(x,y+37,50,10,false);
                    g.fillOval(x+10,y+15,25,25);
                    g.drawLine(x+25,y+25,x-8,y+25);
                    break;
                default://其他情况暂不考虑
                    break;
            }
    }

    @Override //字符输出时,该方法触发
    public void keyTyped(KeyEvent e) {

    }

    @Override//有按键按下时,该方法触发
    public void keyPressed(KeyEvent e) {
        if (e.getKeyCode() == KeyEvent.VK_W){//前进
            moveUp();
            tank.direct = 0;
        }else if(e.getKeyCode() == KeyEvent.VK_S){
            moveDown();
            tank.direct = 2;
        }else if(e.getKeyCode() == KeyEvent.VK_A){
            moveLeft();
            tank.direct = 3;
        }
        else if(e.getKeyCode() == KeyEvent.VK_D){
            moveRight();
            tank.direct = 1;
        }else if(e.getKeyCode() == 32){
            System.out.println("氮气加速~");
            speek +=5;
        }else{
            System.out.println(e.getKeyCode());
        }
        if (e.getKeyCode() == KeyEvent.VK_J){
            if (tank.shoots.size() <= 5) {
                tank.fire();
            }
        }
        repaint();
    }

    @Override//有按键松开时,该方法触发
    public void keyReleased(KeyEvent e) {

    }
    public void moveUp(){
        tank.y-=speek;
    }
    public void moveLeft(){
        tank.x-=speek;
    }
    public void moveRight(){
        tank.x+=speek;
    }
    public void moveDown(){
        tank.y+=speek;
    }

    //此方法判断我方坦克子弹是否击中敌人坦克
    public  void himEnemyTank(Vector<shoot> shoots , EnemyTanks e ){
        for(int i = 0 ; i<shoots.size();i++){
            shoot s = shoots.get(i);
            //上下和左右的x,y是相同的,所以写两个判断即可
            switch (e.direct){
                case 0:
                case 2:
                    if (s.x>e.x&&s.x< e.x+40
                            && s.y>e.y&&s.y<e.y+60){//如果此条件成立就代表,地方在朝上或者朝下的方向被击中了
                        e.live = false;
                        s.lief = false;
                        booms.add(new boom(e.x, e.y));//当有一个坦克被击中就添加一个要执行的爆炸效果
                    }
                    break;
                case 1:
                case 3:
                    if (s.x>e.x&&s.x< e.x+60
                            && s.y>e.y&&s.y<e.y+40){//如果此条件成立就代表,地方在朝左或者朝右的方向被击中了
                        e.live = false;
                        s.lief = false;
                        booms.add(new boom(e.x, e.y));//当有一个坦克被击中就添加一个要执行的爆炸效果
                    }
                    break;
            }

        }

    }

    @Override
    public void run() {
        while (true){
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if (tank.shoot!=null && tank.shoot.lief ){//如果我方坦克的子弹创建了,且线程未结束就对每个敌人坦克进行判断
                for (int i = 0; i < enemyTanks.size(); i++) {
                    EnemyTanks e = this.enemyTanks.get(i);
                    himEnemyTank(tank.shoots,e);
                }
            }
                for (int i = 0; i < enemyTanks.size(); i++) {//如果敌人坦克被击中,那么就把此坦克从集合中删除
                    if (!(enemyTanks.get(i).live)){
                        enemyTanks.remove(i);
                    }
                }
            this.repaint();
        }
    }
}

实现让敌人坦克的子弹消亡后再发射新子弹

在前面已经实现让敌人坦克发射一颗子弹,但是只能发射一颗。
要让坦克发射新的子弹,需要判断前一个子弹消亡再创建新的子弹。
因为在前面定义敌人坦克的子弹用的是集合存放,且绘制敌人坦克的子弹用的也是遍历。
所以只需在敌人坦克类EnemyTanks的run方法中做一个判断,当子弹集合 = =0时(如果要让敌人坦克发射多颗子弹,可以把0变大)
就创建新的子弹到集合中。

//敌人坦克类
public class EnemyTanks extends Tank implements Runnable{
    boolean live = true;
    Vector<shoot> shoots1 = new Vector<>();
    public EnemyTanks(int x, int y, int direct) {
        super(x, y, direct);
    }

    @Override
    public void run() {
        while (live){
            System.out.println("敌人坦克");
            if (live && shoots1.size() == 0){
                shoot Ds = null;
                switch (direct){//根据此时坦克的朝向创建对应的子弹发送初始位置
                    case 0://当坦克朝上时
                        System.out.println("上");
                        Ds = new shoot(x,y,0);
                        break;
                    case 1://当坦克朝右时
                        Ds = new shoot(x+60,y-35,1);
                        break;
                    case 2://当坦克朝下时
                        System.out.println("下");
                        Ds = new shoot(x,y,2);
                        break;
                    case 3://当坦克朝左边时
                        System.out.println("左");
                        Ds = new shoot(x,y-35,3);
                        break;
                }
                System.out.println("创建子弹");
                shoots1.add(Ds);
                new Thread(Ds).start();
            }
            for (int i = 0; i < 30; i++) {
                try {
                    Thread.sleep(200);//睡眠一会防止,还没动几步就换方向了
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                switch (direct){
                    case 0://if (y >= 0) {
                            y -= 2;
                        }
                        break;
                    case 1://if ( x+60 < 500) {
                            x += 2;
                        }
                        break;
                    case 2://if (y+50 < 600) {
                            y += 2;
                        }
                        break;
                    case 3://if (x >= 0 ) {
                            x -= 2;
                        }
                        break;
                }
            }
            direct = (int)(Math.random()*4);//随机调整方向
        }
    }
}
//MyPanel类
public class MyPanel extends Panel implements KeyListener,Runnable{
    int speek = 1;
    Tank tank;
    Vector<EnemyTanks> enemyTanks = new Vector<>();//用于保存敌人坦克
    Vector<boom>booms = new Vector<>();//用于保存要爆炸的地方
    Image image1 = Toolkit.getDefaultToolkit().getImage(MyPanel.class.getResource("/b1.png"));//用于保存爆炸效果的图片
    Image image2 = Toolkit.getDefaultToolkit().getImage(MyPanel.class.getResource("/b2.png"));
    Image image3 = Toolkit.getDefaultToolkit().getImage(MyPanel.class.getResource("/b3.png"));
    public MyPanel(){
        //初始化我方坦克
        tank = new Tank(20,460,0);
        //初始化敌方坦克
        for (int i = 0; i < 3; i++) {
            EnemyTanks enemyTank = new EnemyTanks(100*(i+1),0,2);
            //启动敌人坦克线程
            new Thread(enemyTank).start();
            //初始化敌方坦克子弹
            shoot S = new shoot(enemyTank.x,enemyTank.y,enemyTank.direct);
            //将子弹放入敌方坦克的shoots集合进行管理
            enemyTank.shoots1.add(S);
            //启动敌方坦克的子弹
            new Thread(S).start();
            enemyTanks.add(enemyTank);
        }
    }
   @Override
    public void paint(Graphics g) {
        super.paint(g);
       System.out.println("调用paint");
       g.fillRect(0,0,500,600);
       //画我方坦克
       drawTank(tank.x,tank.y,g,tank.direct,0);

       //画敌方坦克
       for (int i = 0; i < enemyTanks.size(); i++) {
           EnemyTanks e = enemyTanks.get(i);
           if (e.live) {//如果敌方坦克的live不是false才进行绘制
               drawTank(e.x, e.y, g, e.direct, 1);
               //画出敌方坦克子弹
               for (int j = 0; j < e.shoots1.size(); j++) {
                   shoot s = e.shoots1.get(j);
                   if (s.lief) {
                       g.setColor(Color.RED);
                       g.drawRect(s.x + 20, s.y + 60, 3, 3);
                   } else {
                       e.shoots1.remove(s);
                   }
               }
           }
       }
       //画我方坦克的子弹
       for (int i = 0; i < tank.shoots.size(); i++) {
           shoot s = tank.shoots.get(i);
           if (s!= null && s.lief){
               g.setColor(Color.PINK);
               g.drawRect(s.x,s.y,3,3);
           }else if(!s.lief){
               tank.shoots.remove(s);//当该子弹线程消亡后就把此对象从集合中删除
           }
       }
       //当爆炸效果集合不是null时,说明有坦克被击中了,得开始绘制爆炸效果了
       if (booms!=null){
           try {
               Thread.sleep(50);//因为第一次会额外调用一次paint,所以最好休眠一下,不然第一次爆炸不显示
           } catch (InterruptedException e) {
               e.printStackTrace();
           }
           for (int i = 0; i < booms.size(); i++) {
               boom boom = booms.get(i);
               if (boom.duration>=7){
                   g.drawImage(image1,boom.x,boom.y,60,60,this);
               }else if(boom.duration>=5){
                   g.drawImage(image2,boom.x,boom.y,60,60,this);
               }
               else if(boom.duration>=3){
                   g.drawImage(image3,boom.x,boom.y,60,60,this);
               }
               boom.DownDur();
               if (boom.duration == 0){
                   booms.remove(boom);
               }
           }
       }
    }
    /**
    @param x 坦克的起始横坐标
    @param y 坦克的起始纵坐标
    @param g 画笔
    @param direct 根据方向绘制不同朝向的坦克
    @param type  根据0或者1 绘制不同颜色的坦克
    */
    public void drawTank(int x,int y,Graphics g,int direct,int type){
        switch (type){
            case 0:
                g.setColor(Color.PINK);//自己的坦克就粉色
                break;
            case 1:
                g.setColor(Color.RED);//敌人的坦克就红色
                break;
        }
            switch (direct){//0上,1右,2下,3左
                case 0://0表示绘制方向朝上的坦克
                    g.fill3DRect(x,y,10,50,false);
                    g.fill3DRect(x+10,y+5,25,40,false);
                    g.fill3DRect(x+35,y,10,50,false);
                    g.fillOval(x+10,y+15,25,25);
                    g.drawLine(x+22,y-5,x+22,y+25);
                    break;
                case 1://1表示绘制方向朝右的坦克
                    g.fill3DRect(x,y+7,50,10,false);
                    g.fill3DRect(x+5,y+14,40,25,false);
                    g.fill3DRect(x,y+37,50,10,false);
                    g.fillOval(x+10,y+15,25,25);
                    g.drawLine(x+25,y+25,x+58,y+25);
                    break;
                case 2://2表示绘制方向朝下的坦克
                    g.fill3DRect(x,y,10,50,false);
                    g.fill3DRect(x+10,y+5,25,40,false);
                    g.fill3DRect(x+35,y,10,50,false);
                    g.fillOval(x+10,y+12,25,25);
                    g.drawLine(x+22,y+55,x+22,y+25);
                    break;
                case 3://3表示绘制方向朝左的坦克
                    g.fill3DRect(x,y+7,50,10,false);
                    g.fill3DRect(x+5,y+14,40,25,false);
                    g.fill3DRect(x,y+37,50,10,false);
                    g.fillOval(x+10,y+15,25,25);
                    g.drawLine(x+25,y+25,x-8,y+25);
                    break;
                default://其他情况暂不考虑
                    break;
            }
    }

    @Override //字符输出时,该方法触发
    public void keyTyped(KeyEvent e) {

    }

    @Override//有按键按下时,该方法触发
    public void keyPressed(KeyEvent e) {
        if (e.getKeyCode() == KeyEvent.VK_W){//前进
            moveUp();
            tank.direct = 0;
        }else if(e.getKeyCode() == KeyEvent.VK_S){
            moveDown();
            tank.direct = 2;
        }else if(e.getKeyCode() == KeyEvent.VK_A){
            moveLeft();
            tank.direct = 3;
        }
        else if(e.getKeyCode() == KeyEvent.VK_D){
            moveRight();
            tank.direct = 1;
        }else if(e.getKeyCode() == 32){
            System.out.println("氮气加速~");
            speek +=5;
        }else{
            System.out.println(e.getKeyCode());
        }
        if (e.getKeyCode() == KeyEvent.VK_J){
            if (tank.shoots.size() <= 5) {
                tank.fire();
            }
        }
        repaint();
    }

    @Override//有按键松开时,该方法触发
    public void keyReleased(KeyEvent e) {

    }
    public void moveUp(){
        tank.y-=speek;
    }
    public void moveLeft(){
        tank.x-=speek;
    }
    public void moveRight(){
        tank.x+=speek;
    }
    public void moveDown(){
        tank.y+=speek;
    }

    //此方法判断我方坦克子弹是否击中敌人坦克
    public  void himEnemyTank(Vector<shoot> shoots , EnemyTanks e ){
        for(int i = 0 ; i<shoots.size();i++){
            shoot s = shoots.get(i);
            //上下和左右的x,y是相同的,所以写两个判断即可
            switch (e.direct){
                case 0:
                case 2:
                    if (s.x>e.x&&s.x< e.x+40
                            && s.y>e.y&&s.y<e.y+60){//如果此条件成立就代表,地方在朝上或者朝下的方向被击中了
                        e.live = false;
                        s.lief = false;
                        booms.add(new boom(e.x, e.y));//当有一个坦克被击中就添加一个要执行的爆炸效果
                    }
                    break;
                case 1:
                case 3:
                    if (s.x>e.x&&s.x< e.x+60
                            && s.y>e.y&&s.y<e.y+40){//如果此条件成立就代表,地方在朝左或者朝右的方向被击中了
                        e.live = false;
                        s.lief = false;
                        booms.add(new boom(e.x, e.y));//当有一个坦克被击中就添加一个要执行的爆炸效果
                    }
                    break;
            }

        }

    }

    @Override
    public void run() {
        while (true){
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if (tank.shoot!=null && tank.shoot.lief ){//如果我方坦克的子弹创建了,且线程未结束就对每个敌人坦克进行判断
                for (int i = 0; i < enemyTanks.size(); i++) {
                    EnemyTanks e = this.enemyTanks.get(i);
                    himEnemyTank(tank.shoots,e);
                }
            }
                for (int i = 0; i < enemyTanks.size(); i++) {//如果敌人坦克被击中,那么就把此坦克从集合中删除
                    if (!(enemyTanks.get(i).live)){
                        enemyTanks.remove(i);
                    }
                }
            this.repaint();
        }
    }
}

实现敌人坦克子弹击中我方坦克,我方坦克消失且爆炸

将我方坦克定义一个live属性,然后在MyPanel类,定义一个方法用于判断我方坦克是否被击中。(被击中的方法前面写过)
如果被击中就跟之前判断敌人坦克一样,live改成false,且添加一个爆炸对象。再在绘制我方坦克时,添加一个判断,如果我方坦克的live是false就不绘制。

//MyPanel类
public class MyPanel extends Panel implements KeyListener,Runnable{
    int speek = 1;
    Tank tank;
    Vector<EnemyTanks> enemyTanks = new Vector<>();//用于保存敌人坦克
    Vector<boom>booms = new Vector<>();//用于保存要爆炸的地方
    Image image1 = Toolkit.getDefaultToolkit().getImage(MyPanel.class.getResource("/b1.png"));//用于保存爆炸效果的图片
    Image image2 = Toolkit.getDefaultToolkit().getImage(MyPanel.class.getResource("/b2.png"));
    Image image3 = Toolkit.getDefaultToolkit().getImage(MyPanel.class.getResource("/b3.png"));
    public MyPanel(){
        //初始化我方坦克
        tank = new Tank(20,460,0);
        //初始化敌方坦克
        for (int i = 0; i < 3; i++) {
            EnemyTanks enemyTank = new EnemyTanks(100*(i+1),0,2);
            //启动敌人坦克线程
            new Thread(enemyTank).start();
            //初始化敌方坦克子弹
            shoot S = new shoot(enemyTank.x,enemyTank.y,enemyTank.direct);
            //将子弹放入敌方坦克的shoots集合进行管理
            enemyTank.shoots1.add(S);
            //启动敌方坦克的子弹
            new Thread(S).start();
            enemyTanks.add(enemyTank);
        }
    }
   @Override
    public void paint(Graphics g) {
        super.paint(g);
       System.out.println("调用paint");
       g.fillRect(0,0,500,600);
       //画我方坦克
       if (tank.live) {
           drawTank(tank.x, tank.y, g, tank.direct, 0);
       }

       //画敌方坦克
       for (int i = 0; i < enemyTanks.size(); i++) {
           EnemyTanks e = enemyTanks.get(i);
           if (e.live) {//如果敌方坦克的live不是false才进行绘制
               drawTank(e.x, e.y, g, e.direct, 1);
               //画出敌方坦克子弹
               for (int j = 0; j < e.shoots1.size(); j++) {
                   shoot s = e.shoots1.get(j);
                   if (s.lief) {
                       g.setColor(Color.RED);
                       g.drawRect(s.x + 20, s.y + 60, 3, 3);
                   } else {
                       e.shoots1.remove(s);
                   }
               }
           }
       }
       //画我方坦克的子弹
       for (int i = 0; i < tank.shoots.size(); i++) {
           shoot s = tank.shoots.get(i);
           if (s!= null && s.lief){
               g.setColor(Color.PINK);
               g.drawRect(s.x,s.y,3,3);
           }else if(!s.lief){
               tank.shoots.remove(s);//当该子弹线程消亡后就把此对象从集合中删除
           }
       }
       //当爆炸效果集合不是null时,说明有坦克被击中了,得开始绘制爆炸效果了
       if (booms!=null){
           try {
               Thread.sleep(50);//因为第一次会额外调用一次paint,所以最好休眠一下,不然第一次爆炸不显示
           } catch (InterruptedException e) {
               e.printStackTrace();
           }
           for (int i = 0; i < booms.size(); i++) {
               boom boom = booms.get(i);
               if (boom.duration>=7){
                   g.drawImage(image1,boom.x,boom.y,60,60,this);
               }else if(boom.duration>=5){
                   g.drawImage(image2,boom.x,boom.y,60,60,this);
               }
               else if(boom.duration>=3){
                   g.drawImage(image3,boom.x,boom.y,60,60,this);
               }
               boom.DownDur();
               if (boom.duration == 0){
                   booms.remove(boom);
               }
           }
       }
    }
    /**
    @param x 坦克的起始横坐标
    @param y 坦克的起始纵坐标
    @param g 画笔
    @param direct 根据方向绘制不同朝向的坦克
    @param type  根据0或者1 绘制不同颜色的坦克
    */
    public void drawTank(int x,int y,Graphics g,int direct,int type){
        switch (type){
            case 0:
                g.setColor(Color.PINK);//自己的坦克就粉色
                break;
            case 1:
                g.setColor(Color.RED);//敌人的坦克就红色
                break;
        }
            switch (direct){//0上,1右,2下,3左
                case 0://0表示绘制方向朝上的坦克
                    g.fill3DRect(x,y,10,50,false);
                    g.fill3DRect(x+10,y+5,25,40,false);
                    g.fill3DRect(x+35,y,10,50,false);
                    g.fillOval(x+10,y+15,25,25);
                    g.drawLine(x+22,y-5,x+22,y+25);
                    break;
                case 1://1表示绘制方向朝右的坦克
                    g.fill3DRect(x,y+7,50,10,false);
                    g.fill3DRect(x+5,y+14,40,25,false);
                    g.fill3DRect(x,y+37,50,10,false);
                    g.fillOval(x+10,y+15,25,25);
                    g.drawLine(x+25,y+25,x+58,y+25);
                    break;
                case 2://2表示绘制方向朝下的坦克
                    g.fill3DRect(x,y,10,50,false);
                    g.fill3DRect(x+10,y+5,25,40,false);
                    g.fill3DRect(x+35,y,10,50,false);
                    g.fillOval(x+10,y+12,25,25);
                    g.drawLine(x+22,y+55,x+22,y+25);
                    break;
                case 3://3表示绘制方向朝左的坦克
                    g.fill3DRect(x,y+7,50,10,false);
                    g.fill3DRect(x+5,y+14,40,25,false);
                    g.fill3DRect(x,y+37,50,10,false);
                    g.fillOval(x+10,y+15,25,25);
                    g.drawLine(x+25,y+25,x-8,y+25);
                    break;
                default://其他情况暂不考虑
                    break;
            }
    }

    @Override //字符输出时,该方法触发
    public void keyTyped(KeyEvent e) {

    }

    @Override//有按键按下时,该方法触发
    public void keyPressed(KeyEvent e) {
        if (e.getKeyCode() == KeyEvent.VK_W){//前进
            moveUp();
            tank.direct = 0;
        }else if(e.getKeyCode() == KeyEvent.VK_S){
            moveDown();
            tank.direct = 2;
        }else if(e.getKeyCode() == KeyEvent.VK_A){
            moveLeft();
            tank.direct = 3;
        }
        else if(e.getKeyCode() == KeyEvent.VK_D){
            moveRight();
            tank.direct = 1;
        }else if(e.getKeyCode() == 32){
            System.out.println("氮气加速~");
            speek +=5;
        }else{
            System.out.println(e.getKeyCode());
        }
        if (e.getKeyCode() == KeyEvent.VK_J){
            if (tank.shoots.size() <= 5) {
                tank.fire();
            }
        }
        repaint();
    }

    @Override//有按键松开时,该方法触发
    public void keyReleased(KeyEvent e) {

    }
    public void moveUp(){
        tank.y-=speek;
    }
    public void moveLeft(){
        tank.x-=speek;
    }
    public void moveRight(){
        tank.x+=speek;
    }
    public void moveDown(){
        tank.y+=speek;
    }
    //此方法判断敌人坦克是否击中我方坦克
    public void hitMyTank(){
        //便利敌人坦克
        for (int i = 0; i < enemyTanks.size(); i++) {
            EnemyTanks e = this.enemyTanks.get(i);
            for (int j = 0; j < e.shoots1.size(); j++) {
                shoot Dshoot = e.shoots1.get(j);
                himMyTank(Dshoot,tank);
            }

        }
    }
    //此方法判断敌人坦克是否击中我方坦克
    public  void himMyTank(shoot s , Tank e ){
            //上下和左右的x,y是相同的,所以写两个判断即可
            switch (e.direct){
                case 0:
                case 2:
                    if (s.x>e.x&&s.x< e.x+40
                            && s.y-40>e.y&&s.y<e.y+60){//如果此条件成立就代表,地方在朝上或者朝下的方向被击中了
                        e.live = false;
                        s.lief = false;
                        booms.add(new boom(e.x, e.y));//当有一个坦克被击中就添加一个要执行的爆炸效果
                    }
                    break;
                case 1:
                case 3:
                    if (s.x>e.x&&s.x< e.x+60
                            && s.y>e.y&&s.y<e.y-20){//如果此条件成立就代表,地方在朝左或者朝右的方向被击中了
                        e.live = false;
                        s.lief = false;
                        booms.add(new boom(e.x, e.y));//当有一个坦克被击中就添加一个要执行的爆炸效果
                    }
                    break;
            }



    }
    //此方法判断我方子弹是否击中敌人坦克
    public  void himEnemyTank(Vector<shoot> shoots , Tank e ){
        for(int i = 0 ; i<shoots.size();i++){
            shoot s = shoots.get(i);
            //上下和左右的x,y是相同的,所以写两个判断即可
            switch (e.direct){
                case 0:
                case 2:
                    if (s.x>e.x&&s.x< e.x+40
                            && s.y>e.y&&s.y<e.y+60){//如果此条件成立就代表,地方在朝上或者朝下的方向被击中了
                        e.live = false;
                        s.lief = false;
                        booms.add(new boom(e.x, e.y));//当有一个坦克被击中就添加一个要执行的爆炸效果
                    }
                    break;
                case 1:
                case 3:
                    if (s.x>e.x&&s.x< e.x+60
                            && s.y>e.y&&s.y<e.y+40){//如果此条件成立就代表,地方在朝左或者朝右的方向被击中了
                        e.live = false;
                        s.lief = false;
                        booms.add(new boom(e.x, e.y));//当有一个坦克被击中就添加一个要执行的爆炸效果
                    }
                    break;
            }

        }

    }

    @Override
    public void run() {
        while (true){
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if (tank.shoot!=null && tank.shoot.lief ){//如果我方坦克的子弹创建了,且线程未结束就对每个敌人坦克进行判断
                for (int i = 0; i < enemyTanks.size(); i++) {
                    EnemyTanks e = this.enemyTanks.get(i);
                    himEnemyTank(tank.shoots,e);
                }
            }
                for (int i = 0; i < enemyTanks.size(); i++) {//如果敌人坦克被击中,那么就把此坦克从集合中删除
                    if (!(enemyTanks.get(i).live)){
                        enemyTanks.remove(i);
                    }
                }
                //判断我方坦克是否被击中
            hitMyTank();
            this.repaint();
        }
    }
}

你可能感兴趣的:(JAVASE,All,java,开发语言,jvm)