【JAVA学习笔记】63 -坦克大战1.3-敌方发射子弹,击中坦克消失并爆炸,敌人坦克随机移动,规定范围限制移动

项目代码

https://github.com/yinhai1114/Java_Learning_Code/tree/main/IDEA_Chapter18/src/com/yinhai/tankgame1_3

〇、要求

增加功能

1.让敌人的坦克也能够发射子弹(可以有多颗子弹)

2.当我方坦克击中敌人坦克时,敌人的坦克就消失,如果能做出爆炸效果更好.

3.让敌人的坦克也可以自由随机的上下左右移动

4.控制我方的坦克和敌人的坦克在规定的范围移动

一、敌人坦克也能发射子弹

思路

        1.敌人的坦克也使用Vector来保存它的子弹,因为多个敌人有多个子弹

        2.调用设计方法,就给该坦克初始化一个Shot对象,同时启动Shot

        3.在绘制敌人坦克时,需要Enemy坦克,如果子弹消亡,记得回收该子弹

1.新建Enemy类

这一段代码类似于Hero类,有shotBullet方法,该方法创建了子弹对象,和1.1版本的功能一样,启动shot线程,这个类也创建了enemyBullets用于存放敌人射出的子弹对象

public class Enemy extends Tank {
    Vector<Bullet> enemyBullets = new Vector<>();
    private int type = 1;
    public Enemy(int x, int y,double speed) {
        super(x, y,speed);
        setDirect(2);
    }

    public int getTYPE() {
        return type;
    }
    public Bullet shotBullet(){
        Bullet bullet = null;
        switch (getDirect()){
            case 0:
                bullet = new Bullet(this.getX() + 18,this.getY() - 10,50,getDirect());
                break;
            case 1:
                bullet  = new Bullet(this.getX() + 60,this.getY() +18,50,getDirect());
                break;
            case 2:
                bullet = new Bullet(this.getX() + 18,this.getY() +60,50,getDirect());
                break;
            case 3:
                bullet = new Bullet(this.getX() - 10,this.getY()+18,50,getDirect());
                break;
        }
        enemyBullets.add(bullet);
        Bullet.Shot shot = bullet.new Shot();
        Thread thread = new Thread(shot);
        thread.start();
        return bullet;
    }
}

2.MyPanel类的paint方法

该方法改进,将1.2的绘画子弹方法进行封装,paintBullet方法,其本质还是1.2版本的思路,循环遍历列表,消亡我就添加到消亡列表,然后remove子弹列表里的所有消亡列表,最后清空消亡列表,我们的Enemy保存为Vector类,记得取出后再调用特有属性

public void paint(Graphics g) {

        super.paint(g);
        paintBullet(hero.heroBullets, g);
        for (int i = 0;i < enemies.size();i++){
            Enemy enemy = enemies.get(i);
            enemy.shotBullet();
            paintBullet(enemy.enemyBullets, g);
        }
    }
    public void paintBullet(Vector<Bullet> bullets,Graphics g){
        Vector<Bullet> unliveBullets = new Vector<>();
        bullets.removeAll(unliveBullets);
        for (int i = 0; i < bullets.size(); i++) {
            Bullet bullet = bullets.get(i);
            if(!bullet.isLive()){
                unliveBullets.add(bullet);
            }
            if(bullet != null && bullet.isLive()){
                drawBullet(g,bullet,hero.getTYPE());
            }
        }
        unliveBullets.clear();
    }

效果

最后调用shotBullet即可发射子弹,将调用方法写在画板的paint方法里,效果如下

二、击中敌人坦克时消失

思路

        1.应当编写一个判断方法,判断是否击中

        2.如果击中,敌人坦克消亡应当有一个属性值,将其置为false,子弹也需要置为false

        3.什么时候判断,应当在一个线程的循环里进行重复的判断

        4.应当再paint方法内停止绘画已经消亡的坦克,并且溢出列表内的坦克

1.判断是否击中

1)在画板中判断是否击中,写两个方法纯粹是塞到一块太难看了,一个方法hitEnemyTank是负责判断子弹的范围,另外一个hitIf是循环取出子弹和循环取出敌人对象塞到hitEnemyTank方法里,如果击中,将新增的isLive置为false;

    public static void hitEnemyTank(Bullet b, Enemy enemy) {
        switch (enemy.getDirect()) {
            case 0:
            case 2:
                if (b.getX() > enemy.getX() && b.getX() < enemy.getX() + 40
                        && b.getY() > enemy.getY() && b.getY() < enemy.getY() + 60) {
                    b.setLive(false);
                    enemy.setLive(false);
                }break;
            case 1:
            case 3:
                if (b.getX() > enemy.getX() && b.getX() < enemy.getX() + 60
                        && b.getY() > enemy.getY() && b.getY() < enemy.getY() + 40) {
                    b.setLive(false);
                    enemy.setLive(false);
                }break;

        }
    }
    public static void hitIf(Hero hero,Vector<Enemy> enemies){
        for (int i = 0; i < hero.heroBullets.size(); i++) {
            if(hero.heroBullets.get(i) == null){
                continue;
            }
            Bullet bullet = hero.heroBullets.get(i);
            for (int j = 0; j < enemies.size() ; j++) {
                Enemy enemy = enemies.get(j);
                hitEnemyTank(bullet,enemy);
            }
        }
    }

2.在画板线程里调用方法

        

3.如何让坦克消失

在paint方法内设置门槛,循环取出列表内的敌人,如果为空就继续跳到for开头(因为我们可能已经移除过一次中间的元素,如果不判断会抛出异常)。不为空,获取该元素,并查看是否还存活,如果不存活remove该元素,然后继续循环,最后绘出坦克,注意这里为什么要使用i--,因为不使用i--会跳过一个敌人

 remove会自动前移数组,如果不i--,会导致这次线程不绘画本应该存在的下一个坦克,下一个坦克会在下一次线程中继续被绘出来,所以会闪一下(来自GPT的帮助)

    @Override
    public void paint(Graphics g) {
        super.paint(g);
        for (int i = 0; i < enemies.size(); i++) {
            if (enemies.get(i) == null) {
                continue;
            }
            Enemy enemy = enemies.get(i);
            if(!enemy.isLive()){
                enemies.remove(enemy);
                i--;//为什么需要i-- 是因为在处理敌人数组时,如果你使用 remove 方法来删除一个元素,它会将数组中的元素往前移动填补被删除元素的位置,这样数组中不会存在 null 元素。
                continue;
            }
            drawTank(enemy.getX(), enemy.getY(), g, enemy.getDirect(), enemy.getTYPE());
        }
    }

4.记得将Bullet线程以通知的方式结束

中间量为isLive

效果

二(加强)、爆炸效果

思路 

        使用绘图里的输出图片完成

坦克只在被击中的时候死亡,所以当一个坦克死亡的时候把坦克的位置用这三张图片替代,然后如果不做成一个像子弹一样的类的话很难保证不堵塞,因为图片太快了需要休眠让图片依次走,单独写一个炸弹类,类内定义一个Life,每执行一次线程就life--,相当于执行完爆炸效果需要9个线程的时间

1.定义Bomb类

该类写了一个life,用于执行坦克的图片的消亡过程

public class Bomb {
    private int x;
    private int y;
    private int life = 9;
    private boolean isLive = true;

    public int getLife() {
        return life;
    }

    public int getX() {
        return x;
    }

    public int getY() {
        return y;
    }

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

    public boolean isLive() {
        return isLive;
    }

    public void lifeDown(){
            if(life > 0){
                --life;
            }else{
                isLive = false;
            }
    }
}

2.添加Bomb对象

当我们击中坦克时,在该坦克处创建一个Bomb对象,该对象记录当前enemy的坐标。

 public void hitEnemyTank(Bullet b, Enemy enemy) {
        switch (enemy.getDirect()) {
            case 0:
            case 2:
                if (b.getX() > enemy.getX() && b.getX() < enemy.getX() + 40
                        && b.getY() > enemy.getY() && b.getY() < enemy.getY() + 60) {
                    b.setLive(false);
                    enemy.setLive(false);
                    bombs.add(new Bomb(enemy.getX(), enemy.getY()));
                    System.out.println("子弹击中");
                }
                break;
            case 1:
            case 3:
                if (b.getX() > enemy.getX() && b.getX() < enemy.getX() + 60
                        && b.getY() > enemy.getY() && b.getY() < enemy.getY() + 40) {
                    b.setLive(false);
                    enemy.setLive(false);
                    bombs.add(new Bomb(enemy.getX(), enemy.getY()));
                    System.out.println("子弹击中");
                }
                break;

        }
    }

3.通过在paint方法内绘出炸弹效果

因为paint方法是在线程内被run方法反复执行,所以每调用一次bombEffect都会让bomb对象的life--,当处理完后移除该炸弹对象,注意如果只设置一个对象存放bomb会导致多个坦克的爆炸效果出现问题 

    public void paint(Graphics g) {

        super.paint(g);
        bombEffect(g);
    }
     public void bombEffect(Graphics g) {
        for (int i = 0; i < bombs.size(); i++) {
            Bomb bomb = bombs.get(i);
            if(bomb.getLife()>0){
                bomb.lifeDown();
                if (bomb.getLife() > 6) {
                    g.drawImage(image, bomb.getX(), bomb.getY(), 60, 60, this);
                } else if (bomb.getLife() > 3) {
                    g.drawImage(image1, bomb.getX(), bomb.getY(), 60, 60, this);
                } else {
                    g.drawImage(image2, bomb.getX(), bomb.getY(), 60, 60, this);
                }
            }else {
                bombs.remove(bomb);
            }
        }
    }

效果

        目前存在一个问题,就是第一个对象不会正常显示爆炸效果,考虑并行导致出现单线程里语句的干扰,找不到合理的解释。

三、敌人坦克随机移动

思路

敌人坦克可以自由移动,则需要将其设置为多线程(多个敌人同时移动), 其次在重写的run方法内实现randomMove方法,实现随机方向,加一个判断是否移动,然后再定义个值,判断是否转向

1.将enemy设置为多线程

设置为多线程后,重写run方法,在run里实现坦克的移动,记得在创建enemy对象的地方启动该线程

2.move方法

使用math.random的方式来随机移动,

public class Enemy extends Tank implements Runnable {
    Vector<Bullet> enemyBullets = new Vector<>();
    private int type = 1;
    private boolean isLive = true;
    private int count;

    public boolean isLive() {
        return isLive;
    }
    public void randomMove() {
        //先随机是否移动

        if ((int)(Math.random() * 4) == 3) {//判断是否可以移动,0-3,四分之3的概率可以移动
            return;
        }
        count++;//一个计数器,增加移动的次数
        switch (getDirect()) {//根据方向进行移动
            case 0:
                moveUp();
                break;
            case 1:
                moveRight();
                break;
            case 2:
                moveDown();
                break;
            case 3:
                moveLeft();
                break;
        }
        if (count >= (int) (Math.random() * 40)) {//当移动的次数大于某个值的时候,改变方向,0-39的范围
            setDirect((int) (Math.random() * 4));//随机给一个方向
            count = 0;//计数为0
        }
    }

    @Override
    public void run() {
        while (isLive) {
            try {
                Thread.sleep(500);
                randomMove();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }
}

效果

实现了坦克的随机移动

不过没有设置碰撞,和边界,坦克会瞎跑不见或者叠在其他坦克上

四、控制我方坦克和敌人的坦克在规定范围内移动

思路

        创建一个静态的Map,用于表示当前地图的大小,然后在地图类内定义方法判断tank是否还在游戏游戏区域,该方法在tank的move内使用

1.定义map类,编写判断方法

        在该map类初始化时赋值,然后写判断方法

注意,判断方法不能写成

if(tank.x < mapminX){return false;}

if(tank.x > mapmaxX){return false;}

if(tank.x < mapminY){return false;}

if(tank.x > mapmaxY){return false;}

return ture;

        写成这样会导致方法调用在移动执行之前,但是每次判断都是false,导致执行不到移动方法,后果就是我们的tank被边界抓住了,无法移动,所以我们获取面向,如果是上,我们就只限制tank的y不能大于minY即可。为什么mapmaxX要减tank.speed,因为如果不减,如果本来的边界是1600 - 60 = 1540 ,判断完之后坦克是还能往右边走的,就会 变成 1540 + speed = 1560的位置才不能往前走,炮管会突出去。所以最好是加个speed。

public class Map {
    private static int mapMinX;
    private static int mapMaxX;
    private static int mapMinY;
    private static int mapMaxY;

    public static int getMapMinX() {
        return mapMinX;
    }

    public static int getMapMaxX() {
        return mapMaxX;
    }

    public static int getMapMinY() {
        return mapMinY;
    }

    public static int getMapMaxY() {
        return mapMaxY;
    }

    public Map(int mapMinX, int mapMinY, int mapMaxX, int mapMaxY) {
        this.mapMinX = mapMinX;
        this.mapMinY = mapMinY;
        this.mapMaxX = mapMaxX;
        this.mapMaxY = mapMaxY;
    }

    public static boolean scopeIf(Tank tank) {
        switch (tank.getDirect()) {
            case 0:
                if (tank.getY() < mapMinY +tank.getSpeed()) {
                    return false;
                }
                break;
            case 1:
                if (tank.getX() > mapMaxX - 60 - tank.getSpeed()) {
                    return false;
                }
                break;
            case 2:
                if (tank.getY() > mapMaxY - 60 - tank.getSpeed()) {
                    return false;
                }
                break;
            case 3:
                if (tank.getX() < mapMinX + tank.getSpeed()) {
                    return false;
                }
                break;
        }
        return true;
    }
}

2.在hero和enemy的移动方法内调用该方法

这样的好处就是不用动之前的代码,动来动去自己都忘了

    public void heroMove() {
        if(!Map.scopeIf(this)){
            return;
        }
        {/*...根据面向执行移动*/}
    }
    public void randomMove() {
        //先随机是否移动
        if ((int) (Math.random() * 4) == 3) {
            return;
        }
        if(!Map.scopeIf(this)){
            setDirect((int) (Math.random() * 4));
            return;
        }
        {/*...根据面向执行移动*/}
    }

效果

现在都已经限制在这个黑色区域内了包括hero坦克

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

人工智能-卷积神经网络

从全连接层到卷积 我们之前讨论的多层感知机十分适合处理表格数据&#xff0c;其中行对应样本&#xff0c;列对应特征。 对于表格数据&#xff0c;我们寻找的模式可能涉及特征之间的交互&#xff0c;但是我们不能预先假设任何与特征交互相关的先验结构。 此时&#xff0c;多层感…

Java锁常见面试题

图片引用自&#xff1a;不可不说的Java“锁”事 - 美团技术团队 1 java内存模型 java内存模型(JMM)是线程间通信的控制机制。JMM定义了主内存和线程之间抽象关系。线程之间的共享变量存储在主内存中&#xff0c;每个线程都有一个私有的本地内存&#xff0c;本地内存中存储了该…

计算机毕业设计java+springboot+vue的旅游攻略平台

项目介绍 本系统结合计算机系统的结构、概念、模型、原理、方法&#xff0c;在计算机各种优势的情况下&#xff0c;采用JAVA语言&#xff0c;结合SpringBoot框架与Vue框架以及MYSQL数据库设计并实现的。员工管理系统主要包括个人中心、用户管理、攻略管理、审核信息管理、积分…

Crypto(9)[MRCTF2020]keyboard

下载题目&#xff0c;看看里面是什么 这是什么鬼&#xff0c;由题目可以获得线索&#xff0c;keyboard,不是键盘吗&#xff0c;然后看了看别人写的wp&#xff0c;发现是九键&#xff0c;有几个数字对应的密文就是第几个字母 比如第一个6&#xff0c;对应的字母是mno&#xff0c…

80个10倍提升Excel技能的ChatGPT提示

你是否厌倦了在使用Excel时感觉像个新手&#xff1f;你是否想将你的技能提升到更高的水平&#xff0c;成为真正的Excel大师&#xff1f;嗯&#xff0c;如果你正在使用ChatGPT&#xff0c;那么成为Excel专家简直易如反掌。 你只需要了解一些最有用的Excel提示&#xff0c;就能在…

龙迅LT8619C HDMI转LVDS/RGB/BT656/BT1120/BT601

LT8619C 描述&#xff1a; Lontium的LT8619C是一款高性能的HDMI/双模式DP接收器芯片&#xff0c;符合HDMI 1.4规范。TTL输出可支持RGB、BT656、BT1120&#xff0c;输出分辨率最多可支持4Kx2K30Hz。为了方便地实现一个多媒体系统&#xff0c;LT8619C支持8通道高质量的I2S音频或…

CSDN每日一题学习训练——Java版(两数相加、二叉树的锯齿形层序遍历、解数独)

版本说明 当前版本号[20231106]。 版本修改说明20231106初版 目录 文章目录 版本说明目录两数相加题目解题思路代码思路补充说明参考代码 二叉树的锯齿形层序遍历题目解题思路代码思路参考代码 解数独题目解题思路代码思路补充说明参考代码 两数相加 题目 给你两个 非空 的…

ARMday02(汇编语法、汇编指令)

汇编语法 汇编文件中的内容 1.伪操作&#xff1a;在汇编程序中不占用存储空间&#xff0c;但是可以在程序编译时起到引导和标识作用 .text .global .glbal .if .else .endif .data .word.... 2.汇编指令&#xff1a;每一条汇编指令都用来标识一个机器码&#xff0c;让计算机做…

【亚马逊云科技产品测评】活动征文|亚马逊云科技AWS之EC2详细测评

引言 &#xff08;授权声明&#xff1a;本篇文章授权活动官方亚马逊云科技文章转发、改写权&#xff0c;包括不限于在 Developer Centre, 知乎&#xff0c;自媒体平台&#xff0c;第三方开发者媒体等亚马逊云科技官方渠道&#xff09; 在当前的数字化时代&#xff0c;云服务已…

RK3568平台开发系列讲解(音视频篇)RTMP 推流

🚀返回专栏总目录 文章目录 一、RTMP 的工作原理二、RTMP 流媒体服务框架2.1、Nginx 流媒体服务器2.2、FFmpeg 推流沉淀、分享、成长,让自己和他人都能有所收获!😄 📢目前常见的视频监控和视频直播都是使用了 RTMP、RTSP、HLS、MPEG-DASH、 WebRTC流媒体传输协议等。 R…

Apache Doris (五十二): Doris Join类型 - Broadcast Join

🏡 个人主页:IT贫道_大数据OLAP体系技术栈,Apache Doris,Clickhouse 技术-CSDN博客 🚩 私聊博主:加入大数据技术讨论群聊,获取更多大数据资料。 🔔 博主个人B栈地址:豹哥教你大数据的个人空间-豹哥教你大数据个人主页-哔哩哔哩视频 目录 1. Broadcast Join原理

虹科示波器 | 汽车免拆检测 | 2017款长安福特翼虎车发动机故障灯异常点亮

一、故障现象 一辆2017款长安福特翼虎车&#xff0c;搭载CAF488WQ9发动机&#xff0c;累计行驶里程约为8.9万km。该车因发动机故障灯异常点亮在其他维修厂检修&#xff0c;维修人员用故障检测仪检测&#xff0c;提示气缸3失火&#xff0c;调换火花塞、点火线圈及喷油器&#xf…

时间序列预测模型实战案例(七)(TPA-LSTM)结合TPA注意力机制的LSTM实现多元预测

论文地址->TPA-LSTM论文地址 项目地址-> TPA-LSTM时间序列预测实战案例 本文介绍 本文通过实战案例讲解TPA-LSTM实现多元时间序列预测&#xff0c;在本文中所提到的TPA和LSTM分别是注意力机制和深度学习模型,通过将其结合到一起实现时间序列的预测&#xff0c;本文利用…

基于DevEco Studio的OpenHarmony应用原子化服务(元服务)入门教程

一、创建项目 二、创建卡片 三、应用服务代码 Index.ets Entry Component struct Index {State TITLE: string OpenHarmony;State CONTEXT: string 创新召见未来&#xff01;;build() {Row() {Column() {Text(this.TITLE).fontSize(30).fontColor(0xFEFEFE).fontWeight(…

petalinux 2022.2 在 ubantu18.04 下的安装

下载 Ubuntu下载&#xff1a; https://releases.ubuntu.com/18.04/ubuntu-18.04.6-desktop-amd64.iso petalinux 下载&#xff1a; https://www.xilinx.com/support/download/index.html/content/xilinx/en/downloadNav/embedded-design-tools/2022-2.html 安装虚拟机 安装…

给定n个点或一个凸边形,求其最小外接矩形,可视化

这里写目录标题 原理代码 原理 求n个点的最小外接矩形问题可以等价为先求这n个点的凸包&#xff0c;再求这个凸包的最小外接矩形。 其中求凸包可以使用Graham-Scan算法 需要注意的是&#xff0c; 因为Graham-Scan算法要求我们从先找到凸包上的一个点&#xff0c;所以我们可以先…

[pytorch]手动构建一个神经网络并且训练

0.写在前面 上一篇博客全都是说明类型的,实际代码能不能跑起来两说,谨慎观看.本文中直接使用fashions数据实现softmax的简单训练并且完成结果输出.实现一个预测并且观测到输出结果. 并且更重要的是,在这里对一些训练的过程,数据的形式,以及我们在softmax中主要做什么以及怎么…

02|LangChain | 从入门到实战 -六大组件之Models IO

by&#xff1a;wenwenC9 上一篇文章 01&#xff5c;LangChain | 从入门到实战-介绍 一、Models IO 组成及其说明 与语言模型的交互&#xff0c;比如在线GPT系列&#xff0c;或各种离线模型 任何语言模型应用程序的核心元素都是XXX模型。LangChain 提供了与任何语言模型交互的…

QQ邮箱批量发送

场景 已有用户邮箱,需要批量对他们发送一些广告信息。 完整代码 # coding=gbk import smtplib import csv from email.mime.text import MIMEText from email.mime.multipart import MIMEMultipartdef send_email(msg_from, passwd, msg_to_list, text_content)

Harbor企业级Registry基础镜像仓库的详细安装使用教程(保姆级)

Harbor Docker 官方提供的私有仓库 registry&#xff0c;用起来虽然简单 &#xff0c;但在管理的功能上存在不足。 Harbor是vmware一个用于存储和分发Docker镜像的企业级Registry服务器&#xff0c;harbor使用的是官方的docker registry(v2命名是distribution)服务去完成。 ha…