【Java学习笔记】62 - 坦克大战1.2,我方发射子弹(可存在多个)

2024-03-08 13:40

本文主要是介绍【Java学习笔记】62 - 坦克大战1.2,我方发射子弹(可存在多个),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

项目代码

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

目录

项目代码

1.创建一个Bullet类

2.给Hero类加入子弹功能

3.监听键盘输入事件调用hero.shotBullet

4.创建绘画方法

5.调用绘画方法

6.画板设置为多线程

7.结束,完成hero坦克发射子弹的功能


在坦克大战游戏(1.1版)基础上添加如下功能:当玩家按一下j键,就发射颗子弹。 

        个人思路,考虑创建bullet类,因为将来我们的坦克可以射出乱七八糟的子弹。定义大小,颜色,方向,速度,改变子弹位置应当在Bullet类内完成;

       考虑在Hero内写shotBullet方法,该方法能调用生成新的子弹;

        考虑在画板上使用监听者,当我们使用J键的时候就调用创建一个新的子弹对象,画子弹代码在MyPanel上完成;

        考虑将画板变成线程,不停的repaint画板。

        可能发射一堆子弹,子弹最好是做多线程避免堵塞;

        

1.创建一个Bullet类

1)该类应当有xydirect方向,speed速度,isLive是否存活,因为子弹会被发射,定义一个Shot内部类,当我们子弹被创建的时候意味着子弹要被射出去,所以构造器内创建一个shot对象

2)在shot内部类内我们定义子弹的移动方向和路径,当direct为不同时 x y变化;

3)当我们的子弹碰到边界或者敌人时,该子弹对象应当被置为不用的空间,所以加上if判断是否消亡

4)子弹对象理应是多线程的对象,所以Shot类实现Runable接口,但其实Shot类可能有点多余了,因为一个Bullet好像也能实现该接口,也能定义乱七八糟的类型啥的,再看看,如果需要后面再进行更改

public class Bullet {private int x;private int y;private int direct = 0;//子弹的方向private double speed;private boolean isLive = true;Shot shot = null;public int getX() {return x;}public int getY() {return y;}public int getDirect() {return direct;}public double getSpeed() {return speed;}public boolean isLive() {return isLive;}public Bullet(int x, int y, double speed, int direct) {this.x = x;this.y = y;this.speed = speed;shot = new Shot();this.direct = direct;}public class Shot implements Runnable {public Shot() {}@Overridepublic void run() {while (true){try {Thread.sleep(100);} 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;}System.out.println("子弹坐标" + x + "子弹坐标" + y);//如果子弹移动到面板边界时,应该销毁if(!(x >= 0 && x <1600 && y>= 0 && y <= 900)){isLive = false;break;}}}}
}

2.给Hero类加入子弹功能

1)按正常来说,我们的Hero调用某个方法时,就应当创建一个子弹对象,所以定义shotBullet类,射出子弹,当执行hero.shotBullet时,在该方法内创建我们的bullet对象,根据我们的坦克位置,传入子弹的初始位置

2)因为子弹需要多个同时进行操作,所以实现Runnable来完成多线程,因为是Hero类创建的对象,所以在Hero内开启线程

    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;}Bullet.Shot shot = bullet.new Shot();Thread thread = new Thread(shot);thread.start();return bullet;}

3.监听键盘输入事件调用hero.shotBullet

1)这个没啥好说的,MyPanel实现监听接口,当按下的键为J的时候执行方法

2)我们的方法返回的是Bullet对象,如果只在MyPanel内定义一个Bullet bullet,会导致每一次激活该监听都会让bullet指向该方法返回的对象,会导致我们的这个MyPanel类每次都只有一个子弹对象,这显然是不合理的,所以我们要创建一个线程安全的列表存入我们的子弹类

    public void keyPressed(KeyEvent e) {//如果按下的是J就调用hero的shotBullet方法if(e.getKeyCode() == KeyEvent.VK_J){bullets.add(hero.shotBullet());}this.repaint();}

4.创建绘画方法

1)在该类内创建一个方法drawBullet,该方法可以绘出一个子弹

2)填充为圆形,颜色为青色

public void drawBullet(Graphics g,Bullet bullet) {g.setColor(Color.CYAN);g.fillOval(bullet.getX(), bullet.getY(), 5, 5);}

5.调用绘画方法

1)我们已经创建了绘画方法,应当在paint内调用该方法

2)调用方法的时候我们已经接受了该子弹对象存入了列表内,然后我们再在paint内取出该对象,把他画出来

3)这时候问题又来了,我们的列表存入了该bullet对象,如果我们一直发射,会出现内存溢出的情况,为了避免该情况,我们加一个存活判断(并不是真的不存活了,如果我们的列表一直执行该对象,会让他一直被调用,这种情况回收机制不会回收该内存空间),如果islive为false,我们就丢到另外一个列表里,然后bullets.removeAll(unliveBullets)即可。这种方式当然不是最好的,看看有没有更好的解决方法再进行改进;

4)进行判断,当我们的bullet取出来是个null或者已经死掉了,我们就不画他

5)该列表是有序的,所以我们可能会有很多不必要的东西占用该列表的空间大小,想想怎么解决

    Vector<Bullet> bullets = new Vector<>();public void paint(Graphics g) {//有个问题 这里的bullet只会接受一个对象,所以想想怎么保证绘出多个bullet//可以用ArrayList接受,但是怎么销毁为空的对象呢?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);}}unliveBullets.clear();}

6.画板设置为多线程

也可以在paint里调用repaint方法,即下图

应该没区别,我们在上面已经处理过消亡的子弹了,这样调用每次有子弹存活就会repaint一次,每按一次也会repaint一次,没差别应该,有的话可能是线程的安全问题吧,太多地方调用repaint了,所以干脆直接让画板进入多线程得了

public class MyPanel extends JPanel implements KeyListener,Runnable{@Overridepublic void run() {while (true){try {Thread.sleep(50);} catch (InterruptedException e) {e.printStackTrace();}this.repaint();}}
}

7.结束,完成hero坦克发射子弹的功能

最后记得在主程序内调用画板线程

效果图,可以再调调,但大体就是这样了

这篇关于【Java学习笔记】62 - 坦克大战1.2,我方发射子弹(可存在多个)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/787267

相关文章

51单片机学习记录———定时器

文章目录 前言一、定时器介绍二、STC89C52定时器资源三、定时器框图四、定时器模式五、定时器相关寄存器六、定时器练习 前言 一个学习嵌入式的小白~ 有问题评论区或私信指出~ 提示:以下是本篇文章正文内容,下面案例可供参考 一、定时器介绍 定时器介绍:51单片机的定时器属于单片机的内部资源,其电路的连接和运转均在单片机内部完成。 定时器作用: 1.用于计数系统,可

问题:第一次世界大战的起止时间是 #其他#学习方法#微信

问题:第一次世界大战的起止时间是 A.1913 ~1918 年 B.1913 ~1918 年 C.1914 ~1918 年 D.1914 ~1919 年 参考答案如图所示

Java五子棋之坐标校正

上篇针对了Java项目中的解构思维,在这篇内容中我们不妨从整体项目中拆解拿出一个非常重要的五子棋逻辑实现:坐标校正,我们如何使漫无目的鼠标点击变得有序化和可控化呢? 目录 一、从鼠标监听到获取坐标 1.MouseListener和MouseAdapter 2.mousePressed方法 二、坐标校正的具体实现方法 1.关于fillOval方法 2.坐标获取 3.坐标转换 4.坐

Spring Cloud:构建分布式系统的利器

引言 在当今的云计算和微服务架构时代,构建高效、可靠的分布式系统成为软件开发的重要任务。Spring Cloud 提供了一套完整的解决方案,帮助开发者快速构建分布式系统中的一些常见模式(例如配置管理、服务发现、断路器等)。本文将探讨 Spring Cloud 的定义、核心组件、应用场景以及未来的发展趋势。 什么是 Spring Cloud Spring Cloud 是一个基于 Spring

[word] word设置上标快捷键 #学习方法#其他#媒体

word设置上标快捷键 办公中,少不了使用word,这个是大家必备的软件,今天给大家分享word设置上标快捷键,希望在办公中能帮到您! 1、添加上标 在录入一些公式,或者是化学产品时,需要添加上标内容,按下快捷键Ctrl+shift++就能将需要的内容设置为上标符号。 word设置上标快捷键的方法就是以上内容了,需要的小伙伴都可以试一试呢!

随想录 Day 69 并查集 107. 寻找存在的路径

随想录 Day 69 并查集 107. 寻找存在的路径 理论基础 int n = 1005; // n根据题目中节点数量而定,一般比节点数量大一点就好vector<int> father = vector<int> (n, 0); // C++里的一种数组结构// 并查集初始化void init() {for (int i = 0; i < n; ++i) {father[i] = i;}

Tolua使用笔记(上)

目录   1.准备工作 2.运行例子 01.HelloWorld:在C#中,创建和销毁Lua虚拟机 和 简单调用。 02.ScriptsFromFile:在C#中,对一个lua文件的执行调用 03.CallLuaFunction:在C#中,对lua函数的操作 04.AccessingLuaVariables:在C#中,对lua变量的操作 05.LuaCoroutine:在Lua中,

AssetBundle学习笔记

AssetBundle是unity自定义的资源格式,通过调用引擎的资源打包接口对资源进行打包成.assetbundle格式的资源包。本文介绍了AssetBundle的生成,使用,加载,卸载以及Unity资源更新的一个基本步骤。 目录 1.定义: 2.AssetBundle的生成: 1)设置AssetBundle包的属性——通过编辑器界面 补充:分组策略 2)调用引擎接口API

Javascript高级程序设计(第四版)--学习记录之变量、内存

原始值与引用值 原始值:简单的数据即基础数据类型,按值访问。 引用值:由多个值构成的对象即复杂数据类型,按引用访问。 动态属性 对于引用值而言,可以随时添加、修改和删除其属性和方法。 let person = new Object();person.name = 'Jason';person.age = 42;console.log(person.name,person.age);//'J

java8的新特性之一(Java Lambda表达式)

1:Java8的新特性 Lambda 表达式: 允许以更简洁的方式表示匿名函数(或称为闭包)。可以将Lambda表达式作为参数传递给方法或赋值给函数式接口类型的变量。 Stream API: 提供了一种处理集合数据的流式处理方式,支持函数式编程风格。 允许以声明性方式处理数据集合(如List、Set等)。提供了一系列操作,如map、filter、reduce等,以支持复杂的查询和转