Java Swing扫雷游戏demo分享

好多年前写过简略的扫雷游戏,模拟windows的。

后来由于多次搬迁环境,弄丢了,遗憾不已啊。

于是趁着这两年还在编程的道路上,趁热再次编写了一次,同时也扩展了功能,更接近windows的扫雷。

此次重写是用Javaswing实现的(eclipse开发),考虑到各位看客可能大部分是Android岗位,于是我着重注意了功能结构化的处理,使游戏核心算法与UI分离,使用回调交互,便于迁移到android环境。

本人对swing不是很熟练,一直以来用swing做项目的人很少,学习资料也少,所有的这些都是在网上现查现用的。各位看客不会没关系,都是jdk里面的api,跟android、winform都很像。

本文思路分以下几步骤讲解:

1、效果图和基本原理。

2、核心算法。

3、程序结构和部分重要函数。

4、demo源码下载。

一、效果图:

瞧瞧效果图,顶部是一个图片按钮,使用鼠标事件处理了按下、释放、点击时的图片效果。

中间部分用的是swing的GridLayout布局,跟android的GridLayout一样,除了部分用法不一样。

在某个格子上点击左键时,如果是雷,则输了。同时翻开所有的格子。

在某个格子上点击右键时,会标记为旗子;再点右键,变成问号,再点又还原。

已经点开了的格子上,点击右键无效。

如果点击的某个格子是空白的,则会递归摊开他周边所有不是雷的格子。

游戏结束时,点击顶部的小黄脸按钮,重新开始游戏。

以上部分,是主要功能描述。

二、核心算法:

其实啊,就是两个二维数组的对应关系,界面格子二维数组 <==> 程序后台格子状态二维数组。

1、先制造二维数组,然后随机生成一定数量的随机数(认为是雷,赋给对应的数组位置),要求在二维数组范围内。后台数据二维数组的每个元素,使用了一个对象,需要保存当前格子的好几种状态。

[html] view plain copy

  1. private void initButtons() {
  2. isClickComplete = true;
  3. if (buttonArr == null) {
  4. buttonArr = new JButton[mRowNums][mColumnNums];
  5. }
  6. for (int i = 0; i < mRowNums; i++) {
  7. for (int j = 0; j < mColumnNums; j++) {
  8. if (buttonArr[i][j] == null) {
  9. buttonArr[i][j] = new JButton();
  10. }
  11. setButtonImage(buttonArr[i][j], MineType.MINE_STATUS_BLANK);
  12. }
  13. }
  14. }

JButton的二维数组,添加到GridLayout布局中。

[html] view plain copy

  1. // 初始化所有格子
  2. private void resetOrCreateGrids(int rowNums, int columnNums) {
  3. if (beanArr == null) {
  4. beanArr = new MineBean[rowNums][columnNums];
  5. }
  6. for (int i = 0; i < beanArr.length; i++) {
  7. for (int j = 0; j < beanArr[i].length; j++) {
  8. if (beanArr[i][j] == null) {
  9. beanArr[i][j] = new MineBean();
  10. }
  11. beanArr[i][j].reset();
  12. }
  13. }
  14. }

[html] view plain copy

  1. public class MineBean {
  2. public static int MINE_VALUE = 9;// 是雷子的格子值
  3. private boolean isClickOpen = false;// 是否左键点开了格子
  4. private int mineCount = 0;// 周围雷的个数标记值(如果是9,则为雷)
  5. private int imageStatus = MineType.MINE_STATUS_BLANK;// 未点开图片状态
  6. ...
  7. }

后台数据服务二维数组,每个元素使用一个类来存储数据。

[html] view plain copy

  1. public class MineType {
  2. public static final int MINE_STATUS_OPEN_0 = 0;// 周围8个格子中没有雷
  3. public static final int MINE_STATUS_OPEN_1 = 1;// 周围8个格子中有1个雷
  4. public static final int MINE_STATUS_OPEN_2 = 2;// 周围8个格子中有2个雷
  5. public static final int MINE_STATUS_OPEN_3 = 3;// 周围8个格子中有3个雷
  6. public static final int MINE_STATUS_OPEN_4 = 4;// 周围8个格子中有4个雷
  7. public static final int MINE_STATUS_OPEN_5 = 5;// 周围8个格子中有5个雷
  8. public static final int MINE_STATUS_OPEN_6 = 6;// 周围8个格子中有6个雷
  9. public static final int MINE_STATUS_OPEN_7 = 7;// 周围8个格子中有7个雷
  10. public static final int MINE_STATUS_OPEN_8 = 8;// 周围8个格子中有8个雷
  11. public static final int MINE_STATUS_OPEN_9 = 9;// 周围8个格子中有9个雷
  12. public static final int MINE_STATUS_BLANK = 10;// 默认格子图片
  13. public static final int MINE_STATUS_FLAG = 11;// 格子标记为旗子
  14. public static final int MINE_STATUS_UNKNOW = 12;// 格子标记为问号
  15. public static final int MINE_STATUS_MINE_CLICK = 13;// 点击了雷子时的图片
  16. public static final int MINE_STATUS_DEAD = 14;// 失败时,顶部按钮图片
  17. public static final int MINE_STATUS_MILE = 15;// 开始时,顶部按钮图片
  18. public static final int MINE_STATUS_WAIT = 16;// 等待时,顶部按钮图片
  19. public static final int MINE_STATUS_WIN = 17;// 胜利时,顶部按钮图片
  20. public static final int MINE_STATUS_LOGO = 18;// logo标记
  21. }

格子需要显示的图片状态。有左键点击后需要显示的状态,有右键点击有需要显示的状态。

[html] view plain copy

  1. private ArrayList<Point> getAroundGrids(int i, int j) {
  2. if (beanArr == null) {
  3. return null;
  4. }
  5. // 取当前格子周围的8个点
  6. Point point1 = new Point((i - 1), (j - 1));
  7. Point point2 = new Point((i - 1), (j));
  8. Point point3 = new Point((i - 1), (j + 1));
  9. Point point4 = new Point((i), (j - 1));
  10. Point point5 = new Point((i), (j + 1));
  11. Point point6 = new Point((i + 1), (j - 1));
  12. Point point7 = new Point((i + 1), (j));
  13. Point point8 = new Point((i + 1), (j + 1));
  14. ArrayList<Point> aroundList = new ArrayList<>();
  15. aroundList.add(point1);
  16. aroundList.add(point2);
  17. aroundList.add(point3);
  18. aroundList.add(point4);
  19. aroundList.add(point5);
  20. aroundList.add(point6);
  21. aroundList.add(point7);
  22. aroundList.add(point8);
  23. for (int k = 0; k < aroundList.size(); k++) {
  24. Point pointTemp = aroundList.get(k);
  25. if (pointTemp.x < 0 || pointTemp.x >= beanArr.length || pointTemp.y < 0
  26. || pointTemp.y >= beanArr[0].length) {
  27. // 越界
  28. aroundList.remove(k);
  29. k--;
  30. }
  31. }
  32. return aroundList;
  33. }

这个方法是获取当前位置的周围8个格子算法。需要注意的是,靠边的格子再获取周围8个格子时,会包含越界的。在加入到集合中时,需要过滤越界的数据。

然后就不用再考虑[左上角、右上角、左下角、右下角,左边、上边、右边、下边、内部]等多种情况了。

2、循环后台数据二维数组,每循环一步,找到它周围的8个格子(过滤掉不在UI范围内的),统计雷子个数,然后给当前这个格子赋值(周围雷子数量值)。

3、最核心的算法:点一个空白格子时,会摊开一大片。这个其实很简单,每次点击一个格子时,显示对应的UI的同时,继续找到它周边的8个格子,然后循环递归调用当前函数。

[html] view plain copy

  1. public void leftClick(int i, int j) {
  2. if (beanArr == null || this.callBack == null || this.isGameOver) {
  3. return;
  4. }
  5. MineBean mineBean = getMineBean(i, j);
  6. if (mineBean == null) {
  7. return;
  8. }
  9. if (mineBean.isClickOpen()) {
  10. return;
  11. }
  12. mineBean.setClickOpen(true);
  13. if (unOpenMines < 0) {
  14. unOpenMines = 0;
  15. }
  16. unOpenMines--;
  17. if (mineBean.isMineNow()) {
  18. isGameOver = true;
  19. gameOver(i, j);
  20. return;
  21. }
  22. if (gameStartTime <= 0) {
  23. gameStartTime = System.currentTimeMillis();
  24. }
  25. if (this.callBack != null) {
  26. this.callBack.onLeftClick(mineBean, i, j);
  27. }
  28. checkWin();
  29. if (mineBean.getMineCount() == MineType.MINE_STATUS_OPEN_0) {
  30. // 递归摊开一片
  31. recursionAround(i, j);
  32. }
  33. }
  34. private void recursionAround(int i, int j) {
  35. ArrayList<Point> list = getAroundGrids(i, j);
  36. for (int k = 0; k < list.size(); k++) {
  37. Point tempPoint = list.get(k);
  38. if (tempPoint == null) {
  39. continue;
  40. }
  41. MineBean mineBean = getMineBean(tempPoint.x, tempPoint.y);
  42. if (mineBean == null) {
  43. continue;
  44. }
  45. if (mineBean.isMineNow()) {
  46. continue;
  47. }
  48. if (mineBean.isClickOpen()) {
  49. continue;
  50. }
  51. leftClick(tempPoint.x, tempPoint.y);
  52. }
  53. }

上面代码就是点击某个格子后,显示UI,同时处理递归摊开一片的算法。

4、UI点击格子时,提供横竖坐标,传递给算法工具类对象处理。算法函数中找到对应的数据位置,判断情况再回调给UI显示。避免UI与算法函数耦合度太高,难以移植。

[html] view plain copy

  1. public interface CallBack {
  2. void onInit();
  3. void onWin(long time);// 胜利
  4. void onGameOver();// 失败
  5. void onLeftClick(MineBean mineBean, int i, int j);
  6. void onRightClick(MineBean mineBean, int i, int j);
  7. }

以上部分是核心算法描述。

三、程序结构和部分重要函数。

[html] view plain copy

  1. // 初始化随机雷子
  2. private void makeRandomMines() {
  3. if (beanArr == null) {
  4. return;
  5. }
  6. int nowMines = 0;
  7. while (nowMines < mMineCount) {
  8. int i = random.nextInt(beanArr.length);
  9. int j = random.nextInt(beanArr[0].length);
  10. MineBean mineBean = beanArr[i][j];
  11. if (!mineBean.isMineNow()) {
  12. mineBean.setMineNow();
  13. nowMines++;
  14. }
  15. }
  16. }
  17. // 计算格子周围雷子状态
  18. private void initGridAroundStatus() {
  19. if (beanArr == null) {
  20. return;
  21. }
  22. for (int i = 0; i < beanArr.length; i++) {
  23. for (int j = 0; j < beanArr[i].length; j++) {
  24. MineBean mineBean = beanArr[i][j];
  25. if (mineBean.isMineNow()) {
  26. // 当前格子是雷
  27. continue;
  28. }
  29. // 取当前格子周围有效的格子集合
  30. ArrayList<Point> list = getAroundGrids(i, j);
  31. int mineCount = 0;
  32. Point tempPoint = null;
  33. MineBean tempBean = null;
  34. // 统计这些点是否是雷子
  35. for (int k = 0; k < list.size(); k++) {
  36. tempPoint = list.get(k);
  37. if (tempPoint == null) {
  38. continue;
  39. }
  40. tempBean = getMineBean(tempPoint.x, tempPoint.y);
  41. if (tempBean == null) {
  42. continue;
  43. }
  44. if (tempBean.isMineNow()) {
  45. mineCount++;
  46. }
  47. }
  48. mineBean.setMineCount(mineCount);
  49. }
  50. }
  51. }

怎么判断输赢呢?

[html] view plain copy

  1. private void checkWin() {
  2. if (flagMines != unOpenMines) {
  3. return;
  4. }
  5. isGameOver = true;
  6. if (this.callBack != null) {
  7. this.callBack.onWin(System.currentTimeMillis() - gameStartTime);
  8. }
  9. }

我弄了两个变量,flagMines被标记的旗子,unOpenMines未被点开的格子个数。如果两者相等,就是胜利了。如果点到了雷,就输了

核心算法和逻辑都已描述,可能说的不直观,如果没进入状态还是很难看明白的,慢慢领悟吧。

还好后面提供源码下载。欢迎留言批评。

http://download.csdn.net/detail/fesdgasdgasdg/9867460

时间: 2024-08-04 11:05:53

Java Swing扫雷游戏demo分享的相关文章

java swing开发扫雷游戏源代码

原文:java swing开发扫雷游戏源代码 源代码下载地址:http://www.zuidaima.com/share/1550463547886592.htm java 扫雷 游戏源码 源代码截图:

java 简单的扫雷游戏

//扫雷游戏 package Bible001; import javax.swing.*; import java.awt.event.*; import java.awt.*; public class Mine extends MouseAdapter { private JFrame mainFrame; private int[][] data; private JButton[][] buttons; private JButton startJB; private Label l;

Java Swing打猎射击游戏编程代码下载

代码下载地址:http://www.zuidaima.com/share/1858069987494912.htm 原文:Java Swing打猎射击游戏编程代码下载 这是一款java swing编写的打猎射击游戏,从这款游戏的编程中,我们可以练习如何进行射击对象的消失及相关按键的监听. 该游戏的一大亮点还是对运动图片的二级缓冲功能,运行时可以感受到非常的流畅,对于想从事游戏编程的人员来说是必须要掌握的一个技能点. 游戏操作:通过鼠标点击野猪和小鸟来是实现击中功能,子弹不足时提示"装载子弹...

java swing实现的多线程实例代码教程-赛马demo

代码下载:http://www.zuidaima.com/share/1825492473826304.htm 原文:java swing实现的多线程实例代码教程-赛马demo 项目截图: 运行截图: java swing实现的多线程实例代码教程-赛马demo,布布扣,bubuko.com

java swing实现俄罗斯方块游戏源代码下载

原文:java swing实现俄罗斯方块游戏源代码下载 源代码下载:http://www.zuidaima.com/share/1550463372790784.htm 源代码截图:

java swing开发的图像生成器demo实例源代码下载,实现绘制图像,截屏功能。

一个类似于画画的javase程序 绘制图形 原文:java swing开发的图像生成器demo实例源代码下载,实现绘制图像,截屏功能. java源代码下载地址:http://www.zuidaima.com/share/1550463330028544.htm 获取屏幕 打开调色板

java swing开发打飞机的小游戏源代码下载

原文:java swing开发打飞机的小游戏源代码下载 源代码下载地址:http://www.zuidaima.com/share/1550463716084736.htm 这是我为了熟悉java设计模式而练习的代码.其中肯定有很多不足的地方.希望各位牛人们 能多多指点.谢谢

java swing开发俄罗斯方块游戏

原文:java swing开发俄罗斯方块游戏 源代码下载地址:http://www.zuidaima.com/share/1550463573740544.htm 简单的俄罗斯方块游戏,可以读取记录

java swing开发短小精悍的俄罗斯方块小游戏源代码下载,仅300行代码

原文:java swing开发短小精悍的俄罗斯方块小游戏源代码下载,仅300行代码 源代码下载地址:http://www.zuidaima.com/share/1550463495146496.htm java swing开发短小精悍的俄罗斯方块小游戏源代码下载,仅300行代码, 很久以前找到的一个Swing实现的俄罗斯方块,短线精悍,算法值得一看 经验证代码可用,确实短小精悍,值得下载. package com.zuidaima.swing.game; import java.awt.*; i