Java Swing扫雷游戏demo分享
2017-06-11 22:49
316 查看
好多年前写过简略的扫雷游戏,模拟windows的。
后来由于多次搬迁环境,弄丢了,遗憾不已啊。
于是趁着这两年还在编程的道路上,趁热再次编写了一次,同时也扩展了功能,更接近windows的扫雷。
此次重写是用java swing实现的(eclipse开发),考虑到各位看客可能大部分是android岗位,于是我着重注意了功能结构化的处理,使游戏核心算法与UI分离,使用回调交互,便于迁移到android环境。
本人对swing不是很熟练,一直以来用swing做项目的人很少,学习资料也少,所有的这些都是在网上现查现用的。各位看客不会没关系,都是jdk里面的api,跟android、winform都很像。
本文思路分以下几步骤讲解:
1、效果图和基本原理。
2、核心算法。
3、程序结构和部分重要函数。
4、demo源码下载。
一、效果图:
瞧瞧效果图,顶部是一个图片按钮,使用鼠标事件处理了按下、释放、点击时的图片效果。
中间部分用的是swing的GridLayout布局,跟android的GridLayout一样,除了部分用法不一样。
在某个格子上点击左键时,如果是雷,则输了。同时翻开所有的格子。
在某个格子上点击右键时,会标记为旗子;再点右键,变成问号,再点又还原。
已经点开了的格子上,点击右键无效。
如果点击的某个格子是空白的,则会递归摊开他周边所有不是雷的格子。
游戏结束时,点击顶部的小黄脸按钮,重新开始游戏。
以上部分,是主要功能描述。
二、核心算法:
其实啊,就是两个二维数组的对应关系,界面格子二维数组 <==> 程序后台格子状态二维数组。
1、先制造二维数组,然后随机生成一定数量的随机数(认为是雷,赋给对应的数组位置),要求在二维数组范围内。后台数据二维数组的每个元素,使用了一个对象,需要保存当前格子的好几种状态。
然后就不用再考虑[左上角、右上角、左下角、右下角,左边、上边、右边、下边、内部]等多种情况了。
2、循环后台数据二维数组,每循环一步,找到它周围的8个格子(过滤掉不在UI范围内的),统计雷子个数,然后给当前这个格子赋值(周围雷子数量值)。
3、最核心的算法:点一个空白格子时,会摊开一大片。这个其实很简单,每次点击一个格子时,显示对应的UI的同时,继续找到它周边的8个格子,然后循环递归调用当前函数。
4、UI点击格子时,提供横竖坐标,传递给算法工具类对象处理。算法函数中找到对应的数据位置,判断情况再回调给UI显示。避免UI与算法函数耦合度太高,难以移植。
以上部分是核心算法描述。
三、程序结构和部分重要函数。
怎么判断输赢呢?
我弄了两个变量,flagMines被标记的旗子,unOpenMines未被点开的格子个数。如果两者相等,就是胜利了。如果点到了雷,就输了
核心算法和逻辑都已描述,可能说的不直观,如果没进入状态还是很难看明白的,慢慢领悟吧。
还好后面提供源码下载。欢迎留言批评。
http://download.csdn.net/detail/fesdgasdgasdg/9867460
后来由于多次搬迁环境,弄丢了,遗憾不已啊。
于是趁着这两年还在编程的道路上,趁热再次编写了一次,同时也扩展了功能,更接近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
相关文章推荐
- java swing实现的扫雷游戏及改进版完整示例
- Java手机游戏习作“扫雷”,免费下载(含Java源代码)。
- 跟我学Java Swing之游戏设计(1)
- Java Swing LookAndFeel Demo
- 利用Java Swing 实现游戏开发
- 清华大学计算中心培训部-技术分享:Java中Jlist的Swing组件
- Java扫雷游戏一例
- 小demo, java swing窗口编程(JDK 1.6)
- 用Java编写扫雷游戏--算法思考
- 初学Java 写的一个简陋的Swing Demo
- 累了休息一会儿吧——分享一个JavaScript版扫雷游戏
- 跟我学Java Swing之游戏设计(2)
- 用JAVA制作的扫雷游戏
- JAVA编写扫雷游戏布雷图
- Java Swing LookAndFeel Demo
- 利用Java Swing 实现游戏开发
- 利用 Java Swing 实现游戏开发
- 跟我学Java Swing之游戏设计(2)-Java基础-Java-编程开发
- 纯Java开发的游戏引擎V0.4--DEMO2 -- 物理引擎
- Java 游戏 扫雷 Applet 多媒体版