扫雷想必是大家小时候玩过的很经典的一款游戏,今天我就将其源代码及图片素材分享出来,供大家学习和参考,如若文案有错落的地方,还望大家指正。
目录
一.准备工作:
二.数组:初始化数表
1.用memset函数先全部初始化为0
2.布置雷
3.统计有多少个雷
4.“加密”处理
三.图像准备
四.数据的接收与处理
1.数据的接收与“打印”
2.数据处理
五.判断输赢与对话框
1.输赢
2.输赢之后的对话框
六.插入背景音乐
七.完整代码分享
一.准备工作:
(1)由于要包含<easyx.h>头文件,在c++环境下,检查自己有没有下载EasyX,没有则需到官网进行下载:EasyX Graphics Library for C++
(2)vs环境下, E0167 "const char *" 类型的实参与 "LPCWSTR" 类型的形参不兼容 , C2664 “MCIERROR mciSendStringW(LPCWSTR,LPWSTR,UINT,HWND)”: 无法将参数 1 从“const char [35]”转换为“LPCWSTR” 等问题可以通过修改项目属性解决。
1.项目-->属性-->配置属性-->高级-->字符集改成使用多字节字符集
2.项目-->属性-->C/C++ --> 语言-->符合模式改成否
(3)扫雷也肯定需要图片贴纸,下面是我为大家提供的图片素材,至于背景音乐,自己准备。
https://pan.baidu.com/s/1eUqI1q81Etqf75U_eRG4RA
提取码:ux3c
//先提前定义常量#define ROW 9#define COL 9#define mine 10 //雷的数量#define imgw 40 //图像的尺寸
二.数组:初始化数表
我们想要什么形式的数表?不同的集合的值应对应不同的对象。
首先是雷与不是雷必须区分开。如果-1表示雷,其他数字就必须代表不是雷。
其次,由于游戏规则,我们还要统计格子周围有几个雷,最好0表示周围没有雷,1表示周围有1个雷,2表示有两个……以此类推,方便后续的贴图。我们当然也不是一定需要数表上的数字这么显示,只需要雷的返回值与贴图的下标一一对应即可。
1.用memset函数先全部初始化为0
memset(map, 0, sizeof(int) * ROW * COL);
2.布置雷
我们将雷设置为-1,不是雷设置为0,这样统计雷的个数时,只用在雷的九宫格外围都+1:
srand((unsigned int)time(NULL)); for (int k = 1; k <= mine; k++){int x = rand() % ROW;int y = rand() % COL;if (map[x][y] == 0) //防止雷少设,达到所有雷全部被安置;map[x][y] = -1;}
放置10个雷,通过rand函数,确定随机雷的坐标。
3.统计有多少个雷
我们只需要以雷为中心,遍历棋盘中每一个元素,如果是雷,就在在九宫格外圈都加1,循环即可。注意到,棋盘的边缘存在越界的问题,故在if判断句里,不仅要保证是雷,而且要保证坐标不越界。
for (int i = 0; i < ROW; i++){for (int j = 0; j < COL; j++){if (map[i][j] == -1){for ( int r = i - 1; r <= i + 1; r++){for (int c = j - 1; c <= j + 1; c++){if ((r >= 0 && r < ROW && c >= 0 && c < COL) && map[r][c] != -1){++map[r][c];}}}}}}
4.“加密”处理
由于我们玩游戏的时候是看不到哪里是雷,哪里不是雷的,有个白色的东西盖住,他的代码实现就必须与雷区分开,与周围有几个雷的区分开,-1表示雷对应1个图像,0~8表示没有雷和周围有几个雷对应9个图像,那我们直接加20,19~28就可以表示在这一段时用白色的盖子盖住。同样的道理,再加20表示右键的小红旗.你可以将这些值理解为门牌号或者一个地址,贴纸具体贴哪张取决于值是什么。
for (int i = 0; i < ROW; i++){for (int j = 0; j < COL; j++){map[i][j] += 20;}}
三.图像准备
我们先创造一个窗口,载入数据
initgraph(ROW * imgw, COL * imgw);loadresource();/void loadresource() {for (int i = 0; i < 12; i++){char path[50] = { 0 };sprintf_s(path, "./images/%d.jpg", i);loadimage(images + i, path, imgw, imgw);}}
开始根据值进行贴纸:
void draw(int map[][COL]){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 * imgw, i * imgw, &images[map[i][j]]);}else if (map[i][j] == -1){putimage(j * imgw, i * imgw, &images[9]);}else if (map[i][j] >= 19 && map[i][j] <= 28){putimage(j * imgw, i * imgw, &images[10]);}else if (map[i][j] >= 39){putimage(j * imgw, i * imgw, &images[11]);}}}}
四.数据的接收与处理
1.数据的接收与“打印”
接收--》处理---》打印
while (true){ExMessage msg;while (peekmessage(&msg, EM_MOUSE)) //不断接收数据{switch (msg.message) //处理鼠标传来的数据{case WM_LBUTTONDOWN:case WM_RBUTTONDOWN:mouseMsg(&msg, map); //具体如何处理break;}}draw(map); //最后还是要展现出来,才看得见的。}
2.数据处理
draw(),只是描述了什么时候什么样的值贴什么样的图,只是静态的,数据处理,将值的大小发生改变,贴什么样的图随之改变就实现了将这个过程动起来。
操作对应值的变换,进而对应贴图变换,游戏往前推进。
左键:格子打开,19到28的数,跑到-1到8上对应的数上,贴图随之而变,并且还要实现,如果周围也没有雷,则炸开一片的功能。
右键:插小红旗,19到28的数,跑到29到48上,图由格子变成小红旗
void mouseMsg(ExMessage* msg, int map[][COL]){intc =msg->x / imgw;int r =msg->y / imgw;if (msg->message == WM_LBUTTONDOWN){if (map[r][c] >= 19 && map[r][c] <= 28){map[r][c] -= 20;boomblank(map, r, c);//system("cls");//show(map); }}else if (msg->message == WM_RBUTTONDOWN){if (map[r][c] >= 19 && map[r][c] <= 28){map[r][c] += 20;//system("cls");//show(map);}else if (map[r][c] >= 39){map[r][c] -= 20;//system("cls");//show(map);}}}void boomblank(int map[][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;boomblank(map, i, j);}}}}}
五.判断输赢与对话框
1.输赢
输了,就是点到雷了,赢了就是雷扫完了,无雷可扫了
int juge(int map[][COL], int row, int col){if (map[row][col] == -1 || map[row][col] == 19){return -1;}int cet = 0;for (int i = 0; i <= ROW; i++){for (int j = 0; j <= COL; j++){if (map[i][j] >= 0 && map[i][j] <= 8)cet++;}}if (ROW * COL - mine == cet){return 1;}return 0;}
2.输赢之后的对话框
输了再来一局,赢了大家也可以自由发挥
int ret =juge(map,msg.y/imgw,msg.x/imgw);if (ret == -1){int select = MessageBox(GetHWnd(), " 您被炸死了\n点击确认再来一局","扫雷", MB_OKCANCEL);if (select == IDOK){init(map);}else{exit(0);}}else if (ret == 1){int select = MessageBox(GetHWnd(), "你赢了\n点击确认再来一局", "扫雷", MB_OKCANCEL);if (select == IDOK){init(map);}else{exit(0);}}
六.插入背景音乐
需先声明#include<mmsystem.h> #pragma comment(lib,"winmm.lib")
然后再在想要的位置插入
mciSendString("open ./images/furina.mp3 alias bgm",NULL,0,NULL);mciSendString("play bgm", NULL, 0, NULL);//具体放什么背景音乐,大家下载到对应路径即可
七.完整代码分享
#define _CRT_SECURE_NO_WARNINGS 1#include<stdio.h>#include<stdlib.h>#include<time.h>#include<easyx.h>#include<graphics.h>#include<Windows.h>#include<mmsystem.h>#pragma comment(lib,"winmm.lib")#define ROW 9#define COL 9#define mine 10#define imgw 40IMAGE images[12];void loadresource(); // 导入图像资源void init(int map[][COL]); //初始化雷void show(int map[][COL]);void draw(int map[][COL]);void mouseMsg(ExMessage* msg, int map[][COL]);void boomblank(int map[][COL], int row, int col);int juge(int map[][COL], int row, int col);int main(){int map[ROW][COL];init(map);//show(map);initgraph(ROW * imgw, COL * imgw);//,EW_SHOWCONSOLEloadresource();mciSendString("open ./images/furina.mp3 alias bgm",NULL,0,NULL);mciSendString("play bgm", NULL, 0, NULL);while (true){ExMessage msg;while (peekmessage(&msg, EM_MOUSE)){switch (msg.message){case WM_LBUTTONDOWN:case WM_RBUTTONDOWN:mouseMsg(&msg, map);int ret =juge(map,msg.y/imgw,msg.x/imgw);if (ret == -1){int select = MessageBox(GetHWnd(), " 您被炸死了\n点击确认再来一局","扫雷", MB_OKCANCEL);if (select == IDOK){init(map);}else{exit(0);}}else if (ret == 1){int select = MessageBox(GetHWnd(), "你赢了\n点击确认再来一局", "扫雷", MB_OKCANCEL);if (select == IDOK){init(map);}else{exit(0);}}break;}}draw(map);}return 0;}void loadresource() {for (int i = 0; i < 12; i++){char path[50] = { 0 };sprintf_s(path, "./images/%d.jpg", i);loadimage(images + i, path, imgw, imgw);}} void init(int map[][COL]){// 全部初始化为零memset(map, 0, sizeof(int) * ROW * COL);srand((unsigned int)time(NULL));for (int k = 1; k <= mine; k++){int x = rand() % ROW;int y = rand() % COL;if (map[x][y] == 0) //防止雷少设,达到所有雷全部被安置;map[x][y] = -1;}for (int i = 0; i < ROW; i++){for (int j = 0; j < COL; j++){if (map[i][j] == -1){for ( int r = i - 1; r <= i + 1; r++){for (int c = j - 1; c <= j + 1; c++){if ((r >= 0 && r < ROW && c >= 0 && c < COL) && map[r][c] != -1){++map[r][c];}}}}}}for (int i = 0; i < ROW; i++){for (int j = 0; j < COL; j++){map[i][j] += 20;}}}void show(int map[][COL]){for (int i = 0; i < ROW; i++){for (int j = 0; j < COL; j++){printf("%2d ", map[i][j]);}printf("\n");}}void draw(int map[][COL]){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 * imgw, i * imgw, &images[map[i][j]]);}else if (map[i][j] == -1){putimage(j * imgw, i * imgw, &images[9]);}else if (map[i][j] >= 19 && map[i][j] <= 28){putimage(j * imgw, i * imgw, &images[10]);}else if (map[i][j] >= 39){putimage(j * imgw, i * imgw, &images[11]);}}}}void mouseMsg(ExMessage* msg, int map[][COL]){intc =msg->x / imgw;int r =msg->y / imgw;if (msg->message == WM_LBUTTONDOWN){if (map[r][c] >= 19 && map[r][c] <= 28){map[r][c] -= 20;boomblank(map, r, c);//system("cls");//show(map); }}else if (msg->message == WM_RBUTTONDOWN){if (map[r][c] >= 19 && map[r][c] <= 28){map[r][c] += 20;//system("cls");//show(map);}else if (map[r][c] >= 39){map[r][c] -= 20;//system("cls");//show(map);}}}void boomblank(int map[][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;boomblank(map, i, j);}}}}}int juge(int map[][COL], int row, int col){if (map[row][col] == -1 || map[row][col] == 19){return -1;}int cet = 0;for (int i = 0; i <= ROW; i++){for (int j = 0; j <= COL; j++){if (map[i][j] >= 0 && map[i][j] <= 8)cet++;}}if (ROW * COL - mine == cet){return 1;}return 0;}