在做完连连看以后,想到要做一个多线程游戏,本来是做的一个跳伞的小游戏的。但是做到一半的时候,觉得可玩性太低了。后面想来想去还是打算做一个以前玩过的雷霆战机小游戏,也就是飞机大战。

1.效果展示
2.绘制背景
3.方向类
4.飞机类
5.子弹类
6.爆炸类
7.道具类
8.总结一下界面类里面的绘制线程
9.播放音乐
10.开始界面

1.效果展示

直接放图了。

博主自己特别喜欢的一个特效,吃道具后能够变声,而且附带数码宝贝的音效,但是只能展示动图了,配合音效会更有感觉一点。

然后是动图

2.绘制背景

我们先不管游戏的开始界面啥的,先从主要的开始入手。
第一步就是绘制背景界面了。
在实际的效果中,像是飞机在飞一样,其实只是背景图片在移动,然后看上去就感觉飞机在飞。

在博主画的两个框里面,蓝色代表背景图片。黑色代表软件界面。因为背景图片的长度是大于软件界面的,所以将背景图慢慢移动,就会造成一种动态画面的效果。然后将背景图片调用两次,第一张放完后就放第二张,第二张放完后就再放第一张,循环下去,就会有背景一直在动的感觉。

因为背景图片是bmp格式的,博主试了一下,改为jpg或者png都不能在eclipse里面显示。
所以等下如果要用到这张背景图就不要改格式了。

而且bmp格式的图片,好像不能直接调用,不然显示不出来。
下面的代码博主试了一下就是读取bmp图片且调用的代码。
这段代码是每次读取图片用到的工具类。

import java.awt.Image;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;
import javax.imageio.ImageIO;public class GameImage {private GameImage() {};//工具类一般设置为私有public static Image getImage(String path) {URL u = GameImage.class.getClassLoader().getResource(path);BufferedImage img = null;try {img = ImageIO.read(u);} catch (IOException e) {e.printStackTrace();}return img;}
}

下面是关于背景图片部分的代码
其中绘画部分是要放在同一个线程里面的,不然会导致画面中背景或者飞机或者子弹不停的闪烁。
所以这个绘制背景的代码应该放到绘制线程里面。

int posY=-650;//窗体的高度减去图片高度,810-1460=-650
int posY2 = posY-1412;//posY是第一张图片y坐标,posY2是第二张图片y坐标while(true){if(posY>=760){//交替posY = posY2 -gameBg.getHeight(null);}else{if(posY2>=760){//交替posY2 = posY - gameBg.getHeight(null);}else{if(begin==false){//开始滚动posY += 2;bg.drawImage(gameBg, 0, posY,  null);posY2 +=2;bg.drawImage(gameBg, 0, posY2,  null);}}}

3.方向类

因为敌方飞机和我方飞机都需要移动。
而在键盘监听器里面最好不去键盘按键实现方法,而是键盘按键代表一个状态,然后根据状态调用方法。
所以我们用一个枚举类来写方向。
代码如下:

public enum Direction {//枚举类型L, R, RD, D, LD, STOP,LU, U, RU
}

4.飞机类

我们先写飞机类的构造函数,方便去调用

public void pic(){//调用飞机图片myImgs[0] = GameImage.getImage("resources/plane1.png");myImgs[1] = GameImage.getImage("resources/plane23.png");enemyImgs[0] = GameImage.getImage("resources/enemy3.png");bossImgs[0] = GameImage.getImage("resources/boss6.png");}public Plane(int num, int x,int y,int speed,boolean good,gameUI gameui) {pic();this.num=num;this.x=x;this.y=y;this.speed=speed;this.good=good;this.gameui=gameui;if(good==true) {//good为true时,为我自己飞机,调用我飞机图片ensureImg = myImgs[num];WIDTH = ensureImg.getWidth(null);HEIGHT = ensureImg.getHeight(null);}else {if(num==0) {ensureImg = enemyImgs[0];dir = Direction.D;//设置普通敌机方向只有向下WIDTH = ensureImg.getWidth(null);HEIGHT = ensureImg.getHeight(null);}}}

然后再写飞机的键盘监听器方法。
因为我在方向类里面说了,在键盘监听器里用按键表示状态。
所以飞机类里面都是按键表示状态。
特别注意,在按键时比如左移bL的状态为true;松开时左移的状态为false;不然会按一次键就一只移动。

public void Press(KeyEvent e) {int key = e.getKeyCode();switch(key) {case KeyEvent.VK_LEFT :bL = true;break;case KeyEvent.VK_UP :bU = true;break;case KeyEvent.VK_RIGHT :bR = true;break;case KeyEvent.VK_DOWN :bD = true;break;}locateDirection();}public void Release(KeyEvent e) {int key = e.getKeyCode();switch(key) {case KeyEvent.VK_LEFT :bL = false;break;case KeyEvent.VK_UP :bU = false;break;case KeyEvent.VK_RIGHT :bR = false;break;case KeyEvent.VK_DOWN :bD = false;break;case KeyEvent.VK_M:fire();break;}locateDirection();
}public void locateDirection() {if(bL && !bU && !bR && !bD) dir = Direction.L;  else if(bL && bU && !bR && !bD) dir = Direction.LU;  else if(!bL && bU && !bR && !bD) dir = Direction.U;  else if(!bL && bU && bR && !bD) dir = Direction.RU;  else if(!bL && !bU && bR && !bD) dir = Direction.R;  else if(!bL && !bU && bR && bD) dir = Direction.RD;  else if(!bL && !bU && !bR && bD) dir = Direction.D;  else if(bL && !bU && !bR && bD) dir = Direction.LD;  else if(!bL && !bU && !bR && !bD) dir = Direction.STOP; }

监听键盘后,我们根据飞机的状态再来移动。
所以需要写一个移动的方法。

 public void move() {switch(dir) {case L:x -= speed;break;case LU:x-=speed;y-=speed;break;case U:y-=speed;break;case RU:x+=speed;y-=speed;break;case R:x+=speed;break;case RD:  x += speed;  y += speed;  break;  case D:  y += speed;  break;  case LD:  x -= speed;  y += speed;  break; case STOP:break;}if(x<0)   x=0;//左边界if(y<40)  y=40;//上边界if(x+ensureImg.getWidth(null)>600) x=600-ensureImg.getWidth(null);//右边界if(y+ensureImg.getHeight(null)>760) y=760-ensureImg.getHeight(null);//}

然后是画飞机的方法。
这个方法一定在绘画线程里面和其他所有的绘画方法一起被调用,不然会导致画面闪烁。

最后是创建敌机的方法。

 public void createEnemy() {if(this.es.size()<4){//使敌机数量保持在4架Plane ePlane = new Plane(0,r.nextInt(500),0,5,false,this);//敌机this.es.add(ePlane);if(r.nextInt(50)>30) {ePlane.fire();}}
}

5.子弹类

和飞机类的流程差不大多。
为了方便生成子弹,所以我们第一步任然是写一个子弹类的构造方法。
pic()仍为调用图片方法。


public void pic() {myImgs[0] = GameImage.getImage("resources/m5.png");enemyImgs[0] = GameImage.getImage("resources/em1.png");}public Bullet(int x,int y,int speed,int randIndex,boolean good,gameUI gameui) {pic();this.x=x;this.y=y;this.randIndex = randIndex;this.speed = speed;this.good=good;this.gameui=gameui;pic();if(good==true) {ensureImg = myImgs[0];dir = Direction.U;WIDTH = ensureImg.getWidth(null);HEIGHT = ensureImg.getHeight(null);this.power = 20;//我子弹威力为10}else {dir = Direction.D;ensureImg = enemyImgs[0];WIDTH = ensureImg.getWidth(null);HEIGHT = ensureImg.getHeight(null);this.power = 10;//敌方子弹威力为5}}

写完构造类以后,我们想一想,子弹剩下的就是移动和达到飞机的情况了。
然后情况又分为我方子弹命中敌机,敌机子弹命中我机两种情况。
所以剩下需要写的方法分别是对子弹的移动和命中飞机的方法。

private void move() {  switch(dir) {  case U:  y -= speed;  break; case D:y += speed;  break;  }  if(x < 0 || y < 0 || x > 880 || y > 760) {  isAlive = false;//出界就设置为false  }}public boolean hitPlane(Plane p) {  //敌方攻击我//intersects(Rcetangle),判断该Rectangle与当前Rectangle是否相交   if(this.isAlive && this.getRect().intersects(p.getRect()) && p.getAlive() && this.good != p.isgood()) {  this.isAlive = false;p.setLife(p.getLife()-this.power);if(p.isgood()==false){//敌方飞机FALSEp.setAlive(false);//飞机死亡  Explode e = new Explode(x, y, gameui);  gameui.explodes.add(e);//添加爆炸return true;}else if(p.isgood()==true){//我方飞机TRUEif(p.life<=0){p.setAlive(false);//飞机死亡  //myplane.setAlive(false);Explode e = new Explode(x, y, gameui);  gameui.explodes.add(e);//添加爆炸return true;}}}  return false;  }public boolean hitPlanes(List<Plane> planes) {  //我攻击敌方for(int i=0; i<planes.size(); i++) {  if(hitPlane(planes.get(i))) {  es.remove(i);return true;  }  }return false;  }

这里需要注意的点是,先写一个hitPlane敌方攻击我的方法,在另一个攻击敌方的方法里面对这个方法进行调用就可以了,算是一个需要注意的敌方。
另一个是es为敌方飞机的队列,哪架飞机被击中,就在队列里面被移去。

6.爆炸类

爆炸类比飞机类和子弹类都更加简单。
因为只需要在飞机死亡后绘制爆炸。
所以爆炸类的方法就只有构造方法和绘画方法。

 public void pic() {images[0]=GameImage.getImage("Resources/blast_0_5.png");}public Explode(int x,int y,gameUI gameui) {super();this.x=x;this.y=y;this.gameui=gameui;}public void draw(Graphics g) {pic();WIDTH = images[0].getWidth(null);HEIGHT = images[0].getWidth(null);if(!live) {gameui.explodes.remove(this);//爆炸结束移除//System.out.println("live="+live+" ");return;}if(live) {g.drawImage(images[0], x-(WIDTH/2), y-(HEIGHT/2), null);//System.out.println("画爆炸图片");live=false;}}

相对于前面的飞机类和子弹类,爆炸类也没有什么需要多讲的地方了。

7.道具类

这个道具,博主因为时间的关系只写了一种道具,就是前面展示了的变身的道具类。
方法依然是旧几个,构造方法,移动方法,绘制方法,道具相撞飞机调用的方法。
只是飞机吃到道具后,这里需要特别注意一下!
因为博主所有的绘画方法都是放在一个绘画线程里面被调用。
而我的变身GIF图片有5秒左右,这个时候需要暂停以前的绘画线程,然后把画变身单独做一个新线程用join方法插入,这样才能有我的GIF那样的效果。

控制变身的线程

public class ControlThread extends Thread{Graphics g;public void run() {while(true) {g.drawImage(jinhua.getImage(),0,0,590,800,null);    }}
}

道具类的方法

public void draw(Graphics g) {pic();if(isAlive==false) {return;//道具被吃掉则消失}T_WIDTH = propImgs[0].getWidth(null);T_HEIGHT = propImgs[0].getHeight(null);g.drawImage(propImgs[0],x,y,T_WIDTH,T_HEIGHT,null);}public void move() {if(x<=0 || x>=561) {speedx = -speedx;}if(y<=0 || y>=750) {speedy = -speedy;}x-=speedx;y+=speedy;}public boolean hitProp(Plane myplane) {if(myplane.isgood()==false) {return true;}else if(myplane.isgood()==true) {if(this.isAlive && this.getRect().intersects(myplane.getRect()) && myplane.getAlive()) {this.isAlive=false;myplane.setAlive(false);return true;}//System.out.println("没有碰到");}return false;}

8.总结一下界面类里面的绘制线程

让大家容易看懂,直接贴代码了。
因为这一段代码稍微有点长,博主直接把注释打在代码注释里面了。

public class BgThread extends Thread{Graphics g;JTextField textField;JTextField hitField;gameUI gameui;Image[] myImgs;ControlThread t2;public void run() {while(true){if(posY>=760){//交替posY = posY2 -gameBg.getHeight(null);}else{if(posY2>=760){//交替posY2 = posY - gameBg.getHeight(null);}else{if(begin==false){//开始滚动posY += 2;bg.drawImage(gameBg, 0, posY,  null);posY2 +=2;bg.drawImage(gameBg, 0, posY2,  null);}}}myplane.draw(bg);this.textField.setText("剩余生命:"+myplane.life);//绘制子弹for(int i=0; i<bs.size(); i++) {//将集合中的子弹都绘制出来  Bullet b = bs.get(i);  b.es=es;//子弹中的飞机列表esb.draw(bg);//  System.out.println("重绘部分在画子弹"); b.hitPlanes(es);b.hitPlane(myplane);} createEnemy();//绘制敌机for(int i=0; i<es.size(); i++) {  Plane p = es.get(i);p.draw(bg);p.hitBorder(es);}prop.draw(bg);prop.move();if(prop.hitProp(myplane)) {//变身myplane.num=1;myplane.ensureImg = myImgs[1];//    bg.drawImage(jinhua.getImage(),0,150,590,500,null);t2.start();new PlaySound("进化.mp3", false).start();try {t2.join(5000);t2.stop();} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}// bg.drawImage(jinhua.getImage(),0,150,590,500,null);myplane.setAlive(true);}//绘爆炸for(int i=0;i<explodes.size();i++) {Explode e = explodes.get(i);e.draw(bg);count++;}this.hitField.setText("击落敌机:"+count/2);g.drawImage(buffer, 0,0, null);//把所有的东西从缓存画下来try {Thread.sleep(50);//滚动速度的设定} catch (InterruptedException e) {e.printStackTrace();}}}
}

9.播放音乐。

以前在做连连看的时候,插入的音乐需要转换成为WAV格式才能使用,然后这一段代码可以直接适用MP3格式了。很方便,也直接贴出来了。

import java.io.InputStream;
import javazoom.jl.player.advanced.AdvancedPlayer;/*** * 必须使用多线程,播放音效**/
public class PlaySound extends Thread{private String mp3Url;private boolean isLoop;public PlaySound(String mp3Url, boolean isLoop) {super();this.mp3Url = mp3Url;this.isLoop = isLoop;}public void run() {do{//读取音频文件流InputStream mp3 = PlaySound.class.getClassLoader().getResourceAsStream("resources/"+mp3Url);try {//创建播放器AdvancedPlayer adv = new AdvancedPlayer(mp3);//播放adv.play();   } catch (Exception e) {e.printStackTrace();}}while(isLoop);}
}

10.开始界面。

开始界面其实和以前讲过的qq登录界面差不多。
只要在按钮加上监听器就好。所以也直接贴代码了。

import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Graphics;import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;public class StartUI extends JFrame{public void showUI() {StartUI startFrame = new StartUI();startFrame.setSize(900,650);startFrame.setTitle("Design By TangNan");startFrame.setLocationRelativeTo(null);//居中startFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//关闭FlowLayout flowl = new FlowLayout();//布局startFrame.setLayout(flowl);StartListener startL = new StartListener();startFrame.addMouseListener(startL);startL.j=startFrame;ImageIcon image =new ImageIcon(getClass().getResource("resources/FJDAZ_START1.png"));JLabel iconLabel = new JLabel(image);startFrame.add(iconLabel);Dimension btnsize = new Dimension(125,50);JButton btn1 = new JButton("开始游戏");btn1.setPreferredSize(btnsize);startFrame.add(btn1);btn1.addActionListener(startL);JButton btn2 = new JButton("提示说明");btn2.setPreferredSize(btnsize);startFrame.add(btn2);btn2.addActionListener(startL);startFrame.setVisible(true);Graphics g = startFrame.getGraphics();//获取画板放在可视化之后}
}

最后,飞机大战到这里就算简单的完成了。其实博主还有其他蛮多想加的效果和一些BUG都还没改,但没太多时间去继续弄飞机大战,等以后有空闲时间的话,会再继续完善飞机大战。

JavaSwing多线程小游戏雷霆战机相关推荐

  1. java多线程小游戏_Java多线程实现方块赛跑小游戏

    本文实例为大家分享了Java实现方块赛跑小游戏的具体代码,供大家参考,具体内容如下 在一个图形界面上构造两个位于同一起跑线方块,起跑线位于界面靠左位置, A 方块先开始运动,向右移动 50 像素后停止 ...

  2. JS小游戏-宇宙战机

    游戏介绍: 业余时间写的一个飞行射击游戏,纵向,共六关. 游戏需求: 1.战机可发射子弹,子弹可通过获取道具升级. 2.战机可放bomb,可获取道具增加数量. 3.战机可蓄力攻击. 4.道具有三种,分 ...

  3. Java多线程-小游戏方块赛跑

    在一个图形界面上构造两个位于同一起跑线方块,起跑线位于界面靠左位置, A 方块先开始运动,向右移动 50 像素后停止,B 方块开始运动,向右移动 100 像素后停 止,A 方块继续向右运动 100 像 ...

  4. 雷霆战机源代码c语言,C++实现雷霆战机可视化小游戏

    用C++和easyx实现简单的雷霆战机小游戏 之前在网上看了许多关于c++或者是其他语言实现雷霆战机的帖子,大多不完整,或者是要付费才能阅读,现将源码展示如下,仅作学习交流之用. 基本原理 基本思路 ...

  5. c++ 实现雷霆战机可视化小游戏

    用c++和easyx实现简单的雷霆战机小游戏 之前在网上看了许多关于c++或者是其他语言实现雷霆战机的帖子,大多不完整,或者是要付费才能阅读,现将源码展示如下,仅作学习交流之用. 基本原理 基本思路 ...

  6. android 仿雷霆战机、打飞机等简易u小游戏

    时间不多,不做细说,先上效果图 直接上源码 码云中国 git 下载地址: 仿雷霆战机.打飞机小游戏

  7. jq开发雷霆战机小游戏

    这是我写的第二个jq小游戏了写了一个多星期,挺不容易的,写篇博客记录一下 游戏界面: 英雄机有能量条:能量条的积攒通过打爆敌机和打boss获得,能量条到达20%英雄机的弹孔可以升级为双管炮火弹孔,能量 ...

  8. java 雷霆战机 教程,java swing实现简单的雷霆战机小游戏项目源码附带视频指导修改教程...

    <p> <span style="font-family:微软雅黑;font-size:19px;color:#666666;background-color:#FFFFF ...

  9. AndroidPlayPlane战机小游戏

    android毕业设计战机小游戏:可供参考借鉴,当时闲来无事写的,愿各位同学在Android道路上越走越远!!! 主要功能介绍: 飞机自动向前飞行,手指拖动飞机即可进行飞机方向以及前后控制 飞机自动发 ...

最新文章

  1. timesten 修改最大连接数
  2. 10年测试工程师首谈测试前景,如何25天进入滴滴大厂
  3. 用数学范畴定义生命的尝试
  4. Python计算机视觉:第九章 图像分割
  5. tomcat启动占了12g_windows server tomcat服务器部署内存占用高问题
  6. C. Barcode dp
  7. SDOI2017R1(姑且是游记)
  8. 分布式系统基础-1、架构演进
  9. 11年写的一篇文章----智能终端安全现状及前景展望
  10. 【廖雪峰官方网站/Java教程】Maven基础
  11. ASCII、GB2312、GBK、GB18030、Unicode、UTF-8、BIG5 编码分析
  12. 计算机桌面标题栏怎么锁定,(excel图表标题设置)在excel表格中,如何锁定标题栏不被修改?...
  13. 【狂神说Java】SpringBoot最新教程IDEA版通俗易懂
  14. 2021年烷基化工艺考试试卷及烷基化工艺模拟考试题
  15. 如果用seagull php框架开发一个类似zen cart,Oscommerce,magento这样的模块会什么样
  16. 韦东山嵌入式第一期学习笔记DAY_3——7_1_裸机开发步骤简介
  17. 从0开始学Unity做SLG系列(GameFramework框架)
  18. 福禄克DSX5000怎么导出数据
  19. 二见钟情——设计模式
  20. 小程序开发(首页设计)

热门文章

  1. [ArcGIS] 栅格计算器
  2. cglib BeanCopier的使用
  3. 华为堪称最大黑洞!清华大学/北京大学的学生,毕业后都去哪了?
  4. 武大计算机博士毕业生工资,北航、交大、武大…中国重点大学毕业生工资公布,你达标了吗?...
  5. Python将两个列表合并成一个列表
  6. Teigha.NET V19 操作DWG、DXF文件
  7. 开播4小时播放量破亿,《你是我的荣耀》为何能够大受好评?
  8. 单例模式(Python中的单例类)
  9. 关于在英伟达官网下载 CUDA 10.0 win10版本失败原因探索
  10. Formal equivalence verification 形式验证之等价验证 FEV 第8章