一:基本规划(文章最后有完整的游戏代码)
1:游戏规则
详细的规则请参考Micsoft的扫雷游戏
2:开发流程
- 实现UI界面
- 实现扫雷图生成算法
- 实现点击逻辑操作
- 实现游戏死亡检测与胜利检测
3:主要算法(后面有具体实现):
地图生成算法:
生成地图算法:
for i from 0 to boomCount(雷数量):
获取空白格数量
随机生成一个在 0 到 空白格数量区间内的数字
从 0,0 位置开始计数找到生成的随机数的位置
将该空白格位置标记为雷
游戏检测胜利算法:
定义一个变量保存未被点击的空白格数量
该值 = 总格数量 - 雷数量
每次点击后 不断减少该值,直至该值为0
空白格周围雷数量检测:
获取该空白格周围雷的数量
获取该位置横纵坐标
定义四个方向boolean值,用来保存该空白格是否为边界点
根据四个boolean值检测该点 周围8个格子的雷数量
(上,左,下,右,左上,右上,左下,右下)
返回雷数量 并写入地图数据
空白格自动翻牌算法:因为原来的游戏中带有这个机制,当被翻的格子为没有数字的空白格则自动翻开周围的格子
在用户操作点击后会进入格子判定阶段
首先保存该格子是否为可点击的
如果不可点击则该格子已经翻过(这个主要避免自动翻格死循环)直接跳出
如果可点击,将该格子置为不可点击
进行判断
如果为雷则游戏结束
如果为数字格,则将Button的标题写入数字
如果为空白格,自动将周围的8格格子翻开(需要做边界检测)
二:代码基本实现
1.首先是主类
程序的入口类:GameMain
public class GameMain {
public static void main(String[] args) {
System.out.println("Game Start");
GamePanel gamePanel = new GamePanel();
System.out.println("Game Over");
}
}
GamePanel 为游戏类
定义了几个重要的静态变量:长宽高 字体 雷数量 未被点击的空白格数量等
private JFrame jFrame;
private static final int GAME_PANEL_WIDTH = 500;
private static final int GAME_PANEL_HEIGHT = 500;
private static final int GAME_PANEL_LENGTH = 10;
private static final int BOOM_COOUNT = 10;
private int lastBoomCount;
private JButton[][] jButtons;
private int[][] maps;
private Font font;
2.视图层
因为业务逻辑不是很多 所以为了快速开发,我直接将所有的业务逻辑塞到了视图层中
首先是 初始化的过程
初始化的过程做了几件事,首先我们实例了一个新的JFrame,同时初始化一个Button数组和int类型的数据数组
数据数组保存了雷的信息和空白格的信息
初始化我分为三块:
- 初始化Button数组
- 初始化数组数据
- 初始化JFrame对象
- 初步设置JFrame对象
- 调用游戏初始化方法
- 设置窗口可见
public GamePanel() {
font = new Font(null, 0,20);
jButtons = new JButton[GAME_PANEL_LENGTH][GAME_PANEL_LENGTH];
maps = new int[GAME_PANEL_LENGTH][GAME_PANEL_LENGTH];
for (int i = 0; i < GAME_PANEL_LENGTH; i++) {
for (int j = 0; j < GAME_PANEL_LENGTH; j++) {
jButtons[i][j] = new JButton();
jButtons[i][j].setFont(font);
ButtonAction buttonAction = new ButtonAction();
buttonAction.setIndex(i, j, this);
jButtons[i][j].addMouseListener(buttonAction);
maps[i][j] = 0;
}
}
jFrame = new JFrame("扫雷");
jFrame.setSize(GAME_PANEL_WIDTH, GAME_PANEL_HEIGHT);
jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jFrame.setResizable(false);
initPanel();
jFrame.setVisible(true);
}
然后为每个Button添加鼠标监听,该类需要保存该Button的位置和游戏(GamePanel类,为了 调用该类的方法进行翻格)
class ButtonAction implements MouseListener {
private int indexX;
private int indexY;
private GamePanel jFrame;
public void setIndex(int indexX, int indexY, GamePanel jFrame) {
this.indexX = indexX;
this.indexY = indexY;
this.jFrame = jFrame;
}
public void mouseClicked(MouseEvent e) {
if (e.getButton() == MouseEvent.BUTTON1) {
jFrame.changeOnClick(indexX, indexY, true);
} else if (e.getButton() == MouseEvent.BUTTON3) {
jFrame.changeOnClick(indexX, indexY, false);
}
}
//我省略的三个接口方法,自己实现的时候要写出来
}
这段内容中还保存了鼠标左键还是鼠标右键,因为要对格子标记的时候,需要按右键,这个时候不翻格
监听写完了就该写生成地图的逻辑了
/**
* 初始化地图
*/
private void initMaps() {
for (int i = 0; i < BOOM_COOUNT; i++) {
int boomIndex = new Random().nextInt(getBlockCount());
setBoomByCount(boomIndex);
}
}
/**
* 将该点标记为雷
*
* @param boomIndex
*/
p