坦克大战进阶--发射子弹

news2024/11/24 15:52:28

坦克大战进阶–发射子弹

1. 坦克大战0.3

1.1 分析

利用线程基础的知识,把坦克大战再次进阶一下:当我们按下J键,坦克就能够发射一颗子弹。

1.2 思路

  1. 当发射一颗子弹后,就相当于启动一个线程
  2. Mytank 有子弹的对象,当按下J时,我们就启动一个发射行为(线程),让子弹不停的移动,形成一个射击的效果
  3. 我们MyPanel需要不停的重绘子弹,才能出现该效果
  4. 当子弹移动到面板的边界时,就应该销毁(把启动的子弹的线程销毁)

1.3 代码实现

  • 父类 Tank
public class Tank {
    private int x;//坦克的横坐标
    private int y;//坦克的纵坐标

    //坦克的方向 0向上 1向右 2向下 3向左
    private int direction;
    //坦克的速度
    private int speed = 1;

    public int getSpeed() {
        return speed;
    }

    public void setSpeed(int speed) {
        this.speed = speed;
    }

    //添加上下左右移动方法
    //向上
    public void moveUp(){
        y-=speed;
    }
    //向下
    public void moveDown(){
        y+=speed;
    }
    //向左
    public void moveLeft(){
        x-=speed;
    }
    //向右
    public void moveRight(){
        x+=speed;
    }

    public int getDirection() {
        return direction;
    }

    public void setDirection(int direction) {
        this.direction = direction;
    }

    public Tank(int x, int y) {
        this.x = x;
        this.y = y;
    }

    public int getX() {
        return x;
    }

    public void setX(int x) {
        this.x = x;
    }

    public int getY() {
        return y;
    }

    public void setY(int y) {
        this.y = y;
    }
}
  • 敌人坦克 EnemyTank
public class EnemyTank extends Tank{

    public EnemyTank(int x, int y) {
        super(x, y);
    }
}
  • 我们的坦克 MyTank
public class MyTank extends Tank {
    //定义一个shot对象,表示一个射击行为(线程)
    Shot shot = null;
    public MyTank(int x, int y) {
        super(x, y);
    }

    public void shotEnemyTank(){
        //创建shot对象
        switch (getDirection()){
            case 0://向上
                shot = new Shot(getX()+20,getY(),0);
                break;
            case 1://向右
                shot = new Shot(getX() + 60, getY() + 20,1);
                break;
            case 2://向下
                shot = new Shot(getX() + 20, getY() + 60,2);
                break;
            case 3://向左
                shot = new Shot(getX(), getY() + 20,3);
                break;
        }
        //启动我们的shot线程
        new Thread(shot).start();
    }
}
  • 子弹 Shot
public class Shot implements Runnable {//射击子弹
    int x;//子弹x坐标
    int y;//子弹y坐标
    int direction;//子弹方向
    int speed = 2;//子弹速度
    boolean isLive = true;//子弹是否还存活

    public Shot(int x, int y, int direction) {
        this.x = x;
        this.y = y;
        this.direction = direction;

    }

    @Override
    public void run() {//射击行为
        while (true) {
            try {//让子弹休眠一下
                Thread.sleep(50);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            //根据方向来改变x,y坐标
            switch (direction) {
                case 0://向上
                    y -= speed;
                    break;
                case 1://向右
                    x += speed;
                    break;
                case 2://向下
                    y += speed;
                    break;
                case 3://向左
                    x -= speed;
                    break;
            }
            //这里用于调试,输入子弹坐标
            System.out.println("子弹x =" + x + " y =" + y);
            //当子弹超出边界就销毁子弹
            if (!(x > 0 && x < 1000 && y > 0 && y < 750)) {
                isLive = false;
                break;
            }
        }
    }
}
  • 面板显示 MyPanel
//为了监听键盘事件,实现 KeyListener
//为了让panel不停的重绘,实现 Runnable,当做一个线程使用
public class MyPanel extends JPanel implements KeyListener, Runnable {
    //定义我的坦克
    MyTank myTank = null;
    //定义敌人坦克,放入到 Vector 
    Vector<EnemyTank> enemyTanks = new Vector<>();
    int enemyTanksize = 3;

    public MyPanel() {
        //初始化自己的坦克
        myTank = new MyTank(100, 100);
        //初始化敌人的坦克
        for (int i = 0; i < enemyTanksize; i++) {
            EnemyTank enemyTank = new EnemyTank((100 * (i + 1)), 0);
            enemyTank.setDirection(2);
            enemyTanks.add(enemyTank);
        }
    }

    @Override
    public void paint(Graphics g) {
        super.paint(g);
        //设置填充矩形,默认黑色
        g.fillRect(0, 0, 1000, 750);

        myTank.setSpeed(5);
        //画出坦克-封装方法
        //自己的坦克
        drawTank(myTank.getX(), myTank.getY(), g, myTank.getDirection(), 1);

        //画出自己的子弹
        if (myTank.shot != null && myTank.shot.isLive == true) {
            g.draw3DRect(myTank.shot.x, myTank.shot.y, 5, 5, true);
        }
        //敌人的坦克
        for (int i = 0; i < enemyTanksize; i++) {
            EnemyTank enemyTank = enemyTanks.get(i);
            drawTank(enemyTank.getX(), enemyTank.getY(), g, enemyTank.getDirection(), 0);
        }
    }
    //编写方法,画出坦克

    /**
     * @param x         坦克的左上角x坐标
     * @param y         坦克的左上角y坐标
     * @param g         画笔
     * @param direction 坦克的方向
     * @param type      坦克的类型
     */
    public void drawTank(int x, int y, Graphics g, int direction, int type) {
        switch (type) {
            case 0://敌人的坦克
                g.setColor(Color.cyan);
                break;
            case 1://我们的坦克
                g.setColor(Color.yellow);
                break;
        }
        //根据坦克的方向,来绘制坦克
        switch (direction) {//0向上 1向右 2向下 3向左
            case 0://默认方向向上
                // 先画第一个矩形 大小 10*60
                //坦克左边轮子
                // 定点(x,y)
                g.fill3DRect(x, y, 10, 60, false);
                // 第二个矩形 大小 20*40
                //坦克身体
                // 定点(x+10,y+10)
                g.fill3DRect(x + 10, y + 10, 20, 40, false);
                // 第三个矩形 大小 10*60
                //坦克右边轮子
                // 定点(x+30,y)
                g.fill3DRect(x + 30, y, 10, 60, false);
                // 上面的圆盖子 大小 (20,20)
                // 定点(x+10,y+20)
                g.fillOval(x + 10, y + 20, 20, 20);
                // 最后的炮管
                // 定点1 (x+20,y)
                // 定点2 (x+20,y+30)
                g.drawLine(x + 20, y, x + 20, y + 30);
                // 画出子弹
                g.drawLine(x + 20, y, x + 20, y + 5);

                break;
            case 1://默认方向向右
                // 先画第一个矩形 大小 60*10
                //坦克上边轮子
                g.fill3DRect(x, y, 60, 10, false);
                // 第二个矩形 大小 40*20
                //坦克身体
                g.fill3DRect(x + 10, y + 10, 40, 20, false);
                // 第三个矩形 大小 10*60
                //坦克下边轮子
                g.fill3DRect(x, y + 30, 60, 10, false);
                // 上面的圆盖子 大小 (20,20)
                g.fillOval(x + 20, y + 10, 20, 20);
                // 最后的炮管
                g.drawLine(x + 60, y + 20, x + 30, y + 20);
                // 画出子弹
                g.drawLine(x + 60, y + 20, x + 55, y + 20);
                break;
            case 2://默认方向向下
                // 先画第一个矩形 大小 10*60
                //坦克左边轮子
                g.fill3DRect(x, y, 10, 60, false);
                // 第二个矩形 大小 20*40
                //坦克身体
                g.fill3DRect(x + 10, y + 10, 20, 40, false);
                // 第三个矩形 大小 10*60
                //坦克右边轮子
                g.fill3DRect(x + 30, y, 10, 60, false);
                // 上面的圆盖子 大小 (20,20)
                g.fillOval(x + 10, y + 20, 20, 20);
                // 最后的炮管
                g.drawLine(x + 20, y + 60, x + 20, y + 30);
                // 画出子弹
                g.drawLine(x + 20, y + 60, x + 20, y + 55);
                break;
            case 3://默认方向向左
                // 先画第一个矩形 大小 60*10
                //坦克上边轮子
                g.fill3DRect(x, y, 60, 10, false);
                // 第二个矩形 大小 40*20
                //坦克身体
                g.fill3DRect(x + 10, y + 10, 40, 20, false);
                // 第三个矩形 大小 10*60
                //坦克下边轮子
                g.fill3DRect(x, y + 30, 60, 10, false);
                // 上面的圆盖子 大小 (20,20)
                g.fillOval(x + 20, y + 10, 20, 20);
                // 最后的炮管
                g.drawLine(x, y + 20, x + 30, y + 20);
                // 画出子弹
                g.drawLine(x, y + 20, x + 5, y + 20);
                break;
            default:
                System.out.println("暂时不作处理");
        }
    }

    @Override
    public void keyTyped(KeyEvent e) {

    }

    //处理 wsad 按下的情况
    @Override
    public void keyPressed(KeyEvent e) {
        if (e.getKeyCode() == KeyEvent.VK_W) {
            //改变坦克的方向
            myTank.setDirection(0);
            myTank.moveUp();
        } else if (e.getKeyCode() == KeyEvent.VK_S) {
            myTank.setDirection(2);
            myTank.moveDown();
        } else if (e.getKeyCode() == KeyEvent.VK_A) {
            myTank.setDirection(3);
            myTank.moveLeft();
        } else if (e.getKeyCode() == KeyEvent.VK_D) {
            myTank.setDirection(1);
            myTank.moveRight();
        }

        //如果用户按下J键,就是发射子弹
        if (e.getKeyCode() == KeyEvent.VK_J) {
            myTank.shotEnemyTank();//发射子弹
        }
        this.repaint();
    }

    @Override
    public void keyReleased(KeyEvent e) {

    }

    //让子弹不停的重绘
    @Override
    public void run() {
        while (true) {
            try {//每隔200毫秒,重绘
                Thread.sleep(100);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            this.repaint();
        }
    }
}
  • 主界面
public class MyTankGame03 extends JFrame {
    //定义 MyPanel
    MyPanel mp = null;

    public static void main(String[] args) {
        MyTankGame03 myTankGame01 = new MyTankGame03();
    }

    public MyTankGame03() {
        //初始化
        mp = new MyPanel();
        //将mp放入到Thread并启动
        Thread thread = new Thread(mp);
        thread.start();
        //面板(游戏的绘图区域)
        this.add(mp);
        //面板大小
        this.setSize(1000, 750);
        //添加键盘监听
        this.addKeyListener(mp);
        //当点击窗口的 × , 程序完全退出
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        //设置显示
        this.setVisible(true);
    }
}
  • 这样程序运行后,当我们按下J键,坦克就能够发射一颗子弹,如下图所示:

2. 坦克大战0.4

2.1 分析

我们再次进阶坦克大战:

  1. 让敌人的坦克也能够发射子弹(可以有多颗子弹)
  2. 当我方坦克击中敌人坦克时,敌人的坦克就消失,如果能做出爆炸效果更好。
  3. 让敌人的坦克也可以自由随机的上下左右移动
  4. 控制我方的坦克和敌人的坦克在规定的范围移动分析->解决

2.2 思路

思路一:让敌人的坦克也能够发射子弹(可以有多颗子弹)

  1. 在敌人坦克类,使用Vector保存多个shot
  2. 当每创建一个敌人坦克对象,给该敌人坦克对象初始化一个 Shot对象,同时启动shot
  3. 在绘制敌人坦克时,需要遍历敌人坦克对象Vector,绘制所有的子弹,当子弹isLive==false时,就从Vector移除

思路二:让敌人的坦克也可以自由随机的上下左右移动思路分析

  1. 因为要求敌人的坦克,可以自由移动,因此需要将敌人坦克当做线程使用
  2. 我们需要EnemyTankimplementsRunnable
  3. 在run 方法写上我们相应的业务代码.
  4. 在创建敌人坦克对象时,启动线程

2.3 代码实现

  • 父类 Tank
public class Tank {
    private int x;//坦克的横坐标
    private int y;//坦克的纵坐标

    //坦克的方向 0向上 1向右 2向下 3向左
    private int direction;
    //坦克的速度
    private int speed = 1;

    public int getSpeed() {
        return speed;
    }

    public void setSpeed(int speed) {
        this.speed = speed;
    }

    //添加上下左右移动方法
    //向上
    public void moveUp() {
        y -= speed;
    }

    //向下
    public void moveDown() {
        y += speed;
    }

    //向左
    public void moveLeft() {
        x -= speed;
    }

    //向右
    public void moveRight() {
        x += speed;
    }

    public int getDirection() {
        return direction;
    }

    public void setDirection(int direction) {
        this.direction = direction;
    }

    public Tank(int x, int y) {
        this.x = x;
        this.y = y;
    }

    public int getX() {
        return x;
    }

    public void setX(int x) {
        this.x = x;
    }

    public int getY() {
        return y;
    }

    public void setY(int y) {
        this.y = y;
    }
}
  • 敌人坦克 EnemyTank
public class EnemyTank extends Tank implements Runnable {
    //在敌人坦克类,使用Vector保存多个shot
    Vector<Shot> shots = new Vector<>();
    boolean isLive = true;

    public EnemyTank(int x, int y) {
        super(x, y);
    }

    @Override
    public void run() {
        while (true) {
            //设置坦克移动
            switch (getDirection()) {
                case 0:
                    //让坦克保持一个方向走100步
                    for (int i = 0; i < 100; i++) {
                        if (getY() > 0) {
                            moveUp();
                        }
                        //休眠100毫秒
                        try {
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
                            throw new RuntimeException(e);
                        }
                    }
                    break;
                case 1:
                    //让坦克保持一个方向走100步
                    for (int i = 0; i < 100; i++) {
                        if (getX() + 60 < 1000) {
                            moveRight();
                        }
                        //休眠100毫秒
                        try {
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
                            throw new RuntimeException(e);
                        }
                    }
                    break;
                case 2:
                    //让坦克保持一个方向走100步
                    for (int i = 0; i < 100; i++) {
                        if (getY() + 60 < 750) {
                            moveDown();
                        }
                        //休眠100毫秒
                        try {
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
                            throw new RuntimeException(e);
                        }
                    }
                    break;
                case 3:
                    //让坦克保持一个方向走100步
                    for (int i = 0; i < 100; i++) {
                        if (getX() > 0) {
                            moveLeft();
                        }
                        //休眠100毫秒
                        try {
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
                            throw new RuntimeException(e);
                        }
                    }
                    break;
            }

            //随机改变方向
            setDirection((int) (Math.random() * 4));

            //考虑线程什么时候退出
            if (!isLive) {
                break;
            }
        }
    }
}
  • 我们的坦克 MyTank
public class MyTank extends Tank {
    //定义一个shot对象,表示一个射击行为(线程)
    Shot shot = null;

    public MyTank(int x, int y) {
        super(x, y);
    }

    public void shotEnemyTank() {
        //创建shot对象
        switch (getDirection()) {
            case 0://向上
                shot = new Shot(getX() + 20, getY(), 0);
                break;
            case 1://向右
                shot = new Shot(getX() + 60, getY() + 20, 1);
                break;
            case 2://向下
                shot = new Shot(getX() + 20, getY() + 60, 2);
                break;
            case 3://向左
                shot = new Shot(getX(), getY() + 20, 3);
                break;
        }
        //启动我们的shot线程
        new Thread(shot).start();
    }
}
  • 子弹 Shot
public class Shot implements Runnable {//射击子弹
    int x;//子弹x坐标
    int y;//子弹y坐标
    int direction;//子弹方向
    int speed = 2;//子弹速度
    boolean isLive = true;//子弹是否还存活

    public Shot(int x, int y, int direction) {
        this.x = x;
        this.y = y;
        this.direction = direction;

    }

    @Override
    public void run() {//射击行为
        while (true) {
            try {//让子弹休眠一下
                Thread.sleep(50);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            //根据方向来改变x,y坐标
            switch (direction) {
                case 0://向上
                    y -= speed;
                    break;
                case 1://向右
                    x += speed;
                    break;
                case 2://向下
                    y += speed;
                    break;
                case 3://向左
                    x -= speed;
                    break;
            }
            //这里用于调试,输入子弹坐标
            System.out.println("子弹x =" + x + " y =" + y);
            //当子弹超出边界就销毁子弹
            //当子弹碰到敌人坦克时,也应该结束线程
            if (!(x > 0 && x < 1000 && y > 0 && y < 750 && isLive)) {
                System.out.println("子弹消失");
                isLive = false;
                break;
            }
        }
    }
}
  • 爆炸效果 Boom
public class Boom {
    int x, y;//炸弹的坐标
    int life = 9;//炸弹的生命周期
    boolean isLive = true;

    public Boom(int x, int y) {
        this.x = x;
        this.y = y;
    }

    //减少生命值
    public void lifeDown() {//配合图片爆炸效果
        if (life > 0) {
            life--;
        } else {
            isLive = false;
        }
    }
}
  • 面板显示 MyPanel
//为了监听键盘事件,实现 KeyListener
//为了让panel不停的重绘,实现 Runnable,当做一个线程使用
public class MyPanel extends JPanel implements KeyListener, Runnable {
    //定义我的坦克
    MyTank myTank = null;
    //定义敌人坦克,放入到 Vector 
    Vector<EnemyTank> enemyTanks = new Vector<>();
    //定义一个Vector,用于存放炸弹
    //当子弹击中坦克时,就加入一个Boom对象booms
    Vector<Boom> booms = new Vector<>();

    int enemyTanksize = 3;

    //定义三张图片,用于显示爆炸效果
    Image image1 = null;
    Image image2 = null;
    Image image3 = null;

    public MyPanel() {
        //初始化自己的坦克
        myTank = new MyTank(100, 100);
        //初始化敌人的坦克
        for (int i = 0; i < enemyTanksize; i++) {
            //创建一个敌人坦克
            EnemyTank enemyTank = new EnemyTank((100 * (i + 1)), 0);
            //设置方向
            enemyTank.setDirection(2);

            //启动敌人坦克,让他动起来
            new Thread(enemyTank).start();

            //给该enemyTank对象加入一颗子弹
            Shot shot = new Shot(enemyTank.getX() + 20, enemyTank.getY() + 60, enemyTank.getDirection());
            //加入到enemyTank的Vector成员
            enemyTank.shots.add(shot);
            //立即启动
            new Thread(shot).start();

            enemyTanks.add(enemyTank);
        }
        //初始化图片
        image1 = Toolkit.getDefaultToolkit().getImage(Panel.class.getResource("/bomb_1.gif"));
        image2 = Toolkit.getDefaultToolkit().getImage(Panel.class.getResource("/bomb_2.gif"));
        image3 = Toolkit.getDefaultToolkit().getImage(Panel.class.getResource("/bomb_3.gif"));
    }

    @Override
    public void paint(Graphics g) {
        super.paint(g);
        //设置填充矩形,默认黑色
        g.fillRect(0, 0, 1000, 750);

        myTank.setSpeed(5);
        //画出坦克-封装方法
        //自己的坦克
        drawTank(myTank.getX(), myTank.getY(), g, myTank.getDirection(), 1);

        //画出自己的子弹
        if (myTank.shot != null && myTank.shot.isLive == true) {
            g.draw3DRect(myTank.shot.x, myTank.shot.y, 5, 5, true);
        }

        //如果booms集合中有对象,就画出
        for (int i = 0; i < booms.size(); i++) {
            //取出炸弹
            Boom boom = booms.get(i);
            //根据当前这个boom对象的life值画出对应的图片
            if (boom.life > 6) {
                g.drawImage(image1, boom.x, boom.y, 80, 80, this);
            } else if (boom.life > 30) {
                g.drawImage(image2, boom.x, boom.y, 80, 80, this);
            } else {
                g.drawImage(image3, boom.x, boom.y, 80, 80, this);
            }
            //让炸弹的生命值减少
            boom.lifeDown();
            //如果boom.life为0,就从booms的集合中删除
            if (boom.life == 0) {
                booms.remove(boom);
            }
        }

        //画出敌人的坦克,遍历Vector
        for (int i = 0; i < enemyTanks.size(); i++) {
            //从Vector取出坦克
            EnemyTank enemyTank = enemyTanks.get(i);
            //判断当前坦克是否还存活
            if (enemyTank.isLive) {//如果敌人的坦克是存活的,才画出该坦克
                drawTank(enemyTank.getX(), enemyTank.getY(), g, enemyTank.getDirection(), 0);

                //画出 enemyTank所有子弹
                for (int j = 0; j < enemyTank.shots.size(); j++) {
                    //取出子弹
                    Shot shot = enemyTank.shots.get(j);
                    //绘制子弹
                    if (shot.isLive) {
                        g.draw3DRect(shot.x, shot.y, 5, 5, true);
                    } else {
                        //从 Vector 移除
                        enemyTank.shots.remove(shot);
                    }
                }
            }
        }
    }
    //编写方法,画出坦克

    /**
     * @param x         坦克的左上角x坐标
     * @param y         坦克的左上角y坐标
     * @param g         画笔
     * @param direction 坦克的方向
     * @param type      坦克的类型
     */
    public void drawTank(int x, int y, Graphics g, int direction, int type) {
        switch (type) {
            case 0://敌人的坦克
                g.setColor(Color.cyan);
                break;
            case 1://我们的坦克
                g.setColor(Color.yellow);
                break;
        }
        //根据坦克的方向,来绘制坦克
        switch (direction) {//0向上 1向右 2向下 3向左
            case 0://默认方向向上
                // 先画第一个矩形 大小 10*60
                //坦克左边轮子
                // 定点(x,y)
                g.fill3DRect(x, y, 10, 60, false);
                // 第二个矩形 大小 20*40
                //坦克身体
                // 定点(x+10,y+10)
                g.fill3DRect(x + 10, y + 10, 20, 40, false);
                // 第三个矩形 大小 10*60
                //坦克右边轮子
                // 定点(x+30,y)
                g.fill3DRect(x + 30, y, 10, 60, false);
                // 上面的圆盖子 大小 (20,20)
                // 定点(x+10,y+20)
                g.fillOval(x + 10, y + 20, 20, 20);
                // 最后的炮管
                // 定点1 (x+20,y)
                // 定点2 (x+20,y+30)
                g.drawLine(x + 20, y, x + 20, y + 30);
                // 画出子弹
                g.drawLine(x + 20, y, x + 20, y + 5);
                break;
            case 1://默认方向向右
                // 先画第一个矩形 大小 60*10
                //坦克上边轮子
                g.fill3DRect(x, y, 60, 10, false);
                // 第二个矩形 大小 40*20
                //坦克身体
                g.fill3DRect(x + 10, y + 10, 40, 20, false);
                // 第三个矩形 大小 10*60
                //坦克下边轮子
                g.fill3DRect(x, y + 30, 60, 10, false);
                // 上面的圆盖子 大小 (20,20)
                g.fillOval(x + 20, y + 10, 20, 20);
                // 最后的炮管
                g.drawLine(x + 60, y + 20, x + 30, y + 20);
                // 画出子弹
                g.drawLine(x + 60, y + 20, x + 55, y + 20);
                break;
            case 2://默认方向向下
                // 先画第一个矩形 大小 10*60
                //坦克左边轮子
                g.fill3DRect(x, y, 10, 60, false);
                // 第二个矩形 大小 20*40
                //坦克身体
                g.fill3DRect(x + 10, y + 10, 20, 40, false);
                // 第三个矩形 大小 10*60
                //坦克右边轮子
                g.fill3DRect(x + 30, y, 10, 60, false);
                // 上面的圆盖子 大小 (20,20)
                g.fillOval(x + 10, y + 20, 20, 20);
                // 最后的炮管
                g.drawLine(x + 20, y + 60, x + 20, y + 30);
                // 画出子弹
                g.drawLine(x + 20, y + 60, x + 20, y + 55);
                break;
            case 3://默认方向向左
                // 先画第一个矩形 大小 60*10
                //坦克上边轮子
                g.fill3DRect(x, y, 60, 10, false);
                // 第二个矩形 大小 40*20
                //坦克身体
                g.fill3DRect(x + 10, y + 10, 40, 20, false);
                // 第三个矩形 大小 10*60
                //坦克下边轮子
                g.fill3DRect(x, y + 30, 60, 10, false);
                // 上面的圆盖子 大小 (20,20)
                g.fillOval(x + 20, y + 10, 20, 20);
                // 最后的炮管
                g.drawLine(x, y + 20, x + 30, y + 20);
                // 画出子弹
                g.drawLine(x, y + 20, x + 5, y + 20);
                break;
            default:
                System.out.println("暂时不作处理");
        }
    }

    //编写方法,判断我方子弹是否击中敌人的坦克
    //什么时候判断我方子弹是否击中敌人?
    // 在run方法里判断比较适合
    public void hitTank(Shot s, EnemyTank enemyTank) {
        //判断击中坦克
        switch (enemyTank.getDirection()) {
            case 0://向上
            case 2://向下
                //子弹的x坐标大于坦克的最左边x坐标
                // 或者小于了坦克最右边x坐标(坦克宽40)
                //子弹的y坐标大于坦克的最上边y坐标
                // 或者小于了坦克最下边y坐标(坦克高60)
                if (s.x > enemyTank.getX() && s.x < enemyTank.getX() + 40
                        && s.y > enemyTank.getY() && s.y < enemyTank.getY() + 60) {
                    //子弹消失
                    s.isLive = false;
                    //敌人的坦克消失
                    enemyTank.isLive = false;
                    //当我们的子弹击中敌人坦克时,将enemyTank从Vector拿掉
                    enemyTanks.remove(enemyTank);

                    //创建Boom对象,加入到booms集合
                    Boom boom = new Boom(enemyTank.getX(), enemyTank.getY());
                    booms.add(boom);
                    break;
                }

            case 1://向右
            case 3://向左
                //子弹的x坐标大于坦克的最左边x坐标
                // 或者小于了坦克最右边x坐标(坦克宽60)
                //子弹的y坐标大于坦克的最上边y坐标
                // 或者小于了坦克最下边y坐标(坦克高40)
                if (s.x > enemyTank.getX() && s.x < enemyTank.getX() + 60
                        && s.y > enemyTank.getY() && s.y < enemyTank.getY() + 40) {
                    //子弹消失
                    s.isLive = false;
                    //敌人的坦克消失
                    enemyTank.isLive = false;
                    //当我们的子弹击中敌人坦克时,将enemyTank从Vector拿掉
                    enemyTanks.remove(enemyTank);

                    //创建Boom对象,加入到booms集合
                    Boom boom = new Boom(enemyTank.getX(), enemyTank.getY());
                    booms.add(boom);
                    break;
                }
        }
    }


    @Override
    public void keyTyped(KeyEvent e) {

    }

    //处理 wsad 按下的情况
    @Override
    public void keyPressed(KeyEvent e) {
        if (e.getKeyCode() == KeyEvent.VK_W) {
            //改变坦克的方向
            myTank.setDirection(0);
            if (myTank.getY() > 0) {
                myTank.moveUp();
            }
        } else if (e.getKeyCode() == KeyEvent.VK_S) {
            myTank.setDirection(2);
            if (myTank.getY() + 60 < 750) {
                myTank.moveDown();
            }
        } else if (e.getKeyCode() == KeyEvent.VK_A) {
            myTank.setDirection(3);
            if (myTank.getX() > 0) {
                myTank.moveLeft();
            }
        } else if (e.getKeyCode() == KeyEvent.VK_D) {
            myTank.setDirection(1);
            if (myTank.getX() + 60 < 1000) {
                myTank.moveRight();
            }
        }

        //如果用户按下J键,就是发射子弹
        if (e.getKeyCode() == KeyEvent.VK_J) {
            myTank.shotEnemyTank();//发射子弹
        }
        this.repaint();
    }

    @Override
    public void keyReleased(KeyEvent e) {

    }

    //让子弹不停的重绘
    @Override
    public void run() {
        while (true) {
            try {//每隔200毫秒,重绘
                Thread.sleep(100);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            //判断是否击中了敌人的坦克
            if (myTank.shot != null && myTank.shot.isLive) {//当前我的子弹还存活
                for (int i = 0; i < enemyTanks.size(); i++) {
                    EnemyTank enemyTank = enemyTanks.get(i);
                    hitTank(myTank.shot, enemyTank);
                }
            }
            this.repaint();
        }
    }
}
  • 主界面
public class MyTankGame04 extends JFrame {
    //定义 MyPanel
    MyPanel mp = null;

    public static void main(String[] args) {
        MyTankGame04 myTankGame01 = new MyTankGame04();
    }

    public MyTankGame04() {
        //初始化
        mp = new MyPanel();
        //将mp放入到Thread并启动
        Thread thread = new Thread(mp);
        thread.start();
        //面板(游戏的绘图区域)
        this.add(mp);
        //面板大小
        this.setSize(1000, 750);
        //添加键盘监听
        this.addKeyListener(mp);
        //当点击窗口的 × , 程序完全退出
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        //设置显示
        this.setVisible(true);
    }
}

3. 坦克大战0.4再次进阶

3.1 分析

坦克进阶:

  1. 我方坦克在发射的子弹消亡后,才能发射新的子弹=>扩展(发多颗子弹怎么办控制在我们的面板上,最多只有5颗.
  2. 让敌人坦克发射的子弹消亡后,可以再发射子弹
  3. 当敌人的坦克击中我方坦克时,我方坦克消失,并出现爆炸效果.(判断敌人的坦克是否击中我的坦克)
  4. 让敌人坦克可以最多发射3颗(在面板上),我们的坦克可以发射3颗并且能够出现正常的爆炸效果即可.

3.2 思路

我方坦克在发射的子弹消亡后,才能发射新的子弹.=>扩展(发多颗子弹怎么办)

  1. 在按下J键,我们判断当前myTank对象的子弹,是否已经销毁
  2. 如果没有销毁,就不去触发shotEnemyTank
  3. 如果已经销毁,才去触发shotEnemyTank
  4. 如果要发射多颗子弹,就使用Vector保存
  5. 在绘制我方子弹时,需要遍历该Vector集合

3.3 代码实现

  • 父类 Tank
public class Tank {
    private int x;//坦克的横坐标
    private int y;//坦克的纵坐标

    boolean isLive = true;

    //坦克的方向 0向上 1向右 2向下 3向左
    private int direction;
    //坦克的速度
    private int speed = 2;

    public int getSpeed() {
        return speed;
    }

    public void setSpeed(int speed) {
        this.speed = speed;
    }

    //添加上下左右移动方法
    //向上
    public void moveUp() {
        y -= speed;
    }

    //向下
    public void moveDown() {
        y += speed;
    }

    //向左
    public void moveLeft() {
        x -= speed;
    }

    //向右
    public void moveRight() {
        x += speed;
    }

    public int getDirection() {
        return direction;
    }

    public void setDirection(int direction) {
        this.direction = direction;
    }

    public Tank(int x, int y) {
        this.x = x;
        this.y = y;
    }

    public int getX() {
        return x;
    }

    public void setX(int x) {
        this.x = x;
    }

    public int getY() {
        return y;
    }

    public void setY(int y) {
        this.y = y;
    }

}
  • 敌人坦克 EnemyTank
public class EnemyTank extends Tank implements Runnable {
    //在敌人坦克类,使用Vector保存多个shot
    Vector<Shot> shots = new Vector<>();

    public EnemyTank(int x, int y) {
        super(x, y);
    }

    @Override
    public void run() {
        while (true) {

            //从这里我们判断如果shots.size()==0,
            // 说明子弹已经销毁,再创建一颗子弹放入shots
            //并启动
            if (isLive && shots.size() < 3) {
                //创建一个临时变量
                Shot s = null;
                //判断坦克的方向
                //创建对应的子弹
                switch (getDirection()) {
                    case 0://向上
                        s = new Shot(getX() + 20, getY(), 0);
                        break;
                    case 1://向右
                        s = new Shot(getX() + 60, getY() + 20, 1);
                        break;
                    case 2://向下
                        s = new Shot(getX() + 20, getY() + 60, 2);
                        break;
                    case 3://向左
                        s = new Shot(getX(), getY() + 20, 3);
                        break;
                }
                //添加一颗子弹
                shots.add(s);
                //启动
                new Thread(s).start();
            }


            //设置坦克移动
            switch (getDirection()) {
                case 0:
                    //让坦克保持一个方向走100步
                    for (int i = 0; i < 100; i++) {
                        if (getY() > 0) {
                            moveUp();
                        }
                        //休眠100毫秒
                        try {
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
                            throw new RuntimeException(e);
                        }
                    }
                    break;
                case 1:
                    //让坦克保持一个方向走100步
                    for (int i = 0; i < 100; i++) {
                        if (getX() + 60 < 1000) {
                            moveRight();
                        }
                        //休眠100毫秒
                        try {
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
                            throw new RuntimeException(e);
                        }
                    }
                    break;
                case 2:
                    //让坦克保持一个方向走100步
                    for (int i = 0; i < 100; i++) {
                        if (getY() + 60 < 750) {
                            moveDown();
                        }
                        //休眠100毫秒
                        try {
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
                            throw new RuntimeException(e);
                        }
                    }
                    break;
                case 3:
                    //让坦克保持一个方向走100步
                    for (int i = 0; i < 100; i++) {
                        if (getX() > 0) {
                            moveLeft();
                        }
                        //休眠100毫秒
                        try {
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
                            throw new RuntimeException(e);
                        }
                    }
                    break;
            }

            //随机改变方向
            setDirection((int) (Math.random() * 4));

            //考虑线程什么时候退出
            if (!isLive) {
                break;
            }
        }
    }
}
  • 我们的坦克 MyTank
public class MyTank extends Tank {
    //定义一个shot对象,表示一个射击行为(线程)
    Shot shot = null;
    //创建多个子弹
    Vector<Shot> shots = new Vector<>();

    //创建一个属性判断我们是否存活


    public MyTank(int x, int y) {
        super(x, y);
    }

    public void shotEnemyTank() {
        if (shots.size() == 5) {
            return;
        }
        //创建shot对象
        switch (getDirection()) {
            case 0://向上
                shot = new Shot(getX() + 20, getY(), 0);
                break;
            case 1://向右
                shot = new Shot(getX() + 60, getY() + 20, 1);
                break;
            case 2://向下
                shot = new Shot(getX() + 20, getY() + 60, 2);
                break;
            case 3://向左
                shot = new Shot(getX(), getY() + 20, 3);
                break;
        }
        //把新创建的shot放入到shots集合中
        shots.add(shot);
        //启动我们的shot线程
        new Thread(shot).start();
    }
}
  • 子弹 Shot
public class Shot implements Runnable {//射击子弹
    int x;//子弹x坐标
    int y;//子弹y坐标
    int direction;//子弹方向
    int speed = 2;//子弹速度
    boolean isLive = true;//子弹是否还存活

    public Shot(int x, int y, int direction) {
        this.x = x;
        this.y = y;
        this.direction = direction;

    }

    @Override
    public void run() {//射击行为
        while (true) {
            try {//让子弹休眠一下
                Thread.sleep(50);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            //根据方向来改变x,y坐标
            switch (direction) {
                case 0://向上
                    y -= speed;
                    break;
                case 1://向右
                    x += speed;
                    break;
                case 2://向下
                    y += speed;
                    break;
                case 3://向左
                    x -= speed;
                    break;
            }
            //这里用于调试,输入子弹坐标
            System.out.println("子弹x =" + x + " y =" + y);
            //当子弹超出边界就销毁子弹
            //当子弹碰到敌人坦克时,也应该结束线程
            if (!(x > 0 && x < 1000 && y > 0 && y < 750 && isLive)) {
                System.out.println("子弹消失");
                isLive = false;
                break;
            }
        }
    }
}
  • 爆炸效果 Boom
public class Boom {
    int x, y;//炸弹的坐标
    int life = 9;//炸弹的生命周期
    boolean isLive = true;

    public Boom(int x, int y) {
        this.x = x;
        this.y = y;
    }

    //减少生命值
    public void lifeDown() {//配合图片爆炸效果
        if (life > 0) {
            life--;
        } else {
            isLive = false;
        }
    }
}
  • 面板显示 MyPanel
//为了监听键盘事件,实现 KeyListener
//为了让panel不停的重绘,实现 Runnable,当做一个线程使用
public class MyPanel extends JPanel implements KeyListener, Runnable {
    //定义我的坦克
    MyTank myTank = null;
    //定义敌人坦克,放入到 Vector 
    Vector<EnemyTank> enemyTanks = new Vector<>();
    //定义一个Vector,用于存放炸弹
    //当子弹击中坦克时,就加入一个Boom对象booms
    Vector<Boom> booms = new Vector<>();

    int enemyTanksize = 3;

    //定义三张图片,用于显示爆炸效果
    Image image1 = null;
    Image image2 = null;
    Image image3 = null;

    public MyPanel() {
        //初始化自己的坦克
        myTank = new MyTank(300, 600);
        //初始化敌人的坦克
        for (int i = 0; i < enemyTanksize; i++) {
            //创建一个敌人坦克
            EnemyTank enemyTank = new EnemyTank((200 * (i + 1)), 0);
            //设置方向
            enemyTank.setDirection(2);

            //启动敌人坦克,让他动起来
            new Thread(enemyTank).start();

            //给该enemyTank对象加入一颗子弹
            Shot shot = new Shot(enemyTank.getX() + 20, enemyTank.getY() + 60, enemyTank.getDirection());
            //加入到enemyTank的Vector成员
            enemyTank.shots.add(shot);
            //立即启动
            new Thread(shot).start();

            enemyTanks.add(enemyTank);
        }
        //初始化图片
        image1 = Toolkit.getDefaultToolkit().getImage(Panel.class.getResource("/bomb_1.gif"));
        image2 = Toolkit.getDefaultToolkit().getImage(Panel.class.getResource("/bomb_2.gif"));
        image3 = Toolkit.getDefaultToolkit().getImage(Panel.class.getResource("/bomb_3.gif"));
    }

    @Override
    public void paint(Graphics g) {
        super.paint(g);
        //设置填充矩形,默认黑色
        g.fillRect(0, 0, 1000, 750);

        myTank.setSpeed(5);
        //画出坦克-封装方法
        //自己的坦克
        //增加一个if判断,当自己不为空并且还存活的情况下才画坦克
        if (myTank != null && myTank.isLive) {
            drawTank(myTank.getX(), myTank.getY(), g, myTank.getDirection(), 1);
        }
        //画出自己的子弹(一颗)
//        if (myTank.shot != null && myTank.shot.isLive == true) {
//            g.draw3DRect(myTank.shot.x, myTank.shot.y, 5, 5, true);
//        }
        //现在我们要画多颗子弹
        //将myTank的子弹shots遍历取出
        for (int i = 0; i < myTank.shots.size(); i++) {
            //取出子弹
            Shot shot = myTank.shots.get(i);
            if (shot != null && shot.isLive) {
                g.draw3DRect(shot.x, shot.y, 5, 5, true);
            } else {//否则该shot对象无效,就从shots集合中拿掉
                myTank.shots.remove(shot);
                break;
            }
        }

        //如果booms集合中有对象,就画出
        for (int i = 0; i < booms.size(); i++) {
            //取出炸弹
            Boom boom = booms.get(i);
            //根据当前这个boom对象的life值画出对应的图片
            if (boom.life > 6) {
                g.drawImage(image1, boom.x, boom.y, 80, 80, this);
            } else if (boom.life > 30) {
                g.drawImage(image2, boom.x, boom.y, 80, 80, this);
            } else {
                g.drawImage(image3, boom.x, boom.y, 80, 80, this);
            }
            //让炸弹的生命值减少
            boom.lifeDown();
            //如果boom.life为0,就从booms的集合中删除
            if (boom.life == 0) {
                booms.remove(boom);
            }
        }

        //画出敌人的坦克,遍历Vector
        for (int i = 0; i < enemyTanks.size(); i++) {
            //从Vector取出坦克
            EnemyTank enemyTank = enemyTanks.get(i);
            //判断当前坦克是否还存活
            if (enemyTank.isLive) {//如果敌人的坦克是存活的,才画出该坦克
                drawTank(enemyTank.getX(), enemyTank.getY(), g, enemyTank.getDirection(), 0);

                //画出 enemyTank所有子弹
                for (int j = 0; j < enemyTank.shots.size(); j++) {
                    //取出子弹
                    Shot shot = enemyTank.shots.get(j);
                    //绘制子弹
                    if (shot.isLive) {
                        g.draw3DRect(shot.x, shot.y, 5, 5, true);
                    } else {
                        //从 Vector 移除
                        enemyTank.shots.remove(shot);
                    }
                }
            }
        }
    }
    //编写方法,画出坦克

    /**
     * @param x         坦克的左上角x坐标
     * @param y         坦克的左上角y坐标
     * @param g         画笔
     * @param direction 坦克的方向
     * @param type      坦克的类型
     */
    public void drawTank(int x, int y, Graphics g, int direction, int type) {
        switch (type) {
            case 0://敌人的坦克
                g.setColor(Color.cyan);
                break;
            case 1://我们的坦克
                g.setColor(Color.yellow);
                break;
        }
        //根据坦克的方向,来绘制坦克
        switch (direction) {//0向上 1向右 2向下 3向左
            case 0://默认方向向上
                // 先画第一个矩形 大小 10*60
                //坦克左边轮子
                // 定点(x,y)
                g.fill3DRect(x, y, 10, 60, false);
                // 第二个矩形 大小 20*40
                //坦克身体
                // 定点(x+10,y+10)
                g.fill3DRect(x + 10, y + 10, 20, 40, false);
                // 第三个矩形 大小 10*60
                //坦克右边轮子
                // 定点(x+30,y)
                g.fill3DRect(x + 30, y, 10, 60, false);
                // 上面的圆盖子 大小 (20,20)
                // 定点(x+10,y+20)
                g.fillOval(x + 10, y + 20, 20, 20);
                // 最后的炮管
                // 定点1 (x+20,y)
                // 定点2 (x+20,y+30)
                g.drawLine(x + 20, y, x + 20, y + 30);
                // 画出子弹
                g.drawLine(x + 20, y, x + 20, y + 5);
                break;
            case 1://默认方向向右
                // 先画第一个矩形 大小 60*10
                //坦克上边轮子
                g.fill3DRect(x, y, 60, 10, false);
                // 第二个矩形 大小 40*20
                //坦克身体
                g.fill3DRect(x + 10, y + 10, 40, 20, false);
                // 第三个矩形 大小 10*60
                //坦克下边轮子
                g.fill3DRect(x, y + 30, 60, 10, false);
                // 上面的圆盖子 大小 (20,20)
                g.fillOval(x + 20, y + 10, 20, 20);
                // 最后的炮管
                g.drawLine(x + 60, y + 20, x + 30, y + 20);
                // 画出子弹
                g.drawLine(x + 60, y + 20, x + 55, y + 20);
                break;
            case 2://默认方向向下
                // 先画第一个矩形 大小 10*60
                //坦克左边轮子
                g.fill3DRect(x, y, 10, 60, false);
                // 第二个矩形 大小 20*40
                //坦克身体
                g.fill3DRect(x + 10, y + 10, 20, 40, false);
                // 第三个矩形 大小 10*60
                //坦克右边轮子
                g.fill3DRect(x + 30, y, 10, 60, false);
                // 上面的圆盖子 大小 (20,20)
                g.fillOval(x + 10, y + 20, 20, 20);
                // 最后的炮管
                g.drawLine(x + 20, y + 60, x + 20, y + 30);
                // 画出子弹
                g.drawLine(x + 20, y + 60, x + 20, y + 55);
                break;
            case 3://默认方向向左
                // 先画第一个矩形 大小 60*10
                //坦克上边轮子
                g.fill3DRect(x, y, 60, 10, false);
                // 第二个矩形 大小 40*20
                //坦克身体
                g.fill3DRect(x + 10, y + 10, 40, 20, false);
                // 第三个矩形 大小 10*60
                //坦克下边轮子
                g.fill3DRect(x, y + 30, 60, 10, false);
                // 上面的圆盖子 大小 (20,20)
                g.fillOval(x + 20, y + 10, 20, 20);
                // 最后的炮管
                g.drawLine(x, y + 20, x + 30, y + 20);
                // 画出子弹
                g.drawLine(x, y + 20, x + 5, y + 20);
                break;
            default:
                System.out.println("暂时不作处理");
        }
    }

    // 创建一个方法 hitMyTank
    // 判断敌人的子弹是否打中我们
    public void hitMyTank() {
        //遍历所有敌人坦克
        for (int i = 0; i < enemyTanks.size(); i++) {
            //取出敌人坦克
            EnemyTank enemyTank = enemyTanks.get(i);
            for (int j = 0; j < enemyTank.shots.size(); j++) {
                //取出子弹
                Shot shot = enemyTank.shots.get(j);
                //判断shot是否击中我们的坦克
                if (myTank.isLive && shot.isLive) {
                    hitTank(shot, myTank);
                }
            }
        }
    }

    // 现我们的坦克可以发射多个子弹
    // 在判断我方子弹是否击中敌人坦克时,
    // 就需要把我们的子弹集合中所有的子弹都取出,
    // 和敌人的所有坦克进行判断
    public void hitEnemyTank() {
        //遍历我们的子弹
        for (int j = 0; j < myTank.shots.size(); j++) {
            Shot shot = myTank.shots.get(j);
            if (shot != null && shot.isLive) {//当前我的子弹还存活
                for (int i = 0; i < enemyTanks.size(); i++) {
                    EnemyTank enemyTank = enemyTanks.get(i);
                    hitTank(shot, enemyTank);
                }
            }
        }
    }

    // 编写方法,判断我方子弹是否击中敌人的坦克
    // 什么时候判断我方子弹是否击中敌人?
    // 在run方法里判断比较适合

    // 因为现在这方法用于判断我们的坦克跟敌人的坦克了
    // 所以这里把 EnemyTank enemyTank 改为 Tank tank
    //改用两者的父类 Tank 对象
    public void hitTank(Shot s, Tank tank) {
        //判断击中坦克
        switch (tank.getDirection()) {
            case 0://向上
            case 2://向下
                //子弹的x坐标大于坦克的最左边x坐标
                // 或者小于了坦克最右边x坐标(坦克宽40)
                //子弹的y坐标大于坦克的最上边y坐标
                // 或者小于了坦克最下边y坐标(坦克高60)
                if (s.x > tank.getX() && s.x < tank.getX() + 40
                        && s.y > tank.getY() && s.y < tank.getY() + 60) {
                    //子弹消失
                    s.isLive = false;
                    //敌人的坦克消失
                    tank.isLive = false;
                    //当我们的子弹击中敌人坦克时,将enemyTank从Vector拿掉
                    enemyTanks.remove(tank);

                    //创建Boom对象,加入到booms集合
                    Boom boom = new Boom(tank.getX(), tank.getY());
                    booms.add(boom);
                    break;
                }

            case 1://向右
            case 3://向左
                //子弹的x坐标大于坦克的最左边x坐标
                // 或者小于了坦克最右边x坐标(坦克宽60)
                //子弹的y坐标大于坦克的最上边y坐标
                // 或者小于了坦克最下边y坐标(坦克高40)
                if (s.x > tank.getX() && s.x < tank.getX() + 60
                        && s.y > tank.getY() && s.y < tank.getY() + 40) {
                    //子弹消失
                    s.isLive = false;
                    //敌人的坦克消失
                    tank.isLive = false;
                    //当我们的子弹击中敌人坦克时,将enemyTank从Vector拿掉
                    enemyTanks.remove(tank);

                    //创建Boom对象,加入到booms集合
                    Boom boom = new Boom(tank.getX(), tank.getY());
                    booms.add(boom);
                    break;
                }
        }
    }


    @Override
    public void keyTyped(KeyEvent e) {

    }

    //处理 wsad 按下的情况
    @Override
    public void keyPressed(KeyEvent e) {
        if (e.getKeyCode() == KeyEvent.VK_W) {
            //改变坦克的方向
            myTank.setDirection(0);
            if (myTank.getY() > 0) {
                myTank.moveUp();
            }
        } else if (e.getKeyCode() == KeyEvent.VK_S) {
            myTank.setDirection(2);
            if (myTank.getY() + 60 < 750) {
                myTank.moveDown();
            }
        } else if (e.getKeyCode() == KeyEvent.VK_A) {
            myTank.setDirection(3);
            if (myTank.getX() > 0) {
                myTank.moveLeft();
            }
        } else if (e.getKeyCode() == KeyEvent.VK_D) {
            myTank.setDirection(1);
            if (myTank.getX() + 60 < 1000) {
                myTank.moveRight();
            }
        }

        //如果用户按下J键,就是发射子弹
        if (e.getKeyCode() == KeyEvent.VK_J) {
            //判断当前myTank子弹是否已经销毁 发射一颗子弹
//            if (myTank.shot == null || !myTank.shot.isLive) {
//                myTank.shotEnemyTank();//发射子弹
//            }
            //判断当前myTank子弹是否已经销毁 发射duo颗子弹
            myTank.shotEnemyTank();
        }
        this.repaint();
    }

    @Override
    public void keyReleased(KeyEvent e) {

    }

    //让子弹不停的重绘
    @Override
    public void run() {
        while (true) {
            try {//每隔200毫秒,重绘
                Thread.sleep(100);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            //把下面注释的这段代码封装到上面的方法里面
            //判断是否击中了敌人的坦克
//            if (myTank.shot != null && myTank.shot.isLive) {//当前我的子弹还存活
//                for (int i = 0; i < enemyTanks.size(); i++) {
//                    EnemyTank enemyTank = enemyTanks.get(i);
//                    hitTank(myTank.shot, enemyTank);
//                }
//            }
            //判断敌人坦克是否击中我们
            hitMyTank();
            hitEnemyTank();
            this.repaint();
        }
    }
}
  • 主界面
public class MyTankGame04 extends JFrame {
    //定义 MyPanel
    MyPanel mp = null;

    public static void main(String[] args) {
        MyTankGame04 myTankGame01 = new MyTankGame04();
    }

    public MyTankGame04() {
        //初始化
        mp = new MyPanel();
        //将mp放入到Thread并启动
        Thread thread = new Thread(mp);
        thread.start();
        //面板(游戏的绘图区域)
        this.add(mp);
        //面板大小
        this.setSize(1000, 750);
        //添加键盘监听
        this.addKeyListener(mp);
        //当点击窗口的 × , 程序完全退出
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        //设置显示
        this.setVisible(true);
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/552812.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

MSP432笔记5——外部中断

所用单片机型号&#xff1a;MSP432P401r 今日继续我的MSP432电赛速通之路。 外部中断是个很有用的配置 STM32几乎每个I/O口都能配置复用为外部中断 但MSP432并不是这样。 我经过查阅数据手册发现支持中断的引脚为&#xff1a; P1^0~ P1^7 P3^0~ P3^7 P5^0~ P5^…

Gateway服务网关入门

Gateway服务网关 Spring Cloud Gateway 是 Spring Cloud 的一个全新项目&#xff0c;该项目是基于 Spring 5.0&#xff0c;Spring Boot 2.0 和 Project Reactor 等响应式编程和事件流技术开发的网关&#xff0c;它旨在为微服务架构提供一种简单有效的统一的 API 路由管理方式。…

【网络字节序】

网络字节序 我们已经知道&#xff0c;内存中的多字节数据相对于内存地址有大端和小端之分&#xff0c;磁盘文件中的多字节数据相对于文件中的偏移地址也有大端小端之分。网络数据流同样有大端小端之分&#xff0c;那么如何定义网络数据流的地址呢&#xff1f;发送主机通常将发送…

【C++】21年精通C++之泛型编程和模板初阶知识

❤️前言 大家好&#xff01;今天和大家一起学习关于C泛型编程和模板初阶的相关知识。 正文 我们之前已经学习了C中非常重要的一个特性——函数重载&#xff0c;函数重载很好地提高了我们代码的可读性。但是对于适配多种参数的某种函数来说&#xff0c;我们如果使用函数重载就…

感知程序从ros切换到cyber_rt框架下,pcl相关问题

1.在ubuntu20.04下&#xff0c;原感知程序需要的是pcl1.8.1&#xff0c;车上其他程序使用的是pcl.1.10.0或者pcl1.10.0&#xff0c;在编译pcl1.10.0时会编译通不过&#xff0c;而pcl1.10.1可以顺利编译通过&#xff0c;安装pcl1.8.1时遇到的问题可能如下&#xff0c;及对应的修…

CTF必看~ PHP反序列化漏洞6:绝妙_wakeup绕过技巧

作者&#xff1a;Eason_LYC 悲观者预言失败&#xff0c;十言九中。 乐观者创造奇迹&#xff0c;一次即可。 一个人的价值&#xff0c;在于他所拥有的。可以不学无术&#xff0c;但不能一无所有&#xff01; 技术领域&#xff1a;WEB安全、网络攻防 关注WEB安全、网络攻防。我的…

iptables防火墙2

iptables防火墙 一&#xff1a;SNAT原理与应用 SNAT 应用环境&#xff1a;局域网主机共享单个公网IP地址接入Internet&#xff08;私有不能早Internet中正常路由&#xff09;SNAT原理&#xff1a;修改数据包的源地址。 SNAT转换前提条件&#xff1a; 1.局域网各主机已正确设…

新星计划 Electron+vue2 桌面应用 2 搭建及运行

基础内容&#xff1a;新星计划 Electronvue2 桌面应用 1 基础_lsswear的博客-CSDN博客 根据使用过的经验和官网的描述&#xff0c;大概可以有四种方式&#xff1a; 自己创建项目&#xff08;仅使用npm&#xff09;用Electron脚手架HBuilder编译为web&#xff0c;再用Electron…

MSP432笔记4:时钟与滴答计时器

所用单片机型号&#xff1a;MSP432P401r 今日继续更新我的MSP432电赛速通笔记&#xff1a; 提示&#xff1a; 本节内容相当于讲述delay_ms&#xff08;&#xff09; 和delay_us&#xff08;&#xff09; 俩延时函数的由来&#xff0c; 所以不需要花费过多时间斟酌 MSP432单…

论文阅读_音频表示_wav2vec_2.0

论文信息 name_en: wav2vec 2.0: A Framework for Self-Supervised Learning of Speech Representations name_ch: wav2vec 2.0&#xff1a;语音表示自监督学习框架 paper_addr: http://arxiv.org/abs/2006.11477 date_read: 2023-04-27 date_publish: 2020-10-22 tags: [‘深…

C++深度解析:虚函数的使用与避免

C深度解析&#xff1a;虚函数的使用与避免 1. 虚函数的基本概念与原理 (Basic Concepts and Principles of Virtual Functions)1.1 虚函数的定义与作用 (Definition and Role of Virtual Functions)1.2 虚函数的底层实现 (Underlying Implementation of Virtual Functions)1.3 …

【CANN训练营0基础赢满分秘籍】进阶班 Atlas 200I DK 智能小车

1 智能小车三维结构设计 1.1 基本模块 坚固酷炫结构模块运动控制模块超声波传感器模块摄像头视觉模块其他传感器模块 1.2 结构设计基本原则 从零开始设计并搭建智能小车&#xff0c;在满足外观要求的基础上&#xff0c;要满足小车运转过程中的运动干涉率为O&#xff0c;并且…

【CANN训练营0基础赢满分秘籍】进阶班 应用开发深入讲解

1 AIPP AIPP (Artificial Intelligence Pre-Processing)人工智能预处理&#xff0c;在AI Corfe上完成数据预处理。 1.1 静态AIPP 构造AIPP配置文件*.cfg使能静态AIPP&#xff0c;将其配置参数保存在模型文件中。 atc --framework3--soc_versionS[soc_version) --model SHOM…

基于51单片机的电子琴Protues仿真设计

一、设计背景 基于51单片机的电子琴是一款由51单片机控制器、音频模块和硬件阵列组成的数字化乐器。它可以模拟各种乐器的音效&#xff0c;同时也具有许多常规电子琴所没有的高级功能。 首先&#xff0c;这种电子琴是以数字信号处理技术为基础的。通过软件编程&#xff0c;将…

【JUC】Java对象内存布局和对象头

【JUC】Java对象内存布局和对象头 文章目录 【JUC】Java对象内存布局和对象头1. 对象的内存布局1.1 对象头1.1.1 对象标记1.1.2 类元信息/类型指针 1.2 实例数据1.3 对齐填充 2. 测试 1. 对象的内存布局 在 HotSpot 虚拟机里&#xff0c;对象在堆内存中的存储布局可以划分为三…

MSP432学习笔记6:中断优先级管理

所用型号&#xff1a;MSP432P401R 今日继续我的MSP432电赛速通之路。 主要学习的是&#xff1a;中断优先级管理、软件挂起中断、屏蔽中断优先级 目录 MSP432具有8级可编程的中断优先级。 中断优先级管理库函数&#xff1a; 软件挂起中断&#xff1a; 屏蔽中断优先级&#…

微信小程序富文本插件mp-html

使用场景&#xff1a; 偏偏后端传过来的数据又要用到富文本标签&#xff0c;然后找了很多组件&#xff0c;要不就是下载量低&#xff0c;要不就是里面功能太少&#xff0c;只有这款mp-html组件深得我心&#xff0c;里面功能丰富&#xff0c;简单实用&#xff0c;真的绝绝子&…

DMA直接存储器存取

目录 存储器映像 寄存器 DMA框图 DMA基本结构 DMA请求映射 数据宽度与对齐 ​编辑 存储器到存储器 ​编辑 外设与存储器 来源b站江科大stm3入门教程 存储器映像 寄存器 DMA框图 AHB从设备&#xff08;DMA自身的寄存器&#xff09;连接在总线矩阵右侧的AHB总线上 所以DMA既…

LeetCode:509. 斐波那契数 70. 爬楼梯 746. 使用最小花费爬楼梯

509. 斐波那契数 题目 斐波那契数 &#xff08;通常用 F(n) 表示&#xff09;形成的序列称为 斐波那契数列 。该数列由 0 和 1 开始&#xff0c;后面的每一项数字都是前面两项数字的和。也就是&#xff1a; F(0) 0&#xff0c;F(1) 1 F(n) F(n - 1) F(n - 2)&#xff0c;…

无底线内卷?谈谈如何在职场中实现人生巅峰

在竞争激烈的职场上&#xff0c;各种职场难题时常出现&#xff0c;如何进行有效沟通、如何应对工作压力、如何提升职业能力等&#xff0c;这都是需要去克服的问题。 1. 尝试人际沟通A&#xff1a;TO 企业B&#xff1a;TO 员工 2. 适应工作压力A&#xff1a;原因B&#xff1a;TO…