前言:
前一段时间写了一个仿QQ的聊天室,主要涉及Java通信有关的知识,今天写的这个飞机大战主要是复习之前写过的,这次我会加入一些其他的元素和对界面进行一些完善。
这篇博客我会写的比较通透,主要是自己用来复习和巩固知识点的,所以大家如果感兴趣的话,可以参考我的这篇博客!特别是正在学习Java的同学哦!!!
实现飞机大战设计的知识点还是比较多的,所以特别推荐编程小白先上手这种简单的游戏,这样既可以巩固简单的Java基础知识,又可以实现自己编写的小作品,展现自己的能力,何乐而不为呢?下面我就简单地介绍一下实现飞机大战要了解和使用的相关知识吧ƪ(˘⌣˘)ʃ
综合前面的分析我们就可以搭建一个简单的框架:
/**
* 接口
* @author 梦想少年
* 定义子类共有的抽象的方法:移动、画图、设置和获得坐标
*/
public interface Entity {
//移动方法
public void move();
//画图的方法
public void draw(Graphics g);
//设置和得到X和Y坐标的方法
public void setX(int startX);
public void setY(int startY);
public int getX();
public int getY();
}
/**
* 自己的战机类
* @author 梦想少年
* 实现接口中的方法,控制移动和检测是否是受到攻击
*/
public class MyFeiJi implements Entity {
//控制飞机上下左右移动
boolean up=false;
boolean down=false;
boolean left=false;
boolean right=false;
private int x,y;
//得分和血量
private int scores;
private int hp;
//我机图片
private Image image_myfeiji;
public MyFeiJi(){
image_myfeiji=new ImageIcon("D:\\常用\\图片\\"
+ "Camera Roll\\image\\战机.png").getImage();
}
//移动界限
public void move() {
if(x<=40){
x=40;
}
if(x>=1790){
x=1790;
}
if(y<=100){
y=100;
}
if(y>=910){
y=910;
}
//移动速度
if(up){
y-=3;
}
if(down){
y+=3;
}
if(left){
x-=4;
}
if(right){
x+=3;
}
}
public void draw(Graphics g) {
g.setFont(new Font("黑体",Font.BOLD, 35));
g.setColor(Color.RED);
g.drawString("我的血量:"+hp,30,90);
g.setFont(new Font("黑体",Font.BOLD, 35));
g.setColor(Color.RED);
g.drawString("我的得分:"+scores,30,140);
g.drawImage(image_myfeiji, x, y, null);
}
//判断是否输赢
public boolean isDefeat(){
if(hp<=0){
return true;
}else{
return false;
}
}
//设置和获得坐标的方法
public void setX(int startX) {
this.x=startX;
}
public void setY(int startY) {
this.y=startY;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
//检测我的飞机是否被敌机子弹击中
public boolean isShootByDiJiBullet(DiJiBullet dijibullet){
int x1=this.x;//我的飞机x坐标
int y1=this.y;//我的飞机y坐标
int width1=this.image_myfeiji.getWidth(null);//我机的宽度
int height1=this.image_myfeiji.getHeight(null);//我机的高度
int x11=x1+width1/2;
int y11=y1+height1/2;//确定中心位置
//敌机的横纵坐标和高度
int x2=dijibullet.getX();
int y2=dijibullet.getY();
int width2=dijibullet.getDiJiBulletWidth();
int height2=dijibullet.getDiJiBulletHeight();
int x22=x2+width2/2;
int y22=y2+height2/2;
//计算距离
int distanceX=Math.abs(x22-x11);
int distanceY=Math.abs(y22-y11);
//是否碰撞
if(distanceX<=30&&distanceY<=25){
return true;
}else{
return false;
}
}
//检测我的飞机是否被敌机碰撞
public boolean isShootByDiJi(Diji diji){
int x1=this.x;//我的飞机x坐标
int y1=this.y;//我的飞机y坐标
int width1=this.image_myfeiji.getWidth(null);//我机的宽度
int height1=this.image_myfeiji.getHeight(null);//我机的高度
int x11=x1+width1/2;
int y11=y1+height1/2;//确定中心位置
//敌机的横纵坐标和高度
int x2=diji.getX();
int y2=diji.getY();
int width2=diji.getDiJiWidth();
int height2=diji.getDiJiHeight();
int x22=x2+width2/2;
int y22=y2+height2/2;
//计算距离
int distanceX=Math.abs(x22-x11);
int distanceY=Math.abs(y22-y11);
//是否碰撞
if(distanceX<=100&&distanceY<=100){
return true;
}else{
return false;
}
}
//掉血方法
public void subBlood(){
this.hp--;
}
public void subDoubleBlood(){
this.hp-=5;
}
//加分方法
public void addScores(){
scores+=5;
}
public void addDoubleScores(){
scores+=10;
}
//设置血量
public void setHP(int hp){
this.hp=hp;
}
public int getHP(){
return hp;
}
}
/**
* 敌机类
* @author 梦想少年
* 实现接口中的方法,随机产生敌机并设置敌机相关的一些属性
*/
public class Diji implements Entity{//敌机
private int x,y;
//控制敌机可以追踪战机
private int automaticMove;
//判断敌机是否爆炸
private boolean isDied;
//爆炸音乐
File musicFile;
//敌机图片
private Image dijiImage;
//爆炸图片
private Image diedImage;
//产生子弹的数量
private int dijiBulletCount;
//初始化
public Diji(){
musicFile=new File("C:\\Users\\梦想少年\\Music\\"
+ "AudioConvert\\大爆炸.wav");
dijiImage=new ImageIcon("D:\\常用\\图片\\"
+ "Camera Roll\\image\\diji.png").getImage();
diedImage=new ImageIcon("D:\\常用\\图片\\"
+ "Camera Roll\\image\\1\\7.png").getImage();
Random random=new Random();
x=random.nextInt(100)+1900;
y=random.nextInt(800)+80;
}
//移动方法
public void move() {
if(automaticMove==1){
y--;
}
if(automaticMove==2){
y++;
}
x-=2;
}
//画出敌机
public void draw(Graphics g) {
g.drawImage(dijiImage,x,y,128,128,null);
if(isDied){
g.drawImage(diedImage, x, y, null);
}
}
//判断是否越界
public boolean isOutBounds(){
if(x<0){
return true;
}else{
return false;
}
}
//设置飞机炮炸的方法
public void Blast(){
isDied=true;
}
//打开敌机炮炸音乐
@SuppressWarnings("deprecation")
public void startMusic(){
URL url;
try {
url = musicFile.toURL();
AudioClip aud;
aud=Applet.newAudioClip(url);
aud.play();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
//TODO Auto-generated catch block
e.printStackTrace();
}
aud.stop();
} catch (MalformedURLException e) {
//e.printStackTrace();
}
}
//获得和设置坐标
public void setX(int startX) {
this.x=startX;
}
public void setY(int startY) {
this.y=startY;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
//发射子弹计数器和获得产生子弹的数量
public void addBulletCount(){
dijiBulletCount++;
}
public int getBulletCount(){
return dijiBulletCount;
}
//获得敌机子弹的高度和宽度
public int getDiJiHeight(){
return dijiImage.getHeight(null);
}
public int getDiJiWidth(){
return dijiImage.getWidth(null);
}
}
/**
* 飞机子弹类
* @author 梦想少年
* 实现接口中的方法,检测是否攻击到敌方
*/
public class MyFeiJiBullet implements Entity {
//子弹类型
private int bulletType;
//坐标
private int x,y;
//子弹图片
Image image;
//初始化图片
public MyFeiJiBullet(){
image=new ImageIcon("D:\\常用\\图片\\"
+ "Camera Roll\\image\\bullet2.png").getImage();
}
//移动
public void move() {
x+=7;
}
//画图
public void draw(Graphics g) {
if(bulletType==1){
g.drawImage(image, x, y, 100, 100, null);
}
}
//检测是否越界
public boolean isOutBounds(){
if(x>1920){
return true;
}else{
return false;
}
}
//设置子弹类型的方法
public void setBulletType(int bulletType){
this.bulletType=bulletType;
}
//设置和获得坐标的方法
public void setX(int startX) {
this.x=startX;
}
public void setY(int startY) {
this.y=startY;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
//检测我的飞机是否被敌机子弹击中
public boolean isShootDiJiBullet(DiJiBullet dijibullet){
int x1=this.x;//我的子弹x坐标
int y1=this.y;//我的子弹y坐标
int width1=this.image.getWidth(null);//我的子弹宽度
int height1=this.image.getHeight(null);//我的子弹高度
int x11=x1+width1/2;
int y11=y1+height1/2;//确定中心位置
//敌机的横纵坐标和高度
int x2=dijibullet.getX();
int y2=dijibullet.getY();
int width2=dijibullet.getDiJiBulletWidth();
int height2=dijibullet.getDiJiBulletHeight();
int x22=x2+width2/2;
int y22=y2+height2/2;
//计算距离
int distanceX=Math.abs(x22-x11);
int distanceY=Math.abs(y22-y11);
//是否碰撞
if(distanceX<=200&&distanceY<=100){
return true;
}else{
return false;
}
}
//检测我的飞机是否被敌机碰撞
public boolean isShootDiJi(Diji diji){
int x1=this.x;//我的子弹x坐标
int y1=this.y;//我的子弹y坐标
int width1=this.image.getWidth(null);//我的子弹宽度
int height1=this.image.getHeight(null);//我的子弹高度
int x11=x1+width1/2;
int y11=y1+height1/2;//确定中心位置
//敌机的横纵坐标和高度
int x2=diji.getX();
int y2=diji.getY();
int width2=diji.getDiJiWidth();
int height2=diji.getDiJiHeight();
int x22=x2+width2/2;
int y22=y2+height2/2;
//计算距离
int distanceX=Math.abs(x22-x11);
int distanceY=Math.abs(y22-y11);
//是否碰撞
if(distanceX<=100&&distanceY<=100){
return true;
}else{
return false;
}
}
}
/**
* 敌机子弹类
* @author 梦想少年
* 实现接口中的全部方法,同时要控制边界
*/
public class DiJiBullet implements Entity {
private int x,y;
private Image image;
//初始化
public DiJiBullet(){
image=new ImageIcon("D:\\常用\\图片\\"
+ "Camera Roll\\image\\1\\5.PNG").getImage();
}
//移动的方法
public void move() {
x-=3;
}
//画图的方法
public void draw(Graphics g) {
g.drawImage(image, x, y, null);
}
//检测是否越界
public boolean isOutBounds(){
if(x<0){
return true;
}else{
return false;
}
}
//设置和获得坐标的方法
public void setX(int startX) {
this.x=startX;
}
public void setY(int startY) {
this.y=startY;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
//获得敌机子弹的高度和宽度
public int getDiJiBulletHeight(){
return image.getHeight(null);
}
public int getDiJiBulletWidth(){
return image.getWidth(null);
}
}
/**
* 背景图片音乐类
* @author 梦想少年
* 实现接口中的方法,画出移动的图片、一些界面的设置
*/
public class BackGroundPhotosMusic implements Entity{//添加背景
private int x,y;
private Image image;
//背景音乐(一定要wav格式的音乐才有效)
private AudioClip audio;
//播放关闭音乐字符串和状态
private boolean isMusic;
private String musicString;
//游戏状态
private boolean isGame;
private String gameString;
//初始化背景图片
public BackGroundPhotosMusic(){
image=new ImageIcon("D:\\常用\\图片\\"
+ "Camera Roll\\image\\bg11.jpg").getImage();
File musicFile=new File("C:\\Users\\梦想少年\\Music\\"
+ "AudioConvert\\薛之谦 - 天后 (Live).wav");
try {
audio=Applet.newAudioClip(musicFile.toURI().toURL());
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//移动背景
public void move() {
x--;
}
//画出背景
public void draw(Graphics g) {
if((x+image.getWidth(null)<=0)){
x=0;
}
g.drawImage(image, x, y, 1920, 1080, null);
g.drawImage(image, x+image.getWidth(null), y, 1920, 1080, null);
//相关设置
g.setFont(new Font("黑体",Font.BOLD,30));
g.setColor(Color.BLUE);
g.drawString("游戏设置",30,180);
//音乐启停
g.setFont(new Font("黑体",Font.BOLD,20));
g.setColor(Color.RED);
g.drawString(musicString,35,220);
g.setColor(Color.WHITE);
g.drawRect(35,200,85,25);
//暂停游戏
g.setFont(new Font("黑体",Font.BOLD,20));
g.setColor(Color.RED);
g.drawString(gameString,35,250);
g.setColor(Color.WHITE);
g.drawRect(35,230,85,25);
//退出游戏
g.setFont(new Font("黑体",Font.BOLD,20));
g.setColor(Color.RED);
g.drawString("退出游戏",35,280);
g.setColor(Color.WHITE);
g.drawRect(35,260,85,25);
}
public void setX(int startX){}
public void setY(int startY){}
public int getX() {
return 0;
}
public int getY() {
return 0;
}
//播放音乐
public void startMusic(){
if(!isMusic){
audio.loop();
musicString="暂停音乐";
isMusic=true;
}else{
audio.stop();
musicString="打开音乐";
isMusic=false;
}
}
//游戏停止
public void StartGame(){
if(!isGame){
gameString="暂停游戏";
isGame=true;
}else{
gameString="开始游戏";
isGame=false;
}
}
//返回游戏状态
public boolean gameState(){
return isGame;
}
}
/**
* 主界面类
* @author 梦想少年
* 创建队列控制所有的对象的移动和画图,同时实现键盘监听器控制战机的移动和发射炮弹
* 其他功能(1.开始界面2.游戏界面3.背景音乐和爆炸音乐)
*/
public class MainUI implements KeyListener,MouseListener{
//创建主队列
ArrayList<Entity> wholelist=new ArrayList<>();
public static void main(String[] args){
MainUI ui=new MainUI();
ui.showUI();
}
//创建飞机对象
MyFeiJi myfeiji=new MyFeiJi();
//创建背景对象
BackGroundPhotosMusic background=new BackGroundPhotosMusic();
//主界面
public void showUI(){
JFrame jframe=new JFrame("雷霆战机");
jframe.setSize(1920, 1080);
jframe.setLayout(new FlowLayout());
jframe.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jframe.setLocationRelativeTo(null);
jframe.addKeyListener(this);
jframe.addMouseListener(this);
jframe.setVisible(true);
//将背景对象加入主队列
background.startMusic();
background.StartGame();
wholelist.add(background);
//将玩家飞机加入队列,注意顺序,避免画图覆盖
myfeiji.setX(300);
myfeiji.setY(500);
myfeiji.setHP(100);
wholelist.add(myfeiji);
//开启清除线程
RemoveThread remove=new RemoveThread(wholelist);
remove.start();
//定时产生敌机&&&及它的子弹
Timer timer=new Timer();
timer.schedule(new TimerTask() {
public void run(){
//敌机
Diji diji1=new Diji();
wholelist.add(diji1);
//敌机子弹
for(int i=0;i<=30;i++){
DiJiBullet dijizidan1=new DiJiBullet();
//设置初始坐标
dijizidan1.setX(diji1.getX()-20);
dijizidan1.setY(diji1.getY()+45);
//子弹计数加1
diji1.addBulletCount();
wholelist.add(dijizidan1);
//控制子弹的数量
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(diji1.getBulletCount()==5){
break;
}
}
}
},1,1);
//双缓冲改善画面
Graphics g=jframe.getGraphics();
BufferedImage bufferedimage=new BufferedImage(jframe.getWidth(),
jframe.getHeight(), BufferedImage.TYPE_3BYTE_BGR);
Graphics bufferG=bufferedimage.getGraphics();
//不断遍历主队列并不断调用主方法,画出所有图像
while(true){
for(int i=0;i<wholelist.size();i++){
Entity whole=wholelist.get(i);
whole.draw(bufferG);
if(!myfeiji.isDefeat()&&background.gameState()){
whole.move();
}else if(myfeiji.getHP()<=0){
//游戏输赢
int result=JOptionPane.showConfirmDialog(jframe,""
+ "是否退出游戏?");
if(result==0){
System.exit(0);
}
}
}
//画出缓存图片
g.drawImage(bufferedimage,0,0,null);
try {
Thread.sleep(5);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public void keyPressed(KeyEvent e){
//获得键盘事件
int code=e.getKeyCode();
if(code==KeyEvent.VK_F){
//子弹1
MyFeiJiBullet myfeijibullet1=new MyFeiJiBullet();
//设置坐标
myfeijibullet1.setX(myfeiji.getX()+100);
myfeijibullet1.setY(myfeiji.getY()-10);
//设置子弹类型
myfeijibullet1.setBulletType(1);
//加入队列
wholelist.add(myfeijibullet1);
//子弹2
MyFeiJiBullet myfeijibullet2=new MyFeiJiBullet();
//设置坐标
myfeijibullet2.setX(myfeiji.getX()+100);
myfeijibullet2.setY(myfeiji.getY()+55);
//设置子弹类型
myfeijibullet2.setBulletType(1);
//加入队列
wholelist.add(myfeijibullet2);
}
//上下左右控制飞机移动
if(code==KeyEvent.VK_UP){
myfeiji.up=true;
}
if(code==KeyEvent.VK_DOWN){
myfeiji.down=true;
}
if(code==KeyEvent.VK_LEFT){
myfeiji.left=true;
}
if(code==KeyEvent.VK_RIGHT){
myfeiji.right=true;
}
}
public void keyTyped(KeyEvent e){}
public void keyReleased(KeyEvent e){
int code=e.getKeyCode();
if(code==KeyEvent.VK_UP){
myfeiji.up=false;
}
if(code==KeyEvent.VK_DOWN){
myfeiji.down=false;
}
if(code==KeyEvent.VK_LEFT){
myfeiji.left=false;
}
if(code==KeyEvent.VK_RIGHT){
myfeiji.right=false;
}
}
//游戏菜单相关设置
public void mouseClicked(MouseEvent e){}
public void mousePressed(MouseEvent e){
//获得点击的X和Y坐标
int x=e.getX();
int y=e.getY();
//音乐开停
if(x>=35&&x<=85&&y>=200&&y<=225){
background.startMusic();
//界面开停
}else if(x>=35&&x<=85&&y>=230&&y<=255){
background.StartGame();
}else if(x>=35&&x<=85&&y>=260&&y<=285){
System.exit(0);
}
}
public void mouseReleased(MouseEvent e){}
public void mouseEntered(MouseEvent e){}
public void mouseExited(MouseEvent e){}
}
/**
* 去除线程类
* @author 梦想少年
* 创建线程来实现对越界子弹敌机的去除,控制飞机的碰撞
*/
public class RemoveThread extends Thread{
//各个对象的队列
ArrayList<Entity> list;
//攻击后我机的加分判断
private boolean isShootDiJiBullet;
private boolean isShootDiJi;
//初始化
public RemoveThread(ArrayList<Entity> list) {
this.list=list;
}
public void run(){
while(true){
//休息一下
try {
Thread.sleep(2);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//清除所有的越界敌机、飞机子弹、敌机子弹
for(int i=0;i<list.size();i++){
Entity entity=list.get(i);
//清除敌机
if(entity instanceof Diji){
Diji diji=(Diji)entity;
if(diji.isOutBounds()){
list.remove(diji);
}
//清除我机子弹
}else if(entity instanceof MyFeiJiBullet){
MyFeiJiBullet myfeijibullet=(MyFeiJiBullet)entity;
if(myfeijibullet.isOutBounds()){
list.remove(myfeijibullet);
}
//清除敌机子弹
}else if(entity instanceof DiJiBullet){
DiJiBullet dijibullet=(DiJiBullet)entity;
if(dijibullet.isOutBounds()){
list.remove(dijibullet);
}
}
//------------------------------------------------------
}
//清除和处理碰撞
for(int i=0;i<list.size();i++){
Entity entity1=list.get(i);
for(int j=0;j<list.size();j++){
Entity entity2=list.get(j);
if(entity1 instanceof MyFeiJi){
MyFeiJi myfeiji=(MyFeiJi)entity1;
//设置一个加分的开关
if(isShootDiJi){
myfeiji.addDoubleScores();
isShootDiJi=false;
}else if(isShootDiJiBullet){
myfeiji.addScores();
isShootDiJiBullet=false;
}
//-------------------------------
if(entity2 instanceof DiJiBullet){
DiJiBullet dijibullet=(DiJiBullet)entity2;
if(myfeiji.isShootByDiJiBullet(dijibullet)){
list.remove(dijibullet);//清除敌机子弹
myfeiji.subBlood();//我机掉血
}
}//以上是敌机子弹范围
else if(entity2 instanceof Diji){
Diji diji=(Diji)entity2;
if(myfeiji.isShootByDiJi(diji)){
diji.Blast();//敌机爆炸
diji.startMusic();//播放爆炸音乐
list.remove(diji);//去除敌机
myfeiji.subDoubleBlood();//我机掉多倍的血量
}
}//以上是敌机范围
}//我的飞机范围
else if(entity1 instanceof MyFeiJiBullet){
MyFeiJiBullet myfeijibullet=(MyFeiJiBullet)entity1;
if(entity2 instanceof DiJiBullet){
DiJiBullet dijibullet=(DiJiBullet)entity2;
if(myfeijibullet.isShootDiJiBullet(dijibullet)&&
i<list.size()&&j<list.size()){
list.remove(myfeijibullet);//去除飞机子弹
list.remove(dijibullet);//去除敌机子弹
isShootDiJiBullet=true;//打开加分开关
}
}else if(entity2 instanceof Diji){
Diji diji=(Diji)entity2;
if(myfeijibullet.isShootDiJi(diji)&&
i<list.size()&&j<list.size()){
list.remove(myfeijibullet);//去除我机子弹
diji.Blast();//打开爆炸图片
diji.startMusic();//打开爆炸音乐
list.remove(diji);//去除敌机
isShootDiJi=true;//打开加分开关
}
}
}//我的飞机子弹范围
}
//-------------------------
}
//------------------------------------------------------
}
}
}
先来看看效果吧!!!!
整体效果还是不错的,代码的可拓展性还是比较好的,大家如果想要添加一些别的功能:大boss、奖赏、添加技能等等。但是在编写的时候我遇到了一些问题:
1.清除线程存在错位和碰撞检测不及时的问题
2.不能很好地解决敌机爆炸和爆炸背景音乐播放的延时问题
大家如果有问题我们可以相互讨论啊ヾ(≧▽≦*)o,最后记得留下一个赞哦!!!