当前位置:   article > 正文

C语言零基础项目:迷宫游戏!详细思路+源码分享_easyx迷宫游戏

easyx迷宫游戏

每天一个C语言小项目,提升你的编程能力!

迷宫游戏其实有很多种类型,比如说营救公主的一款,又比如说闯关的一款,其他的一些种类这里就不一一列举了。但是不管哪一款迷宫游戏,唯一不变的就是迷宫的实现这一核心功能,每一款迷宫游戏无非就是根据自己的游戏性质在迷宫之中添加一些必要的元素罢了。

我们今天的主要目标就是用C语言来实现迷宫的原本功能,纯天然无污染,没有添加任何防腐剂(因为没有其他的花里胡哨的功能)

上面就是咱们的效果图预览,以图形库easyX来搭配C语言语法实现的迷宫界面,然后也实现了例如一些按键操作,边界检测这样的功能,整体来说还是比较完善的。

本项目编译环境:Visual Studio 2019/2022,EasyX插件

代码展示:

  1. #include <stdio.h>
  2. #include <graphics.h>
  3. #include <time.h>
  4. //
  5. // 定义全局变量
  6. //
  7. BYTE** g_aryMap = NULL; // 迷宫地图
  8. SIZE g_szMap; // 迷宫地图的尺寸
  9. IMAGE g_imgSight(360, 280); // 游戏的视野
  10. RECT g_rtSight; // 游戏的视野的范围
  11. IMAGE g_imgItem(180, 20); // 地图元素
  12. IMAGE g_imgGPS; // 迷你地图,用于显示游戏者在地图中的位置
  13. POINT g_ptGPS; // 迷你地图的显示位置
  14. SIZE g_szGPS; // 迷你地图的尺寸
  15. POINT g_ptPlayer; // 游戏者的位置
  16. // 枚举地图元素,兼做元素位置的 x 坐标
  17. enum MAPITEM { MAP_WALL = 0, MAP_PLAYER = 20, MAP_GROUND = 40, MAP_MARKRED = 60, MAP_MARKGREEN = 80, MAP_MARKYELLOW = 100, MAP_ENTRANCE = 120, MAP_EXIT = 140, MAP_OUTSIDE = 160 };
  18. // 枚举用户的控制命令
  19. enum CMD { CMD_QUIT = 1, CMD_UP = 2, CMD_DOWN = 4, CMD_LEFT = 8, CMD_RIGHT = 16, CMD_MARKRED = 32, CMD_MARKGREEN = 64, CMD_MARKYELLOW = 128, CMD_CLEARMARK = 256 };
  20. //
  21. // 函数声明
  22. //
  23. void Welcome(); // 绘制游戏界面
  24. void InitImage(); // 初始化游戏图片
  25. void InitGame(); // 初始化游戏数据
  26. void GetMazeSize(); // 提示用户输入迷宫大小
  27. void MakeMaze(int width, int height); // 生成迷宫:初始化(注:宽高必须是奇数)
  28. void TravelMaze(int x, int y); // 生成迷宫:遍历 (x, y) 四周
  29. MAPITEM GetMazeItem(int x, int y); // 获取指定坐标的迷宫元素
  30. void Paint(); // 绘制视野范围内的迷宫
  31. int GetCmd(); // 获取用户输入的命令
  32. void DispatchCmd(int cmd); // 处理用户输入的命令
  33. void OnUp(); // 向上移动
  34. void OnLeft(); // 向左移动
  35. void OnRight(); // 向右移动
  36. void OnDown(); // 向下移动
  37. void OnMark(MAPITEM value); // 在地图中做标记
  38. bool CheckWin(); // 检查是否到出口
  39. bool Quit(); // 询问用户是否退出游戏
  40. //
  41. // 函数定义
  42. //
  43. // 主程序
  44. void main()
  45. {
  46. // 初始化
  47. initgraph(640, 480); // 创建绘图窗口
  48. srand((unsigned)time(NULL)); // 设置随机种子
  49. // 显示主界面
  50. Welcome();
  51. // 初始化
  52. InitImage();
  53. InitGame();
  54. // 游戏过程
  55. int c;
  56. while( !(((c = GetCmd()) & CMD_QUIT) && Quit()) )
  57. {
  58. DispatchCmd(c);
  59. Paint();
  60. if (CheckWin())
  61. break;
  62. // 延时
  63. Sleep(100);
  64. }
  65. // 清理迷宫地图占用的内存
  66. for(int x = 0; x < g_szMap.cx + 2; x++)
  67. delete[] g_aryMap[x];
  68. delete [] g_aryMap;
  69. // 关闭图形模式
  70. closegraph();
  71. }
  72. // 绘制游戏界面
  73. void Welcome()
  74. {
  75. // 绘制渐变色外框
  76. for(int i=0; i<128; i++)
  77. {
  78. setlinecolor(RGB(0, 0, (127 - i) << 1));
  79. rectangle(149 - i, 109 - (i >> 1), 490 + i, 370 + (i >> 1));
  80. }
  81. // 设置字体样式
  82. settextcolor(WHITE);
  83. setbkmode(TRANSPARENT);
  84. // 绘制标题
  85. settextstyle(36, 0, _T("宋体"));
  86. outtextxy(248, 40, _T("迷  宫"));
  87. // 绘制操作说明
  88. settextstyle(12, 0, _T("宋体"));
  89. outtextxy(50, 382, _T("控制说明:"));
  90. outtextxy(74, 400, _T("方向键或 A/S/D/W:移动"));
  91. outtextxy(74, 418, _T("空格、Y、G:在地图上做红、黄、绿色 M 标记"));
  92. outtextxy(74, 436, _T("C:清除地图上的标记"));
  93. outtextxy(74, 454, _T("ESC:退出程序"));
  94. }
  95. // 初始化游戏图片
  96. void InitImage()
  97. {
  98. // 预绘制游戏图片到 IMAGE 缓存(可以修改为加载图片以获得更好效果)
  99. SetWorkingImage(&g_imgItem);
  100. cleardevice();
  101. // 绘制 PLAYER
  102. setorigin(MAP_PLAYER, 0);
  103. setfillcolor(YELLOW);
  104. setlinecolor(YELLOW);
  105. fillellipse(2, 2, 17, 17);
  106. setlinecolor(BLACK);
  107. line(7, 7, 7, 8);
  108. line(12, 7, 12, 8);
  109. arc(5, 6, 14, 14, 3.34, 6.08);
  110. // 绘制墙壁
  111. setorigin(MAP_WALL, 0);
  112. settextcolor(BROWN);
  113. setfillstyle((BYTE*)"\x20\x20\x20\xff\x04\x04\x04\xff");
  114. setlinecolor(BROWN);
  115. solidrectangle(1, 1, 18, 18);
  116. rectangle(0, 0, 19, 19);
  117. // 绘制红色标记
  118. setorigin(MAP_MARKRED, 0);
  119. setlinecolor(RED);
  120. moveto(5, 15);
  121. linerel(0, -10); linerel(5, 5); linerel(5, -5); linerel(0, 10);
  122. // 绘制绿色标记
  123. setorigin(MAP_MARKGREEN, 0);
  124. setlinecolor(GREEN);
  125. moveto(5, 15);
  126. linerel(0, -10); linerel(5, 5); linerel(5, -5); linerel(0, 10);
  127. // 绘制黄色标记
  128. setorigin(MAP_MARKYELLOW, 0);
  129. setlinecolor(YELLOW);
  130. moveto(5, 15);
  131. linerel(0, -10); linerel(5, 5); linerel(5, -5); linerel(0, 10);
  132. // 绘制入口
  133. setorigin(MAP_ENTRANCE, 0);
  134. setlinecolor(GREEN);
  135. settextstyle(12, 0, _T("宋体"));
  136. outtextxy(4, 4, _T("入"));
  137. // 绘制出口
  138. setorigin(MAP_EXIT, 0);
  139. outtextxy(4, 4, _T("出"));
  140. // 绘制迷宫外面的空地
  141. setorigin(MAP_OUTSIDE, 0);
  142. settextcolor(GREEN);
  143. setfillstyle((BYTE*)"\x50\x55\x22\x20\x05\x55\x22\x02");
  144. solidrectangle(0, 0, 19, 19);
  145. // 恢复坐标系
  146. setorigin(0, 0);
  147. // 显示作者
  148. SetWorkingImage();
  149. settextcolor(BLUE);
  150. TCHAR author[] = _T("Powered by zhaoh1987@qq.com");
  151. outtextxy(471, 4, author);
  152. settextcolor(LIGHTBLUE);
  153. outtextxy(470, 3, author);
  154. }
  155. // 初始化游戏数据
  156. void InitGame()
  157. {
  158. // 提示用户输入迷宫大小
  159. GetMazeSize();
  160. // 初始化参数
  161. if (g_aryMap != NULL)
  162. { // 清理迷宫地图占用的内存
  163. for(int x = 0; x < g_szMap.cx + 2; x++)
  164. delete[] g_aryMap[x];
  165. delete [] g_aryMap;
  166. }
  167. MakeMaze(g_szMap.cx, g_szMap.cy); // 创建迷宫
  168. g_ptPlayer.x = 2; // 设置游戏者的位置
  169. g_ptPlayer.y = 2;
  170. g_rtSight.left = 0; // 设置视野范围
  171. g_rtSight.top = 0;
  172. g_rtSight.right = 17;
  173. g_rtSight.bottom= 13;
  174. // 设置 GPS 显示区
  175. setfillcolor(BLUE);
  176. solidrectangle(522, 368, 637, 471);
  177. if (g_szMap.cx > g_szMap.cy) { g_szGPS.cx = 100; g_szGPS.cy = (int)(100.0 * g_szMap.cy / g_szMap.cx + 0.5); }
  178. else { g_szGPS.cy = 100; g_szGPS.cx = (int)(100.0 * g_szMap.cx / g_szMap.cy + 0.5); }
  179. Resize(&g_imgGPS, g_szGPS.cx, g_szGPS.cy);
  180. g_ptGPS.x = 530 + 50 - g_szGPS.cx / 2;
  181. g_ptGPS.y = 370 + 50 - g_szGPS.cy / 2;
  182. // 画迷你地图外框
  183. setlinecolor(RED);
  184. rectangle(g_ptGPS.x - 1, g_ptGPS.y - 1, g_ptGPS.x + g_szGPS.cx, g_ptGPS.y + g_szGPS.cy);
  185. // 画迷你地图入口和出口
  186. setlinecolor(YELLOW);
  187. moveto(g_ptGPS.x - 8, g_ptGPS.y + g_szGPS.cy / g_szMap.cy);
  188. linerel(7, 0); linerel(-3, -3); moverel(3, 3); linerel(-3, 3);
  189. moveto(g_ptGPS.x + g_szGPS.cx, g_ptGPS.y + g_szGPS.cy - g_szGPS.cy / g_szMap.cy);
  190. linerel(7, 0); linerel(-3, -3); moverel(3, 3); linerel(-3, 3);
  191. // 绘制游戏区
  192. Paint();
  193. }
  194. // 提示用户输入迷宫大小
  195. void GetMazeSize()
  196. {
  197. g_szMap.cx = g_szMap.cy = 0;
  198. // 获取用户输入的宽高
  199. TCHAR s[4];
  200. while(g_szMap.cx < 20 || g_szMap.cx > 200)
  201. {
  202. InputBox(s, 4, _T("请输入迷宫的宽度\n范围:20~200"), _T("输入"), _T("25"));
  203. g_szMap.cx = _ttoi(s);
  204. }
  205. while(g_szMap.cy < 20 || g_szMap.cx > 200)
  206. {
  207. InputBox(s, 4, _T("请输入迷宫的高度\n范围:20~200"), _T("输入"), _T("25"));
  208. g_szMap.cy = _ttoi(s);
  209. }
  210. // 确保宽高为奇数
  211. if (g_szMap.cx % 2 != 1) g_szMap.cx++;
  212. if (g_szMap.cy % 2 != 1) g_szMap.cy++;
  213. }
  214. // 生成迷宫:初始化(注:宽高必须是奇数)
  215. void MakeMaze(int width, int height)
  216. {
  217. if (width % 2 != 1 || height % 2 != 1)
  218. return;
  219. int x, y;
  220. // 定义迷宫尺寸,并分配迷宫内存
  221. g_aryMap = new BYTE*[width + 2];
  222. for(x = 0; x < width + 2; x++)
  223. {
  224. g_aryMap[x] = new BYTE[height + 2];
  225. memset(g_aryMap[x], MAP_WALL, height + 2);
  226. }
  227. // 定义边界
  228. for (x = 0; x <= width + 1; x++)
  229. g_aryMap[x][0] = g_aryMap[x][height + 1] = MAP_GROUND;
  230. for (y = 1; y <= height; y++)
  231. g_aryMap[0][y] = g_aryMap[width + 1][y] = MAP_GROUND;
  232. // 定义入口和出口
  233. g_aryMap[1][2] = MAP_ENTRANCE;
  234. g_aryMap[width][height - 1] = MAP_EXIT;
  235. // 从任意点开始遍历生成迷宫
  236. TravelMaze(((rand() % (width - 1)) & 0xfffe) + 2, ((rand() % (height - 1)) & 0xfffe) + 2);
  237. // 将边界标记为迷宫外
  238. for (x = 0; x <= width + 1; x++)
  239. g_aryMap[x][0] = g_aryMap[x][height + 1] = MAP_OUTSIDE;
  240. for (y = 1; y <= height; y++)
  241. g_aryMap[0][y] = g_aryMap[width + 1][y] = MAP_OUTSIDE;
  242. }
  243. // 生成迷宫:遍历 (x, y) 四周
  244. void TravelMaze(int x, int y)
  245. {
  246. // 定义遍历方向
  247. int d[4][2] = {0, 1, 1, 0, 0, -1, -1, 0};
  248. // 将遍历方向乱序
  249. int n, t, i;
  250. for(i = 0; i < 4; i++)
  251. {
  252. n = rand() % 4;
  253. t = d[i][0], d[i][0] = d[n][0], d[n][0] = t;
  254. t = d[i][1], d[i][1] = d[n][1], d[n][1] = t;
  255. }
  256. // 尝试周围四个方向
  257. g_aryMap[x][y] = MAP_GROUND;
  258. for(i = 0; i < 4; i++)
  259. if (g_aryMap[x + 2 * d[i][0]][y + 2 * d[i][1]] == MAP_WALL)
  260. {
  261. g_aryMap[x + d[i][0]][y + d[i][1]] = MAP_GROUND;
  262. TravelMaze(x + d[i][0] * 2, y + d[i][1] * 2); // 递归
  263. }
  264. }
  265. // 获取指定坐标的迷宫元素
  266. MAPITEM GetMazeItem(int x, int y)
  267. {
  268. return (MAPITEM)g_aryMap[x][y];
  269. }
  270. // 绘制视野范围内的迷宫
  271. void Paint()
  272. {
  273. int x1, y1;
  274. // 绘制视野内的迷宫
  275. SetWorkingImage(&g_imgSight);
  276. for(int x = g_rtSight.left; x <= g_rtSight.right; x++)
  277. for(int y = g_rtSight.top; y <= g_rtSight.bottom; y++)
  278. {
  279. x1 = (x - g_rtSight.left) * 20;
  280. y1 = (y - g_rtSight.top) * 20;
  281. putimage(x1, y1, 20, 20, &g_imgItem, GetMazeItem(x, y), 0);
  282. }
  283. // 绘制游戏者
  284. x1 = (g_ptPlayer.x - g_rtSight.left) * 20;
  285. y1 = (g_ptPlayer.y - g_rtSight.top) * 20;
  286. putimage(x1, y1, 20, 20, &g_imgItem, MAP_PLAYER, 0);
  287. // 绘制迷你地图
  288. SetWorkingImage(&g_imgGPS);
  289. cleardevice();
  290. int tx = (int)((g_ptPlayer.x - 1) * g_szGPS.cx / (double)(g_szMap.cx - 1) + 0.5);
  291. int ty = (int)((g_ptPlayer.y - 1) * g_szGPS.cy / (double)(g_szMap.cy - 1) + 0.5);
  292. setlinecolor(YELLOW);
  293. circle(tx, ty, 1);
  294. // 更新到绘图窗口
  295. SetWorkingImage();
  296. putimage(150, 110, 340, 260, &g_imgSight, 10, 10);
  297. putimage(g_ptGPS.x, g_ptGPS.y, &g_imgGPS);
  298. }
  299. // 获取用户输入的命令
  300. int GetCmd()
  301. {
  302. int c = 0;
  303. if (GetAsyncKeyState(VK_LEFT) & 0x8000) c |= CMD_LEFT;
  304. if (GetAsyncKeyState(VK_RIGHT) & 0x8000) c |= CMD_RIGHT;
  305. if (GetAsyncKeyState(VK_UP) & 0x8000) c |= CMD_UP;
  306. if (GetAsyncKeyState(VK_DOWN) & 0x8000) c |= CMD_DOWN;
  307. if (GetAsyncKeyState('A') & 0x8000) c |= CMD_LEFT;
  308. if (GetAsyncKeyState('D') & 0x8000) c |= CMD_RIGHT;
  309. if (GetAsyncKeyState('W') & 0x8000) c |= CMD_UP;
  310. if (GetAsyncKeyState('S') & 0x8000) c |= CMD_DOWN;
  311. if (GetAsyncKeyState(' ') & 0x8000) c |= CMD_MARKRED;
  312. if (GetAsyncKeyState('G') & 0x8000) c |= CMD_MARKGREEN;
  313. if (GetAsyncKeyState('Y') & 0x8000) c |= CMD_MARKYELLOW;
  314. if (GetAsyncKeyState('C') & 0x8000) c |= CMD_CLEARMARK;
  315. if (GetAsyncKeyState(VK_ESCAPE) & 0x8000) c |= CMD_QUIT;
  316. return c;
  317. }
  318. // 处理用户输入的命令
  319. void DispatchCmd(int cmd)
  320. {
  321. if (cmd & CMD_UP) OnUp();
  322. if (cmd & CMD_DOWN) OnDown();
  323. if (cmd & CMD_LEFT) OnLeft();
  324. if (cmd & CMD_RIGHT) OnRight();
  325. if (cmd & CMD_MARKRED) OnMark(MAP_MARKRED);
  326. if (cmd & CMD_MARKGREEN) OnMark(MAP_MARKGREEN);
  327. if (cmd & CMD_MARKYELLOW) OnMark(MAP_MARKYELLOW);
  328. if (cmd & CMD_CLEARMARK) OnMark(MAP_GROUND);
  329. }
  330. // 向上移动
  331. void OnUp()
  332. {
  333. if (g_ptPlayer.y > 1 && GetMazeItem(g_ptPlayer.x, g_ptPlayer.y - 1) != MAP_WALL)
  334. {
  335. g_ptPlayer.y--;
  336. if (g_ptPlayer.y - g_rtSight.top < 4 && g_rtSight.top > 0)
  337. {
  338. g_rtSight.top--;
  339. g_rtSight.bottom--;
  340. }
  341. }
  342. }
  343. // 向左移动
  344. void OnLeft()
  345. {
  346. if (g_ptPlayer.x > 1 && GetMazeItem(g_ptPlayer.x - 1, g_ptPlayer.y) != MAP_WALL && GetMazeItem(g_ptPlayer.x - 1, g_ptPlayer.y) != MAP_ENTRANCE)
  347. {
  348. g_ptPlayer.x--;
  349. if (g_ptPlayer.x - g_rtSight.left < 5 && g_rtSight.left > 0)
  350. {
  351. g_rtSight.left--;
  352. g_rtSight.right--;
  353. }
  354. }
  355. }
  356. // 向右移动
  357. void OnRight()
  358. {
  359. if (g_ptPlayer.x < g_szMap.cx && GetMazeItem(g_ptPlayer.x + 1, g_ptPlayer.y) != MAP_WALL)
  360. {
  361. g_ptPlayer.x++;
  362. if (g_rtSight.right - g_ptPlayer.x < 5 && g_rtSight.right <= g_szMap.cx)
  363. {
  364. g_rtSight.left++;
  365. g_rtSight.right++;
  366. }
  367. }
  368. }
  369. // 向下移动
  370. void OnDown()
  371. {
  372. if (g_ptPlayer.y < g_szMap.cy && GetMazeItem(g_ptPlayer.x, g_ptPlayer.y + 1) != MAP_WALL)
  373. {
  374. g_ptPlayer.y++;
  375. if (g_rtSight.bottom - g_ptPlayer.y < 4 && g_rtSight.bottom <= g_szMap.cy)
  376. {
  377. g_rtSight.top++;
  378. g_rtSight.bottom++;
  379. }
  380. }
  381. }
  382. // 在地图中做标记
  383. void OnMark(MAPITEM value)
  384. {
  385. g_aryMap[g_ptPlayer.x][g_ptPlayer.y] = value;
  386. }
  387. // 检查是否到出口
  388. bool CheckWin()
  389. {
  390. if (g_ptPlayer.x == g_szMap.cx && g_ptPlayer.y == g_szMap.cy - 1)
  391. {
  392. HWND hwnd = GetHWnd();
  393. if (MessageBox(hwnd, _T("恭喜你走出来了!\n您想再来一局吗?"), _T("恭喜"), MB_YESNO | MB_ICONQUESTION) == IDYES)
  394. {
  395. InitGame();
  396. return false;
  397. }
  398. else
  399. return true;
  400. }
  401. return false;
  402. }
  403. // 询问用户是否退出游戏
  404. bool Quit()
  405. {
  406. HWND hwnd = GetHWnd();
  407. return (MessageBox(hwnd, _T("您确定要退出游戏吗?"), _T("询问"), MB_OKCANCEL | MB_ICONQUESTION) == IDOK);
  408. }

大家赶紧去动手试试吧!

此外,我也给大家分享我收集的其他资源,从最零基础开始的教程到C语言C++项目案例,帮助大家在学习C语言的道路上披荆斩棘!

整理分享(多年学习的源码、项目实战视频、项目笔记,基础入门教程)最重要的是你可以在群里面交流提问编程问题哦!

欢迎转行和学习编程的伙伴,利用更多的资料学习成长比自己琢磨更快哦!(↓↓↓↓↓↓)

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Monodyee/article/detail/358885
推荐阅读
相关标签
  

闽ICP备14008679号