当前位置:   article > 正文

【C语言实现贪吃蛇】(内含源码)

【C语言实现贪吃蛇】(内含源码)

前言:首先在实现贪吃蛇小游戏之前,我们要先了解Win32 API的有关知识

1.Win32 API

Windows这个多作业系统除了协调应用程序的执行、分配内存、管理资源之外,它同时也是一个很大的服务中心,调佣这个中心的各种服务(每一种服务就是一个函数),它可以帮助应用程序达到开启视窗、秒回图形、使用周边设备等目的,由于这些函数的服务对象是应用程序,所以便称之为Application Pragramming Interface,简称API函数Win32 API也就是Microsoft Windows32位平台的程序应用编程接口。

2.控制台程序

平常我们运用起来的黑框程序就是控制台程序

我们可以通过执行命令来改变控制台的长宽

在我们执行cmd命令之前,我们首先要先做以下事情

点开设置,然后将windows决定改为windows控制台主机,然后点击保存,我们就可以执行有关命令了,(如果控制台主机不可以,可以将其改为windows决定

下面我介绍两个控制台窗口执行命令,可以用C语言函数的system来实现。

1.改变控制台窗口大小(mode命令)

  1. #include<stdio.h>
  2. #include<windows.h>
  3. int main()
  4. {
  5. system("mode con cols=50 lines=20");//控制台列改为50列,行就改为20行
  6. return 0;
  7. }

可以发现控制台行为20行,列为50行,但看起来却像个正方形,这是为什么呢?其实是因为控制台窗口行和列的比例并不是1:1的。

其坐标位置如下

2.改变控制台的窗口名字(title命令)

  1. #include<stdio.h>
  2. #include<windows.h>
  3. int main()
  4. {
  5. system("title 贪吃蛇");
  6. return 0;
  7. }

3.控制台屏幕上的坐标

COORDWindows API中定义的一个结构体,表示一个字符在控制台屏幕缓冲区上得坐标,坐标系(0,0)的原点位于缓冲区的顶部左侧单元格。

其中COORD的类型的声明为:

    typedef struct _COORD
    {
        short x;
        short y;
    };

给坐标赋值为:

COORD pos={ 10, 15 };

4.GetStdHandle

GetStdHandle 是一个Windows API函数。它用于从一个特定的标准设备(标准输入、标准输出或标准错误)中取得一个句柄(用来标识不同设备的数值),使用这个句柄可以操作设备。

HANDLE GetStdHandle(DWORD nStdHandle);

  1. HANDLE hOutput = NULL;
  2. //获取标准输出的句柄(⽤来标识不同设备的数值)
  3. hOutput = GetStdHandle(STD_OUTPUT_HANDLE);

5.GetConsoleCursorInfo

其函数原型为:

BOOL WINAPI GetConsoleCursorInfo(
  _In_  HANDLE               hConsoleOutput,
  _Out_ PCONSOLE_CURSOR_INFO lpConsoleCursorInfo

);

HANDLE为获得的标准输出句柄
PCONSOLE_CURSOR_INFO 是指向 CONSOLE_CURSOR_INFO 结构的指针,该结构接收有关主机游标

6.CONSOLE_CURSOR_INFO

这个结构体包含有关控制台光标的信息

typedef struct _ CONSOLE_CURSOR_INFO {
DWORD dwSize;
BOOL bVisible;
} CONSOLE_CURSOR_INFO, *PCONSOLE_CURSOR_INFO;

其中dwSize为由光标填充的字符单元格的百分比。此值介于1到100之间。光标外观会变化,范围从完全填充单元格但单元底部的水平线条。

这里的黑色长方型为光标。

那么其占完全填充单元格的百分之多少呢?这里我们就可以通过一个代码来演示

  1. #include<stdio.h>
  2. #include<windows.h>
  3. int main()
  4. {
  5. HANDLE houtput = NULL;//返回void*的指针
  6. houtput = GetStdHandle(STD_OUTPUT_HANDLE);
  7. //定义一个光标信息的结构体
  8. CONSOLE_CURSOR_INFO cursor_info = { 0 };
  9. //获取与houtput句柄相关的控制台上的光标信息,并存放在cursor_info中
  10. GetConsoleCursorInfo(houtput, &cursor_info);
  11. printf("%d", cursor_info.dwSize);
  12. return 0;
  13. }

可以观察到其占完全填充光标的25%。

完全填充光标:

那如果我们想要将光标信息隐藏该怎么办呢?

bVisible表示游标的可见性。如果光标可见,则此成员为true.

  1. #include<stdio.h>
  2. #include<windows.h>
  3. #include<stdbool.h>
  4. int main()
  5. {
  6. HANDLE houtput = NULL;//返回void*的指针
  7. houtput = GetStdHandle(STD_OUTPUT_HANDLE);
  8. //定义一个光标信息的结构体
  9. CONSOLE_CURSOR_INFO cursor_info = { 0 };
  10. //获取与houtput句柄相关的控制台上的光标信息,并存放在cursor_info中
  11. GetConsoleCursorInfo(houtput, &cursor_info);
  12. cursor_info.bVisible = false;//隐藏光标
  13. return 0;
  14. }

但这样并不能隐藏光标信息,还需要借助一个函数SetConsoleCursorInfo来设置控制台屏幕缓冲区的光标大小和可见性。

7.SetConsoleCursorInfo

其函数原型为:

BOOL WINAPI SetConsoleCursorInfo (
HANDLE hConsoleOutput,
const CONSOLE_CURSOR_INFO *lpConsoleCursorInfo
);
  1. #include<stdio.h>
  2. #include<windows.h>
  3. #include<stdbool.h>
  4. int main()
  5. {
  6. HANDLE houtput = NULL;//返回void*的指针
  7. houtput = GetStdHandle(STD_OUTPUT_HANDLE);
  8. //定义一个光标信息的结构体
  9. CONSOLE_CURSOR_INFO cursor_info = { 0 };
  10. //获取与houtput句柄相关的控制台上的光标信息,并存放在cursor_info中
  11. GetConsoleCursorInfo(houtput, &cursor_info);
  12. cursor_info.bVisible = false;//隐藏光标
  13. SetConsoleCursorInfo(houtput, &cursor_info);
  14. return 0;
  15. }

我们就可以观察到光标被隐藏了。

8.SetConsoleCursorPosition

作用:设置指定控制台屏幕缓冲区中的光标位置,我们将想要设置的坐标信息放在COORD类型的pos中,调用SetConsolePostion函数将光标位置设定到指定的位置。

函数原型:

BOOL WINAPI SetConsoleCursorPosition (
HANDLE hConsoleOutput,
COORD pos
);

 这里我们就用Set_Pos分装一个设置光标的函数

  1. void Set_Pos(short x,short y)
  2. {
  3. //获取标准输出设备的句柄
  4. HANDLE houtput = NULL;
  5. houtput=GetStdHandle(STD_OUTPUT_HANDLE);
  6. //定位光标的位置
  7. COORD pos = { x,y };
  8. SetConsoleCursorPosition(houtput, pos);
  9. }

9.GetAsyncKeyState

获取按键情况,GetAsynKeyState的函数原型如下:

SHORT GetAsyncKeyState (
int vKey
);

将键盘上的每一个键的虚拟键值传递给函数,函数通过返回值来分辨键值的状态。

GetAsyncKeyState的返回值是short类型,在上一次调用GetAsyncKeyState函数后,如果返回的16为的short数据中,最高位是1,说明按键的状态时按下,如果最高位是0,说明按键的转台是抬起;如果最低位被置为1则说明,该按键被按过,否则为0。

这里为了方便判断按键是否被按过,我们可以定义一个宏,来判断GetAsynKeyState返回值最低为是否为1.

#define KEY_PRESS(VK) (( GetAsyncKeyState(VK) & 0x1 )? 1 : 0)

 参考:虚拟键码 (Winuser.h) - Win32 apps

这里虚拟键码就可以参照上面的链接。

讲完了上面的有关知识我们就可以开始实现贪吃蛇了

首先先展示贪吃蛇的大致画面

QQ202453-133347

这里为了实现游戏地图的打印,我们就需要讲解一下控制台有关知识,控制台窗口的坐标如下所示,横向的是x轴,纵向的是y轴,从上到下依次增长。

在游戏地图上,我们打印墙体使用的是宽字符:■,打印蛇使用宽字符●,打印食物使用的宽字符是★

那什么是宽字符呢?

普通字符是占一个字节的,宽字符是占两个字节的。

过去C语言并不适用于非英语国家使用,C语言最初假定字符都是单字节的。但是这些假定并不是在世界的任何地方都适用。后来为了适应C语言国际化·,C语言的标准不断加入了国际化的支持。比如:加入宽字符的类型wchar_t和宽字符的输入和输出函数。加入了<locale.h>头文件,提供了允许程序员针对特定地区调整程序行为的函数。

10.setlocale

函数原型:char*setlocale(int category,const char*locale);

setlocale函数用于修改当前地区,可以针对一个类项,也可以所有类项,如果第一个参数是LC_ALL,就是影响所有的类项。

C标准给出了第二个参数定义了两种可能取值:“C”(正常模式)和“ ”(本地模式)。

从任意程序开始,都会隐藏执行调用:

setlocale(LC_ALL,"C");

如果想切换到本地模式就支持宽字符(汉字)的输出:

setlocale(LC_ALL," ");

11.宽字符的打印

宽字符的字面量必须加上前缀“L”,否则C语言会把字面量当做窄字符类型处理,前缀“L”在单引号面前,表示宽字符,对应wprintf()的占位付为%lc;在双引号面前,表示宽字符串,对应占位付就为%ls.

  1. #include <stdio.h>
  2. #include<locale.h>
  3. int main() {
  4. setlocale(LC_ALL, "");
  5. wchar_t ch = L'●';
  6. printf("%c%c\n", 'a', 'b');
  7. wprintf(L"%lc\n", ch);
  8. return 0;
  9. }

注:这个宽字符的实现要在Windows控制台主机上实现。

这里我们就实现棋盘27行,58列的棋盘,在围绕地图画出墙

这里我们蛇身的初始长度为5,在固定的一个坐标处开始,比如我们在(24,5)处开始打印,连续5个节点。

注:蛇的每个节点的x坐标必须是2个倍数,否则可能会出现蛇的⼀个节点有一半出现在墙体。

关于食物,就是在墙体内随机生成一个坐标(x的坐标必须是2的倍数),坐标不能和蛇的身体重合,然后打印★。

下面我们开始实现贪吃蛇(代码内含注释)

snake.h

  1. #define _CRT_SECURE_NO_WARNINGS 1
  2. #define KEY_PRESS(VK) (( GetAsyncKeyState(VK) & 0x1 )? 1 : 0)
  3. #define POS_X 24
  4. #define POS_Y 5
  5. #define WALL L'■'
  6. #define BODY L'●'
  7. #define FOOD L'★'
  8. #include<stdio.h>
  9. #include<stdlib.h>
  10. #include<stdbool.h>
  11. #include<windows.h>
  12. #include<locale.h>
  13. #include<time.h>
  14. #include<conio.h>
  15. int choice;//选择穿墙还是不穿墙
  16. //蛇的方向
  17. enum DIRECTION
  18. {
  19. UP = 1,
  20. DOWN,
  21. LEFT,
  22. RIGHT
  23. };
  24. //蛇的状态
  25. //正常、撞墙、撞到自己、正常退出
  26. enum GAME_STATUS
  27. {
  28. OK,
  29. KILL_BY_WALL,
  30. KILL_BY_SELF,
  31. END_NORMAL
  32. };
  33. typedef struct SnakeNode
  34. {
  35. //坐标
  36. int x;
  37. int y;
  38. //指向下一个节点的指针
  39. struct SnakeNode* next;
  40. }SnakeNode, *pSnakeNode;
  41. typedef struct Snake
  42. {
  43. pSnakeNode _pSnake;//指向蛇头的指针
  44. pSnakeNode _pFood;//指向食物节点的指针
  45. enum DIRECTION _dir;//蛇的方向
  46. enum GAME_STATUS _status;//游戏的状态
  47. int _food_weight;//一个食物的分数
  48. int _score;//总分数
  49. int _sleep_time;//休息时间,时间越短,速度越快
  50. }Snake,*pSnake;
  51. //设置颜色
  52. void color(int c); //(每次置为其他颜色时都要将其再置为白色,方便设置其他颜色,(也可以根据自己需求设置))
  53. //定义光标
  54. void Set_Pos(short x, short y);
  55. //游戏初始化
  56. void GameStart(pSnake ps);
  57. //欢迎界面的打印
  58. void WelcomeToGame();
  59. //创建地图
  60. void CreateMap();
  61. //初始化蛇身
  62. void InitSnake(pSnake ps);
  63. //创建食物
  64. void CreateFood(pSnake ps);
  65. //游戏运行的逻辑
  66. void GameRun(pSnake ps);
  67. //蛇的移动-走一步
  68. void SnakeMove(pSnake ps);
  69. //判断下一个坐标是否为食物
  70. int NextIsFood(pSnakeNode pNextNode, pSnake ps);
  71. //吃掉食物
  72. void EatFood(pSnakeNode pNextNode, pSnake ps);
  73. //不是食物
  74. void NoFood(pSnakeNode pNextNode, pSnake ps);
  75. //蛇是否撞墙
  76. bool KillByWall(pSnake ps);
  77. //蛇撞墙不会死
  78. void WallSnakeMove(pSnake ps);
  79. //穿墙
  80. int NoKillByWall(pSnake ps, pSnakeNode pn);
  81. //蛇是否撞到自己
  82. bool KillBySelf(pSnake ps);
  83. //游戏结束
  84. void GameEnd(pSnake ps);

snake.c

  1. #define _CRT_SECURE_NO_WARNINGS 1
  2. #include"snake.h"
  3. void color(int c)
  4. {
  5. SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), c); //颜色设置
  6. //注:SetConsoleTextAttribute是一个API(应用程序编程接口)
  7. }
  8. void Set_Pos(short x,short y)
  9. {
  10. //获取标准输出设备的句柄
  11. HANDLE houtput = NULL;
  12. houtput=GetStdHandle(STD_OUTPUT_HANDLE);
  13. //定位光标的位置
  14. COORD pos = { x,y };
  15. SetConsoleCursorPosition(houtput, pos);
  16. }
  17. void WelcomeToGame()
  18. {
  19. Set_Pos(38, 14);
  20. wprintf(L"欢迎来到贪吃蛇小游戏\n");
  21. Set_Pos(42, 20);
  22. system("pause");
  23. system("cls");
  24. Set_Pos(25, 14);
  25. wprintf(L"用↑,↓,←,→来控制蛇的移动,按F3加速,F4减速\n");
  26. Set_Pos(25,15);
  27. wprintf(L"加速能够得到更高的分数\n");
  28. Set_Pos(42, 20);
  29. system("pause");
  30. system("cls");
  31. }
  32. void CreateMap()
  33. {
  34. color(3);
  35. //UP
  36. for (int i = 1; i <= 29; i++)
  37. {
  38. wprintf(L"%lc",WALL);
  39. }
  40. //DOWN
  41. Set_Pos(0, 26);
  42. for (int i = 1; i <= 29; i++)
  43. {
  44. wprintf(L"%lc", WALL);
  45. }
  46. //LEFT
  47. for (int i = 1; i < 26; i++)
  48. {
  49. Set_Pos(0, i);
  50. wprintf(L"%lc", WALL);
  51. }
  52. //RIGHT
  53. for (int i = 1; i < 26; i++)
  54. {
  55. Set_Pos(56, i);
  56. wprintf(L"%lc", WALL);
  57. }
  58. color(7);
  59. }
  60. void InitSnake(pSnake ps)
  61. {
  62. pSnakeNode cur=NULL;
  63. for (int i = 0; i < 5; i++)
  64. {
  65. cur = (pSnakeNode)malloc(sizeof(SnakeNode));
  66. if (cur == NULL)
  67. {
  68. perror("InitSnake()::malloc()");
  69. return;
  70. }
  71. cur->next = NULL;
  72. cur->x = POS_X + 2 * i;
  73. cur->y = POS_Y;
  74. //头插
  75. if (ps->_pSnake == NULL)
  76. {
  77. ps->_pSnake = cur;
  78. }
  79. else
  80. {
  81. cur->next = ps->_pSnake;
  82. ps->_pSnake = cur;
  83. }
  84. }
  85. cur = ps->_pSnake;
  86. while (cur)
  87. {
  88. Set_Pos(cur->x, cur->y);
  89. wprintf(L"%lc",BODY);
  90. cur = cur->next;
  91. }
  92. //设置贪吃蛇的属性
  93. ps->_dir = RIGHT;//默认向右走
  94. ps->_score = 0;
  95. ps->_food_weight = 10;
  96. ps->_sleep_time = 200;
  97. ps->_status = OK;
  98. }
  99. void CreateFood(pSnake ps)
  100. {
  101. int x = 0;
  102. int y = 0;
  103. again:
  104. do
  105. {
  106. x = rand() % 53 + 2;
  107. y = rand() % 25 + 1;
  108. } while (x % 2 != 0);
  109. //x和y的坐标不能和蛇的身体坐标冲突
  110. pSnakeNode cur = ps->_pSnake;
  111. while (cur)
  112. {
  113. if (x == cur->x && cur->y == y)
  114. {
  115. goto again;
  116. }
  117. cur = cur->next;
  118. }
  119. //创建食物的节点
  120. pSnakeNode pFood = (pSnakeNode)malloc(sizeof(SnakeNode));
  121. if (pFood == NULL)
  122. {
  123. perror("CreateFood()::malloc()");
  124. return;
  125. }
  126. pFood->x = x;
  127. pFood->y = y;
  128. pFood->next = NULL;
  129. Set_Pos(x, y);
  130. color(12); //颜色设置为红色
  131. wprintf(L"%lc", FOOD);
  132. color(7); //颜色设置为白色
  133. ps->_pFood = pFood;
  134. }
  135. void GameStart(pSnake ps)
  136. {
  137. //0.设置窗口大小,光标隐藏
  138. ps->_pSnake = NULL;
  139. system("mode con cols=100 lines=30");
  140. system("title 贪吃蛇");
  141. HANDLE houtput = GetStdHandle(STD_OUTPUT_HANDLE);
  142. CONSOLE_CURSOR_INFO CursorInfo = { 0 };
  143. GetConsoleCursorInfo(houtput, &CursorInfo);
  144. CursorInfo.bVisible = false;
  145. SetConsoleCursorInfo(houtput, &CursorInfo);
  146. //1.打印欢迎界面
  147. WelcomeToGame();
  148. //2.绘制地图
  149. CreateMap();
  150. //3.创建蛇
  151. InitSnake(ps);
  152. //4.创建食物
  153. CreateFood(ps);
  154. //5.设置游戏的相关信息
  155. }
  156. void PrintHelpInfo()
  157. {
  158. color(6);
  159. Set_Pos(64, 10);
  160. wprintf(L"%ls", L"不能穿墙,不能咬到自己\n");
  161. Set_Pos(64, 11);
  162. wprintf(L"%ls", L"用↑,↓,←,→来控制蛇的移动\n");
  163. Set_Pos(64, 12);
  164. wprintf(L"%ls", L"按F3加速,F4减速\n");
  165. Set_Pos(64, 13);
  166. wprintf(L"%ls", L"按ESC退出游戏,按空格暂停游戏\n");
  167. Set_Pos(64, 15);
  168. wprintf(L"%ls", L"贪吃蛇小游戏");
  169. color(7);
  170. }
  171. void Pause()
  172. {
  173. while (1)
  174. {
  175. Sleep(200);
  176. if (KEY_PRESS(VK_SPACE))
  177. break;
  178. }
  179. }
  180. int NextIsFood(pSnakeNode pNextNode, pSnake ps)
  181. {
  182. return (ps->_pFood->x == pNextNode->x && ps->_pFood->y == pNextNode->y);
  183. }
  184. void EatFood(pSnakeNode pNextNode, pSnake ps)
  185. {
  186. //头插
  187. ps->_pFood->next = ps->_pSnake;
  188. ps->_pSnake = ps->_pFood;
  189. //释放旧的节点
  190. free(pNextNode);
  191. pNextNode = NULL;
  192. pSnakeNode cur = ps->_pSnake;
  193. //将头置为红色
  194. color(12); //颜色设置为红色
  195. Set_Pos(cur->x, cur->y);
  196. wprintf(L"%lc", BODY);
  197. cur = cur->next;
  198. color(7); //颜色设置为白色
  199. while (cur)
  200. {
  201. Set_Pos(cur->x, cur->y);
  202. wprintf(L"%lc", BODY);
  203. cur = cur->next;
  204. }
  205. ps->_score += ps->_food_weight;
  206. Set_Pos(76, 8);//定位光标到分数旁边
  207. printf("+%2d", ps->_food_weight);
  208. Sleep(100);
  209. Set_Pos(76, 8);
  210. printf(" ");
  211. //重新创建食物
  212. CreateFood(ps);
  213. }
  214. bool KillByWall(pSnake ps)
  215. {
  216. if (ps->_pSnake->x == 0 || ps->_pSnake->x == 56
  217. || ps->_pSnake->y == 0 || ps->_pSnake->y == 26)
  218. {
  219. ps->_status = KILL_BY_WALL;
  220. return false;
  221. }
  222. return true;
  223. }
  224. bool KillBySelf(pSnake ps)
  225. {
  226. pSnakeNode cur = ps->_pSnake->next;
  227. while (cur)
  228. {
  229. if (cur->x == ps->_pSnake->x && cur->y == ps->_pSnake->y)
  230. {
  231. ps->_status = KILL_BY_SELF;
  232. return false;
  233. }
  234. cur = cur->next;
  235. }
  236. return true;
  237. }
  238. void NoFood(pSnakeNode pNextNode, pSnake ps)
  239. {
  240. //头插
  241. pNextNode->next = ps->_pSnake;
  242. ps->_pSnake = pNextNode;
  243. //撞墙
  244. if (!KillByWall(ps))
  245. {
  246. return;
  247. }
  248. //撞到自己
  249. if (!KillBySelf(ps))
  250. {
  251. return;
  252. }
  253. pSnakeNode cur = ps->_pSnake;
  254. //将头置为红色
  255. color(12); //颜色设置为红色
  256. Set_Pos(cur->x, cur->y);
  257. wprintf(L"%lc", BODY);
  258. cur = cur->next;
  259. color(7); //颜色设置为白色(每次置为其他颜色时都要将其再置为白色,以便下一次置色)
  260. while (cur->next->next != NULL)
  261. {
  262. Set_Pos(cur->x, cur->y);
  263. wprintf(L"%lc", BODY);
  264. cur = cur->next;
  265. }
  266. //把最后一个节点打印空格
  267. Set_Pos(cur->next->x, cur->next->y);
  268. printf(" ");//要两个空格
  269. //释放最后一个节点
  270. free(cur->next);
  271. //把倒数第二个节点的next置为空
  272. cur->next = NULL;
  273. }
  274. void SnakeMove(pSnake ps)
  275. {
  276. pSnakeNode pNextNode = (pSnakeNode)malloc(sizeof(SnakeNode));
  277. if (pNextNode == NULL)
  278. {
  279. perror("SnakeMove()::malloc()");
  280. return;
  281. }
  282. switch (ps->_dir)
  283. {
  284. case UP:
  285. pNextNode->x = ps->_pSnake->x;
  286. pNextNode->y = ps->_pSnake->y - 1;
  287. break;
  288. case DOWN:
  289. pNextNode->x = ps->_pSnake->x;
  290. pNextNode->y = ps->_pSnake->y + 1;
  291. break;
  292. case LEFT:
  293. pNextNode->x = ps->_pSnake->x - 2;
  294. pNextNode->y = ps->_pSnake->y;
  295. break;
  296. case RIGHT:
  297. pNextNode->x = ps->_pSnake->x + 2;
  298. pNextNode->y = ps->_pSnake->y;
  299. break;
  300. }
  301. //检测下一个坐标是否为食物
  302. if (NextIsFood(pNextNode, ps))
  303. {
  304. EatFood(pNextNode, ps);
  305. }
  306. else
  307. {
  308. NoFood(pNextNode, ps);
  309. }
  310. //撞墙
  311. KillByWall(ps);
  312. //撞到自己
  313. KillBySelf(ps);
  314. }
  315. int NoKillByWall(pSnake ps, pSnakeNode pn)
  316. {
  317. pSnakeNode pNextNode = (pSnakeNode)malloc(sizeof(SnakeNode));
  318. if (pNextNode == NULL)
  319. {
  320. perror("SnakeMove()::malloc()");
  321. exit(1);
  322. }
  323. if (pn->x == 0)
  324. {
  325. //将头节点穿墙
  326. pNextNode->x = 54;
  327. pNextNode->y = ps->_pSnake->y;
  328. pNextNode->next = NULL;
  329. //判断下一个节点是否为食物
  330. if (NextIsFood(pNextNode, ps))
  331. {
  332. EatFood(pNextNode, ps);
  333. }
  334. else
  335. {
  336. NoFood(pNextNode, ps);
  337. }
  338. free(pn);
  339. return 0;
  340. }
  341. else if (pn->x == 56)
  342. {
  343. //将头节点穿墙
  344. pNextNode->x = 2;
  345. pNextNode->y = ps->_pSnake->y;
  346. pNextNode->next = NULL;
  347. //判断下一个节点是否为食物
  348. if (NextIsFood(pNextNode, ps))
  349. {
  350. EatFood(pNextNode, ps);
  351. }
  352. else
  353. {
  354. NoFood(pNextNode, ps);
  355. }
  356. free(pn);
  357. pn = NULL;
  358. return 0;
  359. }
  360. else if (pn->y == 0)
  361. {
  362. //将头节点穿墙
  363. pNextNode->y = 25;
  364. pNextNode->x = ps->_pSnake->x;
  365. pNextNode->next = NULL;
  366. //判断下一个节点是否为食物
  367. if (NextIsFood(pNextNode, ps))
  368. {
  369. EatFood(pNextNode, ps);
  370. }
  371. else
  372. {
  373. NoFood(pNextNode, ps);
  374. }
  375. free(pn);
  376. pn = NULL;
  377. return 0;
  378. }
  379. else if (pn->y == 26)
  380. {
  381. //将头节点穿墙
  382. pNextNode->y = 1;
  383. pNextNode->x = ps->_pSnake->x;
  384. pNextNode->next = NULL;
  385. //判断下一个节点是否为食物
  386. if (NextIsFood(pNextNode, ps))
  387. {
  388. EatFood(pNextNode, ps);
  389. }
  390. else
  391. {
  392. NoFood(pNextNode, ps);
  393. }
  394. free(pn);
  395. pn = NULL;
  396. return 0;
  397. }
  398. return 1;
  399. }
  400. void WallSnakeMove(pSnake ps)
  401. {
  402. pSnakeNode pNextNode = (pSnakeNode)malloc(sizeof(SnakeNode));
  403. if (pNextNode == NULL)
  404. {
  405. perror("SnakeMove()::malloc()");
  406. return;
  407. }
  408. switch (ps->_dir)
  409. {
  410. case UP:
  411. pNextNode->x = ps->_pSnake->x;
  412. pNextNode->y = ps->_pSnake->y - 1;
  413. break;
  414. case DOWN:
  415. pNextNode->x = ps->_pSnake->x;
  416. pNextNode->y = ps->_pSnake->y + 1;
  417. break;
  418. case LEFT:
  419. pNextNode->x = ps->_pSnake->x - 2;
  420. pNextNode->y = ps->_pSnake->y;
  421. break;
  422. case RIGHT:
  423. pNextNode->x = ps->_pSnake->x + 2;
  424. pNextNode->y = ps->_pSnake->y;
  425. break;
  426. }
  427. if (NoKillByWall(ps, pNextNode))
  428. {
  429. if (NextIsFood(pNextNode, ps))
  430. {
  431. EatFood(pNextNode, ps);
  432. }
  433. else
  434. {
  435. NoFood(pNextNode, ps);
  436. }
  437. }
  438. //检测下一个坐标是否为食物
  439. //撞到自己
  440. KillBySelf(ps);
  441. }
  442. void GameRun(pSnake ps)
  443. {
  444. PrintHelpInfo();
  445. do
  446. {
  447. color(6);
  448. //打印总分数和食物的分值
  449. Set_Pos(64, 8);
  450. printf("总分数:%d\n", ps->_score);
  451. Set_Pos(64, 9);
  452. printf("当前食物的分数:%2d\n", ps->_food_weight);
  453. color(7);
  454. if (KEY_PRESS(VK_UP) && ps->_dir != DOWN)
  455. {
  456. ps->_dir = UP;
  457. }
  458. else if (KEY_PRESS(VK_DOWN) && ps->_dir != UP)
  459. {
  460. ps->_dir = DOWN;
  461. }
  462. else if (KEY_PRESS(VK_LEFT) && ps->_dir != RIGHT)
  463. {
  464. ps->_dir = LEFT;
  465. }
  466. else if (KEY_PRESS(VK_RIGHT) && ps->_dir != LEFT)
  467. {
  468. ps->_dir = RIGHT;
  469. }
  470. else if (KEY_PRESS(VK_SPACE))
  471. {
  472. Pause();
  473. }
  474. else if (KEY_PRESS(VK_ESCAPE))
  475. {
  476. //正常退出游戏
  477. ps->_status = END_NORMAL;
  478. //退出
  479. }
  480. else if (KEY_PRESS(VK_F3))
  481. {
  482. //加速
  483. if (ps->_sleep_time > 80)
  484. {
  485. ps->_sleep_time -= 30;
  486. ps->_food_weight += 2;
  487. }
  488. }
  489. else if (KEY_PRESS(VK_F4))
  490. {
  491. //减速
  492. if (ps->_food_weight >= 2)
  493. {
  494. ps->_sleep_time += 30;
  495. ps->_food_weight -= 2;
  496. }
  497. }
  498. if (choice == '1')
  499. {
  500. WallSnakeMove(ps);//蛇可以穿墙
  501. }
  502. else
  503. {
  504. SnakeMove(ps);//蛇走一步的过程
  505. }
  506. Sleep(ps->_sleep_time);
  507. } while (ps->_status == OK);
  508. }
  509. void GameEnd(pSnake ps)
  510. {
  511. Set_Pos(24, 12);
  512. color(6);
  513. switch (ps->_status)
  514. {
  515. case END_NORMAL:
  516. printf("你主动结束游戏\n");
  517. break;
  518. case KILL_BY_WALL:
  519. printf("你寄了\n");
  520. break;
  521. case KILL_BY_SELF:
  522. printf("一不小心撞到自己了\n");
  523. break;
  524. }
  525. color(7);
  526. pSnakeNode cur = ps->_pSnake;
  527. while (cur)
  528. {
  529. pSnakeNode del = cur;
  530. cur = cur->next;
  531. free(del);
  532. }
  533. }

test.c

  1. #define _CRT_SECURE_NO_WARNINGS 1
  2. #include"snake.h"
  3. #include<locale.h>
  4. void test()
  5. {
  6. int ch = 0;
  7. do
  8. {
  9. color(6); //颜色设置为土黄色
  10. system("cls");
  11. //创建贪吃蛇
  12. Snake snake = { 0 };
  13. //初始化游戏
  14. //1.打印欢迎界面
  15. //2.功能介绍
  16. //3.绘制地图
  17. //4.创建蛇
  18. //5.创建食物
  19. //6.设置游戏的相关信息
  20. Set_Pos(38, 15);
  21. printf("1.穿墙");
  22. Set_Pos(38, 16);
  23. printf("2.不穿墙");
  24. Set_Pos(38, 18);
  25. printf("请选择模式:>");
  26. choice = getchar();
  27. while (getchar() != '\n');
  28. system("cls");
  29. GameStart(&snake);
  30. //运行游戏
  31. GameRun(&snake);
  32. //结束游戏
  33. GameEnd(&snake);
  34. while (_kbhit())
  35. {
  36. // 使用 _getch() 获取按下的键,不阻塞程序
  37. _getch();
  38. // 处理按键事件,可以根据需要进行相应的操作
  39. }
  40. Set_Pos(20, 15);
  41. color(6);
  42. printf("再来一句不老铁?>(Y/N):");
  43. ch = getchar();
  44. color(7);
  45. while (getchar() != '\n');
  46. } while (ch=='Y'||ch=='y');
  47. Set_Pos(0, 27);
  48. }
  49. int main()
  50. {
  51. setlocale(LC_ALL, "");
  52. srand((unsigned int)time(NULL));
  53. test();
  54. return 0;
  55. }

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

闽ICP备14008679号