用Easyx来做一个扫雷小游戏?

扫雷游戏单机版

用Easyx来做一个扫雷小游戏?_第1张图片

描述

        扫雷游戏是一款十分经典的单机小游戏。在nn行mm列的雷区中有一些格子含有地雷(称之为地雷格),其他格子不含地雷(称之为非地雷格)。玩家翻开一个非地雷格时,该格将会出现一个数字——提示周围格子中有多少个是地雷格。游戏的目标是在不翻出任何地雷格的条件下,找出所有的非地雷格。

        如果Easyx.h函数不明确的话,可以到EasyX Graphics Library for C++这个网址进行查看手册。

此游戏纯属个人练习记录,请大佬飘过~

                                                

        扫雷游戏是在一堆格子里去排查,格子的话我们可以定义成二维数组的形式,其中这些格子包括雷,和含有多少雷的信息。如下图第一张所示,左上角就是我含有雷的个数,第二张图片上数字部分是它的周围含有几个雷。

用Easyx来做一个扫雷小游戏?_第2张图片

程序解析

雷区的数据

int main()
{
	// 创建扫雷游戏的数据
	int map[ROW][COL] = { 0 };

	return 0;
}

        我们使用二位数组来设置一个存放 (行)(列)的雷的棋盘,ROW和COL是代表行数和列数,#define 定义出来的。

期盼打印查看函数

// 显示地图函数实现。
void ShowMap(const int map[ROW][COL])
{
	for (int i = 0; i < ROW; i++)
	{
		for (int j = 0; j < COL; j++)
		{
			printf("%2d ",map[i][j]);
		}
		printf("\n");
	}
}

        要注意的就是打印时需要注意一下打印格式。思路就是两层的For遍历出棋盘的数据。

布置雷的函数

        布置雷,我们可以想象雷的数据设置成-1,当然雷的位置肯定是随机的,所以这里我们需要调用srand()和rand这两个C语言标准库函数进行随机给值,对给定的值进行处理。

// 放置雷的函数实现
void BuryMine(int map[ROW][COL], const int mineCount)
{
	int i = 0;
	while (i < mineCount)
	{
		// 产生 随机的 x,y坐标,x限制在ROW内,y限制在COL内。
		int x = rand() % ROW;
		int y = rand() % COL;

		if (map[x][y] == 0)
		{
			map[x][y] = LABLE_MINE;
			i++;			//这里只有设置成功一个雷,数量才会减少,保证必须能够设置10个雷。
		}
	}
}

当然布置完雷还是不行的,我们需要对期周边的数据做提示处理,我们可以看图和看代码,分析其含义,其实就是在雷位置的九宫格周边提示出附近有多少个雷。

用Easyx来做一个扫雷小游戏?_第3张图片

在布置雷的时候还需要注意的就是二位数组的越界问题

// 模拟提示:找到每一个雷周围九宫格加 1 函数实现。
void BuryMineHint(int map[ROW][COL])
{
	// 如何做呢?
	// 遍历数组
	for (int i = 0; i < ROW; i++)
	{
		for (int j = 0; j < COL; j++)
		{
			// 找到雷的位置
			if (map[i][j] == LABLE_MINE)
			{
				//根据雷周围的坐标:
				//遍历范围:x-1 ~ x+1  y-1 ~ y+1
				for (int x = i - 1; x <= i + 1; x++)
				{
					for (int y = j - 1; y <= j + 1; y++)
					{
						// 排除雷本身,排除越界。
						if ((x>=0 && x=0 && y

这是控制台的打印结果:

                                             用Easyx来做一个扫雷小游戏?_第4张图片

 

程序走到这里,我们看一下整体的Main程序中是如何写的?

// 扫雷游戏函数声明。
void MineClearanceGame()
{

	// 设置随机数种子 - 利用时间戳。
	srand((unsigned)time(NULL));
	// 创建扫雷游戏的数据
	int map[ROW][COL] = { 0 };
	// 放置雷 - 给二维数组中增加 “-1” ,-1就代表是类。 
	BuryMine(map, MINE_COUNT);
	// 模拟提示:找到每一个雷周围九宫格加 1 函数声明。
	BuryMineHint(map);

	// 打印扫雷地图。
	ShowMap(map);
}

通过Esayx界面把数据展示给用户

上面雷的数据就已经设定好了,可是是运行在控制台中的啊,那么接下来就利用Esayx这个图形库来把数据进行覆盖!

在做贴图之前,我们先把上面的两个函数进行封装一下,因为不管是设置雷,还是让雷周围的九宫格显示出雷的数量,都是属于初始化的范围,所以我们给他封装 -------InitMine() 函数

// 初始化雷函数实现。
void InitMine(int map[ROW][COL], const int mineCount)
{
	int count = 0;
	while (count < mineCount)
	{
		// 产生 随机的 x,y坐标,x限制在ROW内,y限制在COL内。
		int x = rand() % ROW;
		int y = rand() % COL;

		if (map[x][y] == 0)
		{
			map[x][y] = LABLE_MINE;
			count++;			//这里只有设置成功一个雷,数量才会减少,保证必须能够设置10个雷。
		}
	}

	// 让雷周围的九宫格自己计算出,以自身为中心雷的个数。
	// 如何做呢?
	// 遍历数组
	for (int i = 0; i < ROW; i++)
	{
		for (int j = 0; j < COL; j++)
		{
			// 找到雷的位置
			if (map[i][j] == LABLE_MINE)
			{
				//根据雷周围的坐标:
				//遍历范围:x-1 ~ x+1  y-1 ~ y+1
				for (int x = i - 1; x <= i + 1; x++)
				{
					for (int y = j - 1; y <= j + 1; y++)
					{
						// 排除雷本身,排除越界。
						if ((x >= 0 && x < ROW && y >= 0 && y < COL) && map[x][y] != LABLE_MINE)
						{
							map[x][y]++;
						}
					}
				}
			}

		}
	}
}

// 因为数据与照片会进行关联,所以我们将数据进行偏移一下。
for (int i = 0; i < ROW; i++)
{
	for (int j = 0; j < COL; j++)
	{
		map[i][j] += 20;
	}
}
// 数据进行对比:

                    用Easyx来做一个扫雷小游戏?_第5张图片

 

接下来我们开始编写贴图的函数,提前准备了一些照片。

  用Easyx来做一个扫雷小游戏?_第6张图片

 

1、加载照片

IMAGE img[12] = {0};// 主函数定义
// 加载贴图函数实现。
void LoadImg(IMAGE img[])
{
	// 加载本地图片
	for (int i = 0; i < 12; i++)
	{
		char imgName[20] = { 0 };
		sprintf(imgName, "./images/%d.jpg", i);
		loadimage(img + i, imgName, IMG_WIDTH, IMG_HEIGHT);
	}
}

以上是加载照片的代码,一共11张照片存入到数组中,这里有一个小技巧,我们利用sprintf将路径存入到imgName这个数组中,这样我们可以直接调用,就不需要一个一个打了。

2、贴图

贴图可以对照画图分析:

// 贴图函数实现。
void DrawMap(int map[ROW][COL], IMAGE img[])
{
	// 贴图
	for (int i = 0; i < ROW; i++)
	{
		for (int j = 0; j < COL; j++)
		{
			if (map[i][j] >= 0 && map[i][j] <= 8)				// 贴提示数字
			{
				putimage(j * IMG_WIDTH, i * IMG_HEIGHT, img + map[i][j]);
			}
			else if (map[i][j] == -1)							// 贴雷
			{
				putimage(j * IMG_WIDTH, i * IMG_HEIGHT, img + 9);
			}
			else if (map[i][j] > 9 && map[i][j] <= 28)			// 贴未打开的图。
			{
				putimage(j * IMG_WIDTH, i * IMG_HEIGHT, img + 10);
			}
			else if (map[i][j] > 28)
			{
				putimage(j * IMG_WIDTH, i * IMG_HEIGHT, img + 11);	// 贴标记。
			}
		}
	}
}
// 根据提供的数据进行贴图,我们可以看一下效果。

用Easyx来做一个扫雷小游戏?_第7张图片

 用Easyx来做一个扫雷小游戏?_第8张图片

 

 

实现鼠标操作

        函数完成说表点击左键,就代表翻牌子,鼠标点击右键就代表标记雷,,值得注意的是,在雷盘上是没有坐标的只有鼠标的位置坐标,我们要找到每一个小方框的位置就需要求行(row),列(col)。求取方式其实和上面的贴图的思路差不多,就是反过来求解:

用Easyx来做一个扫雷小游戏?_第9张图片

 

// 鼠标操作函数实现。
void MouseEvent(int map[ROW][COL])
{
	// 判断鼠标是否有动作。
	if (MouseHit())
	{
		// 获取鼠标事件。
		MOUSEMSG msg = GetMouseMsg();
		int row = msg.y / IMG_WIDTH;
		int col = msg.x / IMG_HEIGHT;
		// 如果是点击左键。
		if (msg.uMsg == WM_LBUTTONDOWN)
		{
			// 是没有点击过的数据才能进行贴图。
			if (map[row][col] >= 19 && map[row][col] <= 28)
			{
				map[row][col] -= 20;
				ShowMap(map);// 控制台查看结果。
			}
		}
		// 如果是雷可以进行标记。
		else if (msg.uMsg == WM_RBUTTONDOWN)
		{
			if (map[row][col] >= 19 && map[row][col] <= 28) // 如果没有点击也没有标记过就进行标记。
			{
				map[row][col] += 20;
				ShowMap(map);// 控制台查看结果。
			}
			else if (map[row][col] > 28)					// 如果已经标记了,就取消标记。
			{
				map[row][col] -= 20;
				ShowMap(map);// 控制台查看结果。
			}
		}
	}
}
// 查看实际效果:

用Easyx来做一个扫雷小游戏?_第10张图片

 

游戏递归炸花

先看一下效果图

用Easyx来做一个扫雷小游戏?_第11张图片

 

        当鼠标点击完成狗row和col的坐标是进行保留的,我们可以对这个坐标进行判断,用递归的只是进行扎金花。

// 炸开周围空白的区域函数实现 - 递归。
void OpenNull(int map[ROW][COL], int row, int col)
{
	// 判断鼠标点击后的数值是不是好的区域。
	if (map[row][col] == 0)
	{
		// 周围的每一个都要进行越界,并且要排除越界的问题。
		for (int i = row-1; i <= row+1; i++)
		{
			for (int j = col-1; j <= col+1; j++)
			{
				// 判断如果周围的各自没有被打开,就将他打开。
				if ((i >= 0 && i < ROW && j >= 0 && j < COL) && map[i][j] > 19 && map[i][j] <= 28)
				{
					map[i][j] -= 20;
					OpenNull(map,i,j);
				}
			}
		}

	}
}

判断游戏输赢

        判断游戏,需要每次单机鼠标后,我们就进行判断一下,我们就可以封装一下函数了。

// 判断游戏用过还是失败!
char judge(int map[ROW][COL], int row, int col)
{
	// 如果猜到雷了
	if (map[row][col] == -1)
	{
		// 将所有的雷都打开。
		for (int i = 0; i < ROW; i++)
		{
			for (int j = 0; j < COL; j++)
			{
				// 第一种情况的雷:雷没有打开,也没有被标记。
				if (map[i][j] == 19)
				{
					map[i][j] -= 20;
				}
				else if (map[i][j] == 39)
				{
					map[i][j] -= 40;
				}
			}
		}
		return 'n';
	}

	// 把所有的区域点开,那就说明游戏胜利了
	// 判断数值计算。
	int count = 0;
	// 判断count的数量
	for (int i = 0; i < ROW; i++)
	{
		for (int j = 0; j < COL; j++)
		{
			if (map[i][j] >= 0 && map[i][j] <= 8)
			{
				count++;
			}
		}
	}
	if (count == ROW * COL - MINE_COUNT)
	{
		return 'y';
	}

	return 0;
}

游戏的音效制作

        最后我们来给游戏添加的音乐的色彩,其实也很简单。

// 就是在我们想要的地方增加音乐播放的库程序。
mciSendString("close rightClick", NULL, 0, NULL);
mciSendString("open ./images/rightClick.wav alias rightClick", NULL, 0, NULL);
mciSendString("play rightClick", NULL, 0, NULL);

游戏演示效果

至此这个游戏就已经完成了,当然肯定还有更多优化的地方:比如说来一个菜单?开具的时候设置雷的数量?挑战行和列的数量?

我们演示一下效果吧~

用Easyx来做一个扫雷小游戏?_第12张图片

用Easyx来做一个扫雷小游戏?_第13张图片

你可能感兴趣的:(C,数据结构,c++,开发语言)