Java多线程游戏-雷霆战机

本文档详细介绍了使用Java开发一款名为雷霆战机的多线程游戏的过程,包括加载页面、菜单界面、关卡设计、飞机与子弹类的设计、音效处理、碰撞检测算法以及游戏的暂停和重新开始功能。作者分析了项目存在的问题,如线程过多导致的资源浪费和游戏稳定性不足等,并分享了源代码供读者参考交流。
摘要由CSDN通过智能技术生成

Java多线程游戏-雷霆战机

先来张效果图(结尾附上源码地址,欢迎交流)
在这里插入图片描述

一、总述

飞机大战大家童年的时候都玩过,前两天学了线程方面的知识后,决定写一个关于多线程雷霆战机的游戏(单机)。这里用到了许多有关线程,图片处理,音乐等的技术我刚接触Java半年,做这个游戏磕磕绊绊。花费了很多时间,最后还是有一些瑕疵。素材是从另一位博主那下载的。

素材下载地址:
地址

二、相关技术

  1. 了解什么是线程,进程,多线程,并发线程
  2. 了解线程寿命以及线程的控制
  3. 了解线程安全问题,处理游戏中出现的线程安全问题
  4. 了解Java中的list类以及它的底层实现
  5. 音乐文件的读取与播放
  6. 了解键盘监听
  7. 缓冲区(解决屏幕闪烁问题)

三、功能介绍及讲解

(一)功能简介
1.加载页面
2.菜单页面
3.弹窗提示
4.多重关卡(四关)
5.多种飞机(八种)
6.音效
7.地图动态滚动,爆炸动图
8.分数达到一定程度飞机升级
9.计时
10.暂停\继续游戏(线程控制)
11.开始新游戏(清空原有数据与加载新数据)
12.道具栏与道具使用(刷新道具栏,使用道具,四种道具)
13.关卡boss

(二)功能讲解
1.加载页面的制作
加载页面
关键点:进度条的使用

加载界面,无外乎就是个界面嘛,重要的可能就是那个加载条了吧。这个加载条你可以自己做,也可以用Java中的加载条,这里我用的是Java中自带的加载条。

		JProgressBar bar = new JProgressBar(1, 100);
		bar.setStringPainted(true); //描绘文字
		bar.setString("加载飞机和子弹中,请稍候,精彩马上开始");  //设置显示文字
		bar.setBackground(Color.ORANGE);  //设置背景色
		bar.setBounds(100, 800, 1000, 100);//设置大小和位置
		bar.setValue(20); //设置进度条值

2.菜单页面的制作
在这里插入图片描述
就是提供一些功能按键以及弹窗实现各功能的有序进行。

关键点:弹窗、按钮监听、焦点的获取

弹窗,无外乎也是个界面嘛,要么自己做一个窗口,要么使用Java中自带的弹窗类

JDialog cs=new JDialog();

弹窗的各项操作与窗体JFrame类是一致的。
这里要注意的就是因为后面要实现键盘的监听,所以焦点必须要注意他的位置。并注意获取焦点。

3.多重关卡
这个没啥好说的,我就只是找了四张地图而已。

4.多种飞机
这个比较麻烦,在设计类的时候,可以将各种飞机不同的属性用私有数据来进行初始化。在创建飞机对象的时候将各项数据传进去即可。
下面是我的飞机类代码;

英雄机

public class MyHeroPlane {
	private int Blood;
	private int speed;
	private ImageIcon image;
	public MyHeroPlane(int blood, int speed, ImageIcon image) {
		super();
		Blood = blood;
		this.speed = speed;
		this.image = image;
	}
	public int getBlood() {
		return Blood;
	}
	public int getSpeed() {
		return speed;
	}
	public ImageIcon getImage() {
		return image;
	}
	public void setBlood(int blood) {
		Blood = blood;
	}
	
}

敌机类

public class EnemyPlane extends Thread{
	private int blood;
	private int x,y;
	private ImageIcon image;
	private int speed;
	private boolean islive;
	private int sore;
	private boolean suspend=false;
	public EnemyPlane(int blood, int x, int y, ImageIcon image,int speed,int sore) {
		super();
		this.blood = blood;
		this.x = x;
		this.y = y;
		this.image = image;
		this.speed=speed;
		this.islive=true;
		this.sore=sore;
	}
	public void run()
	{
		while(blood>=0&&islive)
		{
			while(suspend)
			{
				//暂停功能
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			y+=speed;
		}
	}
	public int getBlood() {
		return blood;
	}
	public int getX() {
		return x;
	}
	public int getY() {
		return y;
	}
	public ImageIcon getImage() {
		return image;
	}
	public int getSpeed() {
		return speed;
	}
	public boolean isIslive() {
		return islive;
	}
	public void setIslive(boolean islive) {
		this.islive = islive;
	}
	public void setBlood(int blood) {
		this.blood = blood;
	}
	public int getSore() {
		return sore;
	}
	public boolean isSuspend() {
		return suspend;
	}
	public void setSuspend(boolean suspend) {
		this.suspend = suspend;
	}
	
}

子弹类(我方,敌方子弹)
我方子弹类

public class MyHeroBullets extends Thread{
	private boolean islive=true;
	private int x;
	private int y;
	private int atk;
	private ImageIcon image;
	private int speed;
	private boolean suspend=false;
	public MyHeroBullets(int x, int y, int atk, ImageIcon image, int speed) {
		super();
		this.x = x;
		this.y = y;
		this.atk = atk;
		this.image = image;
		this.speed = speed;
	}

	public void run() {
		while(islive)
		{
			while(suspend)
			{
				//暂停功能
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			try {
				Thread.sleep(50);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			y-=speed;
		}
	}

	public boolean isIslive() {
		return islive;
	}

	public int getX() {
		return x;
	}

	public int getY() {
		return y;
	}

	public int getAtk() {
		return atk;
	}

	public ImageIcon getImage() {
		return image;
	}

	public int getSpeed() {
		return speed;
	}

	public void setIslive(boolean islive) {
		this.islive = islive;
	}

	public boolean isSuspend() {
		return suspend;
	}

	public void setSuspend(boolean suspend) {
		this.suspend = suspend;
	}
	
}

敌方子弹类

public class EnemyBullets extends Thread{
	private int atk;
	private ImageIcon image;
	private int speed;
	private boolean islive=true;
	private int x,y;
	private boolean suspend=false;
	public EnemyBullets(int atk, ImageIcon image, int speed, int x, int y) {
		super();
		this.atk = atk;
		this.image = image;
		this.speed = speed;
		this.x = x;
		this.y = y;
	}
	public void run() {
		while(islive)
		{
			while(suspend)
			{
				//暂停功能
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			try {
				Thread.sleep(50);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			y+=speed;
		}
	}
	public boolean isIslive() {
		return islive;
	}
	public void setIslive(boolean islive) {
		this.islive = islive;
	}
	public int getAtk() {
		return atk;
	}
	public ImageIcon getImage() {
		return image;
	}
	public int getSpeed() {
		return speed;
	}
	public int getX() {
		return x;
	}
	public int getY() {
		return y;
	}
	public boolean isSuspend() {
		return suspend;
	}
	public void setSuspend(boolean suspend) {
		this.suspend = suspend;
	}
	
}

这里我没有将子弹与飞机封装在一起,这个不太方便,各位可以采用封装在一起的方法来实现。

5.音效
话不多说,读取文件后播放,直接上代码。

public class GetMusic extends Thread{
	private String path;
	private boolean flag=true;
	private boolean whileplay;
	private double value ;

	public GetMusic(String path,boolean whileplay,double value) {
		super();
		this.path = path;
		this.whileplay=whileplay;
		this.value=value;
	}
	public void run()
	{
		if(whileplay)
		{
			while(flag)
			{
				playMusic();
			}
		}
		else
			playMusic();
	}
	public void playMusic() {// 背景音乐播放
		 
		try {
			AudioInputStream ais = AudioSystem.getAudioInputStream(new File("music\\"+path));
			AudioFormat aif = ais.getFormat();
			final SourceDataLine sdl;
			DataLine.Info info = new DataLine.Info(SourceDataLine.class, aif);
			sdl = (SourceDataLine) AudioSystem.getLine(info);
			sdl.open(aif);
			sdl.start();
			FloatControl fc = (FloatControl) sdl.getControl(FloatControl.Type.MASTER_GAIN);
			// value可以用来设置音量,从0-2.0
			double value = 2;
			float dB = (float) (Math.log(value == 0.0 ? 0.0001 : value) / Math.log(10.0) * 20.0);
			fc.setValue(dB);
			int nByte = 0;
			int writeByte = 0;
			final int SIZE = 1024 * 64;
			byte[] buffer = new byte[SIZE];
			while (nByte != -1) {
				if(flag) {
					sdl.write(buffer, 0, nByte);
					nByte = ais.read(buffer, 0, SIZE);
				}else {
					
					nByte = ais.read(buffer, 0, 0);
					
				}
			}
			sdl.stop();
 
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	public boolean isFlag() {
		return flag;
	}
	public void setFlag(boolean flag) {
		this.flag = flag;
	}
}

6.飞机升级
这个挺好实现的,你在产生子弹的线程中多产生两路子弹就可以了。当然如果你有什么更好的想法,欢迎交流。
效果图:(达到4000分升级)
在这里插入图片描述
7.暂停\重新开始
我在这里用的是在线程run方法中加入死循环,进入暂停时,所有线程进入死循环。当然这样也有缺点,就是对电脑资源占用较高。也可以采用对线程进行休眠与唤醒来实现,这种方法在我的程序中较为麻烦,就没采用。

8.开始新游戏
这个你可以用重新创建对象来实现,但是如果你之前创建对象使用的是static,那么,你就需要去手动清空所有数据。很不巧,我就是部分对象创建的是static,所以会稍微麻烦一些。

9.道具的使用
为了增加游戏的可玩性,你可以增加一些道具,例如,加血道具,护盾道具,僚机,强化子弹。。。。。
这个你可以自己设计。你可以通过我的源码看懂怎么设计的就不赘述了。上两张效果图。

护盾
在这里插入图片描述

僚机在这里插入图片描述
强化子弹
在这里插入图片描述
10.关卡boss
这个和设计敌机一样,只是吧,你可把boss血量调高一些,发射子弹的方式调一下。
在这里插入图片描述
(三)关键算法
碰撞判断
先看一张图
在这里插入图片描述
碰撞无外乎就是上面四种情况,所以,我们可以有很多种方法去实现它,你可以通过判断两张图片中心点之间的距离,你也可以通过遍历我方飞机的四个点,是否有一个点在敌方飞机或子弹的范围内即可。

四、我这个项目明显的缺点

  1. 所有我方子弹,敌方飞机,敌方子弹都是一个新线程,游戏进行中同时运行了成百上千的线程。造成电脑卡顿,资源浪费。解决方法,移动的线程不必每次创建子弹和飞机时都启动一个线程,用一个移动线程就能控制。
  2. 图片因为是四四方方的一张图片,有一部分留白,所以在写碰撞函数时无法兼顾所有图片,存在一定视觉上的误差(明明打中了却穿过去了,或者明明看上去没打中,但实际上却打中了)
  3. 游戏难度设置不合理(啊哈哈哈哈,因为我没有考虑玩家感受,在打boss的时候我试了一下,基本很难过关)。
  4. 游戏程序稳定性不好,遇到部分不容易出现的bug 会卡死。
  5. 存在一定闪烁问题(我加了缓冲功能,但是因为程序占用了太多资源,执行起来还是有一定的闪烁)。

最后的最后,附上我源代码的链接:
源码

在这里插入图片描述

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值