C/C++游戏项目详细教学:《扫雷》

118 篇文章 62 订阅
64 篇文章 18 订阅

扫雷最原始的版本可以追溯到1973年一款名为“方块”的游戏。

不久,“方块”被改写成了游戏“Rlogic”。在“Rlogic”里,玩家的任务是作为美国海军陆战队队员,为指挥中心探出一条没有地雷的安全路线,如果路全被地雷堵死就算输。两年后,汤姆·安德森在“Rlogic”的基础上又编写出了游戏“地雷”,由此奠定了现代扫雷游戏的雏形。

1981年,微软公司的罗伯特·杜尔和卡特·约翰逊两位工程师在Windows3.1系统上加载了该游戏,扫雷游戏才正式在全世界推广开来。

这款游戏的玩法是在一个9*9(初级),16*16(中级),16*30(高级),或自定义大小的方块矩阵中随机布置一定量的地雷(初级为10个,中级为40个,高级为99个)。由玩家逐个翻开方块,以找出所有地雷为最终游戏目标。如果玩家翻开的方块有地雷,则游戏结束。 

我们今天就来自己写《扫雷》

这个项目并不是很难,今天就和大家从头到尾详细的讲一遍,大家一定要好好看好好学!

行吧,开始了

我们今天用的是Cpp来写,但不是用Windows来创建窗口,而是用他的远房表亲EasyX图形库来创建窗口,因为用图形库会简单很多,刚学C的也可以听懂,而且C/C++不分家,没有什么问题,OK,我们现在开始写代码,今天我就把头文件也分享出来了,主要是教大家如何去调用图形库。(注:没有EasyX图形库的可以去官网下载,也可以进群直接下载)

首先是我们的头文件以及一些宏定义

#include<stdio.h>
#include<graphics.h>//包含图形库头文件
#define ROW 9 //行
#define COL 9 //列  共81个格子
#define MINE_NUM  18//雷的数量
#define IMG_SIZE 40

初始化函数,也是我们的老朋友了

void GameInit()
{
	//1,创建窗口 一行有九张图片每张图片40px,
	initgraph(IMG_SIZE*ROW, IMG_SIZE*COL,SHOWCONSOLE);
	//加载图片(赋值) load加载 第一个参数,存储图片的变量 错误:字符集问题
	for (int i = 0; i < 12; i++)
	{
		char file[20] = "";
		sprintf(file, "./image/%d.jpg", i);
		loadimage(&img[i], file, IMG_SIZE, IMG_SIZE);
	}
	//布雷
	for (int i = 0; i < MINE_NUM; )
	{
		//排除辅助区
		int row = rand() % ROW+1;//0-8  1-9
		int col = rand() % COL+1;
		if (mine[row][col] == 0)
		{
			mine[row][col] = 9;
			i++;
		}
	}
	//雷所在的九宫格,要加1(雷除外)
	for (int i = 1; i < ROW+1; i++)
	{
		for (int k = 1; k < COL+1; k++)
		{
			if (mine[i][k] == 9)
			{
				//遍历雷的九宫格 细心
				for (int a = i - 1; a <= i + 1; a++)
				{
					for (int b = k - 1; b <= k + 1; b++)
					{
						//非雷的格子加1
						if (mine[a][b] != 9)
						{
							mine[a][b]++;
						}
					}
				}
			}
		}
	}
	//加密 开始时全部为掩码图
	for (int i = 1; i < ROW + 1; i++)
	{
		for (int k = 1; k < COL + 1; k++)
		{
			mine[i][k] += 20;
		}
	}

}

鼠标信息处理函数

void MouseEvent()
{
	//检测是否有鼠标消息
	if (MouseHit())
	{
		//获取鼠标消息,坐标,左键还是右键
		MOUSEMSG msg = GetMouseMsg();
		//把坐标转化成数组下标
		openr = msg.y / IMG_SIZE+1;
		openc = msg.x / IMG_SIZE+1;
		//判断是左键还是右键
		switch (msg.uMsg)
		{
		case WM_LBUTTONDOWN:
			//如果格子没有打开,就打开格子,否则不做处理
			if (mine[openr][openc] > 9)
			{
				mine[openr][openc] -= 20;
				OpenNull(openr,openc);
				num++;
			}
			break;
		case WM_RBUTTONDOWN:
			//标记
			if (mine[openr][openc] > 9 && mine[openr][openc] <= 29)
			{
				mine[openr][openc] += 20;
			}
			else
			{
				mine[openr][openc] -= 20;
			}
			break;
		}
	}
}

进行递归操作,判断鼠标点击的地方

//递归打开所有空白,以及空白周围的数字
void OpenNull(int row,int col)
{
	//点击的是空白才能打开
	if (mine[row][col] == 0)
	{
		for (int i = row - 1; i <= row + 1; i++)
		{
			for (size_t k = col-1; k <= col+1; k++)
			{
				//如果为空或者不是雷就打开
				if ((mine[i][k] == 20 || mine[i][k] != 29)&& mine[i][k]>9)
				{
					mine[i][k] -= 20;
					num++;
					OpenNull(i, k);
				}
			}
		}
	}
}

游戏的输赢判断函数,注意理清逻辑

void Jude()
{
	//判断输
	if (mine[openr][openc] == 9)
	{
		int isok = MessageBox(GetHWnd(), "是否继续!", "点到雷了", MB_OKCANCEL);
		if (isok == IDOK)
		{
			mine[openr][openc] += 20;
		}
		else
		{
			exit(666);
		}
	}
	//判断赢
	if (num == ROW*COL - MINE_NUM)
	{
		int isok = MessageBox(GetHWnd(), "是否继续!", "点到雷了", MB_OKCANCEL);
		if (isok == IDOK)
		{
			num = 0;
			GameInit();
		}
		else
		{
			exit(666);
		}
	}
}

最后就是我们的主函数了

int main()
{
	GameInit();
	show();
	//循环处理游戏逻辑
	while (1)
	{
		MouseEvent();
		GameDraw();
		Jude();
	}
	
	getchar();
	return 0;
}

经典游戏《扫雷》完成,OK,简单总结一下,代码很简单,逻辑也不难,重要是大家一定要自己动手去做,这是毋庸置疑的,编程没有捷径,只有不断的学习熟练,加强自己的能力,有条件的话找个老师的话效果会更好,我也特意找了一个朋友叫他把一些关于《扫雷》的教学视频发给我,大家可以去我的主页上查看,我也希望下面的视频可以更好的帮助同学们掌握这个项目,好了,希望大家可以在这里得到自己想要的知识以及快乐吧,也希望大家可以给UP主一个关注,非常感谢大家了!!!

                                          

后续UP主还会发布更多的项目源码以及学习资料,希望大家可以持续关注,有什么问题可以回帖留言,我尽量回答。想要C/C++学习资料以及其他项目的源码的可以加群【765860056】了解。

点击下方链接观看详细视频讲解

【C/C++游戏开发系列教程】开发windows自带经典游戏“扫雷”!
                                                     

  • 7
    点赞
  • 50
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
下面是一个简单的 C++ 扫雷游戏教程,希望能帮到你: 首先,我们需要定义一个二维数组来表示扫雷棋盘,使用 0 表示空白,使用 -1 表示雷,使用其他数字表示周围雷的数量。 ```cpp const int MAXN = 105; int n, m; // 行数和列数 int a[MAXN][MAXN]; // 扫雷棋盘 ``` 接下来,我们需要生成雷的位置。可以使用 `rand()` 函数生成随机数,如果生成的随机数小于某个概率阈值,就将该格子设置为雷。 ```cpp const int P = 10; // 雷的概率为 10% void generateMines() { for (int i = 1; i <= n; i++) { for (int j = 1; j <= m; j++) { if (rand() % 100 < P) a[i][j] = -1; } } } ``` 接着,我们需要计算每个格子周围的雷的数量。遍历整个棋盘,对于每个不是雷的格子,统计周围 8 个格子中的雷的数量,并将该数量保存在该格子的值中。 ```cpp void countMines() { for (int i = 1; i <= n; i++) { for (int j = 1; j <= m; j++) { if (a[i][j] != -1) { int cnt = 0; for (int dx = -1; dx <= 1; dx++) { for (int dy = -1; dy <= 1; dy++) { if (dx == 0 && dy == 0) continue; // 当前格子不算 int x = i + dx, y = j + dy; if (x < 1 || x > n || y < 1 || y > m) continue; // 越界不算 if (a[x][y] == -1) cnt++; } } a[i][j] = cnt; } } } } ``` 现在,我们可以开始实现游戏的主循环了。每次循环,先输出当前棋盘的状态,然后让玩家输入一个坐标,根据该坐标的情况进行处理。如果该格子是雷,游戏结束;如果该格子是空白,扩展该格子周围的空白格子;如果该格子是数字,直接显示该数字。 ```cpp void printBoard(bool showMines = false) { for (int i = 1; i <= n; i++) { for (int j = 1; j <= m; j++) { if (showMines && a[i][j] == -1) cout << '*'; else if (a[i][j] == 0) cout << '.'; else if (a[i][j] == -1) cout << '.'; else cout << a[i][j]; } cout << endl; } } bool isWin() { // 判断是否胜利 for (int i = 1; i <= n; i++) { for (int j = 1; j <= m; j++) { if (a[i][j] >= 0 && a[i][j] <= 8 && !vis[i][j]) { return false; } } } return true; } void play() { memset(vis, 0, sizeof(vis)); int remain = n * m; while (true) { printBoard(); int x, y; cout << "Please enter the coordinate (x, y): "; cin >> x >> y; if (vis[x][y]) { cout << "This cell has already been uncovered." << endl; continue; } remain--; if (a[x][y] == -1) { // 点到雷了 printBoard(true); cout << "Game over!" << endl; break; } else if (a[x][y] == 0) { // 点到空白 queue<pair<int, int>> q; q.push(make_pair(x, y)); vis[x][y] = true; while (!q.empty()) { auto p = q.front(); q.pop(); int x = p.first, y = p.second; for (int dx = -1; dx <= 1; dx++) { for (int dy = -1; dy <= 1; dy++) { if (dx == 0 && dy == 0) continue; int nx = x + dx, ny = y + dy; if (nx < 1 || nx > n || ny < 1 || ny > m) continue; if (vis[nx][ny]) continue; vis[nx][ny] = true; remain--; if (a[nx][ny] == 0) { q.push(make_pair(nx, ny)); } } } } } else { // 点到数字 vis[x][y] = true; if (remain == 10) { // 剩余 10 个格子时,自动打开所有非雷格子并胜利 for (int i = 1; i <= n; i++) { for (int j = 1; j <= m; j++) { if (a[i][j] != -1) { vis[i][j] = true; } } } printBoard(); cout << "You win!" << endl; break; } } if (isWin()) { printBoard(); cout << "You win!" << endl; break; } } } ``` 完整代码如下:

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值