老Java程序员花一天时间写了个飞机大战,媳妇直呼打飞机高手

释放双眼,带上耳机,听听看~!
https://live.csdn.net/v/embed/164869

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

引言:

前两天我发现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类,窗口建出来了
​​老Java程序员花一天时间写了个飞机大战,媳妇直呼打飞机高手

创建菜单及菜单选项

创建菜单

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
老Java程序员花一天时间写了个飞机大战,媳妇直呼打飞机高手
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); 	} } 
老Java程序员花一天时间写了个飞机大战,媳妇直呼打飞机高手

创建背景

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

//绘图方法 @Override public void paint(Graphics g) { 	gameHeight = this.getHeight(); 	gameWidth = this.getWidth(); 	//绘制背景 	g.drawImage((BufferedImage)imageMap.get("bg"), 0, -150, null); } 
老Java程序员花一天时间写了个飞机大战,媳妇直呼打飞机高手

开启主线程

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

//刷新线程,用来重新绘制页面 private class RefreshThread implements Runnable { 	@Override 	public void run() { 		while (startFlag) { 			repaint(); 			try { 				Thread.sleep(50); 			} catch (InterruptedException e) { 				e.printStackTrace(); 			} 		} 	} } 

在GamePanel的构造里面启动这个主线程
老Java程序员花一天时间写了个飞机大战,媳妇直呼打飞机高手
有了这个主线程刷新,待会我们更新飞机的位置,飞机就会移动,不需要另外的代码去调用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); 	} } 
老Java程序员花一天时间写了个飞机大战,媳妇直呼打飞机高手

鼠标事件监听

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

代码里面用一个属性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; 	} } 
老Java程序员花一天时间写了个飞机大战,媳妇直呼打飞机高手

创建子弹类

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

//移动 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); } 
老Java程序员花一天时间写了个飞机大战,媳妇直呼打飞机高手

创建敌机

创建抽象类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坐标增加,没什么好讲的,这里加一个飞机碰撞,就是当敌机跟我方飞机如何判断碰撞的问题。

撞机分析(敌机与我机的撞机)
老Java程序员花一天时间写了个飞机大战,媳妇直呼打飞机高手
老Java程序员花一天时间写了个飞机大战,媳妇直呼打飞机高手
老Java程序员花一天时间写了个飞机大战,媳妇直呼打飞机高手
从上面几个图可看出什么?因为图片是方形的,他们的4个顶点一定至少有一个在对方的范围内。再看一下从左边撞击的图:
老Java程序员花一天时间写了个飞机大战,媳妇直呼打飞机高手
老Java程序员花一天时间写了个飞机大战,媳妇直呼打飞机高手
老Java程序员花一天时间写了个飞机大战,媳妇直呼打飞机高手
从上图看到也是这样,其他两个方向的也是一样的道理,为了稳点我还加了一种情况:

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; } 

测试一下效果
老Java程序员花一天时间写了个飞机大战,媳妇直呼打飞机高手

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

//判断击中敌机 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; } 

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

看到这里的大佬,动动发财的小手 点赞 + 回复 + 收藏,能【 关注 】一波就更好了。
需要源码的 留言 或者 私信,我发给你!

为TA充电
共{{data.count}}人
人已赞赏
代码人生

有个码龄 10 年的程序员跟我说:“他编程从来不用鼠标”,我说:

2021-5-27 22:49:30

代码人生

⭐51W+ 的终端命令行工具,每个都值得拥有

2021-5-28 12:12:36

0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧
个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索