当前位置:   article > 正文

C/C++ 扫雷游戏制作_while (!gameover) {mousemsg mousemsg;while (true)

while (!gameover) {mousemsg mousemsg;while (true) {mousemsg = getmousemsg();

扫雷游戏制作过程

一、创作工具 

   1、visual studio 2022

   2、easyx图形库

二、准备工作

   1、图片

   2、背景音乐   

    整合在一个文件夹里,与源文件放在一起,重命名方便操作

    

三、创作思路

   1、设置雷(-1)代表雷,空白格子为0,数字[0-8](雷周围整体加一,可叠加)雷周围数字出现情况:1—8
  运用二维数组知识
  静态数组int map[ ](不能改变)
  动态数组int **map = malloc(10,sizeof(int):
  生成的数组中每一个格子装了一个int*的指针
  for(int i =0;i<10;i++)
  {
      map[i] = malloc(10,sizeof(int);
  }
  以此完成动态数组的申请;优势:可以随时扩容

   2、利用easyx图形库函数创建一个窗口,创建一个存储图片数组,将准备好的图片放入指定位置(利用坐标实现)
   3、排雷:

         鼠标左击消除对应小格,右击标记(可用别的按键实现)
   4、判定输赢条件

         点到地雷,游戏结束;所有地雷被红旗标记,获得胜利,游戏结束

四、代码实现

    头文件设置,宏观设置

  1. #include<stdio.h>
  2. #include<stdlib.h>
  3. #include<time.h>
  4. #include<easyx.h>//图形库
  5. //动态数组分配需要用到的头文件
  6. #include<mmsystem.h>//添加音乐用到的头文件
  7. #pragma comment(lib,"winmm.lib")//取消警告(库文件)
  8. #define SIZE 20//设置图片尺寸大小
  9. #define num 100//设置雷的数量
  10. //加载资源,图片
  11. IMAGE imgs[12];//存储图片数组

加载填充图片

  1. void loadpng()
  2. {
  3. for (int i = 0;i < 12;i++)
  4. {
  5. char buf[50] = { 0 };
  6. sprintf(buf,"./photos/%d.png", i);//使得下面的图片也能跟着变换,注意要用到spintf完成
  7. //用sprintf时1.项目2.c/c++3.常规4.SDL检查5.选择否,软件问题可以去掉
  8. loadimage(imgs + i, buf, SIZE, SIZE);//用这个加图片时,1.项目2.属性3.高级4.字符集5.选则使用多节字符集
  9. }
  10. };

初始化Map,内存申请

  1. struct Map
  2. {
  3. int** arr;//二维数组指针
  4. int rows;//行数
  5. int cols;//列数
  6. };
  1. void map_init(Map* map, int rows, int cols)
  2. {
  3. map->rows = rows;
  4. map->cols = cols;
  5. map->arr = (int**)calloc(map->rows,sizeof(int*));//calloc是malloc的兄弟函数
  6. if (!map->arr)
  7. {
  8. printf("内存申请失败\n");
  9. return;
  10. }
  11. for (int i = 0;i < map->rows;i++)
  12. {
  13. map->arr[i] = (int*)calloc(map->cols, sizeof(int));//(int*)强转为int
  14. if (!map->arr[i])
  15. {
  16. printf("内存申请失败");//一般来说必须返回确认是否成功
  17. return;
  18. }
  19. }
  20. }

设置雷(-1)

  1. //设置雷 -1表示在0处随机设置-1形成雷
  2. void map_setMine(Map* map)
  3. {
  4. //设置随机数种子
  5. srand(time(NULL));//时间头文件
  6. for (int i = 0;i < num;)
  7. {
  8. //随机产生下标[0-10]
  9. int r = rand() % map->rows;
  10. int c = rand() % map->cols;
  11. if (map->arr[r][c] == 0)
  12. {
  13. map->arr[r][c] = -1;//雷设置完成,注意:此过程可能会使-1位置重复,
  14. //导致设置雷小于指定数,解决方法:加循环
  15. i++; //从for里把i++拿出来表示只有成功设置雷,才自增
  16. }
  17. }
  18. //把以雷为中心的九宫格的数字都加一(雷除外)形成数字0-8
  19. for (int i = 0; i < map->rows; i++)
  20. {
  21. for (int k = 0; k < map->cols; k++)
  22. {
  23. if (map->arr[i][k] == -1)
  24. {
  25. //以雷为中心的九宫格
  26. for (int r = i-1; r <= i+1; r++)
  27. {
  28. for (int c = k-1; c <= k+1; c++)
  29. {
  30. //排除掉雷
  31. if ((r >= 0 && r < map->rows && c >= 0 && c < map->cols)
  32. && map->arr[r][c] != -1)//此时加入边界判断即可恢复代码中断问题
  33. {
  34. map->arr[r][c] += 1;//此时代码有问题,可能会导致代码运行中断,
  35. //因为会越界(雷在边界),九宫格出现行超出列超出的情况
  36. }
  37. }
  38. }
  39. }
  40. }
  41. }
  42. }

 输出一下所有数组,用于观察纠错,修正,

添加排查操作,用于检测标记方格是否为雷,如果排查标记出所有雷,则胜利,游戏结束

  1. //输出一下所有数组
  2. void map_show(Map* map)
  3. {
  4. int cir=0;//设置变量
  5. for (int i = 0;i < map->rows;i++)
  6. {
  7. for (int k = 0;k < map->cols;k++)
  8. {
  9. printf(" %2d ", map->arr[i][k]);
  10. if (map->arr[i][k] == 59)//判断标记的红旗是否为雷
  11. {
  12. cir += 1;
  13. if (cir == num)//如果排查出雷的数量与设置雷的数量一致则胜利
  14. {
  15. {
  16. WinWindow();//输出成功窗口,游戏结束
  17. getchar();
  18. }
  19. }
  20. }
  21. printf("\n\n");
  22. }
  23. }

 覆盖起来,进行加密操作,将(-1—8)数字加20变为(19—28)进行加密

  1. //覆盖起来,加密操作
  2. void map_cover(Map* map)
  3. {
  4. //让现在数组里的数据变得不一样
  5. for (int i = 0; i < map->rows; i++)
  6. {
  7. for (int k = 0; k < map->cols; k++)
  8. {
  9. map->arr[i][k] += 20;
  10. }
  11. }
  12. }

绘制界面,图形窗口

  1. //绘制界面(图形窗口)
  2. void map_draw(Map* map)
  3. {
  4. for (int i = 0;i < map->rows;i++)
  5. {
  6. for (int k = 0; k < map->cols; k++)
  7. {
  8. //求每个格子的坐标
  9. int x = k * SIZE;
  10. int y = i * SIZE;
  11. if (map->arr[i][k] >= 0 && map->arr[i][k] <= 8)
  12. {
  13. putimage(x, y, imgs + map->arr[i][k]);//放置图片
  14. }
  15. else if (map->arr[i][k] == -1)
  16. {
  17. putimage(x, y, imgs + 11);//防置雷的图片
  18. }//加密后最小为19,最大为28
  19. else if (map->arr[i][k] >= 19 && map->arr[i][k] <= 28)
  20. {
  21. putimage(x, y, imgs + 9);//放置蓝色格子
  22. }
  23. else if (map->arr[i][k] >= 59 && map->arr[i][k] <= 68)
  24. {
  25. putimage(x, y, imgs + 10);//插旗标记
  26. }
  27. if (map->arr[i][k] == -1)
  28. {
  29. DieWindow();//点到雷了,生成游戏结束窗口,游戏结束
  30. getchar();//防止闪屏
  31. }
  32. }
  33. }
  34. }

左击格子打开与右击格子标记插旗,二次加密

  1. void map_mouseMsg(Map* map, ExMessage* msg)//消息参数,用鼠标需要的参数
  2. {
  3. //根据鼠标坐标,获得点击的格子的下标
  4. int r = msg->y / SIZE;
  5. int c = msg->x / SIZE;
  6. //判断鼠标左键是否点击
  7. if (msg->message == WM_LBUTTONDOWN&& map->arr[r][c] >= 19 && map->arr[r][c] <= 68)
  8. {
  9. if (map->arr[r][c] != 59)
  10. {
  11. map->arr[r][c] -= 20;//左击减数字,消除加密;
  12. map_recOpen(map, r, c);
  13. system("cls");
  14. map_show(map);//换进来不需要取地址&
  15. }
  16. }
  17. //判断是否为右键点击
  18. if (msg->message == WM_RBUTTONDOWN && map->arr[r][c] >= 19 && map->arr[r][c] <= 28)
  19. {
  20. map->arr[r][c] += 40;//右键标记雷,插旗子二次加密
  21. map_recOpen(map, r, c);
  22. system("cls");
  23. map_show(map);//换进来不需要取地址&
  24. }
  25. }

用递归法实现:若点击为空白格子则打开周围所有空白格子和数字

  1. //如果点到空白格子,递归打开周围的所有的空白格子和数字格子
  2. void map_recOpen(Map* map,int row,int col)
  3. {
  4. //先判断一下是否为空白格子
  5. if (map->arr[row][col] != 0)
  6. {
  7. return;
  8. }
  9. //延申到以空白格子为中心的九宫格
  10. for (int r = row - 1; r <= row + 1; r++)
  11. {
  12. for (int c = col - 1; c <= col + 1; c++)
  13. {
  14. if ((r >= 0 && r < map->rows && c >= 0 && c < map->cols)
  15. &&map->arr[r][c]>=19&&map->arr[r][c]<=28)
  16. {
  17. map->arr[r][c] -= 20;
  18. map_recOpen(map, r, c);//递归,自己调用自己
  19. }
  20. }
  21. }
  22. }

绘制游戏失败窗口,要用到easyx图形库

  1. void DieWindow()
  2. {
  3. BeginBatchDraw();//双缓冲绘图
  4. initgraph(400, 400);
  5. setbkmode(TRANSPARENT);
  6. settextcolor(LIGHTBLUE);
  7. outtextxy(150, 150, "BOOM!BOOM!");
  8. outtextxy(150, 120, "GameOver");
  9. EndBatchDraw();
  10. }

绘制游戏胜利窗口

  1. void WinWindow()
  2. {
  3. BeginBatchDraw();//双缓冲绘图
  4. initgraph(400, 400);
  5. setbkmode(TRANSPARENT);
  6. settextcolor(LIGHTBLUE);
  7. outtextxy(150, 150, "WOW!COOL!");
  8. outtextxy(150, 120, "恭喜你通关了");
  9. EndBatchDraw();
  10. }

主函数

  1. int main()
  2. {
  3. //创建图形窗口
  4. initgraph(800, 600,EW_SHOWCONSOLE);//长,宽,显示控制台
  5. //播放背景音乐
  6. mciSendString("open ./photos/trip.mp3 alias bgm", NULL, 0, NULL);
  7. mciSendString("play bgm repeat", NULL, 0, NULL);//循环播放
  8. loadpng();
  9. Map map;
  10. map_init(&map,30,40);//行,列
  11. map_setMine(&map);//&取地址
  12. map_cover(&map);
  13. //消息循环
  14. while (true)
  15. {
  16. ExMessage msg = {0};
  17. while (peekmessage(&msg, EM_MOUSE))
  18. {
  19. map_mouseMsg(&map, &msg);
  20. //map_show(&map);
  21. map_draw(&map);
  22. //WinGame(&map,m,n);
  23. }//用while循环因为括号内消息为真则继续循环
  24. }
  25. getchar();
  26. return 0;
  27. }

五、代码整体预览

注意函数调用问题和逻辑问题

  1. #include<stdio.h>
  2. #include<stdlib.h>
  3. #include<time.h>
  4. #include<easyx.h>//图形库
  5. //动态数组分配需要用到的头文件
  6. #include<mmsystem.h>//添加音乐用到的头文件
  7. #pragma comment(lib,"winmm.lib")//取消警告(库文件)
  8. #define SIZE 20//图片尺寸
  9. #define num 10//雷的数量
  10. //加载资源,图片
  11. IMAGE imgs[12];//存储图片数组
  12. void loadpng()
  13. {
  14. for (int i = 0;i < 12;i++)
  15. {
  16. char buf[50] = { 0 };
  17. sprintf(buf,"./photos/%d.png", i);//使得下面的图片也能跟着变换,注意要用到spintf完成
  18. //用sprintf时1.项目2.c/c++3.常规4.SDL检查5.选择否,软件问题可以去掉
  19. loadimage(imgs + i, buf, SIZE, SIZE);//用这个加图片时,1.项目2.属性3.高级4.字符集5.选则使用多节字符集
  20. }
  21. };
  22. struct Map
  23. {
  24. int** arr;//二维数组指针
  25. int rows;//行数
  26. int cols;//列数
  27. };
  28. void map_recOpen(Map* map, int row, int col);
  29. void DieWindow();
  30. void WinWindow();
  31. //初始化MAP
  32. void map_init(Map* map, int rows, int cols)
  33. {
  34. map->rows = rows;
  35. map->cols = cols;
  36. map->arr = (int**)calloc(map->rows,sizeof(int*));//calloc是malloc的兄弟函数
  37. if (!map->arr)
  38. {
  39. printf("内存申请失败\n");
  40. return;
  41. }
  42. for (int i = 0;i < map->rows;i++)
  43. {
  44. map->arr[i] = (int*)calloc(map->cols, sizeof(int));//(int*)强转为int
  45. if (!map->arr[i])
  46. {
  47. printf("内存申请失败");//一般来说必须返回确认是否成功
  48. return;
  49. }
  50. }
  51. }
  52. //输出一下所有数组
  53. void map_show(Map* map)
  54. {
  55. int cir=0;
  56. for (int i = 0;i < map->rows;i++)
  57. {
  58. for (int k = 0;k < map->cols;k++)
  59. {
  60. printf(" %2d ", map->arr[i][k]);
  61. if (map->arr[i][k] == 59)
  62. {
  63. cir += 1;
  64. if (cir == num)
  65. {
  66. WinWindow();
  67. getchar();
  68. }
  69. }
  70. }
  71. printf("\n\n");
  72. }
  73. }
  74. //设置雷 -1表示在0处随机设置-1形成雷
  75. void map_setMine(Map* map)
  76. {
  77. //设置随机数种子
  78. srand(time(NULL));//时间头文件
  79. for (int i = 0;i < num;)
  80. {
  81. //随机产生下标[0-10]
  82. int r = rand() % map->rows;
  83. int c = rand() % map->cols;
  84. if (map->arr[r][c] == 0)
  85. {
  86. map->arr[r][c] = -1;//雷设置完成,但是这样会使=1位置重复,导致小于10个,方法:加循环
  87. i++;//从for里把i++拿出来表示只有成功设置雷,才自增
  88. }
  89. }
  90. //把以雷为中心的九宫格的数字都加一(雷除外)形成数字0-8
  91. for (int i = 0; i < map->rows; i++)
  92. {
  93. for (int k = 0; k < map->cols; k++)
  94. {
  95. if (map->arr[i][k] == -1)
  96. {
  97. //以雷为中心的九宫格
  98. for (int r = i-1; r <= i+1; r++)
  99. {
  100. for (int c = k-1; c <= k+1; c++)
  101. {
  102. //排除掉雷
  103. if ((r >= 0 && r < map->rows && c >= 0 && c < map->cols)
  104. && map->arr[r][c] != -1)//此时加入边界判断即可恢复代码中断问题
  105. {
  106. map->arr[r][c] += 1;//此时代码有问题,可能会导致代码运行中断,因为会越界(雷在边界),九宫格出现行超出列超出的情况
  107. }
  108. }
  109. }
  110. }
  111. }
  112. }
  113. }
  114. //绘制界面(图形窗口)
  115. void map_draw(Map* map)
  116. {
  117. for (int i = 0;i < map->rows;i++)
  118. {
  119. for (int k = 0; k < map->cols; k++)
  120. {
  121. //求每个格子的坐标
  122. int x = k * SIZE;
  123. int y = i * SIZE;
  124. if (map->arr[i][k] >= 0 && map->arr[i][k] <= 8)
  125. {
  126. putimage(x, y, imgs + map->arr[i][k]);
  127. }
  128. else if (map->arr[i][k] == -1)
  129. {
  130. putimage(x, y, imgs + 11);
  131. }//加密后最小为19,最大为28
  132. else if (map->arr[i][k] >= 19 && map->arr[i][k] <= 28)
  133. {
  134. putimage(x, y, imgs + 9);
  135. }
  136. else if (map->arr[i][k] >= 59 && map->arr[i][k] <= 68)
  137. {
  138. putimage(x, y, imgs + 10);
  139. }
  140. if (map->arr[i][k] == -1)
  141. {
  142. DieWindow();
  143. getchar();
  144. }
  145. }
  146. }
  147. }
  148. //覆盖起来,加密操作
  149. void map_cover(Map* map)
  150. {
  151. //让现在数组里的数据变得不一样
  152. for (int i = 0; i < map->rows; i++)
  153. {
  154. for (int k = 0; k < map->cols; k++)
  155. {
  156. map->arr[i][k] += 20;
  157. }
  158. }
  159. }
  160. //点击格子打开
  161. void map_mouseMsg(Map* map, ExMessage* msg)//消息参数,用鼠标需要的参数
  162. {
  163. //根据鼠标坐标,获得点击的格子的下标
  164. int r = msg->y / SIZE;
  165. int c = msg->x / SIZE;
  166. //判断鼠标左键是否点击
  167. if (msg->message == WM_LBUTTONDOWN&& map->arr[r][c] >= 19 && map->arr[r][c] <= 68)
  168. {
  169. if (map->arr[r][c] != 59)
  170. {
  171. map->arr[r][c] -= 20;//左击减数字,消除加密;
  172. map_recOpen(map, r, c);
  173. system("cls");
  174. map_show(map);//换进来不需要取地址&
  175. }
  176. }
  177. //判断是否为右键点击
  178. if (msg->message == WM_RBUTTONDOWN && map->arr[r][c] >= 19 && map->arr[r][c] <= 28)
  179. {
  180. map->arr[r][c] += 40;//右键标记雷,插旗子二次加密
  181. map_recOpen(map, r, c);
  182. system("cls");
  183. map_show(map);//换进来不需要取地址&
  184. }
  185. }
  186. //如果点到空白格子,递归打开周围的所有的空白格子和数字格子
  187. void map_recOpen(Map* map,int row,int col)
  188. {
  189. //先判断一下是否为空白格子
  190. if (map->arr[row][col] != 0)
  191. {
  192. return;
  193. }
  194. //遍历空白格子为中心的九宫格
  195. for (int r = row - 1; r <= row + 1; r++)
  196. {
  197. for (int c = col - 1; c <= col + 1; c++)
  198. {
  199. if ((r >= 0 && r < map->rows && c >= 0 && c < map->cols)
  200. &&map->arr[r][c]>=19&&map->arr[r][c]<=28)
  201. {
  202. map->arr[r][c] -= 20;
  203. map_recOpen(map, r, c);//递归,自己调用自己
  204. }
  205. }
  206. }
  207. }
  208. void DieWindow()
  209. {
  210. BeginBatchDraw();//双缓冲绘图
  211. initgraph(400, 400);
  212. setbkmode(TRANSPARENT);
  213. settextcolor(LIGHTBLUE);
  214. outtextxy(150, 150, "BOOM!BOOM!");
  215. outtextxy(150, 120, "GameOver");
  216. EndBatchDraw();
  217. }
  218. void WinWindow()
  219. {
  220. BeginBatchDraw();//双缓冲绘图
  221. initgraph(400, 400);
  222. setbkmode(TRANSPARENT);
  223. settextcolor(LIGHTBLUE);
  224. outtextxy(150, 150, "WOW!COOL!");
  225. outtextxy(150, 120, "恭喜你通关了");
  226. EndBatchDraw();
  227. }
  228. int main()
  229. {
  230. //创建图形窗口
  231. initgraph(800, 600,EW_SHOWCONSOLE);//长,宽,显示控制台
  232. //播放背景音乐
  233. mciSendString("open ./photos/trip.mp3 alias bgm", NULL, 0, NULL);
  234. mciSendString("play bgm repeat", NULL, 0, NULL);//循环播放
  235. loadpng();
  236. Map map;
  237. map_init(&map,30,40);//行列
  238. map_setMine(&map);//&取地址
  239. map_cover(&map);
  240. //消息循环
  241. while (true)
  242. {
  243. ExMessage msg = {0};
  244. while (peekmessage(&msg, EM_MOUSE))
  245. {
  246. map_mouseMsg(&map, &msg);
  247. //map_show(&map);
  248. map_draw(&map);
  249. //WinGame(&map,m,n);
  250. }//用while循环因为括号内消息为真则继续循环
  251. }
  252. getchar();
  253. return 0;
  254. }

六、运行结果预览

生成游戏

 点击空白格子,递归打开周围空白格子和数字

  点击到雷,游戏结束,失败

标记出所有雷,游戏胜利

声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop】
推荐阅读
相关标签
  

闽ICP备14008679号