【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的帮助)

    @Overridepublic 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}}@Overridepublic 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坦克

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

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

相关文章

c-CoSe2-CoN助力Zn-空气电池

硒化钴&#xff08;CoSe2&#xff09;的相变可有效调节其固有的电催化活性&#xff0c;但提高CoSe2的电导率和催化活性/稳定性还是一个挑战。异质结构工程可优化界面性能&#xff0c;促进CoSe2基催化剂上氧电催化的动力学。 基于此&#xff0c;黑龙江大学邹金龙教授等人报道了…

再谈Android重要组件——Handler(Native篇)

前言 最近工作比较忙&#xff0c;没怎么记录东西了。Android的Handler重要性不必赘述&#xff0c;之前也写过几篇关于hanlder的文章了&#xff1a; Handler有多深&#xff1f;连环二十七问Android多线程&#xff1a;深入分析 Handler机制源码&#xff08;二&#xff09; And…

pyspark连接mysql数据库报错

使用pyspark连接mysql数据库代码如下 spark_conf SparkConf().setAppName("MyApp").setMaster("local")spark SparkSession.builder.config(confspark_conf).getOrCreate()url "jdbc:mysql://localhost:3306/test?useUnicodetrue&characterE…

C语言习题整理①

一些C语言习题的整理。 目录 一、判断质数 二、判断回文数 三、判断水仙花数 四、输出乘法表 五、输出杨辉三角 一、判断质数 质数是指在大于1的自然数中&#xff0c;除了1和它本身以外不再有其他因数的自然数。质数又称素数。一个大于1的自然数&#xff0c;除了1和它自身…

为什么有了MAC地址,还需要IP地址?

解释 搞懂这个问题&#xff0c;首先需要了解交换机的功能 交换机内部有一张MAC地址映射表&#xff0c;记录着MAC地址和端口的对应关系。 如果A要给B发送一个数据包&#xff0c;构造如下格式的数据结构&#xff1a; 到达交换机时&#xff0c;交换机内部通过自己维护的 MAC 地…

Angular-07:组件生命周期

三个阶段&#xff1a; ① 挂载阶段1.1 constructor1.2 ngOnInit ② 更新阶段2.1 ngOnChanges2.2 ngAfterViewInit2.3 ngAfterContentInit2.4 ngDoCheck ③ 卸载阶段3.1 onOnDestroy ④ 在组件中添加所有方法并打印 该表按照执行顺序编写 编号函数名实现名说明1constructorcons…

uniapp自定义权限菜单,动态tabbar

已封装为组件&#xff0c;亲测4个菜单项目可以切换&#xff0c; 以下为示例&#xff0c;根据Storage 中 userType 的 值&#xff0c;判断权限菜单 <template><view class"tab-bar pb10"><view class"tabli" v-for"(tab, index) in ta…

DirectX3D 虚拟现实项目 三维物体的光照及着色(五个不同着色效果的旋转茶壶)

文章目录 任务要求原始代码CPP文件代码着色器文件代码 效果展示 任务要求 本篇文章是中国农业大学虚拟现实课程的一次作业内容&#xff0c;需要对五个茶壶模型使用不同的光照进行着色和渲染&#xff0c;然后旋转展示。 本人的代码也是在其他人的代码的基础上修改来的&#xf…

centos获取服务器公网ip

查看公网IP 用下面几个命令&#xff1a; #curl ifconfig.me #curl icanhazip.com #curl cip.cc

前端框架Vue学习 ——(五)前端工程化Vue-cli脚手架

文章目录 Vue-cliVue项目-创建Vue项目-目录结构Vue项目-启动Vue项目-配置端口Vue项目开发流程 Vue-cli 介绍&#xff1a;Vue-cli 是 Vue 官方提供的一个脚手架&#xff0c;用于快速生成一个 Vue 的项目模版 安装 NodeJS安装 Vue-cli npm install -g vue/cliVue项目-创建 图…

《深入理解分布式事务:原理与实战》读书笔记

Mysql事务 锁升级 行锁升级表锁&#xff1a;如果不是索引查找&#xff0c;或者索引失效&#xff0c;此时需要全表扫描&#xff0c;会升级为锁整张表。 为什么Mysql要把扫描到的每一行以及其间隙都加锁&#xff1f;这是为了防止幻读出现。幻读导致的问题是破坏了一致性声明&am…

三国志14信息查询小程序(历史武将信息一览)制作更新过程03-主要页面的设计

1&#xff0c;小程序的默认显示 分为三部分&#xff0c;头部的标题、中间的内容区和底部的标签栏。点击标签可以切换不同页面&#xff0c;这是在app.json文件中配置的。代码如下&#xff1a; //所有用到的页面都需要在 pages 数组中列出&#xff0c;否则小程序可能会出现错误或…

前端面试题之HTML篇

1、src 和 href 的区别 具有src的标签有&#xff1a;script、img、iframe 具有href的标签有&#xff1a;link、a 区别 src 是source的缩写。表示源的意思&#xff0c;指向资源的地址并下载应用到文档中。会阻塞文档的渲染&#xff0c;也就是为什么js脚本放在底部而不是头部的…

skynet学习笔记01— skynet开发环境搭建(超详细)与第一个skynet程序

00、参考资料 https://blog.csdn.net/qq769651718/category_7480207.html 01、前置准备 开发所在目录 mhzzjmhzzj-virtual-machine:~/work/skynetStudy$ pwd /home/mhzzj/work/skynetStudy前置准备 mhzzjmhzzj-virtual-machine:~/work/skynetStudy$ sudo apt install lua5…

CSS示例001:鼠标放div上,实现旋转、放大、移动等效果

GPT能够很好的应用到我们的代码开发中&#xff0c;能够提高开发速度。你可以利用其代码&#xff0c;做出一定的更改&#xff0c;然后实现效能。 css实战中&#xff0c;经常会看到这样的场景&#xff0c;鼠标放到一个图片或者一个div块状时候&#xff0c;会出现旋转、放大、移动…

地址的层次性

地址的层次性 当地址总数并不是很多的情况下&#xff0c;有了唯一地址就可以定位相互通信的主体。然而当地址的总数越来越多时&#xff0c;如何高效地从中找出通信的目标地址将成为一个重要的问题。为此人们发现地址除了具有唯一性还需要具有层次性。其实&#xff0c;在使用电…

基于跳蛛算法的无人机航迹规划-附代码

基于跳蛛算法的无人机航迹规划 文章目录 基于跳蛛算法的无人机航迹规划1.跳蛛搜索算法2.无人机飞行环境建模3.无人机航迹规划建模4.实验结果4.1地图创建4.2 航迹规划 5.参考文献6.Matlab代码 摘要&#xff1a;本文主要介绍利用跳蛛算法来优化无人机航迹规划。 1.跳蛛搜索算法 …

Verilog 基础知识(一) Verilog 基础语法与注意事项

基础知识 0.1 模块(Module) Verilog中的module可以看成一个具有输入输出端口的黑盒子&#xff0c;该黑盒子有输入和输出接口(信号)&#xff0c;通过把输入在盒子中执行某些操作来实现某项功能。(类似于C语言中的函数) 图1 模块示意图 0.1.1 模块描述 图1 所示的顶层模块(top…

【Redis】Redis整合SSMRedis中的缓存穿透、雪崩、击穿的原因以及解决方案(详解)

目录&#xff1a; 目录 一&#xff0c;SSM整合redis 二&#xff0c;redis注解式缓存 三&#xff0c;Redis中的缓存穿透、雪崩、击穿的原因以及解决方案&#xff08;附图&#xff09; 一&#xff0c;SSM整合redis 1.原因&#xff1a; 整合SSM和Redis可以提升系统的性能、可…

2023 年如何学习编程

在当今的数字时代&#xff0c;程序员的角色比以往任何时候都更加重要。编程技能几乎在每个行业都受到高度重视和追捧。从科技初创公司到成熟企业&#xff0c;对具有适当技术能力的开发人员的需求巨大。 无论是考虑转行还是开始&#xff0c;现在都是成为一名程序员的激动人心的…