坦克大战(三)

新增功能:

  1. 解决穿过边界问题

  2. 实现敌方坦克自由移动

  3. 我方坦克实现连续发射,且同一时间最多存在五发子弹

  4. 敌方坦克发射的子弹在销毁后,再次发射子弹

  5. 实现我方坦克被击中时爆炸

功能实现 :

一、解决穿过边界问题

思路:

在超类Tank类的移动方法中添加限制条件,即在坦克到达边界坐标后,无法再次调用移动方法进行移动

实现代码:
//上下左右移动方法
    public void moveUp() {
        if (y - speed >= 0 && y <= 690) {
            y -= speed;
        }
//        System.out.println("x=" + x + " y=" + y);//测试到达边界时坐标
    }

    public void moveRight() {
        if (x >= 0 && x + speed <= 940) {
            x += speed;
        }
//        System.out.println("x=" + x + " y=" + y);
    }

    public void moveDown() {
        if (y >= 0 && y + speed <= 690) {
            y += speed;
        }
//        System.out.println("x=" + x + " y=" + y);
    }

    public void moveLeft() {
        if (x - speed >= 0 && x <= 940) {
            x -= speed;
        }
//        System.out.println("x=" + x + " y=" + y);
    }

 二、实现敌方坦克自由移动

思路:

在enemyTank类中的run方法里,不停的循环获取坦克当前方向,然后根据方向调用对应的移动方法,可以利用(int)(Math.random()*500)得到随机数,根据随机数确定这个方向移动几次,以防止移动过快,延时50毫秒移动一次。这个方向移动结束后,同样使用(int)(Math.random()*4)得到0~3的随机整数,通过setDirect()方法即可随机更改移动方向

代码实现:
   @Override
    public void run() {
        while (true) {

            //根据方向继续向前走
            switch (getDirect()) {
                case 0:
                    //在一个方向上随机多走几步
                    for (int i = 0; i < (int) (Math.random() * 500); i++) {
                        moveUp();
                        //休眠50毫秒
                        try {
                            Thread.sleep(50);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    break;
                case 1:
                    for (int i = 0; i < (int) (Math.random() * 500); i++) {
                        moveRight();
                        //休眠50毫秒
                        try {
                            Thread.sleep(50);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    break;
                case 2:
                    for (int i = 0; i < (int) (Math.random() * 500); i++) {
                        moveDown();
                        //休眠50毫秒
                        try {
                            Thread.sleep(50);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    break;
                case 3:
                    for (int i = 0; i < (int) (Math.random() * 500); i++) {
                        moveLeft();
                        //休眠50毫秒
                        try {
                            Thread.sleep(50);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    break;
            }
            
            ...

            //改变行动方向 0~3
            setDirect((int) (Math.random() * 4));

            //该线程结束条件
            if (!isLive) {
                break;
            }

        }
    }

三、我方坦克实现连续发射,且同一时间最多存在五发子弹

思路:

① 在Tank超类中定义用来存储Shot子弹类的集合Vector<Shot> shots,并且定义一个根据Tank位置和方向创建Shot对象的方法public void shotEnemyTank(),在方法中每调用一次即可创建一个Shot对象,且会添加到shots集合中和启动线程。

② 在MyPanel类的绘制我方子弹的方法,更改为先取出shots中的shot对象,然后判断shot对象是否存活,若存活则进行绘制,否则就从shots中删除该shot对象。

③ 实现同一时间存在五发子弹,则需要在监听按键J处添加判断条件——当myTank的shots.size()<5时,才可调用shotEnemyTank()方法创建新的shot对象。

代码实现:
 //定义一个Shot对象,表示一个射击行为
    Shot shot = null;
    //存储shot对象
    Vector<Shot> shots = new Vector<>();
    //射击
    public void shotEnemyTank() {
        //创建Shot对象,根据Tank对象位置和方向来创建Shot
        // 0 向上   1 向右   2 向下   3 向左
        switch (getDirect()) {//得到Tank方向
            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;
        }
        shots.add(shot);
        //启动Shot线程
        new Thread(shot).start();
    }
//画MyTank发射的子弹
        for (int i = 0; i < myTank.shots.size(); i++) {
            Shot shot = myTank.shots.get(i);
            if (shot != null && shot.isLive) {
                g.setColor(Color.cyan);//青色
                g.fill3DRect(shot.x, shot.y, shot.width, shot.height, false);
            } else {
                myTank.shots.remove(shot);
            }
        }
 //如果用户按下键就会发射子弹
        if (e.getKeyCode() == KeyEvent.VK_J) {//J键
            //一次发射一颗子弹
//            if (myTank.shotVector.size() == 0) {
//                myTank.shotEnemyTank();
//            }
            //连续发射
//            myTank.shotEnemyTank();
            //同一时间最多存在五个子弹
            if (myTank.shots.size() < 5) {
                myTank.shotEnemyTank();
            }
        }

 四、敌方坦克发射的子弹在销毁后,再次发射子弹

思路:

① 在MyPanel类对敌方坦克进行初始化的时候,调用enemyTank.shotEnemyTank()方法创建Shot对象,并启动,可以实现坦克在刚出现时发射一发子弹

② 在enemyTank敌方坦克类的run方法中添加判断条件——当shot.isLive为false时即子弹不存活时,调用shotEnemyTank()方法用于创建新的Shot对象并启动,即可实现当敌方发射的子弹销毁时,再次发射子弹。

代码实现:
 //初始化敌人坦克
        for (int i = 0; i < enemySize; i++) {
            EnemyTank enemyTank = new EnemyTank(100 * (i + 1), 0, 1);
            //启动坦克run方法,让其可以随机移动
            new Thread(enemyTank).start();
            enemyTank.shotEnemyTank();//创建新的Shot对象,并启动

            //加入用于存放敌人坦克的Vector中
            enemyTanks.add(enemyTank);
        }
 if (!shot.isLive) {
                shotEnemyTank();//创建新的Shot对象,并启动
            }

五、实现我方坦克被击中时爆炸实现我方坦克被击中时爆炸

思路:

编写public void hitTankMy()方法用于判断我方坦克是否被敌方击中。首先,遍历enemyTanks集合取出敌方坦克对象,再次遍历敌方坦克对象的shots集合取出shot对象。随后,判断敌方坦克的shot对象是否存活,若存活则传入hitTank(enemyTank.shot, myTank)方法进行击中判断。最后,将hitTankMy()方法放入MyPanel的run方法中循环判断。

代码实现:
 //发射多颗子弹时,判断我方坦克是否被子弹击中
    public void hitTankMy() {

        for (int i = 0; i < enemyTanks.size(); i++) {
            EnemyTank enemyTank = enemyTanks.get(i);//
            for (int j = 0; j < enemyTank.shots.size(); j++) {
                //判断我方坦克是否被子弹击中
                if (enemyTank.shot != null && enemyTank.shot.isLive) {//当子弹还存活时
                    hitTank(enemyTank.shot, myTank);
                }
            }

        }
    }

    //编写方法,判断子弹是否击中坦克 将其放入run方法循环判断
    public void hitTank(Shot s, Tank tank) {
        //判断 s 击中坦克
        // 0 向上   1 向右   2 向下   3 向左
        switch (tank.getDirect()) {
            case 0:
            case 2:
                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;
                    //创建Bomb对象,加入到bombs中
                    Bomb bomb = new Bomb(tank.getX(), tank.getY());
                    bombs.add(bomb);
                }
                break;
            case 1:
            case 3:
                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;
                    //创建Bomb对象,加入到bombs中
                    Bomb bomb = new Bomb(tank.getX(), tank.getY());
                    bombs.add(bomb);
                }
                break;
        }
    }
@Override
    public void run() {//每隔100毫秒,重绘区域,刷新绘图区域,子弹就移动  在创建MyPanel的地方启动线程
        while (true) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            hitTankEnemy();//判断敌人坦克是否被击中
            hitTankMy();//判断我方坦克是否被击中
            this.repaint();//重绘
        }
    }

在学习中不断改进、不断进步,加油!!!

未完待续~

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

渺若星辰_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值