如果你正在写一个扫雷游戏,不管你是在用什么语言来写。阅读本文都可以对你有所帮助!本文将实现扫雷游戏的每一个步骤写出来,同时配以相应的java代码(不用担心看不懂java代码!重要的是看懂我的每一个步骤!)。我会重点说明一下非雷格子的实现(也就是你点开那个格子它会显示1或2或3数字来指明它旁边的雷的个数)。最后说一点,实现扫雷的方法有很多,我这里只不过是其中一种
第一步:准备好游戏运行需要的所有变量
1.抽象出所需类型
1.在游戏中我们会看到一个有许多格子组成的地图,如何我们点击格子就出现了相应的行为。所以我们可以抽象出一种“格子”类型.
2.我们点击一个格子以后,我们的程序是怎么知道我们点击的是哪一个呢?所以我们需要给每一个格子贴一个身份证,而格子的身份证就是他的坐标,所以抽象一个“坐标”类型
3.我们的格子都是在一个地图上,在这个地图中有很多种类的格子,所以我们抽象出一个“地图”类型
然后我们根据需要在每个类中添加相应的成员,
public class Block extends JButton {
boolean isMine; //标记本格子是不是雷
int num; //格子周围雷的个数
int row; //格子所在行
int col; //格子所在列
boolean isShow; //标记本格子是否已经被点开
boolean isSing; //标记本格子是否被插旗子了
public Block(int r,int c ){
row = r;
col = c;
}
public Block(){
}
//默认初始化
}
public class Coordinate {
public int x; //x坐标
public int y; //y坐标
}
在此说明一下应该在格子类中添加一个坐标类变量更加好,而不是用两个row,col变量来表示坐标,同时格子在gui中是一个按钮所以我选择让这个格子类继承自按钮类。
对于“地图”类,我这里说明一下其中的成员变量,以后贴出来的代码基本也是在这个类里面了
int MINE_NUM; //雷格子的个数
private Block[][] allBlocks; //所有格子的集合
private Coordinate[] allMineCoordinate; //所有雷格子的坐标的集合,将来有大的用处哦!
private int m; //地图行数
private int n; //地图列数
public Maps(int m, int n, int num){
MINE_NUM = num;
this.m = m;
this.n = n;
initArray();
//进行相应数组地初始化
setMine();
//雷已经全部埋完,开始根据这个二维数组来生成Maps上的所有blocks
setMineNum();
}
2.设计好图纸了,开始造游戏所需要东西(变量)
1.初始化各变量
/**
*数组初始化
*/
public void initArray(){
//下面3句是初始化雷格子的坐标的集合
allMineCoordinate = new Coordinate[MINE_NUM];
for (int i = 0; i < MINE_NUM; i++)
allMineCoordinate[i] = new Coordinate();
//下面的代码都是在初始化所有格子的集合中的每一个格子
allBlocks = new Block[m][n];
for (int i = 0; i < allBlocks.length; i++) {
for (int j = 0; j < allBlocks[0].length; j++) {
Block block = new Block(i,j);
//下面的4句代码的作用是给每个格子加一个坐标信息,
// 以后鼠标点击格子的时候就通过此信息知道是鼠标点击的哪个格子了
String command_x = String.format("%02d",j);
String command_y = String.format("%02d",i);
String command = command_x + command_y;
block.setActionCommand(command);
//下面2句代码的作用是添加监听器,在格子上安装一个监听器,格子才知道自己被点击了
// 如何告诉相应的事件处理函数来处理
block.addActionListener(button_click_listener);
block.addMouseListener(button_otherClick_listener);
allBlocks[i][j] = block;
}
}
}
上面的代码虽然可以实现目标语法也没错误,但是并不完美甚至是有点恶心,不过我真的没时间去改了,希望读者自己去修改一下。
2.在这么多格子中选择几个格子作为雷格子
/**
*进行埋雷并对雷的位置进行记录
*/
public void setMine()
{
Random r = new Random(); //使用一个随机器
int mineX = 0;
int mineY = 0;
for(int i = 0; i < MINE_NUM; i++){
mineX = r.nextInt(n);
mineY = r.nextInt(m);
if(allBlocks[mineY][mineX].isMine == false){ //只有该地区不存在雷,才在那里“埋雷”
allBlocks[mineY][mineX].isMine = true;
allMineCoordinate[i].y = mineY;
allMineCoordinate[i].x = mineX;
}
else{
i--; //表示此次埋雷失败
}
}
}
在埋雷过程中需要注意的一个问题就是避免在一个地方重复埋雷,因为我们埋雷的位置是通过随机器产生的,所以很有可能产生相同的位置如:第一个雷的位置是(1,1),在通过随机器产生第二个雷的坐标的时候产生的位置也是(1,1)这种情况。我们通过一个判断语句来解决此问题
3.设置非雷格子的代表周围雷的个数的num成员变量的值
/**
* 根据block的所在位置选择出需要处理的格子(九宫格的具体格子)分别是哪一些并通过数组保存加以返回
* */
public Coordinate[] getWaitedProcessBlockCoordinate(int x,int y){
Coordinate[] waitedProcessBlocksCoordinate = null; //不同的情况下面需要处理的个数不同从而确定数组的长度
int k = 0;
if(x >= 1 && x < n-1 && y >= 1 && y < m-1){ //非边界处
waitedProcessBlocksCoordinate = new Coordinate[8];
}
else if(x == 0 || x == n-1 || y == 0 || y == m-1){ //边界处
if((x == y) || (x == 0 && y == m-1) || (y == 0 && x == n-1)){ //四个顶点处
waitedProcessBlocksCoordinate = new Coordinate[3];
}
else {
waitedProcessBlocksCoordinate = new Coordinate[5];
}
}
for (int i = 0;i < waitedProcessBlocksCoordinate.length; i++){ //对数组中的各个元素进行初始化
waitedProcessBlocksCoordinate[i] = new Coordinate();
}
for (int i = x-1;i <= x+1;i++){
for (int j = y-1; j <= y+1; j++){
if((i != x || j != y) && (i >= 0 && i < n) && (j >= 0 && j < m)){ //该点本身以及超出边界的block无须处理
waitedProcessBlocksCoordinate[k].x = i;
waitedProcessBlocksCoordinate[k].y = j;
k++;
}
}
}
return waitedProcessBlocksCoordinate;
}
通过上面那个方法,我们就可以得到(x,y)坐标的格子的九宫格范围(周围)内是哪一些格子了。
大家可以很容易的想到该怎么得到格子周围的雷的个数了吧:不就是遍历使用的非雷格子,然后调用这个方法得到周围的格子,再把这些格子都遍历一遍就知道这些格子中哪一些是雷了,然后设置num就完事了啊!
没错,这样想是可以的但是不是最好的。我们可以通过逆向思维来思考这个问题:非雷格子可以发现九宫格范围内的雷,九宫格范围外的雷是发现不了的。反过来思考一下这句话是不是就是:雷格子只能影响九宫格范围内的格子的num,范围之外的格子他是影响不了的。所以我们直接遍历使用的雷格子,把他们的影响发挥出来就行了啊。
/**
*设置每个格子的附近的雷的数目
* */
public void setMineNum(){
System.out.println("m:"+m+"n:"+n);
Coordinate[] c1;
Coordinate[] c2;
for (int i = 0;i < MINE_NUM;i++){ //只有雷附近的格子才可能感应到雷的存在,所以对于离雷太远的格子我们就可以不考虑
c1 = getWaitedProcessBlockCoordinate(allMineCoordinate[i].x,allMineCoordinate[i].y);
//获得雷附近的格子
for (int i1 = 0; i1 < c1.length; i1++){ //对每个雷附近的格子,雷的个数加一,叠加原理
int y = c1[i1].y;
int x = c1[i1].x;
allBlocks[y][x].num++;
}
}
}
好了到了这一步游戏中所需要的东西都坐标齐全了,静态的东西写完了开始写动的东西了
第二步:写用户的各种操作后我们的程序所对应的反应
接下来的各种事件处理函数都不是什么难的东西了,自己写一下吧!!哈哈。写这个博客写了好久啊。一个上午就没了,哎哎哎