1. 成果演示
Java多线程实现基础坦克大战
相关资料后台滴滴
2. 实现了哪些功能?
- 按键
WDSA
是己方坦克方向键,J
键负责发射子弹。己方一次最多可以发射5
颗子弹。子弹走到边界或者击中敌方坦克时,生命周期结束。 - 当我方子弹击中敌方或者敌人子弹打中我方时,会有爆炸效果
- 敌人坦克随机移动,随机发射子弹。坦克与坦克的位置不能重合,且不能移动到边界之外
- 我方击杀敌人数量动态更新在界面右上角
- 开启游戏时会播放事先准备好的音乐
3. 需要哪些基础?
线程、向上/下转型、子类父类、接口,事件监听、
I/O
流
4. 设计思路
设计思路配合源码食用!!
①绘制坦克
绘图基础
Java
的图形化设计以JFrame
类为框架【可以理解为画板】,JPanel
类为面板【理解为画纸】。X-Y坐标轴以左上角为原点,右边是X轴正向;Y轴下方是正向
-
JPanel
上的画图由paint(Graphics g)
方法实现,其中g可以理解为画笔。Graphics
是一个接口,在JPanel
中实现了初始化,我们重写的时候收到的实际是一个上转型对象,所以可以直接调用方法使用。【简单来说就是JDK
已经帮忙把活干好了,直接拿来用就行】 -
以下情况会自动调用paint()方法
- 窗口最小化再最大化
- 窗口大小发生变化
- 调用
repaint
函数【刷新组件外观方法】被调用
-
使用画笔进行画图时经常需要标明坐标位置以及图像的高宽,高宽是在坐标位置基础上计算的,坐标位置如箭头所示
坦克绘制
不难发现,这辆坦克是由一个长方形1号+长方形2号+长方形1号构成的主体
炮台部分是一个圆形,炮筒部分则是一条直线
各位根据自己的审美喜好,进行绘画拼接即可。我这里的坦克整体宽度是40
像素,长度是60
像素
//提供几个画图方法,其中g是画笔(paint方法中传入的参数),具体使用参考JDK文档
g.fill3DRect //画具有3D效果的矩形
g.fillOval //画椭圆(长轴与短轴相同时就是圆)
g.drawLine //画直线
g.fillRect //用画笔颜色填充矩形,可以用来画面板
经过这些步骤,相信各位已经可以完成坦克以及面板的绘制了~
②我方坦克动起来
其实原理很简单,每个坦克都有自己的坐标【因此将坦克封装成一个对象,拥有自己属性】。而我们对己方坦克进行键盘事件监听,当按下方向键的时,就改变自己的坐标,接着马上调用repaint()方法。这个方法的作用的调用paint()方法,即重新绘图。又因为我们的坐标改变了,所以画出来的就是一幅新的图片。
同时我们知道,方向不同时,构成坦克的各图形坐标位置会有所变化【大小不变】。因此绘图时还要根据方向进行调节。建议将画根据方向画坦克封装成一个drawTank方法,后边只需要传入坐标和方向即可完成绘制
CPU
的运行速度很快,当一直按着方向键的时,就会绘制一张张不同的新图片,连起来看就像动起来了一样。
③我方发射子弹
子弹的本质是什么?
根据常识,子弹发射出去后移动与原坦克没关系了,看作是独立个体,因此要单独封装成一个类,享有自己的属性,同时子弹的移动也体现在坐标的变化上。
因此想要让子弹有规律的动起来,一个想法是让子弹成为一个线程。按下发射键时,子弹线程就启动,并且有规律的改变自己的坐标。当子弹坐标与敌方坦克重合或者走到边界时,生命周期结束。
子弹的移动要满足什么规律?
当我方坦克向上,发射的子弹自然也向上……因此子弹坐标的改变需要根据发射时坦克的方向进行确定,所以像前边举的例子,子弹向上走,只需要改变
Y
轴即可
如何控制最多发射5颗子弹?
子弹虽然自己是一个个体,但是实际上还是和原来的坦克有关系的【不然就不知道是谁发射的子弹】,因此我们可以用一个
Vector
来装当前发射出去的子弹,每发射一颗子弹就将其加入Vector
,当数量达到5
时不允许再发射;当子弹生命周期结束时就将其从Vector
移除
④我方子弹击中敌人坦克
此时敌方坦克应该消失,我们发出的子弹也应该消失
所有敌人坦克可以放在一个Vector
中,每次调用paint()
方法时,遍历敌人坦克集合,接着在这个过程中再遍历我方发射出的子弹,将这些子弹的坐标与当前遍历到的坦克坐标进行对比,如果子弹进入了坦克范围就代表击中了坦克。我们将被击中的坦克移出坦克集合同时将这颗子弹移出己方子弹集合
⑤我方子弹击中敌方产生爆炸效果
爆炸效果如何做?
其实就是提前准备好几张图片,当子弹击中坦克时,在击中的位置绘制这几张图片,从而达到爆炸的效果
每个爆炸有自己的生命值,如生命值为3绘制第一张图片;生命值为2绘制第二张图片……从而达到动态效果
由于爆炸的效果与子弹移动、坦克移动等都是互不干扰的,且其是一个动态变化过程,所以爆炸也要单独封装成一个类,享有自己的属性
//得到图片对象
Image image1 = Toolkit.getDefaultToolkit().getImage(Main.class.getResource("/相对路径"));
g.drawImage(image1, X, Y, 宽, 高, this); //画图方法
爆炸实现具体思路
爆炸可以多处一起发生,因此我们也将爆炸对象
Bomb
存放到Vector
中。每次绘图的时候对这个Vector
进行遍历,得到其中的Bomb
对象根据不同生命值进行爆炸效果绘制。当其生命值为0
时将其移除,移除后自然不会再绘制。
⑥敌方坦克自动移动
所谓自动移动,其实就是定时改个方向和坐标,接着再对页面进行刷新即可。由此我们不难想到将敌人坦克设置为线程,隔一段时间就随机设置一个方向以及这个方向上正向坐标。
但是重绘怎么要怎么触发呢?毕竟只有在JPanel
类以及子类才会有这个方法。
解决办法是将整个面板也设置为一个线程,隔一段时间就进行重绘
⑦敌方坦克自动发射子弹
隔一段时间就得到一个随机数,如果随机数大于某个值就允许其产生一个子弹对象【这是为了控制子弹发射速率】,子弹线程启动,并且将子弹放入自己的子弹集合中
⑧如何防止敌我坦克重叠?
我的作法是在setX以及setY时对新位置进行一个判断,遍历当前面板上存在的所有坦克,如果我的新位置没有进入其他坦克的范围,才被认为是合法的
原理比较简单,但是实现起来需要一些耐心,具体可以参考韩老师给的图↑
⑨如何添加背景音乐?
利用JDK
自带的音乐播放工具进行播放即可,大致也是I/O
流原理,参考以下代码
AutoMusic.java
package TankGame1;
import javax.sound.sampled.*;
import java.io.File;
import java.io.IOException;
public class AutoMusic extends Thread {
String path;
public AutoMusic(String path) {
this.path = path;
}
@Override
public void run() {
File file = new File(path);
AudioInputStream audioInputStream = null;
try {
audioInputStream = AudioSystem.getAudioInputStream(file);
} catch (Exception e) {
e.printStackTrace();
return;
}
AudioFormat format = audioInputStream.getFormat();
SourceDataLine auline = null;
DataLine.Info info = new DataLine.Info(SourceDataLine.class, format);
try {
auline = (SourceDataLine) AudioSystem.getLine(info);
auline.open(format);
} catch (Exception e) {
e.printStackTrace();
}
auline.start();
int nBytesRead = 0;
//这是缓冲
byte[] abData = new byte[512];
try {
while (nBytesRead != -1) {
nBytesRead = audioInputStream.read(abData, 0, abData.length);
if (nBytesRead >= 0) {
auline.write(abData, 0, nBytesRead);
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
auline.drain();
auline.close();
}
}
}
在面板构造函数处将此线程启动即可播放事先准备好的音乐
记住!!文件一定是.wav,否则会报异常。直接将mp3文件修改后缀名是行不通的【好像是内部格式啥的】。酷狗可以将mp3文件转换成wav文件,前提是mp3文件是无损音质【若不知道去哪里搞可在如下地址下载,我提供了样本】
链接:https://pan.baidu.com/s/1Al5a_r-QOntlug3FmiGRgQ 提取码:8p9l
坦克大战爆炸图可在此处下载↓
爆炸效果图
我所使用的JDK为17,若各位JDK版本与我不同,拿到源代码后执行报错,可尝试如下解决办法,后续关于项目的问题也可与我沟通!
6. 总结
坦克大战整个逻辑并不会很难,但是所涉及的内容比较多,一些处理工作也比较繁琐,对于初学者来说这是很好的一个项目。B站韩顺平老师讲得非常好,本人也是看着韩老师的课程一点点摸索处理的,部分实现和老师有所不同,是不错的入门项目!
方便的话请给个免费的赞支持一下哦!