Java Swing扫雷游戏demo分享
好多年前写过简略的扫雷游戏,模拟windows的。
后来由于多次搬迁环境,弄丢了,遗憾不已啊。
于是趁着这两年还在编程的道路上,趁热再次编写了一次,同时也扩展了功能,更接近windows的扫雷。
此次重写是用java swing实现的(eclipse开发),考虑到各位看客可能大部分是android岗位,于是我着重注意了功能结构化的处理,使游戏核心算法与UI分离,使用回调交互,便于迁移到android环境。
本人对swing不是很熟练,一直以来用swing做项目的人很少,学习资料也少,所有的这些都是在网上现查现用的。各位看客不会没关系,都是jdk里面的api,跟android、winform都很像。
本文思路分以下几步骤讲解:
1、效果图和基本原理。
2、核心算法。
3、程序结构和部分重要函数。
4、demo源码下载。
一、效果图:
瞧瞧效果图,顶部是一个图片按钮,使用鼠标事件处理了按下、释放、点击时的图片效果。
中间部分用的是swing的GridLayout布局,跟android的GridLayout一样,除了部分用法不一样。
在某个格子上点击左键时,如果是雷,则输了。同时翻开所有的格子。
在某个格子上点击右键时,会标记为旗子;再点右键,变成问号,再点又还原。
已经点开了的格子上,点击右键无效。
如果点击的某个格子是空白的,则会递归摊开他周边所有不是雷的格子。
游戏结束时,点击顶部的小黄脸按钮,重新开始游戏。
以上部分,是主要功能描述。
二、核心算法:
其实啊,就是两个二维数组的对应关系,界面格子二维数组 <==> 程序后台格子状态二维数组。
1、先制造二维数组,然后随机生成一定数量的随机数(认为是雷,赋给对应的数组位置),要求在二维数组范围内。后台数据二维数组的每个元素,使用了一个对象,需要保存当前格子的好几种状态。
private void initButtons() {isClickComplete = true;if (buttonArr == null) {buttonArr = new JButton[mRowNums][mColumnNums];}for (int i = 0; i < mRowNums; i++) {for (int j = 0; j < mColumnNums; j++) {if (buttonArr[i][j] == null) {buttonArr[i][j] = new JButton();}setButtonImage(buttonArr[i][j], MineType.MINE_STATUS_BLANK);}}
}
JButton的二维数组,添加到GridLayout布局中。
// 初始化所有格子
private void resetOrCreateGrids(int rowNums, int columnNums) {if (beanArr == null) {beanArr = new MineBean[rowNums][columnNums];}for (int i = 0; i < beanArr.length; i++) {for (int j = 0; j < beanArr[i].length; j++) {if (beanArr[i][j] == null) {beanArr[i][j] = new MineBean();}beanArr[i][j].reset();}}
}
public class MineBean {public static int MINE_VALUE = 9;// 是雷子的格子值private boolean isClickOpen = false;// 是否左键点开了格子private int mineCount = 0;// 周围雷的个数标记值(如果是9,则为雷)private int imageStatus = MineType.MINE_STATUS_BLANK;// 未点开图片状态...
}
后台数据服务二维数组,每个元素使用一个类来存储数据。
public class MineType {public static final int MINE_STATUS_OPEN_0 = 0;// 周围8个格子中没有雷public static final int MINE_STATUS_OPEN_1 = 1;// 周围8个格子中有1个雷public static final int MINE_STATUS_OPEN_2 = 2;// 周围8个格子中有2个雷public static final int MINE_STATUS_OPEN_3 = 3;// 周围8个格子中有3个雷public static final int MINE_STATUS_OPEN_4 = 4;// 周围8个格子中有4个雷public static final int MINE_STATUS_OPEN_5 = 5;// 周围8个格子中有5个雷public static final int MINE_STATUS_OPEN_6 = 6;// 周围8个格子中有6个雷public static final int MINE_STATUS_OPEN_7 = 7;// 周围8个格子中有7个雷public static final int MINE_STATUS_OPEN_8 = 8;// 周围8个格子中有8个雷public static final int MINE_STATUS_OPEN_9 = 9;// 周围8个格子中有9个雷public static final int MINE_STATUS_BLANK = 10;// 默认格子图片public static final int MINE_STATUS_FLAG = 11;// 格子标记为旗子public static final int MINE_STATUS_UNKNOW = 12;// 格子标记为问号public static final int MINE_STATUS_MINE_CLICK = 13;// 点击了雷子时的图片public static final int MINE_STATUS_DEAD = 14;// 失败时,顶部按钮图片public static final int MINE_STATUS_MILE = 15;// 开始时,顶部按钮图片public static final int MINE_STATUS_WAIT = 16;// 等待时,顶部按钮图片public static final int MINE_STATUS_WIN = 17;// 胜利时,顶部按钮图片public static final int MINE_STATUS_LOGO = 18;// logo标记
}
格子需要显示的图片状态。有左键点击后需要显示的状态,有右键点击有需要显示的状态。
private ArrayList<Point> getAroundGrids(int i, int j) {if (beanArr == null) {return null;}// 取当前格子周围的8个点Point point1 = new Point((i - 1), (j - 1));Point point2 = new Point((i - 1), (j));Point point3 = new Point((i - 1), (j + 1));Point point4 = new Point((i), (j - 1));Point point5 = new Point((i), (j + 1));Point point6 = new Point((i + 1), (j - 1));Point point7 = new Point((i + 1), (j));Point point8 = new Point((i + 1), (j + 1));ArrayList<Point> aroundList = new ArrayList<>();aroundList.add(point1);aroundList.add(point2);aroundList.add(point3);aroundList.add(point4);aroundList.add(point5);aroundList.add(point6);aroundList.add(point7);aroundList.add(point8);for (int k = 0; k < aroundList.size(); k++) {Point pointTemp = aroundList.get(k);if (pointTemp.x < 0 || pointTemp.x >= beanArr.length || pointTemp.y < 0|| pointTemp.y >= beanArr[0].length) {// 越界aroundList.remove(k);k--;}}return aroundList;
}
这个方法是获取当前位置的周围8个格子算法。需要注意的是,靠边的格子再获取周围8个格子时,会包含越界的。在加入到集合中时,需要过滤越界的数据。
然后就不用再考虑[左上角、右上角、左下角、右下角,左边、上边、右边、下边、内部]等多种情况了。
2、循环后台数据二维数组,每循环一步,找到它周围的8个格子(过滤掉不在UI范围内的),统计雷子个数,然后给当前这个格子赋值(周围雷子数量值)。
3、最核心的算法:点一个空白格子时,会摊开一大片。这个其实很简单,每次点击一个格子时,显示对应的UI的同时,继续找到它周边的8个格子,然后循环递归调用当前函数。
public void leftClick(int i, int j) {if (beanArr == null || this.callBack == null || this.isGameOver) {return;}MineBean mineBean = getMineBean(i, j);if (mineBean == null) {return;}if (mineBean.isClickOpen()) {return;}mineBean.setClickOpen(true);if (unOpenMines < 0) {unOpenMines = 0;}unOpenMines--;if (mineBean.isMineNow()) {isGameOver = true;gameOver(i, j);return;}if (gameStartTime <= 0) {gameStartTime = System.currentTimeMillis();}if (this.callBack != null) {this.callBack.onLeftClick(mineBean, i, j);}checkWin();if (mineBean.getMineCount() == MineType.MINE_STATUS_OPEN_0) {// 递归摊开一片recursionAround(i, j);}
}private void recursionAround(int i, int j) {ArrayList<Point> list = getAroundGrids(i, j);for (int k = 0; k < list.size(); k++) {Point tempPoint = list.get(k);if (tempPoint == null) {continue;}MineBean mineBean = getMineBean(tempPoint.x, tempPoint.y);if (mineBean == null) {continue;}if (mineBean.isMineNow()) {continue;}if (mineBean.isClickOpen()) {continue;}leftClick(tempPoint.x, tempPoint.y);}
}
上面代码就是点击某个格子后,显示UI,同时处理递归摊开一片的算法。
4、UI点击格子时,提供横竖坐标,传递给算法工具类对象处理。算法函数中找到对应的数据位置,判断情况再回调给UI显示。避免UI与算法函数耦合度太高,难以移植。
public interface CallBack {void onInit();void onWin(long time);// 胜利void onGameOver();// 失败void onLeftClick(MineBean mineBean, int i, int j);void onRightClick(MineBean mineBean, int i, int j);
}
以上部分是核心算法描述。
三、程序结构和部分重要函数。
// 初始化随机雷子
private void makeRandomMines() {if (beanArr == null) {return;}int nowMines = 0;while (nowMines < mMineCount) {int i = random.nextInt(beanArr.length);int j = random.nextInt(beanArr[0].length);MineBean mineBean = beanArr[i][j];if (!mineBean.isMineNow()) {mineBean.setMineNow();nowMines++;}}
}// 计算格子周围雷子状态
private void initGridAroundStatus() {if (beanArr == null) {return;}for (int i = 0; i < beanArr.length; i++) {for (int j = 0; j < beanArr[i].length; j++) {MineBean mineBean = beanArr[i][j];if (mineBean.isMineNow()) {// 当前格子是雷continue;}// 取当前格子周围有效的格子集合ArrayList<Point> list = getAroundGrids(i, j);int mineCount = 0;Point tempPoint = null;MineBean tempBean = null;// 统计这些点是否是雷子for (int k = 0; k < list.size(); k++) {tempPoint = list.get(k);if (tempPoint == null) {continue;}tempBean = getMineBean(tempPoint.x, tempPoint.y);if (tempBean == null) {continue;}if (tempBean.isMineNow()) {mineCount++;}}mineBean.setMineCount(mineCount);}}
}
怎么判断输赢呢?
private void checkWin() {if (flagMines != unOpenMines) {return;}isGameOver = true;if (this.callBack != null) {this.callBack.onWin(System.currentTimeMillis() - gameStartTime);}
}
我弄了两个变量,flagMines被标记的旗子,unOpenMines未被点开的格子个数。如果两者相等,就是胜利了。如果点到了雷,就输了
核心算法和逻辑都已描述,可能说的不直观,如果没进入状态还是很难看明白的,慢慢领悟吧。
还好后面提供源码下载。欢迎留言批评。
http://download.csdn.net/detail/fesdgasdgasdg/9867460
Guthub:https://github.com/mengzhinan/MineGame
Java Swing扫雷游戏demo分享相关推荐
- 基于java的扫雷论文_毕业论文基于JAVA的扫雷游戏设计
毕业论文基于JAVA的扫雷游戏设计 课 程 设 计 报 告 课程名称: 计算机技术综合课程设计 题 目: 基于JAVA语言的扫雷游戏设计 学 院: 信息工程 系: 计算机 专 业: 计算机科学与技术 ...
- JAVA版扫雷游戏,清晰易懂,注释多
这是一篇关于JAVA的扫雷游戏,所有的图片均用文字代替,代码可直接运行. 文章目录 开发环境 一.下载方法 二.运行效果展示 三.代码部分 1.代码如下 总结 开发环境 开发工具:eclipse202 ...
- 基于java的扫雷论文_毕业论文基于java的扫雷游戏的设计与实现.doc
毕业论文基于java的扫雷游戏的设计与实现 JAVA程序设计A课程设计 题 目 基于JAVA的扫雷游戏的设计与实现 院 (系) 信息工程学院 专 业 班 级 计算机科学与技术(2)班 学 生 姓 名 ...
- JAVA实现扫雷游戏
后记:经评论区提醒,发现有两个bug没考虑到,感谢大家的提醒 bug1:绘制雷的时候有可能把两个雷随机到同样的位置.解决方法是在绘制雷的for循环内,rRow和rCol生成后做一个检测即可: /* 绘 ...
- java制作扫雷游戏中埋雷的难点_Java 实现经典扫雷游戏
最后一次更新于 2019/07/08 效果演示图 Java 实现经典扫雷游戏 本扫雷游戏有以下功能: 如果点中炸弹会显示炸弹. 玩家左键点击方块能显示该方块周围会出现几个炸弹,如果不存在炸弹的话扫描范 ...
- java swing实现图文混排_跟我学Java Swing之游戏设计(4)
你有没有经历过装修?尽管它是件劳神费力的事,可现代人还是不遗余力地在装修上花尽心思.毕竟,在这个视觉支配感观的时代里,谁会嫌自己家太漂亮呢?今天,就让我们秉着精益求精的完美主义精神,在上次已经完成的游 ...
- java swing 小游戏 炸弹人
java 炸弹人小游戏 用java swing写的炸弹人小游戏,学了一会java后写来练代码的,游戏资源为网上找的以及合作者画制 截图: 主界面: 地图编辑器: 一些说明: 机器人写了个状态机来控制, ...
- 基于java的扫雷游戏的设计
扫雷游戏的基本功能:点击鼠标左键于未知区域,如果未知区域有雷,游戏停止,显示所有的地雷.如果没雷,则显示周围雷数,如果周围没雷,则再查看周围八个区域是否有雷直到有雷为止并显示,玩家需要尽快找出雷区中的 ...
- 基于java的扫雷游戏设计01
开发工具eclipse,jdk1.7 扫雷:游戏说明 扫雷是一种具有迷惑性的对记忆和推理能力的简单测试,它是长久以来最受欢迎的 Windows 游戏之一. 游戏目标:找出空方块,同时避免触雷. 启动游 ...
最新文章
- ARCGIS中某字段递增赋值
- css-padding
- python pandas dataframe 转json_python-将嵌套的json转换为pandas dataframe
- 【MFC系列2】Win32项目转换为MFC项目
- Keep-Alive功能使客户端到服务器端的连接持续有效
- The Falling Leaves UVA - 699
- string.Empty和null三者的区别
- docker--删除container和image
- OpenLayers 3加载矢量地图源
- 四方位陈述RV系列蜗轮蜗杆减速机产品
- Node.js 静态web服务
- 计算机中year函数怎么使用,excel2010中YEAR函数的使用方法
- android电脑局域网传输方案,通过WiFi文件传输在Android和PC之间传输文件 | MOS86
- android 替换类
- 【Vegas原创】HP惠普笔记本重装系统无法引导无法进操作系统的终极解决方法
- 一键制作三维真实地形DEM
- ubuntu 登录界面卡死解决方法
- puppy linux安装到u盘分区,puppy linux如何安装?puppy linux安装到u盘方法
- 未配置appkey或配置错误,uniapp原生安卓插件开发
- 首届全球 Pulsar Hackathon 2021 结果宣布,全球 Top5 团队出炉!
热门文章
- Facebook联手纽约大学,要把核磁共振成像时间缩短10倍
- 油菜花王国(并查集)
- 画出计算机硬件系统结构图,一、计算机的硬件结构.ppt
- pandas计算excel两列的日期差
- 华为p10测试软件,华为p10内存测试软件
- 红色彼岸花计算机谱子,ceecceec
- To https://gitee.com/xxxx/gittest.git解决方案
- 计算机知识小口诀,字根表口诀怎么快速背-小学数学:一年级20以内加减法口诀表,附背诵技巧!...
- 微信小程序实战--仿知识星球(一)
- pdfjs转图片_Vue项目pdf(base64)转图片