当前位置:   article > 正文

C语言贪吃蛇游戏代码,贪吃蛇C语言代码实现大全_贪吃蛇代码

贪吃蛇代码

一、C语言贪吃蛇代码实现前言

设计贪吃蛇游戏的主要目的是让大家夯实C语言基础,训练编程思维,培养解决问题的思路,领略多姿多彩的C语言。

贪吃蛇是非常经典的一款游戏,本次我们模拟在控制台实现贪吃蛇游戏,也就是实现贪吃蛇的基本功能,比如在地图中,用“↑↓←→”控制移动蛇的方向,吃掉食物之后,蛇身体会变长等等。。。。

首先我们得分析,游戏中我们会碰见的一些情况。

①蛇的部分,蛇的身子是一节一节的,此时最容易联想到的数据结构就是顺序表,链表,如果把蛇比做顺序表或者链表,在之后吃到食物的时候,身子肯定会变长,这就涉及到插入的操作,所以为了更高的效率,我们用链表实现我们的蛇的部分,最初我们把蛇身子按照四个结点打印在屏幕。

②蛇的移动,在屏幕上面蛇的移动看起来是整个身子向前方平移一个单位,但是其原理是我们在屏幕的另一个地方把蛇从新打印一遍,又把之前的蛇身子去除掉。

③食物的产生,随机的在地图中产生一个节点,在蛇的头坐标和食物的坐标重复的时候,食物消失,蛇的身子加长,也就是蛇的节点数增加一个。

④蛇在其中的几种状态,正常状态:蛇头节点的坐标没有和墙的坐标以及自己身子的坐标重合,

被自己杀死:蛇头的坐标和蛇身子的坐标重合,撞墙:蛇头的坐标和墙的坐标重合。穿墙:蛇不会撞墙而死。

二、C语言贪吃蛇代码实现游戏说明

C语言贪吃蛇游戏界面当中没有打印相关的按键说明,这里先逐一列出C语言贪吃蛇游戏按键说明

  • 按方向键上下左右,可以实现蛇移动方向的改变。

  • 短时间长按方向键上下左右其中之一,可实现蛇向该方向的短时间加速移动。

  • 按空格键可实现暂停,暂停后按任意键继续游戏。

  • 按Esc键可直接退出游戏。

  • 按R键可重新开始游戏。

除此之外,本游戏还拥有计分系统,可保存玩家的历史最高记录。

三、C语言贪吃蛇代码游戏效果展示

贪吃蛇游戏当中蛇的移动速度可以进行调整,动图当中把速度调得较慢(速度太快导致动图上蛇身显示不全),下面给出的代码当中将蛇的速度调整到了合适的位置,大家可以试试

四、C语言贪吃蛇游戏代码大全如下

大家可以将以下代码复制到自己的编译器当中运行:

  1. #include <stdio.h>
  2. #include <Windows.h>
  3. #include <stdlib.h>
  4. #include <time.h>
  5. #include <conio.h>
  6. #define ROW 22 //游戏区行数
  7. #define COL 42 //游戏区列数
  8. #define KONG 0 //标记空(什么也没有)
  9. #define WALL 1 //标记墙
  10. #define FOOD 2 //标记食物
  11. #define HEAD 3 //标记蛇头
  12. #define BODY 4 //标记蛇身
  13. #define UP 72 //方向键:上
  14. #define DOWN 80 //方向键:下
  15. #define LEFT 75 //方向键:左
  16. #define RIGHT 77 //方向键:右
  17. #define SPACE 32 //暂停
  18. #define ESC 27 //退出
  19. //蛇头
  20. struct Snake
  21. {
  22. int len; //记录蛇身长度
  23. int x; //蛇头横坐标
  24. int y; //蛇头纵坐标
  25. }snake;
  26. //蛇身
  27. struct Body
  28. {
  29. int x; //蛇身横坐标
  30. int y; //蛇身纵坐标
  31. }body[ROW*COL]; //开辟足以存储蛇身的结构体数组
  32. int face[ROW][COL]; //标记游戏区各个位置的状态
  33. //隐藏光标
  34. void HideCursor();
  35. //光标跳转
  36. void CursorJump(int x, int y);
  37. //初始化界面
  38. void InitInterface();
  39. //颜色设置
  40. void color(int c);
  41. //从文件读取最高分
  42. void ReadGrade();
  43. //更新最高分到文件
  44. void WriteGrade();
  45. //初始化蛇
  46. void InitSnake();
  47. //随机生成食物
  48. void RandFood();
  49. //判断得分与结束
  50. void JudgeFunc(int x, int y);
  51. //打印蛇与覆盖蛇
  52. void DrawSnake(int flag);
  53. //移动蛇
  54. void MoveSnake(int x, int y);
  55. //执行按键
  56. void run(int x, int y);
  57. //游戏主体逻辑函数
  58. void Game();
  59. int max, grade; //全局变量
  60. int main()
  61. {
  62. #pragma warning (disable:4996) //消除警告
  63. max = 0, grade = 0; //初始化变量
  64. system("title 贪吃蛇"); //设置cmd窗口的名字
  65. system("mode con cols=84 lines=23"); //设置cmd窗口的大小
  66. HideCursor(); //隐藏光标
  67. ReadGrade(); //从文件读取最高分到max变量
  68. InitInterface(); //初始化界面
  69. InitSnake(); //初始化蛇
  70. srand((unsigned int)time(NULL)); //设置随机数生成起点
  71. RandFood(); //随机生成食物
  72. DrawSnake(1); //打印蛇
  73. Game(); //开始游戏
  74. return 0;
  75. }
  76. //隐藏光标
  77. void HideCursor()
  78. {
  79. CONSOLE_CURSOR_INFO curInfo; //定义光标信息的结构体变量
  80. curInfo.dwSize = 1; //如果没赋值的话,光标隐藏无效
  81. curInfo.bVisible = FALSE; //将光标设置为不可见
  82. HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE); //获取控制台句柄
  83. SetConsoleCursorInfo(handle, &curInfo); //设置光标信息
  84. }
  85. //光标跳转
  86. void CursorJump(int x, int y)
  87. {
  88. COORD pos; //定义光标位置的结构体变量
  89. pos.X = x; //横坐标
  90. pos.Y = y; //纵坐标
  91. HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE); //获取控制台句柄
  92. SetConsoleCursorPosition(handle, pos); //设置光标位置
  93. }
  94. //初始化界面
  95. void InitInterface()
  96. {
  97. color(6); //颜色设置为土黄色
  98. for (int i = 0; i < ROW; i++)
  99. {
  100. for (int j = 0; j < COL; j++)
  101. {
  102. if (j == 0 || j == COL - 1)
  103. {
  104. face[i][j] = WALL; //标记该位置为墙
  105. CursorJump(2 * j, i);
  106. printf("■");
  107. }
  108. else if (i == 0 || i == ROW - 1)
  109. {
  110. face[i][j] = WALL; //标记该位置为墙
  111. printf("■");
  112. }
  113. else
  114. {
  115. face[i][j] = KONG; //标记该位置为空
  116. }
  117. }
  118. }
  119. color(7); //颜色设置为白色
  120. CursorJump(0, ROW);
  121. printf("当前得分:%d", grade);
  122. CursorJump(COL, ROW);
  123. printf("历史最高得分:%d", max);
  124. }
  125. //颜色设置
  126. void color(int c)
  127. {
  128. SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), c); //颜色设置
  129. //注:SetConsoleTextAttribute是一个API(应用程序编程接口)
  130. }
  131. //从文件读取最高分
  132. void ReadGrade()
  133. {
  134. FILE* pf = fopen("贪吃蛇最高得分记录.txt", "r"); //以只读的方式打开文件
  135. if (pf == NULL) //打开文件失败
  136. {
  137. pf = fopen("贪吃蛇最高得分记录.txt", "w"); //以只写的方式打开文件
  138. fwrite(&max, sizeof(int), 1, pf); //将max写入文件(此时max为0),即将最高得分初始化为0
  139. }
  140. fseek(pf, 0, SEEK_SET); //使文件指针pf指向文件开头
  141. fread(&max, sizeof(int), 1, pf); //读取文件当中的最高得分到max当中
  142. fclose(pf); //关闭文件
  143. pf = NULL; //文件指针及时置空
  144. }
  145. //更新最高分到文件
  146. void WriteGrade()
  147. {
  148. FILE* pf = fopen("贪吃蛇最高得分记录.txt", "w"); //以只写的方式打开文件
  149. if (pf == NULL) //打开文件失败
  150. {
  151. printf("保存最高得分记录失败\n");
  152. exit(0);
  153. }
  154. fwrite(&grade, sizeof(int), 1, pf); //将本局游戏得分写入文件当中
  155. fclose(pf); //关闭文件
  156. pf = NULL; //文件指针及时置空
  157. }
  158. //初始化蛇
  159. void InitSnake()
  160. {
  161. snake.len = 2; //蛇的身体长度初始化为2
  162. snake.x = COL / 2; //蛇头位置的横坐标
  163. snake.y = ROW / 2; //蛇头位置的纵坐标
  164. //蛇身坐标的初始化
  165. body[0].x = COL / 2 - 1;
  166. body[0].y = ROW / 2;
  167. body[1].x = COL / 2 - 2;
  168. body[1].y = ROW / 2;
  169. //将蛇头和蛇身位置进行标记
  170. face[snake.y][snake.x] = HEAD;
  171. face[body[0].y][body[0].x] = BODY;
  172. face[body[1].y][body[1].x] = BODY;
  173. }
  174. //随机生成食物
  175. void RandFood()
  176. {
  177. int i, j;
  178. do
  179. {
  180. //随机生成食物的横纵坐标
  181. i = rand() % ROW;
  182. j = rand() % COL;
  183. } while (face[i][j] != KONG); //确保生成食物的位置为空,若不为空则重新生成
  184. face[i][j] = FOOD; //将食物位置进行标记
  185. color(12); //颜色设置为红色
  186. CursorJump(2 * j, i); //光标跳转到生成的随机位置处
  187. printf("●"); //打印食物
  188. }
  189. //判断得分与结束
  190. void JudgeFunc(int x, int y)
  191. {
  192. //若蛇头即将到达的位置是食物,则得分
  193. if (face[snake.y + y][snake.x + x] == FOOD)
  194. {
  195. snake.len++; //蛇身加长
  196. grade += 10; //更新当前得分
  197. color(7); //颜色设置为白色
  198. CursorJump(0, ROW);
  199. printf("当前得分:%d", grade); //重新打印当前得分
  200. RandFood(); //重新随机生成食物
  201. }
  202. //若蛇头即将到达的位置是墙或者蛇身,则游戏结束
  203. else if (face[snake.y + y][snake.x + x] == WALL || face[snake.y + y][snake.x + x] == BODY)
  204. {
  205. Sleep(1000); //留给玩家反应时间
  206. system("cls"); //清空屏幕
  207. color(7); //颜色设置为白色
  208. CursorJump(2 * (COL / 3), ROW / 2 - 3);
  209. if (grade > max)
  210. {
  211. printf("恭喜你打破最高记录,最高记录更新为%d", grade);
  212. WriteGrade();
  213. }
  214. else if (grade == max)
  215. {
  216. printf("与最高记录持平,加油再创佳绩", grade);
  217. }
  218. else
  219. {
  220. printf("请继续加油,当前与最高记录相差%d", max - grade);
  221. }
  222. CursorJump(2 * (COL / 3), ROW / 2);
  223. printf("GAME OVER");
  224. while (1) //询问玩家是否再来一局
  225. {
  226. char ch;
  227. CursorJump(2 * (COL / 3), ROW / 2 + 3);
  228. printf("再来一局?(y/n):");
  229. scanf("%c", &ch);
  230. if (ch == 'y' || ch == 'Y')
  231. {
  232. system("cls");
  233. main();
  234. }
  235. else if (ch == 'n' || ch == 'N')
  236. {
  237. CursorJump(2 * (COL / 3), ROW / 2 + 5);
  238. exit(0);
  239. }
  240. else
  241. {
  242. CursorJump(2 * (COL / 3), ROW / 2 + 5);
  243. printf("选择错误,请再次选择");
  244. }
  245. }
  246. }
  247. }
  248. //打印蛇与覆盖蛇
  249. void DrawSnake(int flag)
  250. {
  251. if (flag == 1) //打印蛇
  252. {
  253. color(10); //颜色设置为绿色
  254. CursorJump(2 * snake.x, snake.y);
  255. printf("■"); //打印蛇头
  256. for (int i = 0; i < snake.len; i++)
  257. {
  258. CursorJump(2 * body[i].x, body[i].y);
  259. printf("□"); //打印蛇身
  260. }
  261. }
  262. else //覆盖蛇
  263. {
  264. if (body[snake.len - 1].x != 0) //防止len++后将(0, 0)位置的墙覆盖
  265. {
  266. //将蛇尾覆盖为空格即可
  267. CursorJump(2 * body[snake.len - 1].x, body[snake.len - 1].y);
  268. printf(" ");
  269. }
  270. }
  271. }
  272. //移动蛇
  273. void MoveSnake(int x, int y)
  274. {
  275. DrawSnake(0); //先覆盖当前所显示的蛇
  276. face[body[snake.len - 1].y][body[snake.len - 1].x] = KONG; //蛇移动后蛇尾重新标记为空
  277. face[snake.y][snake.x] = BODY; //蛇移动后蛇头的位置变为蛇身
  278. //蛇移动后各个蛇身位置坐标需要更新
  279. for (int i = snake.len - 1; i > 0; i--)
  280. {
  281. body[i].x = body[i - 1].x;
  282. body[i].y = body[i - 1].y;
  283. }
  284. //蛇移动后蛇头位置信息变为第0个蛇身的位置信息
  285. body[0].x = snake.x;
  286. body[0].y = snake.y;
  287. //蛇头的位置更改
  288. snake.x = snake.x + x;
  289. snake.y = snake.y + y;
  290. DrawSnake(1); //打印移动后的蛇
  291. }
  292. //执行按键
  293. void run(int x, int y)
  294. {
  295. int t = 0;
  296. while (1)
  297. {
  298. if (t == 0)
  299. t = 3000; //这里t越小,蛇移动速度越快(可以根据次设置游戏难度)
  300. while (--t)
  301. {
  302. if (kbhit() != 0) //若键盘被敲击,则退出循环
  303. break;
  304. }
  305. if (t == 0) //键盘未被敲击
  306. {
  307. JudgeFunc(x, y); //判断到达该位置后,是否得分与游戏结束
  308. MoveSnake(x, y); //移动蛇
  309. }
  310. else //键盘被敲击
  311. {
  312. break; //返回Game函数读取键值
  313. }
  314. }
  315. }
  316. //游戏主体逻辑函数
  317. void Game()
  318. {
  319. int n = RIGHT; //开始游戏时,默认向后移动
  320. int tmp = 0; //记录蛇的移动方向
  321. goto first; //第一次进入循环先向默认方向前进
  322. while (1)
  323. {
  324. n = getch(); //读取键值
  325. //在执行前,需要对所读取的按键进行调整
  326. switch (n)
  327. {
  328. case UP:
  329. case DOWN: //如果敲击的是“上”或“下”
  330. if (tmp != LEFT&&tmp != RIGHT) //并且上一次蛇的移动方向不是“左”或“右”
  331. {
  332. n = tmp; //那么下一次蛇的移动方向设置为上一次蛇的移动方向
  333. }
  334. break;
  335. case LEFT:
  336. case RIGHT: //如果敲击的是“左”或“右”
  337. if (tmp != UP&&tmp != DOWN) //并且上一次蛇的移动方向不是“上”或“下”
  338. {
  339. n = tmp; //那么下一次蛇的移动方向设置为上一次蛇的移动方向
  340. }
  341. case SPACE:
  342. case ESC:
  343. case 'r':
  344. case 'R':
  345. break; //这四个无需调整
  346. default:
  347. n = tmp; //其他键无效,默认为上一次蛇移动的方向
  348. break;
  349. }
  350. first: //第一次进入循环先向默认方向前进
  351. switch (n)
  352. {
  353. case UP: //方向键:上
  354. run(0, -1); //向上移动(横坐标偏移为0,纵坐标偏移为-1)
  355. tmp = UP; //记录当前蛇的移动方向
  356. break;
  357. case DOWN: //方向键:下
  358. run(0, 1); //向下移动(横坐标偏移为0,纵坐标偏移为1)
  359. tmp = DOWN; //记录当前蛇的移动方向
  360. break;
  361. case LEFT: //方向键:左
  362. run(-1, 0); //向左移动(横坐标偏移为-1,纵坐标偏移为0)
  363. tmp = LEFT; //记录当前蛇的移动方向
  364. break;
  365. case RIGHT: //方向键:右
  366. run(1, 0); //向右移动(横坐标偏移为1,纵坐标偏移为0)
  367. tmp = RIGHT; //记录当前蛇的移动方向
  368. break;
  369. case SPACE: //暂停
  370. system("pause>nul"); //暂停后按任意键继续
  371. break;
  372. case ESC: //退出
  373. system("cls"); //清空屏幕
  374. color(7); //颜色设置为白色
  375. CursorJump(COL - 8, ROW / 2);
  376. printf(" 游戏结束 ");
  377. CursorJump(COL - 8, ROW / 2 + 2);
  378. exit(0);
  379. case 'r':
  380. case 'R': //重新开始
  381. system("cls"); //清空屏幕
  382. main(); //重新执行主函数
  383. }
  384. }
  385. }

五、C语言贪吃蛇游戏代码详解

1、贪吃蛇C语言实现游戏框架构建

首先定义游戏界面的大小,定义游戏区行数和列数。

这里将蛇活动的区域称为游戏区,将分数提示的区域称为提示区(提示区占一行)。

此外,我们还需要两个结构体用于表示蛇头和蛇身。蛇头结构体当中存储着当前蛇身的长度以及蛇头的位置坐标。

 蛇身结构体当中存储着该段蛇身的位置坐标。

同时我们需要一个二维数组来标记游戏区各个位置的状态(空、墙、食物、蛇头以及蛇身)。

为了增加代码的可读性,最好运用宏来定义各个位置的状态,而不是在代码中用干巴巴的数字对各个位置的状态进行切换。

当然,为了代码的可读性,我们最好也将需要用到的按键的键值用宏进行定义。

2、隐藏光标

隐藏光标比较简单,定义一个光标信息的结构体变量,然后对光标信息进行赋值,最后用这个光标信息的结构体变量进行光标信息设置即可。

3、光标跳转

光标跳转,也就是让光标跳转到指定位置进行输出。与隐藏光标的操作步骤类似,先定义一个光标位置的结构体变量,然后设置光标的横纵坐标,最后用这个光标位置的结构体变量进行光标位置设置即可。

4、初始化界面

初始化界面完成游戏区“墙”的打印,和提示区的打印即可。

在打印过程中需要注意两点:

 在cmd窗口中一个小方块占两个单位的横坐标,一个单位的纵坐标。

光标跳转函数CursorJump接收的是光标将要跳至位置的横纵坐标。

例如,要用CursorJump函数跳转至 i 行 j 列(以一个小方块为一个单位),就等价于让光标跳转至坐标(2*j,i)处。

  1. //初始化界面
  2. void InitInterface()
  3. {
  4. color(6); //颜色设置为土黄色
  5. for (int i = 0; i < ROW; i++)
  6. {
  7. for (int j = 0; j < COL; j++)
  8. {
  9. if (j == 0 || j == COL - 1)
  10. {
  11. face[i][j] = WALL; //标记该位置为墙
  12. CursorJump(2 * j, i);
  13. printf("■");
  14. }
  15. else if (i == 0 || i == ROW - 1)
  16. {
  17. face[i][j] = WALL; //标记该位置为墙
  18. printf("■");
  19. }
  20. else
  21. {
  22. face[i][j] = KONG; //标记该位置为空
  23. }
  24. }
  25. }
  26. color(7); //颜色设置为白色
  27. CursorJump(0, ROW);
  28. printf("当前得分:%d", grade);
  29. CursorJump(COL, ROW);
  30. printf("历史最高得分:%d", max);
  31. }

注意: 在初始化界面的同时,记得对游戏区相应位置的状态进行标记。

5、C语言贪吃蛇游戏颜色设置

颜色设置函数的作用是,将此后输出的内容颜色都更为所指定的颜色,接收的参数c是颜色代码,十进制颜色代码表如下:

 设置颜色函数在其头文件当中的声明如下:

6、初始化蛇

初始化蛇时将蛇身的长度初始化为2,蛇头的起始位置在游戏区的中央,蛇头向右依次是第0个蛇身、第1个蛇身。

在初始化蛇的信息后,记得对游戏区该位置的状态进行标记。

7、随机生成食物

随机在游戏区生成食物,需要对生成后的坐标进行判断,只有该位置为空才能在此生成食物,否则需要重新生成坐标。食物坐标确定后,需要对游戏区该位置的状态进行标记。

8、打印蛇与覆盖蛇

打印蛇和覆盖蛇这里直接使用一个函数来实现,若传入参数flag为1,则打印蛇;若传入参数为0,则用空格覆盖蛇。

打印蛇:

先根据结构体变量snake获取蛇头的坐标,到相应位置打印蛇头。

然后根据结构体数组body依次获取蛇身的坐标,到相应位置进行打印即可。

覆盖蛇:

用空格覆盖最后一段蛇身即可。

但需要注意在覆盖前判断覆盖的位置是否为(0,0)位置,因为当得分后蛇身长度增加,需要覆盖当前的蛇(进而打印长度增加后的蛇),而此时新加蛇身还未进行赋值(编译器一般默认初始化为0),我们根据最后一段蛇身获取到的坐标便是(0,0),则会用空格对(0,0)位置的墙进行覆盖,需要看完后面的移动蛇函数的实现后再进行理解。(也可以先将该判断去掉,观察蛇吃到食物后(0,0)位置墙的变化再进行分析)

  1. //打印蛇与覆盖蛇
  2. void DrawSnake(int flag)
  3. {
  4. if (flag == 1) //打印蛇
  5. {
  6. color(10); //颜色设置为绿色
  7. CursorJump(2 * snake.x, snake.y);
  8. printf("■"); //打印蛇头
  9. for (int i = 0; i < snake.len; i++)
  10. {
  11. CursorJump(2 * body[i].x, body[i].y);
  12. printf("□"); //打印蛇身
  13. }
  14. }
  15. else //覆盖蛇
  16. {
  17. if (body[snake.len - 1].x != 0) //防止len++后将(0, 0)位置的墙覆盖
  18. {
  19. //将蛇尾覆盖为空格即可
  20. CursorJump(2 * body[snake.len - 1].x, body[snake.len - 1].y);
  21. printf(" ");
  22. }
  23. }
  24. }

9、移动蛇

移动蛇函数的作用就是先覆盖当前所显示的蛇,然后再打印移动后的蛇。

参数说明:

x:蛇移动后的横坐标相对于当前蛇的横坐标的变化。

y:蛇移动后的纵坐标相对于当前蛇的纵坐标的变化。

蛇移动后,各种信息需要变化:

最后一段蛇身在游戏区当中需要被重新标记为空。

蛇头位置在游戏区当中需要被重新标记为蛇身。

存储蛇身坐标信息的结构体数组body当中,需要将第i段蛇身的坐标信息更新为第i-1段蛇身的坐标信息,而第0段,即第一段蛇身的坐标信息需要更新为当前蛇头的坐标信息。

蛇头的坐标信息需要根据传入的参数x和y,进行重新计算。

10、游戏主体逻辑函数

主体逻辑:

首先第一次进入该函数,默认蛇向右移动,进而执行run函数。

直到键盘被敲击,再从run函数返回到Game函数进行按键读取。

读取到键值后需要对读取到的按键进行调整(这是必要的)。

调整后再进行按键执行,然后再进行按键读取,如此循环进行。

按键调整机制:

如果敲击的是“上”或“下”键,并且上一次蛇的移动方向不是“左”或“右”,那么将下一次蛇的移动方向设置为上一次蛇的移动方向,即移动方向不变。

如果敲击的是“左”或“右”键,并且上一次蛇的移动方向不是“上”或“下”,那么将下一次蛇的移动方向设置为上一次蛇的移动方向,即移动方向不变。

如果敲击的按键是空格、Esc、r或是R,则不作调整。

其余按键无效,下一次蛇的移动方向设置为上一次蛇的移动方向,即移动方向不变。

  1. //游戏主体逻辑函数
  2. void Game()
  3. {
  4. int n = RIGHT; //开始游戏时,默认向后移动
  5. int tmp = 0; //记录蛇的移动方向
  6. goto first; //第一次进入循环先向默认方向前进
  7. while (1)
  8. {
  9. n = getch(); //读取键值
  10. //在执行前,需要对所读取的按键进行调整
  11. switch (n)
  12. {
  13. case UP:
  14. case DOWN: //如果敲击的是“上”或“下”
  15. if (tmp != LEFT&&tmp != RIGHT) //并且上一次蛇的移动方向不是“左”或“右”
  16. {
  17. n = tmp; //那么下一次蛇的移动方向设置为上一次蛇的移动方向
  18. }
  19. break;
  20. case LEFT:
  21. case RIGHT: //如果敲击的是“左”或“右”
  22. if (tmp != UP&&tmp != DOWN) //并且上一次蛇的移动方向不是“上”或“下”
  23. {
  24. n = tmp; //那么下一次蛇的移动方向设置为上一次蛇的移动方向
  25. }
  26. case SPACE:
  27. case ESC:
  28. case 'r':
  29. case 'R':
  30. break; //这四个无需调整
  31. default:
  32. n = tmp; //其他键无效,默认为上一次蛇移动的方向
  33. break;
  34. }
  35. first: //第一次进入循环先向默认方向前进
  36. switch (n)
  37. {
  38. case UP: //方向键:上
  39. run(0, -1); //向上移动(横坐标偏移为0,纵坐标偏移为-1)
  40. tmp = UP; //记录当前蛇的移动方向
  41. break;
  42. case DOWN: //方向键:下
  43. run(0, 1); //向下移动(横坐标偏移为0,纵坐标偏移为1)
  44. tmp = DOWN; //记录当前蛇的移动方向
  45. break;
  46. case LEFT: //方向键:左
  47. run(-1, 0); //向左移动(横坐标偏移为-1,纵坐标偏移为0)
  48. tmp = LEFT; //记录当前蛇的移动方向
  49. break;
  50. case RIGHT: //方向键:右
  51. run(1, 0); //向右移动(横坐标偏移为1,纵坐标偏移为0)
  52. tmp = RIGHT; //记录当前蛇的移动方向
  53. break;
  54. case SPACE: //暂停
  55. system("pause>nul"); //暂停后按任意键继续
  56. break;
  57. case ESC: //退出
  58. system("cls"); //清空屏幕
  59. color(7); //颜色设置为白色
  60. CursorJump(COL - 8, ROW / 2);
  61. printf(" 游戏结束 ");
  62. CursorJump(COL - 8, ROW / 2 + 2);
  63. exit(0);
  64. case 'r':
  65. case 'R': //重新开始
  66. system("cls"); //清空屏幕
  67. main(); //重新执行主函数
  68. }
  69. }
  70. }

11、执行按键

参数说明:

x:蛇移动后的横坐标相对于当前蛇的横坐标的变化。

y:蛇移动后的纵坐标相对于当前蛇的纵坐标的变化。

给定一定的时间间隔,若在该时间间隔内键盘被敲击,则退出run函数,返回Game函数进行按键读取。若未被敲击,则先判断蛇到达移动后的位置后是否得分或是游戏结束,然后再移动蛇的位置。

若键盘一直未被敲击,则就会一直执行run函数当中的while函数,蛇就会一直朝一个方向移动,直到游戏结束。

  1. //执行按键
  2. void run(int x, int y)
  3. {
  4. int t = 0;
  5. while (1)
  6. {
  7. if (t == 0)
  8. t = 3000; //这里t越小,蛇移动速度越快(可以根据次设置游戏难度)
  9. while (--t)
  10. {
  11. if (kbhit() != 0) //若键盘被敲击,则退出循环
  12. break;
  13. }
  14. if (t == 0) //键盘未被敲击
  15. {
  16. JudgeFunc(x, y); //判断到达该位置后,是否得分与游戏结束
  17. MoveSnake(x, y); //移动蛇
  18. }
  19. else //键盘被敲击
  20. {
  21. break; //返回Game函数读取键值
  22. }
  23. }
  24. }

12、判断得分与结束

判断得分:

若蛇头即将到达的位置是食物,则得分。得分后需要将蛇身加长,并且更新当前得分,除此之外,还需要重新生成食物。

判断结束:

若蛇头即将到达的位置是墙或者蛇身,则游戏结束。游戏结束后比较本局得分和历史最高得分,给出相应的提示语句,并且询问玩家是否再来一局,可自由发挥。

  1. //判断得分与结束
  2. void JudgeFunc(int x, int y)
  3. {
  4. //若蛇头即将到达的位置是食物,则得分
  5. if (face[snake.y + y][snake.x + x] == FOOD)
  6. {
  7. snake.len++; //蛇身加长
  8. grade += 10; //更新当前得分
  9. color(7); //颜色设置为白色
  10. CursorJump(0, ROW);
  11. printf("当前得分:%d", grade); //重新打印当前得分
  12. RandFood(); //重新随机生成食物
  13. }
  14. //若蛇头即将到达的位置是墙或者蛇身,则游戏结束
  15. else if (face[snake.y + y][snake.x + x] == WALL || face[snake.y + y][snake.x + x] == BODY)
  16. {
  17. Sleep(1000); //留给玩家反应时间
  18. system("cls"); //清空屏幕
  19. color(7); //颜色设置为白色
  20. CursorJump(2 * (COL / 3), ROW / 2 - 3);
  21. if (grade > max)
  22. {
  23. printf("恭喜你打破最高记录,最高记录更新为%d", grade);
  24. WriteGrade();
  25. }
  26. else if (grade == max)
  27. {
  28. printf("与最高记录持平,加油再创佳绩", grade);
  29. }
  30. else
  31. {
  32. printf("请继续加油,当前与最高记录相差%d", max - grade);
  33. }
  34. CursorJump(2 * (COL / 3), ROW / 2);
  35. printf("GAME OVER");
  36. while (1) //询问玩家是否再来一局
  37. {
  38. char ch;
  39. CursorJump(2 * (COL / 3), ROW / 2 + 3);
  40. printf("再来一局?(y/n):");
  41. scanf("%c", &ch);
  42. if (ch == 'y' || ch == 'Y')
  43. {
  44. system("cls");
  45. main();
  46. }
  47. else if (ch == 'n' || ch == 'N')
  48. {
  49. CursorJump(2 * (COL / 3), ROW / 2 + 5);
  50. exit(0);
  51. }
  52. else
  53. {
  54. CursorJump(2 * (COL / 3), ROW / 2 + 5);
  55. printf("选择错误,请再次选择");
  56. }
  57. }
  58. }
  59. }

注意: 若本局得分大于历史最高得分,需要更新最高分到文件。

13、从文件读取最高分

首先需要使用fopen函数打开“贪吃蛇最高得分记录.txt”文件,若是第一次运行该代码,则会自动创建该文件,并将历史最高记录设置为0,之后再读取文件当中的历史最高记录存储在max变量当中,并关闭文件即可。

14、更新最高分到文件

首先使用fopen函数打开“贪吃蛇最高得分记录.txt”,然后将本局游戏的分数grade写入文件当中即可(覆盖式)。

15、主函数

有了以上函数的支撑,写出主函数是相当简单的,但需要注意以下三点:

全局变量grade需要在主函数内初始化为0,不能在全局范围初始化为0,因为当玩家按下R键进行重玩时我们需要将当前分数grade重新设置为0。

随机数的生成起点建议设置在主函数当中。

主函数当中的#pragma语句是用于消除类似以下警告的:

六、穿墙版贪吃蛇C语言代码

1、游戏效果展示

个人认为穿墙版的贪吃蛇比普通版贪吃蛇更好玩,你觉得呢?

2、贪吃蛇C语言游戏代码

以下代码可以直接运行,欢迎试玩:

  1. #include <stdio.h>
  2. #include <Windows.h>
  3. #include <stdlib.h>
  4. #include <time.h>
  5. #include <conio.h>
  6. #define ROW 23 //界面行数
  7. #define COL 42 //界面列数
  8. #define KONG 0 //标记空(什么也没有)
  9. #define FOOD 1 //标记食物
  10. #define HEAD 2 //标记蛇头
  11. #define BODY 3 //标记蛇身
  12. #define UP 72 //方向键:上
  13. #define DOWN 80 //方向键:下
  14. #define LEFT 75 //方向键:左
  15. #define RIGHT 77 //方向键:右
  16. #define SPACE 32 //暂停
  17. #define ESC 27 //退出
  18. //蛇头
  19. struct Snake
  20. {
  21. int len; //记录蛇身长度
  22. int x; //蛇头横坐标
  23. int y; //蛇头纵坐标
  24. }snake;
  25. //蛇身
  26. struct Body
  27. {
  28. int x; //蛇身横坐标
  29. int y; //蛇身纵坐标
  30. }body[ROW*COL]; //开辟足以存储蛇身的结构体数组
  31. int face[ROW][COL]; //标记界面当中各个位置的信息
  32. //隐藏光标
  33. void HideCursor();
  34. //光标跳转
  35. void CursorJump(int x, int y);
  36. //初始化界面
  37. void InitInterface();
  38. //颜色设置
  39. void color(int c);
  40. //从文件读取最高分
  41. void ReadGrade();
  42. //更新最高分到文件
  43. void WriteGrade();
  44. //初始化蛇
  45. void InitSnake();
  46. //随机生成食物
  47. void RandFood();
  48. //判断得分与结束
  49. void JudgeFunc(int x, int y);
  50. //打印蛇/覆盖蛇
  51. void DrawSnake(int flag);
  52. //移动蛇
  53. void MoveSnake(int x, int y);
  54. //执行按键
  55. void run(int x, int y);
  56. //游戏主体逻辑函数
  57. void Game();
  58. int max, grade; //全局变量
  59. int main()
  60. {
  61. #pragma warning (disable:4996) //消除警告
  62. max = 0, grade = 0; //初始化变量
  63. system("title 贪吃蛇"); //设置cmd窗口名称
  64. system("mode con cols=84 lines=23"); //设置cmd窗口大小
  65. HideCursor(); //隐藏光标
  66. ReadGrade(); //从文件读取最高分
  67. InitInterface(); //初始化界面
  68. InitSnake(); //初始化蛇
  69. srand((unsigned int)time(NULL)); //设置随机数生成起点
  70. RandFood(); //随机生成食物
  71. DrawSnake(1); //打印蛇
  72. Game(); //开始游戏
  73. return 0;
  74. }
  75. //隐藏光标
  76. void HideCursor()
  77. {
  78. CONSOLE_CURSOR_INFO curInfo; //定义光标信息的结构体变量
  79. curInfo.dwSize = 1; //如果没赋值的话,光标隐藏无效
  80. curInfo.bVisible = FALSE; //将光标设置为不可见
  81. HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE); //获取控制台句柄
  82. SetConsoleCursorInfo(handle, &curInfo); //设置光标信息
  83. }
  84. //光标跳转
  85. void CursorJump(int x, int y)
  86. {
  87. COORD pos; //定义光标位置的结构体变量
  88. pos.X = x; //横坐标
  89. pos.Y = y; //纵坐标
  90. HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE); //获取控制台句柄
  91. SetConsoleCursorPosition(handle, pos); //设置光标位置
  92. }
  93. //初始化界面
  94. void InitInterface()
  95. {
  96. color(7); //颜色设置为白色
  97. CursorJump(0, 0);
  98. printf("当前得分:%d", grade);
  99. CursorJump(COL, 0);
  100. printf("历史最高得分:%d", max);
  101. color(11); //颜色设置为浅蓝色
  102. for (int i = 1; i < ROW; i++)
  103. {
  104. for (int j = 0; j < COL; j++)
  105. {
  106. if (i == 1 && j != 0 && j != COL - 1) //打印游戏区的上界
  107. {
  108. CursorJump(2 * j, i);
  109. printf("__");
  110. }
  111. else if (i == ROW - 1 && j != 0 && j != COL - 1) //打印游戏区的下界
  112. {
  113. CursorJump(2 * j, i);
  114. printf("▔▔");
  115. }
  116. else if (j == 0 && i != 1 && i != 0 && i != ROW - 1) //打印游戏区的左界
  117. {
  118. CursorJump(2 * j, i);
  119. printf(" >");
  120. }
  121. else if (j == COL - 1 && i != 1 && i != 0 && i != ROW - 1) //打印游戏区的右界
  122. {
  123. CursorJump(2 * j, i);
  124. printf("< ");
  125. }
  126. else
  127. {
  128. face[i][j] = KONG; //其余位置标记为空(非常必要)
  129. }
  130. }
  131. }
  132. }
  133. //颜色设置
  134. void color(int c)
  135. {
  136. SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), c); //颜色设置
  137. }
  138. //从文件读取最高分
  139. void ReadGrade()
  140. {
  141. FILE* pf = fopen("贪吃蛇最高得分记录.txt", "r"); //以只读的方式打开文件
  142. if (pf == NULL) //打开文件失败
  143. {
  144. pf = fopen("贪吃蛇最高得分记录.txt", "w"); //以只写的方式打开文件
  145. fwrite(&max, sizeof(int), 1, pf); //将max写入文件(此时max为0),即将最高得分初始化为0
  146. }
  147. fseek(pf, 0, SEEK_SET); //使文件指针pf指向文件开头
  148. fread(&max, sizeof(int), 1, pf); //读取文件当中的最高得分到max当中
  149. fclose(pf); //关闭文件
  150. pf = NULL; //文件指针及时置空
  151. }
  152. //更新最高分到文件
  153. void WriteGrade()
  154. {
  155. FILE* pf = fopen("贪吃蛇最高得分记录.txt", "w"); //以只写的方式打开文件
  156. if (pf == NULL) //打开文件失败
  157. {
  158. printf("保存最高得分记录失败\n");
  159. exit(0);
  160. }
  161. fwrite(&grade, sizeof(int), 1, pf); //将本局游戏得分写入文件当中
  162. fclose(pf); //关闭文件
  163. pf = NULL; //文件指针及时置空
  164. }
  165. //初始化蛇
  166. void InitSnake()
  167. {
  168. snake.len = 2; //蛇身长度初始化为2
  169. snake.x = COL / 2; //蛇头位置的横坐标
  170. snake.y = ROW / 2; //蛇头位置的纵坐标
  171. //蛇身坐标的初始化
  172. body[0].x = COL / 2 - 1;
  173. body[0].y = ROW / 2;
  174. body[1].x = COL / 2 - 2;
  175. body[1].y = ROW / 2;
  176. //将蛇头和蛇身位置进行标记
  177. face[snake.y][snake.x] = HEAD;
  178. face[body[0].y][body[0].x] = BODY;
  179. face[body[1].y][body[1].x] = BODY;
  180. }
  181. //随机生成食物
  182. void RandFood()
  183. {
  184. int i, j;
  185. do
  186. {
  187. //随机生成食物的横纵坐标
  188. i = rand() % ROW;
  189. j = rand() % COL;
  190. //若食物生成位置不在游戏区,或者生成食物的位置不为空,则重新生成
  191. } while (i <= 1 || i == ROW - 1 || j == 0 || j == COL - 1 || face[i][j] != KONG);
  192. face[i][j] = FOOD; //将食物位置进行标记
  193. color(9); //颜色设置为深蓝色
  194. CursorJump(2 * j, i);
  195. printf("●");
  196. }
  197. //判断得分与结束
  198. void JudgeFunc(int x, int y)
  199. {
  200. int nextX = snake.x + x;
  201. int nextY = snake.y + y;
  202. if (nextX == COL - 1)
  203. nextX = 1;
  204. if (nextX == 0)
  205. nextX = COL - 2;
  206. if (nextY == ROW - 1)
  207. nextY = 2;
  208. if (nextY == 1)
  209. nextY = ROW - 2;
  210. //若即将到达的位置是食物,则得分
  211. if (face[nextY][nextX] == FOOD)
  212. {
  213. snake.len++; //蛇身加长
  214. grade += 10; //更新当前得分
  215. color(7); //颜色设置为白色
  216. CursorJump(0, 0);
  217. printf("当前得分:%d", grade); //重新打印当前得分
  218. RandFood(); //重新随机生成食物
  219. }
  220. //若即将到达的位置是蛇身,则游戏结束
  221. else if (face[nextY][nextX] == BODY)
  222. {
  223. Sleep(1000); //留给玩家反应时间
  224. system("cls"); //清空屏幕
  225. color(7); //颜色设置为白色
  226. CursorJump(2 * (COL / 3), ROW / 2 - 3);
  227. if (grade > max)
  228. {
  229. printf("恭喜你打破最高记录,最高记录更新为%d", grade);
  230. WriteGrade();
  231. }
  232. else if (grade == max)
  233. {
  234. printf("与最高记录持平,加油再创佳绩", grade);
  235. }
  236. else
  237. {
  238. printf("请继续加油,当前与最高记录相差%d", max - grade);
  239. }
  240. CursorJump(2 * (COL / 3), ROW / 2);
  241. printf("GAME OVER");
  242. while (1) //询问玩家是否再来一局
  243. {
  244. char ch;
  245. CursorJump(2 * (COL / 3), ROW / 2 + 3);
  246. printf("再来一局?(y/n):");
  247. scanf("%c", &ch);
  248. if (ch == 'y' || ch == 'Y')
  249. {
  250. system("cls");
  251. main();
  252. }
  253. else if (ch == 'n' || ch == 'N')
  254. {
  255. CursorJump(2 * (COL / 3), ROW / 2 + 5);
  256. exit(0);
  257. }
  258. else
  259. {
  260. CursorJump(2 * (COL / 3), ROW / 2 + 4);
  261. printf("选择错误,请再次选择");
  262. }
  263. }
  264. }
  265. }
  266. //打印蛇与覆盖蛇
  267. void DrawSnake(int flag)
  268. {
  269. if (flag == 1) //打印蛇
  270. {
  271. color(10); //颜色设置为绿色
  272. CursorJump(2 * snake.x, snake.y);
  273. printf("■"); //打印蛇头
  274. //打印蛇身
  275. for (int i = 0; i < snake.len; i++)
  276. {
  277. CursorJump(2 * body[i].x, body[i].y);
  278. printf("■");
  279. }
  280. }
  281. else //覆盖蛇
  282. {
  283. if (body[snake.len - 1].x != 0) //防止len++后(0, 0)位置所显示的信息被覆盖
  284. {
  285. //将蛇尾覆盖为空格即可
  286. CursorJump(2 * body[snake.len - 1].x, body[snake.len - 1].y);
  287. printf(" ");
  288. }
  289. }
  290. }
  291. //移动蛇
  292. void MoveSnake(int x, int y)
  293. {
  294. DrawSnake(0); //先覆盖当前所显示的蛇
  295. face[body[snake.len - 1].y][body[snake.len - 1].x] = KONG; //蛇移动后蛇尾重新标记为空
  296. face[snake.y][snake.x] = BODY; //蛇移动后蛇头的位置变为蛇身
  297. //蛇移动后各个蛇身位置坐标需要更新
  298. for (int i = snake.len - 1; i > 0; i--)
  299. {
  300. body[i].x = body[i - 1].x;
  301. body[i].y = body[i - 1].y;
  302. }
  303. //蛇移动后蛇头位置信息变为第0个蛇身的位置信息
  304. body[0].x = snake.x;
  305. body[0].y = snake.y;
  306. //蛇头的位置更改
  307. snake.x = snake.x + x;
  308. if (snake.x == COL - 1) //越过右界
  309. snake.x = 1;
  310. else if (snake.x == 0) //越过左界
  311. snake.x = COL - 2;
  312. snake.y = snake.y + y;
  313. if (snake.y == ROW - 1) //越过下界
  314. snake.y = 2;
  315. else if (snake.y == 1) //越过上界
  316. snake.y = ROW - 2;
  317. face[snake.y][snake.x] = HEAD; //对蛇头位置进行标记
  318. DrawSnake(1); //打印移动后的蛇
  319. }
  320. //执行按键
  321. void run(int x, int y)
  322. {
  323. int t = 0;
  324. while (1)
  325. {
  326. if (t == 0)
  327. t = 3000; //这里t越小,蛇移动速度越快(可以根据次设置游戏难度)
  328. while (--t)
  329. {
  330. if (kbhit() != 0) //若键盘被敲击,则退出循环
  331. break;
  332. }
  333. if (t == 0) //键盘未被敲击
  334. {
  335. JudgeFunc(x, y); //判断到达该位置后,是否得分与游戏结束
  336. MoveSnake(x, y); //移动蛇
  337. }
  338. else //键盘被敲击
  339. {
  340. break; //返回Game函数读取键值
  341. }
  342. }
  343. }
  344. //游戏主体逻辑函数
  345. void Game()
  346. {
  347. int n = RIGHT; //开始游戏时,默认向后移动
  348. int tmp = 0; //记录蛇的移动方向
  349. goto first; //第一次进入循环先向默认方向前进
  350. while (1)
  351. {
  352. n = getch(); //读取键值
  353. //在执行前,需要对所读取的按键进行调整
  354. switch (n)
  355. {
  356. case UP:
  357. case DOWN: //如果敲击的是“上”或“下”
  358. if (tmp != LEFT&&tmp != RIGHT) //并且上一次蛇的移动方向不是“左”或“右”
  359. {
  360. n = tmp; //那么下一次蛇的移动方向设置为上一次蛇的移动方向
  361. }
  362. break;
  363. case LEFT:
  364. case RIGHT: //如果敲击的是“左”或“右”
  365. if (tmp != UP&&tmp != DOWN) //并且上一次蛇的移动方向不是“上”或“下”
  366. {
  367. n = tmp; //那么下一次蛇的移动方向设置为上一次蛇的移动方向
  368. }
  369. case SPACE:
  370. case ESC:
  371. case 'r':
  372. case 'R':
  373. break; //这四个无需调整
  374. default:
  375. n = tmp; //其他键无效,默认为上一次蛇移动的方向
  376. break;
  377. }
  378. first: //第一次进入循环先向默认方向前进
  379. switch (n)
  380. {
  381. case UP: //方向键:上
  382. if (snake.y - 1 != body[0].y) //改变的方向不能是第0个蛇身的方向
  383. {
  384. run(0, -1); //向上移动(横坐标偏移为0,纵坐标偏移为-1)
  385. tmp = UP; //记录当前蛇的移动方向
  386. }
  387. break;
  388. case DOWN: //方向键:下
  389. if (snake.y + 1 != body[0].y) //改变的方向不能是第0个蛇身的方向
  390. {
  391. run(0, 1); //向下移动(横坐标偏移为0,纵坐标偏移为1)
  392. tmp = DOWN; //记录当前蛇的移动方向
  393. }
  394. break;
  395. case LEFT: //方向键:左
  396. if (snake.x - 1 != body[0].x) //改变的方向不能是第0个蛇身的方向
  397. {
  398. run(-1, 0); //向左移动(横坐标偏移为-1,纵坐标偏移为0)
  399. tmp = LEFT; //记录当前蛇的移动方向
  400. }
  401. break;
  402. case RIGHT: //方向键:右
  403. if (snake.x + 1 != body[0].x) //改变的方向不能是第0个蛇身的方向
  404. {
  405. run(1, 0); //向右移动(横坐标偏移为1,纵坐标偏移为0)
  406. tmp = RIGHT; //记录当前蛇的移动方向
  407. }
  408. break;
  409. case SPACE: //暂停
  410. system("pause>nul"); //暂停后按任意键继续
  411. break;
  412. case ESC: //退出
  413. system("cls"); //清空屏幕
  414. color(7); //颜色设置为白色
  415. CursorJump(COL - 8, ROW / 2);
  416. printf(" 游戏结束 ");
  417. CursorJump(COL - 8, ROW / 2 + 2);
  418. exit(0);
  419. case 'r':
  420. case 'R': //重新开始
  421. system("cls"); //清空屏幕
  422. main(); //重新执行主函数
  423. }
  424. }
  425. }

3、游戏代码详解

跟前面的大同小异这里就不细说了,感兴趣的对照看一下就明白了。

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

闽ICP备14008679号