老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);}@Overridepublic 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 {@Overridepublic 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() {@Overridepublic 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设置为trueif(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() {@Overridepublic 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() {@Overridepublic 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判断//方式2x1 = 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坦克大战

老Java程序员花一天时间写了个飞机大战,很舒服!相关推荐

  1. java开发有不加班的吗_千万不要相信程序员在加班时间写的代码!

    作为一个最底层的程序员,我先记录一些只有底层程序员才会知道的事情.如果多年后,我违背自己进入这个行业的初心,走上管理岗位,也能回想起一些禁忌,避免一些错误. 其中最重要的就是这条:不要相信一个程序员在 ...

  2. 老Java程序员谈谈swing要不要学

    老Java程序员谈谈Swing要不要学 老java程序员谈谈要不要学swing Swing是什么? 1)AWT 在早期JDK1.0发布时,Sun公司就为GUI开发提供了一套基础类库,这套类库被称为AW ...

  3. 千万不要相信程序员在加班时间写的代码!

    其中最重要的就是这条:不要相信一个程序员在加班时间写出来的代码. (软件工程的学说表明,连正常时间好好写的代码,也不要太相信.不过这不是本文的重点,略过不提.) (不懂代码的人,看到本文中的Java代 ...

  4. 不要相信程序员在加班时间写的代码

    http://www.cocoachina.com/programmer/20161215/18359.html 不要相信程序员在加班时间写的代码 2016-12-15 00:01 编辑: suili ...

  5. (转载)不要相信程序员在加班时间写的代码

    作为一个最底层的程序员,我先记录一些只有底层程序员才会知道的事情.如果多年后,我违背自己进入这个行业的初心,走上管理岗位,也能回想起一些禁忌,避免一些错误. 其中最重要的就是这条:不要相信一个程序员在 ...

  6. 面试IT公司的时候,Java程序员的简历应该写多少个项目经验比较合适?

    往期精选 ●  架构师高并发高性能分布式教程(4000G) ●  39阶段精品云计算大数据实战视频教程 ●  互联网技术干货视频教程大全[菜单为准] ●  2017年8月最新Intellij IDEA ...

  7. 老java程序员告诉你要不要选择外包,外包公司的好处你知道吗

    给一些即将进入java行业就业的大学生或者是转行人员一些建议,找工作时是找工资高的外包公司还是工资低的小公司,外包公司的好处你知道吗,本人在b站录制的小视频,你想要的答案全在里面,更多精彩欢迎关注我. ...

  8. Java程序员用下班时间给学弟做了一个在线考试系统

    文章目录 前言 一.框架介绍 1.1.Spring 1.2.SpringMVC 1.3.MyBatis 1.4.SSM整合 二.开发环境 三.系统功能 3.1 考试界面登陆 3.2 选择试题 3.3 ...

  9. 五月的仓颉大神写的 三年java程序员面试感悟 分享给大家

    稀里糊涂的敲了两年代码,应该都算不上敲.因为某种原因迫使要换工作,发现知识及其不牢固并且不知道怎么有条理的学习,所以就找些类似这样的面试博文来看看学些啥.... 原文地址http://www.cnbl ...

  10. Java程序员已经饱和了?这是个老话题了

    这个是老话题了,Java程序员远远没有饱和,年薪10万+是很正常的一个薪酬水平.现在Java程序员挺多的,但是职位需求量更大,特别是能干实干或优秀的程序员是相当缺乏的,没存在饱和一说. 从经验来说,如 ...

最新文章

  1. getOutputStream() has already been called for this response
  2. [异常解决] android studio检测不到手机的解决办法——ADB驱动自己安装
  3. 二、计算机视觉与卷积神经网络
  4. 【C语言简单说】二:第一个C语言程序详解(3)
  5. win10 hyper-v 虚拟机ping不通宿主机问题
  6. 可持久化数据结构、可并堆
  7. android中prop配置参数名,【01-19 技术】安卓系统优化Build.prop 系统参数属性详解...
  8. express 项目文件目录说明及功能描述
  9. MongoDB Replica Set 选举过程
  10. java三星题之_JAVA三星题之playing a TicTacToe game
  11. 来选座 抢座脚本 php,图书馆预约占座管理系统 图书馆预约占座 app
  12. 织梦采集工具-织梦CMS采集教程
  13. Elasticsearch方案管理 Spring Boot 和 Nginx 日志
  14. 分分钟穿透手机,实现远程控制
  15. 网页嵌入媒体播放器代码 选择自 murky 的 Blog
  16. 一个优秀的国产ITSM软件应该有哪些特性?
  17. Minio集群搭建之查看元数据信息
  18. 22-3-16Java冒泡排序
  19. 【找规律】codeforces 710 F
  20. Microsoft Teams Meeting你是认真的吗?

热门文章

  1. rose服务器系统,Rose HA for windows
  2. python 知乎美女_听说知乎大神用python爬取高颜值美女,是怎么操作的?
  3. Adobe Acrobat Pro 2017安装
  4. webots离线网页无法跳转
  5. 存储过程和存储函数的区别
  6. php libiconv close_PHP 5.2.17 编译安装时出现 undefined reference to `libiconv_open’ 错误的解决方法...
  7. 使用C++实现克拉默法则(Cramer‘s law)
  8. 修改hosts文件,解决端口占用方法
  9. js读取json文件
  10. 计算机无法访问桌面,桌面无法显示_电脑桌面显示:无法访问,你可能没有权限使用网络......