• 贪吃蛇(C语言实现)


      贪食蛇(也叫贪吃蛇)是一款经典的小游戏。

    ——————————————————————

      本博客实现使用C语言在Windows环境的控制台中模拟实现贪吃蛇小游戏。

    实行的基本功能:

    •  贪吃蛇地图的绘制

    •  蛇吃食物的功能(上、下、左、右方向键来控制蛇的方向)

    •  蛇撞墙死亡

    •  蛇撞自身死亡

    •  计算得分

    •  蛇身加速、减速

    •  暂停游戏

    主要框架(主要函数):

    1. GameStrat();//游戏开始前的初始化
    2. GameRun();//玩游戏的过程
    3. GameEnd();//善后工作(节点的释放)

    游戏流程设计:

    参考代码:

    snake.h

    1. #pragma once
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. #define WALL L'□'
    8. #define BODY L'●'
    9. #define FOOD L'★'
    10. //蛇默认的起始坐标
    11. #define POS_X 24
    12. #define POS_Y 5
    13. #define KEY_PRESS(VK) ( (GetAsyncKeyState(VK) & 0x1) ? 1 : 0 )
    14. //游戏的状态
    15. enum GAME_STATUS
    16. {
    17. OK=1,//正常运行
    18. ESC, //按了ESC键退出,正常退出
    19. KILL_BY_WALL,//撞墙
    20. KILL_BY_SELF //撞到自身
    21. };
    22. //蛇行走的方向
    23. enum DIRECTION
    24. {
    25. UP=1,
    26. DOWN,
    27. LEFT,
    28. RIGHT
    29. };
    30. //蛇身结点的定义
    31. typedef struct SnakeNode
    32. {
    33. int x;
    34. int y;
    35. struct SnakeNode* next;
    36. }SnakeNode, * pSnakeNode;
    37. //贪吃蛇
    38. typedef struct Snake
    39. {
    40. pSnakeNode pSnake;//维护整条蛇的指针,是指向蛇头
    41. pSnakeNode pFood;//指向食物的指针
    42. int Score;//当前累积的分数
    43. int FoodWeight;//一个食物的分数
    44. int SleepTime;//蛇休眠的时间,休眠的时间越短,蛇的速度越快,休眠的时间越长,蛇的速度越慢
    45. enum GAME_STATUS status;//游戏当前的状态
    46. enum DIRECTION dir;//蛇当前走的方向
    47. //...
    48. }Snake, * pSnake;
    49. //定位控制台光标位置
    50. void SetPos(int x, int y);
    51. //游戏开始前的准备
    52. void GameStart(pSnake ps);
    53. //打印欢迎界面
    54. void WelcomeToGame();
    55. //绘制地图
    56. void CreateMap();
    57. //初始化贪吃蛇
    58. void InitSnake(pSnake ps);
    59. //创建食物
    60. void CreateFood(pSnake ps);
    61. //游戏运行的整个逻辑
    62. void GameRun(pSnake ps);
    63. //打印帮助信息
    64. void PrintHelpInfo();
    65. //蛇移动的函数- 每次走一步
    66. void SnakeMove(pSnake ps);
    67. //判断蛇头的下一步要走的位置处是否是食物
    68. int NextIsFood(pSnake ps, pSnakeNode pNext);
    69. //下一步要走的位置处就是食物,就吃掉食物
    70. void EatFood(pSnake ps, pSnakeNode pNext);
    71. //下一步要走的位置处不是食物,不吃食物
    72. void NotEatFood(pSnake ps, pSnakeNode pNext);
    73. //检测是否撞墙
    74. void KillByWall(pSnake ps);
    75. //检测是否撞自己
    76. void KillBySelf(pSnake ps);
    77. //游戏结束的资源释放
    78. void GameEnd(pSnake ps);

    snake.c

    1. #define _CRT_SECURE_NO_WARNINGS 1
    2. #include "snake.h"
    3. void SetPos(int x,int y)
    4. {
    5. //获得设备句柄
    6. HANDLE hanlde = GetStdHandle(STD_OUTPUT_HANDLE);
    7. //根据句柄设置光标的位置
    8. COORD pos = { x, y };
    9. SetConsoleCursorPosition(hanlde, pos);
    10. }
    11. void WelcomeToGame()
    12. {
    13. //欢迎信息
    14. SetPos(35, 10);
    15. printf("欢迎来到贪吃蛇小游戏\n");
    16. SetPos(38, 20);
    17. system("pause");
    18. system("cls");
    19. //功能介绍信息
    20. SetPos(15, 10);
    21. printf("用 ↑ . ↓ . ← . → 来控制蛇的移动,F3是加速,F4是减速");
    22. SetPos(15, 11);
    23. printf("加速能得到更高的分数");
    24. SetPos(38, 20);
    25. system("pause");
    26. system("cls");
    27. }
    28. void CreateMap()
    29. {
    30. int i = 0;
    31. //上
    32. SetPos(0, 0);
    33. for (i = 0; i <= 56; i += 2)
    34. {
    35. wprintf(L"%lc", WALL);
    36. }
    37. //下
    38. SetPos(0, 26);
    39. for (i = 0; i <= 56; i += 2)
    40. {
    41. wprintf(L"%lc", WALL);
    42. }
    43. //左
    44. for (i = 1; i <= 25; i++)
    45. {
    46. SetPos(0, i);
    47. wprintf(L"%lc", WALL);
    48. }
    49. //右
    50. for (i = 1; i <= 25; i++)
    51. {
    52. SetPos(56, i);
    53. wprintf(L"%lc", WALL);
    54. }
    55. }
    56. void InitSnake(pSnake ps)
    57. {
    58. //创建5个蛇身的结点
    59. pSnakeNode cur = NULL;
    60. int i = 0;
    61. for (i = 0; i < 5; i++)
    62. {
    63. cur = (pSnakeNode)malloc(sizeof(SnakeNode));
    64. if (cur == NULL)
    65. {
    66. perror("InitSnake():malloc()");
    67. return;
    68. }
    69. cur->x = POS_X + 2 * i;
    70. cur->y = POS_Y;
    71. cur->next = NULL;
    72. //头插法
    73. if (ps->pSnake == NULL)
    74. {
    75. ps->pSnake = cur;
    76. }
    77. else
    78. {
    79. cur->next = ps->pSnake;
    80. ps->pSnake = cur;
    81. }
    82. }
    83. //打印蛇身
    84. cur = ps->pSnake;
    85. while (cur)
    86. {
    87. SetPos(cur->x, cur->y);
    88. wprintf(L"%lc", BODY);
    89. cur = cur->next;
    90. }
    91. //贪吃蛇的其他信息初始化
    92. ps->dir = RIGHT;
    93. ps->FoodWeight = 10;
    94. ps->pFood = NULL;
    95. ps->Score = 0;
    96. ps->SleepTime = 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() % 24 + 1;
    108. } while (x % 2 != 0);
    109. //坐标和蛇的身体的每个节点的做坐标比较
    110. pSnakeNode cur = ps->pSnake;
    111. while (cur)
    112. {
    113. if (x == cur->x && y == cur->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. ps->pFood = pFood;
    129. SetPos(x, y);
    130. wprintf(L"%lc", FOOD);
    131. }
    132. void GameStart(pSnake ps)
    133. {
    134. //设置控制台的信息,窗口大小,窗口名
    135. system("mode con cols=100 lines=30");
    136. system("title 贪吃蛇");
    137. //隐藏光标
    138. HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
    139. CONSOLE_CURSOR_INFO CursorInfo;
    140. GetConsoleCursorInfo(handle, &CursorInfo);//获取控制台光标信息
    141. CursorInfo.bVisible = false;
    142. SetConsoleCursorInfo(handle, &CursorInfo);
    143. //打印欢迎信息
    144. WelcomeToGame();
    145. //绘制地图
    146. CreateMap();
    147. //初始化蛇
    148. InitSnake(ps);
    149. //创建食物
    150. CreateFood(ps);
    151. }
    152. void PrintHelpInfo()
    153. {
    154. SetPos(62, 15);
    155. printf("1.不能穿墙,不能咬到自己");
    156. SetPos(62, 16);
    157. printf("2.用 ↑.↓.←.→ 来控制蛇的移动");
    158. SetPos(62, 17);
    159. printf("3.F3是加速,F4是减速");
    160. SetPos(62, 19);
    161. printf("版权@比特就业课");
    162. }
    163. void pause()
    164. {
    165. while (1)
    166. {
    167. Sleep(100);
    168. if (KEY_PRESS(VK_SPACE))
    169. {
    170. break;
    171. }
    172. }
    173. }
    174. int NextIsFood(pSnake ps, pSnakeNode pNext)
    175. {
    176. if (ps->pFood->x == pNext->x && ps->pFood->y == pNext->y)
    177. return 1;//下一个坐标处是食物
    178. else
    179. return 0;
    180. }
    181. void EatFood(pSnake ps, pSnakeNode pNext)
    182. {
    183. pNext->next = ps->pSnake;
    184. ps->pSnake = pNext;
    185. //打印蛇
    186. pSnakeNode cur = ps->pSnake;
    187. while (cur)
    188. {
    189. SetPos(cur->x, cur->y);
    190. wprintf(L"%lc", BODY);
    191. cur = cur->next;
    192. }
    193. ps->Score += ps->FoodWeight;
    194. //释放旧的食物
    195. free(ps->pFood);
    196. //新建食物
    197. CreateFood(ps);
    198. }
    199. void NotEatFood(pSnake ps, pSnakeNode pNext)
    200. {
    201. //头插法
    202. pNext->next = ps->pSnake;
    203. ps->pSnake = pNext;
    204. //释放尾结点
    205. pSnakeNode cur = ps->pSnake;
    206. while (cur->next->next)
    207. {
    208. SetPos(cur->x, cur->y);
    209. wprintf(L"%lc", BODY);
    210. cur = cur->next;
    211. }
    212. //将尾节点的位置打印成空白字符
    213. SetPos(cur->next->x, cur->next->y);
    214. printf(" ");
    215. free(cur->next);
    216. cur->next = NULL;//易错
    217. }
    218. //检测是否撞墙
    219. void KillByWall(pSnake ps)
    220. {
    221. if (ps->pSnake->x == 0 ||
    222. ps->pSnake->x == 56 ||
    223. ps->pSnake->y == 0 ||
    224. ps->pSnake->y == 26)
    225. {
    226. ps->status = KILL_BY_WALL;
    227. }
    228. }
    229. //检测是否撞自己
    230. void KillBySelf(pSnake ps)
    231. {
    232. pSnakeNode cur = ps->pSnake->next;//从第二个节点开始
    233. while (cur)
    234. {
    235. if (cur->x == ps->pSnake->x && cur->y == ps->pSnake->y)
    236. {
    237. ps->status = KILL_BY_SELF;
    238. return;
    239. }
    240. cur = cur->next;
    241. }
    242. }
    243. void SnakeMove(pSnake ps)
    244. {
    245. pSnakeNode pNext = (pSnakeNode)malloc(sizeof(SnakeNode));
    246. if (pNext == NULL)
    247. {
    248. perror("SnakeMove()::malloc()");
    249. return;
    250. }
    251. pNext->next = NULL;
    252. switch (ps->dir)
    253. {
    254. case UP:
    255. pNext->x = ps->pSnake->x;
    256. pNext->y = ps->pSnake->y - 1;
    257. break;
    258. case DOWN:
    259. pNext->x = ps->pSnake->x;
    260. pNext->y = ps->pSnake->y + 1;
    261. break;
    262. case LEFT:
    263. pNext->x = ps->pSnake->x-2;
    264. pNext->y = ps->pSnake->y;
    265. break;
    266. case RIGHT:
    267. pNext->x = ps->pSnake->x+2;
    268. pNext->y = ps->pSnake->y;
    269. break;
    270. }
    271. //下一个坐标处是否是食物
    272. if (NextIsFood(ps, pNext))
    273. {
    274. //是食物就吃掉
    275. EatFood(ps, pNext);
    276. }
    277. else
    278. {
    279. //不是食物就正常一步
    280. NotEatFood(ps, pNext);
    281. }
    282. //检测撞墙
    283. KillByWall(ps);
    284. //检测撞到自己
    285. KillBySelf(ps);
    286. }
    287. void GameRun(pSnake ps)
    288. {
    289. //打印帮助信息
    290. PrintHelpInfo();
    291. do
    292. {
    293. //当前的分数情况
    294. SetPos(62, 10);
    295. printf("总分:%5d\n", ps->Score);
    296. SetPos(62, 11);
    297. printf("食物的分值:%02d\n", ps->FoodWeight);
    298. //检测按键
    299. //上、下、左、右、ESC、空格、F3、F4
    300. if (KEY_PRESS(VK_UP) && ps->dir != DOWN)
    301. {
    302. ps->dir = UP;
    303. }
    304. else if (KEY_PRESS(VK_DOWN) && ps->dir != UP)
    305. {
    306. ps->dir = DOWN;
    307. }
    308. else if (KEY_PRESS(VK_LEFT) && ps->dir != RIGHT)
    309. {
    310. ps->dir = LEFT;
    311. }
    312. else if (KEY_PRESS(VK_RIGHT) && ps->dir != LEFT)
    313. {
    314. ps->dir = RIGHT;
    315. }
    316. else if (KEY_PRESS(VK_ESCAPE))
    317. {
    318. ps->status = ESC;
    319. break;
    320. }
    321. else if (KEY_PRESS(VK_SPACE))
    322. {
    323. //游戏要暂定
    324. pause();//暂定和回复暂定
    325. }
    326. else if(KEY_PRESS(VK_F3))
    327. {
    328. if (ps->SleepTime >= 80)
    329. {
    330. ps->SleepTime -= 30;
    331. ps->FoodWeight += 2;
    332. }
    333. }
    334. else if (KEY_PRESS(VK_F4))
    335. {
    336. if (ps->FoodWeight > 2)
    337. {
    338. ps->SleepTime += 30;
    339. ps->FoodWeight -= 2;
    340. }
    341. }
    342. //走一步
    343. SnakeMove(ps);
    344. //睡眠一下
    345. Sleep(ps->SleepTime);
    346. } while (ps->status == OK);
    347. }
    348. void GameEnd(pSnake ps)
    349. {
    350. SetPos(15, 12);
    351. switch (ps->status)
    352. {
    353. case ESC:
    354. printf("主动退出游戏,正常退出\n");
    355. break;
    356. case KILL_BY_WALL:
    357. printf("很遗憾,撞墙了,游戏结束\n");
    358. break;
    359. case KILL_BY_SELF:
    360. printf("很遗憾,咬到自己了,游戏结束\n");
    361. break;
    362. }
    363. //释放贪吃蛇的链表资源
    364. pSnakeNode cur = ps->pSnake;
    365. pSnakeNode del = NULL;
    366. while (cur)
    367. {
    368. del = cur;
    369. cur = cur->next;
    370. free(del);
    371. }
    372. free(ps->pFood);
    373. ps = NULL;
    374. }

    test.c

    1. #define _CRT_SECURE_NO_WARNINGS 1
    2. #include "snake.h"
    3. void test()
    4. {
    5. //创建贪食蛇
    6. int ch = 0;
    7. do
    8. {
    9. Snake snake = { 0 };
    10. GameStart(&snake);//游戏开始前的初始化
    11. GameRun(&snake);//玩游戏的过程
    12. GameEnd(&snake);//善后的工作
    13. SetPos(20, 15);
    14. printf("再来一局吗?(Y/N):");
    15. ch = getchar();
    16. getchar();// 清理\n
    17. } while (ch == 'Y' || ch =='y');
    18. }
    19. int main()
    20. {
    21. //修改适配本地中文环境
    22. setlocale(LC_ALL, "");
    23. test();//贪吃蛇游戏的测试
    24. SetPos(0, 27);
    25. return 0;
    26. }

    这个博客如果对你有帮助,给博主一个免费的点赞就是最大的帮助

    欢迎各位点赞,收藏和关注哦

    如果有疑问或有不同见解,欢迎在评论区留言哦

    后续我会一直分享双一流211西北大学软件(C,数据结构,C++,Linux,MySQL)的学习干货以及重要代码的分享

  • 相关阅读:
    关于Docker挂载的问题!
    Java -- 每日一问:Exception 和 Error 有什么区别?
    27. 移除元素
    虚拟与实际内存之间的关系
    浅谈智能安全配电装置在老年人建筑中的应用
    Linux环境下Arm端源码编译OpenCV+ncnn目标检测模型实例运行调试完整实践记录
    java计算机毕业设计消防应急管理系统源码+系统+数据库+lw文档+mybatis+运行部署
    scrollTop、clientHeight、 scrollHeight...学完真的理解了
    手机厂商“互卷”之年:“机海战术”失灵,“慢节奏”打法崛起
    《QT从基础到进阶·十六》QT实现客户端和服务端的简单交互
  • 原文地址:https://blog.csdn.net/m0_73751295/article/details/136589628