【Java实践】多线程游戏之多彩飞机大战v1(完整代码、设计思路、技术要点)

主要是利用JAVA的swing和多线程做一个简易飞机大战的小游戏,功能比较简单。完整代码已经上传到 https://download.csdn.net/download/weixin_42368748/12137255 ,可免费下载。

游戏规则

通过鼠标控制己方飞机的左右移动,移动到不同地方按下空格键切换不同的状态(颜色),它发射出来的子弹要打到相同颜色的敌机才能使其击毁。击毁一架敌机加一分,敌机越界则己方扣一滴血,到0则游戏结束。
在这里插入图片描述
在这里插入图片描述

基本框架

简单讲述一下设计思路,首先打开UI布局,然后启动飞行物管理线程;从而生成己方飞机,并且开启管理子弹的线程和管理敌机的线程;这两个线程生成子弹和敌机。

可以看看这个粗略的UML图,大致理解各个类的关系:在这里插入图片描述
GameUI:游戏布局类。用JFrame和JPanel实现游戏布局;创建FlyCtrl对象,同时启动后者这一线程。

FlyCtrl:飞行物管理类,同时也是一个管理整个游戏的线程。包括创建Plane对象、ScoreBoard对象;启动BulletThread线程和ShipThread线程;存储上述两个线程产生的Bullet对象和Ship对象,并进行管理。

Plane:己方飞机类。存储己方飞机的位置、大小、状态等信息;受监听器MListener控制;提供位置信息给BulletThread;包含对自身的绘制。

ScoreBoard:得分和血量显示类。存储当前游戏得分、己方飞机血量;包含加分、扣血等方法;回馈信息给FlyCtrl;作为难度参考提供信息给ShipThread。

BulletThread:管理子弹生成的线程。根据Plane的位置和状态定时生成Bullet对象,加入到FlyCtrl的子弹列表中。

ShipThread:管理敌机生成的线程。根据ScoreBoard提供的当前分数,按不同难度随机生成Ship对象,加入到FlyCtrl的敌机列表中。

Bullet类和Ship类实现FlyObject接口,分别表示一个子弹和一个敌机,包含位置、大小、状态等自身信息,以及移动、绘制自身、得到自身信息的方法。

重要代码展示

挑几个重点的讲一下吧!

(1) UI布局

  • 创建JFrame。
  • 创建中间面板,加进frame中。
  • 创建底部面板,添加标签插入图片,加进frame中。
  • 设置frame可见。
  • 创建FlyCtrl对象,传入中间面板。
  • 创建新线程,传入上述对象(任务)。
  • 线程start,设置标志位。

重点就是启动管理线程:
FlyCtrl实现Runnable接口,所以创建了它的对象后,赋值给一个新的线程,并且start即可。另外,设置标志位是为了方便该线程的控制。

JPanel mainPanel= new JPanel(); //中间区域 游戏主要的区域
mainPanel.setPreferredSize(new Dimension(600,700));
jf.add(mainPanel, BorderLayout.CENTER);		

jf.setVisible(true);
 
FlyCtrl ctrl= new FlyCtrl(mainPanel);  //飞行控制线程,控制所有飞行物	    
Thread ctrlThread = new Thread(ctrl);
ctrlThread.start();
ctrl.setFlag(true);    

(2) 总控线程 FlyCtrl

飞行物管理类,同时也是一个管理整个游戏的线程。结构如下:

  • 获取画板。
  • 创建Plane对象(设置监听器)、ScoreBoard对象。
  • 创建对象并启动BulletThread线程和ShipThread线程。
  • 创建两个列表分别存储待生成的Bullet对象和Ship对象。
  • 绘制全部物品:
    • 创建缓冲图像。
    • 绘制己方飞机。
    • 对子弹进行处理:
      • 移动;
      • 检测是否出界,若出界则删除该子弹;
      • 检测碰撞,若与不同颜色敌机碰撞则删除子弹,同颜色则摧毁处理(删除、音效)。
      • 绘制现有所有子弹。
    • 对敌机进行处理:
      • 移动;
      • 检测是否出界,若出界说明越界,己方飞机扣血;
      • 绘制现有所有敌机。
    • 绘制得分血量牌。
    • 以上均是绘制到缓冲图像上,现在再把缓冲图像绘制到容器的画板上。
  • 线程循环运行。

结合注释看看代码吧:

public class FlyCtrl implements Runnable{
   

	Plane myPlane;
	MListener listener;
	ScoreBoard scoreBoard;
	List<FlyObject> bulletList;
	List<FlyObject> enemyList;	
	AudioClip boomSound;
	AudioClip debloodSound;
	Graphics2D g;
	boolean flag; //线程运行的标志
		
	//初始化
	public void init(){
   
		//创建己方飞机对象
		Plane myPlane = new Plane();  
	    listener.myplane= myPlane;
	    this.myPlane= myPlane;
	    //创建得分、血量显示牌
	    scoreBoard = new ScoreBoard();
	    //存储子弹和敌机的列表
	    bulletList = new ArrayList<FlyObject>();
		enemyList = new ArrayList<FlyObject>();
	    //启动子弹管理线程
	    BulletThread bulletThread = new BulletThread(myPlane,bulletList);
	    bulletThread.start();
	    //启动敌机管理线程
	    ShipThread shipThread = new ShipThread(enemyList,scoreBoard);
	    shipThread.start();
	}
	
	//构造方法
	public FlyCtrl(JPanel mainPanel){
   
		//获取画板
		g = (Graphics2D)mainPanel.getGraphics();
		g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints
  • 8
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
package cn.feike.shoot; import java.awt.Graphics; import java.awt.image.BufferedImage; public abstract class FlyingObject { protected double x;//物体的x坐标 protected double y;//物体的y坐标 protected double width;//物体的宽 protected double heigth;//物体的高 protected BufferedImage image;//当前正在显示的图片 protected int index = 0;//图片数组下标序号,子类中使用 protected double step;//飞行物每次(1/24秒)移动的距离 protected int life;//命 protected int state;//飞行物的状态 public static final int ACTIVE=0;//活着状态 public static final int DEAD=1;//死亡状态 public static final int REMOVE=2;//回收状态 //默认构造器 public FlyingObject() { life = 1; state = ACTIVE; } //有参构造器 public FlyingObject(double width,double heigth){ this();//调用无参数的构造器,必须写在第一行. this.x = (int)(Math.random()*(480-width)); this.y = -heigth; this.width = width; this.heigth = heigth; step = Math.random()*3+0.8;//初始化step为[0.8,3.8)之间的数 } //重写toString方法 public String toString() { return x+","+y+","+width+","+heigth+","+image; } //重写paint,方便子类对象的使用 public void paint(Graphics g) { g.drawImage(image, (int)x, (int)y, null);//绘制图片 } //飞行物移动的move方法 /** * 重构了move,方法实现播放销毁动画功能 */ public void move(){ if(state == ACTIVE){ y += step; return ; } if(state == DEAD){ //从子类对象中获取下一张照片 BufferedImage img = nextImage(); if(img == null){ state = REMOVE;//没有照片则回收 }else{ image = img;//否则把子类的图片传给image } //越界则销毁 if(y>=825){ state = REMOVE; } } } /** * 子类中必须有的方法,返回下一个要播放的照片引用, * 如果返回null表示没有可播放的照片了. */ protected abstract BufferedImage nextImage(); /** * 飞行物被打了一下 */ public void hit(){ if(life>0){ life--; } if(life==0){ state = DEAD; } } /** * 碰撞检测的方法 * 检测物体的位置是否在碰撞的范围内. * (子弹是否在飞行物的碰撞范围内) */ public boolean duang(FlyingObject obj){ //this(x,y,w,h) //obj(x,y,w,h) double x1 = this.x - obj.width; double x2 = this.x + this.width; double y1 = this.y - obj.width; double y2 = this.y + this.heigth; return x1<obj.x&&obj;.x<x2&&y1;<obj.y&&obj;.y<y2; } /** 重构FlyingObject,添加了状态检查方法 */ /** 检查飞行物死了吗 */ public boolean isDead(){ return state == DEAD; } /** 检查飞行物是否活动的 */ public boolean isActive(){ return state == ACTIVE; } /** 检查飞行是否可以被删除*/ public boolean canRemove(){ return state == REMOVE; } /** 飞行物添加"去死"方法*/ public void goDead(){ if(isActive()){ state = DEAD; } } }

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值