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. }

  426. 制作不易,请多关注

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值