老Java程序员花一天时间写了个飞机大战,很舒服![通俗易懂]

引言:前两天我发现CSDN上有两篇飞机大战的文章异常火爆,各种指标都很高(阅读、点赞、评论、收藏等),但都是python写的,竟然不是我大Java,说实话作为老java选手,我心里是有那么一些失落的,难道我大java打飞机不行?就算大java打飞机不行,那我用单身30年的打飞机手速,我肯定行(反正我的代码我做主,就是玩!),于是我决定一展伸手,用java写了一个飞机大战。我就问你们我打飞机行不行,我媳妇都说行,你们呢?欢迎我亲爱的大Java选手们点赞+评论+收藏!给我冲、冲、冲。。。/**游戏窗

大家好,又见面了,我是你们的朋友全栈君。

老Java程序员花1天时间做了个飞机大战

热门专栏推荐

【1】Java小游戏(俄罗斯方块、飞机大战、植物大战僵尸等)
【2】JavaWeb项目实战(图书管理、在线考试、宿舍管理等)
【3】JavaScript精彩实例(飞机大战、贪吃蛇、验证码等)
【4】Java小白入门200例
【5】从零学Java、趣学Java
【6】Idea从零到精通

系列目录

1. Java俄罗斯方块
2. 老Java程序员花2天写了个连连看
3. 老Java程序员花一天时间写了个飞机大战
4. Java五子棋人机版
5. Java植物大战僵尸
6. Java消消乐(天天爱消除)
7. Java贪吃蛇小游戏
8. Java扫雷小游戏
9. Java坦克大战

引言:

前两天我发现CSDN上有两篇飞机大战的文章异常火爆,各种指标都很高(阅读、点赞、评论、收藏等),但都是python写的,竟然不是我大Java,说实话作为老java选手,我心里是有那么一些失落的,难道我大java打飞机不行?就算大java打飞机不行,那我用单身30年的打飞机手速,我肯定行(反正我的代码我做主,就是玩!),于是我决定一展伸手,用java写了一个飞机大战。我就问你们我打飞机行不行,我媳妇都说行,你们呢?欢迎我亲爱的大Java选手们 点赞+评论+收藏!给我冲、冲、冲。。。

代码实现

创建窗口

首先创建一个游戏窗体类GameFrame,继承至JFrame,用来显示在屏幕上(window的对象),每个游戏都有一个窗口,设置好窗口标题、尺寸、布局等就可以。

/* * 游戏窗体类 */
public class GameFrame extends JFrame { 
   
	
	public GameFrame() { 
   
		setTitle("飞机大战");//设置标题
		setSize(526, 685);//设定尺寸
		setLayout(new BorderLayout());
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//点击关闭按钮是关闭程序
        setLocationRelativeTo(null);   //设置居中
    	setResizable(false); //不允许修改界面大小
	}
}

创建面板容器GamePanel继承至JPanel

package main;

import java.awt.Graphics;
import javax.swing.JFrame;
import javax.swing.JPanel;
/* * 画布类 */
public class GamePanel extends JPanel{ 
   
	GamePanel gamePanel=this;
	private JFrame mainFrame=null;
	//构造里面初始化相关参数
	public GamePanel(JFrame frame){ 
   
		this.setLayout(null);
		mainFrame = frame;
		
		mainFrame.setVisible(true);
	}
	
	@Override
	public void paint(Graphics g) { 
   
		
	}
}

再创建一个Main类,来启动这个窗口,用来启动。

package main;
public class Main { 
   
	//主类
	public static void main(String[] args) { 
   
		GameFrame frame = new GameFrame();
		GamePanel panel = new GamePanel(frame);
		frame.add(panel);
		frame.setVisible(true);//设定显示
	}
}

右键执行这个Main类,窗口建出来了
​​窗口

创建菜单及菜单选项

创建菜单

private void  initMenu(){ 

// 创建菜单及菜单选项
jmb = new JMenuBar();
JMenu jm1 = new JMenu("游戏");
jm1.setFont(new Font("微软雅黑", Font.BOLD, 15));// 设置菜单显示的字体
JMenu jm2 = new JMenu("帮助");
jm2.setFont(new Font("微软雅黑", Font.BOLD, 15));// 设置菜单显示的字体
JMenuItem jmi1 = new JMenuItem("开始新游戏");
JMenuItem jmi2 = new JMenuItem("退出");
jmi1.setFont(new Font("微软雅黑", Font.BOLD, 15));
jmi2.setFont(new Font("微软雅黑", Font.BOLD, 15));
JMenuItem jmi3 = new JMenuItem("操作说明");
jmi3.setFont(new Font("微软雅黑", Font.BOLD, 15));
JMenuItem jmi4 = new JMenuItem("胜利条件");
jmi4.setFont(new Font("微软雅黑", Font.BOLD, 15));
jm1.add(jmi1);
jm1.add(jmi2);
jm2.add(jmi3);
jm2.add(jmi4);
jmb.add(jm1);
jmb.add(jm2);
mainFrame.setJMenuBar(jmb);// 菜单Bar放到JFrame上
jmi1.addActionListener(this);
jmi1.setActionCommand("Restart");
jmi2.addActionListener(this);
jmi2.setActionCommand("Exit");
jmi3.addActionListener(this);
jmi3.setActionCommand("help");
jmi4.addActionListener(this);
jmi4.setActionCommand("win");
}

实现ActionListener并重写方法actionPerformed
在这里插入图片描述
actionPerformed方法的实现

@Override
public void actionPerformed(ActionEvent e) { 

String command = e.getActionCommand();
UIManager.put("OptionPane.buttonFont", new FontUIResource(new Font("宋体", Font.ITALIC, 18)));
UIManager.put("OptionPane.messageFont", new FontUIResource(new Font("宋体", Font.ITALIC, 18)));
if ("Exit".equals(command)) { 

Object[] options = { 
 "确定", "取消" };
int response = JOptionPane.showOptionDialog(this, "您确认要退出吗", "",
JOptionPane.YES_OPTION, JOptionPane.QUESTION_MESSAGE, null,
options, options[0]);
if (response == 0) { 

System.exit(0);
} 
}else if("Restart".equals(command)){ 

if(startFlag){ 

Object[] options = { 
 "确定", "取消" };
int response = JOptionPane.showOptionDialog(this, "游戏中,您确认要重新开始吗", "",
JOptionPane.YES_OPTION, JOptionPane.QUESTION_MESSAGE, null,
options, options[0]);
if (response == 0) { 

//需要先结束游戏
realGameEnd(1);
restart();
} 
}else{ 

restart();
}
}else if("help".equals(command)){ 

JOptionPane.showMessageDialog(null, "游戏开始后,要先动鼠标到飞机处,触发移动效果,然后飞机就会跟随鼠标移动!",
"提示!", JOptionPane.INFORMATION_MESSAGE);
}else if("win".equals(command)){ 

JOptionPane.showMessageDialog(null, "得分1000,获得胜利!",
"提示!", JOptionPane.INFORMATION_MESSAGE);
}
}

在这里插入图片描述

创建背景

在GamePanel类中重写paint方法,绘制背景图即可

//绘图方法
@Override
public void paint(Graphics g) { 

gameHeight = this.getHeight();
gameWidth = this.getWidth();
//绘制背景
g.drawImage((BufferedImage)imageMap.get("bg"), 0, -150, null);
}

在这里插入图片描述

开启主线程

主线程,用来重绘页面,重绘全部交给主线程,主线程调用 repaint方法就行,要产生动画就要靠这个repaint。

//刷新线程,用来重新绘制页面
private class RefreshThread implements Runnable { 

@Override
public void run() { 

while (startFlag) { 

repaint();
try { 

Thread.sleep(50);
} catch (InterruptedException e) { 

e.printStackTrace();
}
}
}
}

在GamePanel的构造里面启动这个主线程
在这里插入图片描述
有了这个主线程刷新,待会我们更新飞机的位置,飞机就会移动,不需要另外的代码去调用repaint方法了(这是我的做法,仅供参考)。

创建我方飞机

创建MyPlane类,属性有坐标x、y,宽高、图片、是否存活、是否可以移动等;方法主要有绘制、移动、射击等。

public class MyPlane { 

private int x = 0;
private int y = 0;
private int width = 0;
private int height = 0;
private BufferedImage image = null;
private GamePanel panel=null;
private HashMap imageMap=null;
private boolean alive=true;
private boolean canMove=false;
private int key=1;
private HashMap boomImageMap=null;
private boolean hitFlag=false;//正在碰撞
public MyPlane(int x,int y,int width,int height,GamePanel panel) { 

this.x=x;
this.y=y;
this.width=width;
this.height=height;
this.panel=panel;
this.imageMap=panel.imageMap;
this.image=(BufferedImage)imageMap.get("myplane1");
this.boomImageMap=panel.mypalneBoomImageMap;
}
//绘制
public void draw(Graphics g) { 

g.drawImage(image, x, y, width,height, null);
}
}

创建(这里只是创建好了飞机对象,需要绘制)

//创建自己飞机
private void initMyPlane() { 

myPlane = new MyPlane(200, 530, 132, 86, this);
}

在paint方法中绘制

//绘图方法
@Override
public void paint(Graphics g) { 

gameHeight = this.getHeight();
gameWidth = this.getWidth();
//绘制背景
g.drawImage((BufferedImage)imageMap.get("bg"), 0, -150, null);
//绘制飞机
if(myPlane!=null){ 

myPlane.draw(g);
}
}

在这里插入图片描述

鼠标事件监听

加入监听是为了让飞机跟随鼠标移动,我这里定的规则是第一次鼠标必须移动到飞机上,然后飞机才会跟随。

代码里面用一个属性canMove来控制,默认是false,只有鼠标第一次移入到飞机上时,这个属性设置为true,然后就可以跟随鼠标移动了。

//鼠标事件的创建
private void createMouseListener() { 

MouseAdapter mouseAdapter = new MouseAdapter() { 

@Override
public void mouseMoved(MouseEvent e) { 

int x = e.getX();
int y = e.getY();
if(myPlane==null) return ;
//飞机第一次是不允许移动的,只有飞机的canMove为true才去跟随
if(myPlane.isCanMove()){ 

myPlane.move(x,y);
return;
}
//判断鼠标的移入,如果移动到飞机上则canMove设置为true
if(myPlane.isPoint(x,y)){ 

myPlane.setCanMove(true);
}
}
};
addMouseMotionListener(mouseAdapter);
addMouseListener(mouseAdapter);
}

来实现一下MyPlane的move方法,这里处理了边界,保证飞机不出界,同时保证鼠标在飞机的中间位置

//飞机跟随鼠标移动
public void move(int x,int y) { 

//判断范围,当横向移动在窗口范围内
if(x-width/2>=0 && x<=panel.getWidth()-width/2){ 

this.x=x-width/2;
}
//判断范围,当纵向移动在窗口范围内
if(y-height/2>=0 && y<=panel.getHeight()-height/2){ 

this.y=y-height/2;
}
}

在这里插入图片描述

创建子弹类

属性也就是坐标、宽高这些,给子弹加入移动方法

//移动
void move(){ 

new Thread(new Runnable() { 

@Override
public void run() { 

while(alive){ 

y-=speed;
if(y<=0){ 

clear();
}
try { 

Thread.sleep(50);
} catch (InterruptedException e) { 

e.printStackTrace();
}
}
}
}).start();
}

飞机类加入射击方法,200毫秒创建一发子弹

//射击
void shoot() { 

new Thread(new Runnable() { 

@Override
public void run() { 

while(alive){ 

//创建子弹
createBullet();
try { 

Thread.sleep(200);
} catch (InterruptedException e) { 

e.printStackTrace();
}
}
}
private void createBullet() { 

Bullet bullet = new Bullet(x+width/2-10, y, 20, 30, panel);
panel.bulletList.add(bullet);
new MusicPlayer("/music/shoot.wav").play();
}
}).start();
}

别忘记在paint方法里面绘制子弹出来

//绘制子弹
Bullet bullet=null;
for (int i = 0; i < bulletList.size(); i++) { 

bullet = (Bullet)bulletList.get(i);
bullet.draw(g);
}

在这里插入图片描述

创建敌机

创建抽象类Plane

package main;
import java.awt.Graphics;
public abstract class Plane { 

public abstract void move();
public abstract void draw(Graphics g);
public abstract void boom();
public abstract void clear();
abstract int getX();
abstract int getY();
abstract int getWidth();
abstract int getHeight();
}

创建敌机子类

有个特殊一点的地方: 因为有4种敌机,这里随机1、2、3、4这4个数字作为属性index,然后根据这个index来展现不同的飞机图片,当然也可以通过这个index来设置敌机不同的移动速度,但是我为了偷懒,就全部一样的移动速度,嘿嘿。
移动就是开启线程让y坐标增加,没什么好讲的,这里加一个飞机碰撞,就是当敌机跟我方飞机如何判断碰撞的问题。

撞机分析(敌机与我机的撞机)
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
从上面几个图可看出什么?因为图片是方形的,他们的4个顶点一定至少有一个在对方的范围内。再看一下从左边撞击的图:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
从上图看到也是这样,其他两个方向的也是一样的道理,为了稳点我还加了一种情况:

1.判断敌机的4个点是否在飞机范围内,如果有则表示碰撞了。
2.如果1不成立,则反过来,判断我机的4个点是否在敌机的范围内,如果是表示碰撞了。

//判断飞机与子弹是否碰撞
private boolean isPoint(MyPlane plane) { 

/* * * 两种情况 * 1.需要判断敌机的4个点是否在飞机范围内,如果有则表示碰撞了 * 2.如果步骤1不成立,则反过来,判断我机的4个点是否在敌机的范围内,如果是标志碰撞了 */
//方式1
//左上角
int x1 = x;
int y1 = y;
//右上角
int x2 = x+width;
int y2 = y;
//右下角
int x3 = x+width;
int y3 = y+height;
//左下角
int x4 = x;
int y4 = y+height;
//只要有一个点在范围内,则判断为碰撞
if(comparePointMyPlane(x1,y1,plane)|| comparePointMyPlane(x2,y2,plane)||comparePointMyPlane(x3,y3,plane)||comparePointMyPlane(x4,y4,plane) ){ 

return true;
}
//方式1没成立则用方式2判断
//方式2
x1 = plane.getX();
y1 = plane.getY();
//右上角
x2 = plane.getX()+plane.getWidth();
y2 = plane.getY();
//右下角
x3 = plane.getX()+plane.getWidth();
y3 =plane.getY()+plane.getHeight();
//左下角
x4 = plane.getX();
y4 = plane.getY()+plane.getHeight();
if(comparePoint(x1,y1)|| comparePoint(x2,y2)||comparePoint(x3,y3)||comparePoint(x4,y4) ){ 

return true;
}
return false;
}
//用敌机的坐标来判断
private boolean comparePointMyPlane(int x,int y,MyPlane plane){ 

//大于左上角,小于右下角的坐标则肯定在范围内
if(x>plane.getX() && y >plane.getY()
&& x<plane.getX()+plane.getWidth() && y <plane.getY()+plane.getHeight()	){ 

return  true;
}
return false;
}
//用我机的坐标来判断
private boolean comparePoint(int x,int y){ 

//大于左上角,小于右下角的坐标则肯定在范围内
if(x>this.x && y >this.y
&& x<this.x+this.width && y <this.y+this.height){ 

return  true;
}
return false;
}

测试一下效果
在这里插入图片描述

忘记说击中敌机的了(原理跟刚才差不多,代码直接放了)

//判断击中敌机
protected void hitEnemy() { 

EnemyPlane enemyPlane=null;
List enemys = panel.enemyList;
for (int i = 0; i < enemys.size(); i++) { 

try { 

enemyPlane = (EnemyPlane)enemys.get(i);
} catch (Exception e) { 

}
if(enemyPlane==null) continue;
if(this.isPoint(enemyPlane)){ 

panel.curCount+=enemyPlane.getCount();
//删除当前子弹
clear();
//飞机爆炸
enemyPlane.boom();
if(panel.curCount>=panel.totalCount){ 

panel.myPlane.setCanMove(false);
panel.gameWin();
}
}
}
}
//判断飞机与子弹是否碰撞
private boolean isPoint(EnemyPlane plane) { 

//因为子弹比飞机小,所以只需要判断子弹的4个点是否在飞机范围内,如果有则表示碰撞了
//左上角
int x1 = x;
int y1 = y;
//右上角
int x2 = x+width;
int y2 = y;
//右下角
int x3 = x+width;
int y3 = y+height;
//左下角
int x4 = x;
int y4 = y+height;
//只要有一个点在范围内,则判断为碰撞
if(comparePoint(x1,y1,plane)|| comparePoint(x2,y2,plane)||comparePoint(x3,y3,plane)||comparePoint(x4,y4,plane) ){ 

return true;
}
return false;
}
private boolean comparePoint(int x,int y,EnemyPlane plane){ 

//大于左上角,小于右下角的坐标则肯定在范围内
if(x>plane.getX() && y >plane.getY()
&& x<plane.getX()+plane.getWidth() && y <plane.getY()+plane.getHeight()	){ 

return  true;
}
return false;
}

最后加上计分的、胜利、失败等提示就完成了!

看到这里的大佬,动动发财的小手 点赞 + 回复 + 收藏,能【 关注 】一波就更好了。

代码获取方式
订阅我的专栏《Java游戏大全》后,可以查看专栏内所有的文章,并且联系博主免费获取其中1-3份你心仪的源代码,专栏的文章都是上过csdn热榜的,值得信赖!专栏内目前有[13]篇实例,未来2个月内专栏会更新到15篇以上,一般2周一更,了解一下我的专栏

热门专栏推荐

【1】Java小游戏(俄罗斯方块、飞机大战、植物大战僵尸等)
【2】JavaWeb项目实战(图书管理、在线考试、宿舍管理等)
【3】JavaScript精彩实例(飞机大战、贪吃蛇、验证码等)
【4】Java小白入门200例
【5】从零学Java、趣学Java
【6】Idea从零到精通

★ 更多精彩

1. Java俄罗斯方块
2. 老Java程序员花2天写了个连连看
3. 老Java程序员花一天时间写了个飞机大战
4. Java五子棋人机版
5. Java植物大战僵尸
6. Java消消乐(天天爱消除)
7. Java贪吃蛇小游戏
8. Java扫雷小游戏
9. Java坦克大战

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/126859.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号