手把手教你实现贪吃蛇(上):https://developer.aliyun.com/article/1389416
⭐核心逻辑实现分析(重点)
咱们从三个方面来讲解贪吃蛇的核心逻辑分析:test.c主函数,snake.h包含头文件,snake.c主函数的实现,跟我们玩链表差不多,都是老套路了那贪吃蛇的实现正式上路咯,启程。
🌙snake.h头文件的实现
像头文件这个东西必然是要包含一些头文件的,当然这只是它的一部分作用,像我们定义的链表,为了在各个源文件都能使用,我们就在头文件实现链表。如果函数很多,就把函数头包函数在头文件中,方便调用,那咱们看看在snake.h中有啥?
//包含头文件 #include <locale.h> #include<stdio.h> #include<assert.h> #include<stdlib.h> #include<windows.h> #include<time.h> #include<stdbool.h> //定义图案 #define WALL L'□' #define BODY L'●' #define FOOD L'★' //初始化蛇的位置 #define POS_X 24 #define POS_Y 5 //键位的移动 #define KEY_PRESS(VK) ( (GetAsyncKeyState(VK) & 0x1) ? 1 : 0 ) //枚举键位 enum DIRECTION { UP = 1,//上 DOWN,//下 LEFT,//左 RIGHT//右 }; //枚举游戏状态 enum GAME_STATUS { OK,//正常运行 END_NORMAL,//按ESC退出 KILL_BY_WALL, KILL_BY_SELF }; //贪吃蛇每个节点的描述 typedef struct SnakeNode { int x; int y; struct SnakeNode* next;//下个节点 }SnakeNode, * pSnakeNode; //贪吃蛇组成 typedef struct Snake { pSnakeNode _pSnake;//指向贪吃蛇头结点的指针 pSnakeNode _pFood;//指向食物结点的指针 int _Score;//贪吃蛇累计的总分 int _FoodWeight;//一个食物的分数 int _SleepTime;//每走一步休息的时间,时间越短,速度越快,时间越长,速度越慢 enum DIRECTION _Dir;//描述蛇的方向 enum GAME_STATUS _Status;//游戏的状态:正常、退出、撞墙、吃到自己 }Snake, * pSnake; //游戏开始 - 完成游戏的初始化动作 void GameStart(pSnake ps); //定位坐标 void SetPos(short x, short y); //游戏开始的欢迎界面 void WelComeToGame(); //打印地图 void CreateMap(); //初始化贪吃蛇 void InitSnake(pSnake ps); //创建食物 void CreateFood(pSnake ps); //游戏的正常运行 void GameRun(pSnake ps); //打印帮助信息 void PrintHelpInfo(); //游戏暂定和恢复 void Pause(); //蛇的移动 void SnakeMove(pSnake ps); //判断蛇头到达的坐标处是否是食物 int NextIsFood(pSnake ps, pSnakeNode pnext); //吃掉食物 void EatFood(pSnake ps, pSnakeNode pnext); //不吃食物 void NoFood(pSnake ps, pSnakeNode pnext); //蛇是否撞墙 void KillByWall(pSnake ps); //蛇是否自杀 void KillBySelf(pSnake ps); //游戏结束后的善后处理 void GameEnd(pSnake ps);
🌙snake.c源文件的实现
在这个文件中咱们实现贪吃蛇的函数:实现时当然需要包含snake.h的文件啦,需要用头文件里面的链表等.....
🌠游戏开始 - 初始化游戏
//1. 游戏开始 - 初始化游戏 void GameStart(pSnake ps) { //设置控制台的大小 system("mode con cols=100 lines=30"); system("title 贪吃蛇"); //光标影藏掉 HANDLE hOutput = GetStdHandle(STD_OUTPUT_HANDLE); //影藏光标操作 CONSOLE_CURSOR_INFO CursorInfo; GetConsoleCursorInfo(hOutput, &CursorInfo);//获取控制台光标信息 CursorInfo.bVisible = false; //隐藏控制台光标 SetConsoleCursorInfo(hOutput, &CursorInfo);//设置控制台光标状态 //打印欢迎界面 WelComeToGame(); //创建地图 CreateMap(); //初始化贪食蛇 InitSnake(ps); //创建食物 CreateFood(ps); }
💫设置光标的坐标
光标的移动需要多次实现,为了我们方便使用移动光标位置,所以我们直接实现这个函数。不会有人跟我说光标是啥吧,看我打不死你,看到没:
设置光标位置在win32 API已经介绍咯。
//设置光标的坐标 void SetPos(short x, short y) { COORD pos = { x, y }; HANDLE hOutput = NULL; //获取标准输出的句柄(用来标识不同设备的数值) hOutput = GetStdHandle(STD_OUTPUT_HANDLE); //设置标准输出上光标的位置为pos SetConsoleCursorPosition(hOutput, pos); }
💫打印欢迎界面
其实这个函数大家可以自己设置,把欢迎界面整的好看些,像博主就比较懒就简单设计一下咯。
//打印欢迎界面 void WelComeToGame() { //定位光标 SetPos(40, 14); printf("欢迎来到贪吃蛇小游戏"); SetPos(40, 25); system("pause");//pause是暂停 system("cls"); SetPos(20, 14); printf("使用 ↑ . ↓ . ← . → . 分别控制蛇的移动, F3是加速,F4是减速"); SetPos(40, 25); system("pause"); system("cls"); }
💫创建地图/初始化贪食蛇
大家眼睛擦亮点,计算坐标时不要计算错了,可以根据自己设计自己喜欢的图:
切记一定要把蛇初始状态需要是偶数倍:初始化状态,假设蛇的长度是5,蛇身的每个节点是●,在固定的一个坐标处,比如(24, 5)处开始出现蛇,连续5个节点。注意:蛇的每个节点的x坐标必须是2个倍数,否则可能会出现蛇的⼀个节点有一半儿出现在墙体中,另外⼀般在墙外的现象,坐标不好对齐。
//创建地图 void CreateMap() { //上 SetPos(0, 0); int i = 0; for (i = 0; i <= 56; i += 2) { wprintf(L"%lc", WALL); } //下 SetPos(0, 26); for (i = 0; i <= 56; i += 2) { wprintf(L"%lc", WALL); } //左 for (i = 1; i <= 25; i++) { SetPos(0, i); wprintf(L"%lc", WALL); } //右 for (i = 1; i <= 25; i++) { SetPos(56, i); wprintf(L"%lc", WALL); } } //初始化贪食蛇 void InitSnake(pSnake ps) { //初始化蛇身 0 0 0 0 0 pSnakeNode cur = NULL; //循环5个节点 for (int i = 0; i < 5; i++) { //开辟空间 cur = (pSnakeNode)malloc(sizeof(SnakeNode)); //判断是否开辟成功 if (cur == NULL) { perror("InitSnake:malloc"); exit(-1); } //初始化蛇的位置在(24,5) cur->x = POS_X + i * 2; cur->y = POS_Y; cur->next = NULL; //开始链接 //头插 if (ps->_pSnake == NULL) { ps->_pSnake = cur; } else { cur->next = ps->_pSnake; ps->_pSnake = cur; } } //打印蛇身 cur = ps->_pSnake;//指向贪吃蛇头结点的指针 while (cur) { //定位坐标 SetPos(cur->x, cur->y); //打印 wprintf(L"%lc", BODY); //指向下一个节点 cur = cur->next; } //对贪吃蛇的每个组成都初始化 ps->_Status = OK;//游戏的状态 ps->_Score = 0;//贪吃蛇累计的总分 ps->_pFood = NULL;//指向食物结点的指针 ps->_SleepTime = 200;//每走一步休息的时间 ps->_FoodWeight = 10;//一个食物的分数 //(我们默认蛇开始向右走) ps->_Dir = RIGHT;//描述蛇的方向 }
💫创建食物
关于食物,就是在墙体内随机生成⼀个坐标(x坐标必须是2的倍数),坐标不能和蛇的身体重合,然后打印★。
在创建食物时我们需要注意一些事项:小本本拿好咯
- 食物可能与创建的蛇相同:就必须从新创建食物。(这里采用again)
- 食物需要开辟空间的:方便和蛇链接嘛,毕竟是单链表
- 食物的一些初始化
//创建食物 void CreateFood(pSnake ps) { //初始化 int x = 0; int y = 0; again: do { x = rand() % 53 + 2; y = rand() % 25 + 1; } while (x % 2 != 0);//x坐标必须是2的倍数 //坐标不能和蛇的身体冲突 pSnakeNode cur = ps->_pSnake; while (cur) { //比较坐标 if (cur->x == x && cur->y == y) { goto again; } cur = cur->next; } //给食物开辟空间 pSnakeNode pFood = (pSnakeNode)malloc(sizeof(SnakeNode)); //判断 if (pFood == NULL) { perror("CreateFood()::malloc()"); return; } //初始化 pFood->x = x; pFood->y = y; ps->_pFood = pFood; //打印食物 SetPos(x, y); wprintf(L"%lc", FOOD); }
🌠游戏运行 - 游戏的正常运行过程
//2. 游戏运行 - 游戏的正常运行过程 void GameRun(pSnake ps) { //打印帮助信息 PrintHelpInfo(); //循环方向,直到方向正确 do { //打印得分 SetPos(64, 10); printf("得分:%05d", ps->_Score); SetPos(64, 11); printf("每个食物的分数:%2d", ps->_FoodWeight); //判断蛇方向, if (KEY_PRESS(VK_UP) && ps->_Dir != DOWN) { ps->_Dir = UP; } else if (KEY_PRESS(VK_DOWN) && ps->_Dir != UP) { ps->_Dir = DOWN; } else if (KEY_PRESS(VK_LEFT) && ps->_Dir != RIGHT) { ps->_Dir = LEFT; } else if (KEY_PRESS(VK_RIGHT) && ps->_Dir != LEFT) { ps->_Dir = RIGHT; } else if (KEY_PRESS(VK_ESCAPE)) { ps->_Status = END_NORMAL; break; } else if (KEY_PRESS(VK_SPACE)) { //暂停蛇移动 Pause(); } else if (KEY_PRESS(VK_F3))//加速 { if (ps->_SleepTime >= 80) { ps->_SleepTime -= 30; ps->_FoodWeight += 2; } } else if (KEY_PRESS(VK_F4))//减速 { if (ps->_SleepTime < 320) { ps->_SleepTime += 30; ps->_FoodWeight -= 2; } } //蛇动的时间 Sleep(ps->_SleepTime); //蛇的移动 SnakeMove(ps); } while (ps->_Status == OK); }
💫打印帮助信息
这个函数根据自己的需要而写,这个就是看大家的创意咯
//打印帮助信息 void PrintHelpInfo() { SetPos(64, 15); printf("1.不能撞墙,不能咬到自己"); SetPos(64, 16); printf("2.使用 ↑.↓.←.→ 分别控制蛇的移动"); SetPos(64, 17); printf("3.F3加速,F4减速"); SetPos(64, 18); printf("4.ESC-退出, 空格-暂停游戏"); SetPos(64, 20); printf("旧言专用版权"); }
💫判断蛇头到达的坐标处是否是食物
这个函数其实很挫,判断一下x和y坐标就行。
//判断蛇头到达的坐标处是否是食物 int NextIsFood(pSnake ps, pSnakeNode pnext) { if (ps->_pFood->x == pnext->x && ps->_pFood->y == pnext->y) { return 1; } else { return 0; } }
💫吃掉食物
直接就是头插法:不会的打板子
吃完食物记得创建食物
//吃掉食物 void EatFood(pSnake ps, pSnakeNode pnext) { //头插(把食物当做蛇头) pnext->next = ps->_pSnake; ps->_pSnake = pnext; //打印蛇 pSnakeNode cur = ps->_pSnake; while (cur) { //找坐标 SetPos(cur->x, cur->y); //打印 wprintf(L"%lc", BODY); //找下一个节点 cur = cur->next; } //释放食物空间 free(ps->_pFood); //加分数 ps->_Score += ps->_FoodWeight; //创建食物 CreateFood(ps);//新创建食物 }
💫不吃食物
运转到下一个坐标,开始覆盖,用头插,把前面的蛇尾置为空。
//不吃食物 void NoFood(pSnake ps, pSnakeNode pnext) { //头插 pnext->next = ps->_pSnake; ps->_pSnake = pnext; //打印蛇身 pSnakeNode cur = ps->_pSnake; while (cur->next->next)//找最后一个节点就不再打印 { //找坐标 SetPos(cur->x, cur->y); //打印 wprintf(L"%lc", BODY); //找下一个节点 cur = cur->next; } //本来的尾的图案需要覆盖成空格 SetPos(cur->next->x, cur->next->y); printf(" "); //释放空间 free(cur->next); cur->next = NULL; }
💫蛇是否撞墙
这个函数还是比较简单的,判断墙体的坐标是否和蛇移动到的坐标相同
//蛇是否自杀 void KillBySelf(pSnake ps) { //遍历一遍蛇的坐标就行 pSnakeNode cur = ps->_pSnake->next; while (cur) { if (ps->_pSnake->x == cur->x && ps->_pSnake->y == cur->y) { //游戏状态为自杀 ps->_Status = KILL_BY_SELF; } //找下一个节点 cur = cur->next; } }
💫蛇的移动
蛇既然要移动到下一个坐标,所以需要开辟下一个位置的空间,再判断蛇是否到食物。
等到蛇移动到下一个坐标再判断蛇是否撞墙自杀。
//蛇的移动 void SnakeMove(pSnake ps) { //开辟蛇移动到下一个格子的空间 pSnakeNode pNext = (pSnakeNode)malloc(sizeof(SnakeNode)); //判断 if (pNext == NULL) { perror("SnakeMove()::malloc()"); exit(-1); } pNext->next = NULL; //蛇移动方向 switch (ps->_Dir) { case UP: pNext->x = ps->_pSnake->x; pNext->y = ps->_pSnake->y - 1; break; case DOWN: pNext->x = ps->_pSnake->x; pNext->y = ps->_pSnake->y + 1; break; case LEFT: pNext->x = ps->_pSnake->x - 2; pNext->y = ps->_pSnake->y; break; case RIGHT: pNext->x = ps->_pSnake->x + 2; pNext->y = ps->_pSnake->y; break; } //判断蛇头到达的坐标处是否是食物 if (NextIsFood(ps, pNext)) { //吃掉食物 EatFood(ps, pNext); } else { //不吃食物 NoFood(ps, pNext); } //蛇是否撞墙 KillByWall(ps); //蛇是否自杀 KillBySelf(ps); }
💫暂停蛇移动
让蛇可以有视觉效果,打印时,休息一会儿。
//暂停蛇移动 void Pause() { while (1) { Sleep(100); if (KEY_PRESS(VK_SPACE)) { break; } } }
🌠游戏结束 - 游戏善后(释放资源)
等于释放内存,这个表示我们熟的来:
//3. 游戏结束 - 游戏善后(释放资源) void GameEnd(pSnake ps) { //判断游戏状态 SetPos(20, 12); switch (ps->_Status) { case END_NORMAL: printf("您主动退出游戏\n"); break; case KILL_BY_SELF: printf("自杀了,游戏结束\n"); break; case KILL_BY_WALL: printf("撞墙了,游戏结束\n"); break; } //释放蛇身的结点 pSnakeNode cur = ps->_pSnake; while (cur) { pSnakeNode del = cur; cur = cur->next; free(del); } ps->_pSnake = NULL; }
🌙test.c源文件的实现
直接上代码好叭
//包含头文件 #include"snake.h" void test() { int ch = 0; do { Snake snake = { 0 };//创建了贪吃蛇 //1. 游戏开始 - 初始化游戏 GameStart(&snake); //2. 游戏运行 - 游戏的正常运行过程 GameRun(&snake); //3. 游戏结束 - 游戏善后(释放资源) GameEnd(&snake); SetPos(20, 18); printf("再来一局吗?(Y/N):"); ch = getchar(); getchar();// 清理掉\n } while (ch == 'Y' || ch == 'y'); SetPos(0, 27); } int main() { //设置程序适应本地环境 setlocale(LC_ALL, "");//设置语言环境 srand((unsigned int)time(NULL));//游戏睡眠状态 //调用函数 test(); return 0; }
🌞总代码
💧snake.c
#define _CRT_SECURE_NO_WARNINGS 1 //包含头文件 #include"snake.h" //设置光标的坐标 void SetPos(short x, short y) { COORD pos = { x, y }; HANDLE hOutput = NULL; //获取标准输出的句柄(用来标识不同设备的数值) hOutput = GetStdHandle(STD_OUTPUT_HANDLE); //设置标准输出上光标的位置为pos SetConsoleCursorPosition(hOutput, pos); } //打印欢迎界面 void WelComeToGame() { //定位光标 SetPos(40, 14); printf("欢迎来到贪吃蛇小游戏"); SetPos(40, 25); system("pause");//pause是暂停 system("cls"); SetPos(20, 14); printf("使用 ↑ . ↓ . ← . → . 分别控制蛇的移动, F3是加速,F4是减速"); SetPos(40, 25); system("pause"); system("cls"); } //创建地图 void CreateMap() { //上 SetPos(0, 0); int i = 0; for (i = 0; i <= 56; i += 2) { wprintf(L"%lc", WALL); } //下 SetPos(0, 26); for (i = 0; i <= 56; i += 2) { wprintf(L"%lc", WALL); } //左 for (i = 1; i <= 25; i++) { SetPos(0, i); wprintf(L"%lc", WALL); } //右 for (i = 1; i <= 25; i++) { SetPos(56, i); wprintf(L"%lc", WALL); } } //初始化贪食蛇 void InitSnake(pSnake ps) { //初始化蛇身 0 0 0 0 0 pSnakeNode cur = NULL; //循环5个节点 for (int i = 0; i < 5; i++) { //开辟空间 cur = (pSnakeNode)malloc(sizeof(SnakeNode)); //判断是否开辟成功 if (cur == NULL) { perror("InitSnake:malloc"); exit(-1); } //初始化蛇的位置在(24,5) cur->x = POS_X + i * 2; cur->y = POS_Y; cur->next = NULL; //开始链接 //头插 if (ps->_pSnake == NULL) { ps->_pSnake = cur; } else { cur->next = ps->_pSnake; ps->_pSnake = cur; } } //打印蛇身 cur = ps->_pSnake;//指向贪吃蛇头结点的指针 while (cur) { //定位坐标 SetPos(cur->x, cur->y); //打印 wprintf(L"%lc", BODY); //指向下一个节点 cur = cur->next; } //对贪吃蛇的每个组成都初始化 ps->_Status = OK;//游戏的状态 ps->_Score = 0;//贪吃蛇累计的总分 ps->_pFood = NULL;//指向食物结点的指针 ps->_SleepTime = 200;//每走一步休息的时间 ps->_FoodWeight = 10;//一个食物的分数 //(我们默认蛇开始向右走) ps->_Dir = RIGHT;//描述蛇的方向 } //创建食物 void CreateFood(pSnake ps) { //初始化 int x = 0; int y = 0; again: do { x = rand() % 53 + 2; y = rand() % 25 + 1; } while (x % 2 != 0);//x坐标必须是2的倍数 //坐标不能和蛇的身体冲突 pSnakeNode cur = ps->_pSnake; while (cur) { //比较坐标 if (cur->x == x && cur->y == y) { goto again; } cur = cur->next; } //给食物开辟空间 pSnakeNode pFood = (pSnakeNode)malloc(sizeof(SnakeNode)); //判断 if (pFood == NULL) { perror("CreateFood()::malloc()"); return; } //初始化 pFood->x = x; pFood->y = y; ps->_pFood = pFood; //打印食物 SetPos(x, y); wprintf(L"%lc", FOOD); } //1. 游戏开始 - 初始化游戏 void GameStart(pSnake ps) { //设置控制台的大小 system("mode con cols=100 lines=30"); system("title 贪吃蛇"); //光标影藏掉 HANDLE hOutput = GetStdHandle(STD_OUTPUT_HANDLE); //影藏光标操作 CONSOLE_CURSOR_INFO CursorInfo; GetConsoleCursorInfo(hOutput, &CursorInfo);//获取控制台光标信息 CursorInfo.bVisible = false; //隐藏控制台光标 SetConsoleCursorInfo(hOutput, &CursorInfo);//设置控制台光标状态 //打印欢迎界面 WelComeToGame(); //创建地图 CreateMap(); //初始化贪食蛇 InitSnake(ps); //创建食物 CreateFood(ps); } //打印帮助信息 void PrintHelpInfo() { SetPos(64, 15); printf("1.不能撞墙,不能咬到自己"); SetPos(64, 16); printf("2.使用 ↑.↓.←.→ 分别控制蛇的移动"); SetPos(64, 17); printf("3.F3加速,F4减速"); SetPos(64, 18); printf("4.ESC-退出, 空格-暂停游戏"); SetPos(64, 20); printf("旧言专用版权"); //getchar(); } //判断蛇头到达的坐标处是否是食物 int NextIsFood(pSnake ps, pSnakeNode pnext) { if (ps->_pFood->x == pnext->x && ps->_pFood->y == pnext->y) { return 1; } else { return 0; } } //吃掉食物 void EatFood(pSnake ps, pSnakeNode pnext) { //头插(把食物当做蛇头) pnext->next = ps->_pSnake; ps->_pSnake = pnext; //打印蛇 pSnakeNode cur = ps->_pSnake; while (cur) { //找坐标 SetPos(cur->x, cur->y); //打印 wprintf(L"%lc", BODY); //找下一个节点 cur = cur->next; } //释放食物空间 free(ps->_pFood); //加分数 ps->_Score += ps->_FoodWeight; //创建食物 CreateFood(ps);//新创建食物 } //不吃食物 void NoFood(pSnake ps, pSnakeNode pnext) { //头插 pnext->next = ps->_pSnake; ps->_pSnake = pnext; //打印蛇身 pSnakeNode cur = ps->_pSnake; while (cur->next->next)//找最后一个节点就不再打印 { //找坐标 SetPos(cur->x, cur->y); //打印 wprintf(L"%lc", BODY); //找下一个节点 cur = cur->next; } //本来的尾的图案需要覆盖成空格 SetPos(cur->next->x, cur->next->y); printf(" "); //释放空间 free(cur->next); cur->next = NULL; } //蛇是否撞墙 void KillByWall(pSnake ps) { if (ps->_pSnake->x == 0 || ps->_pSnake->x == 56 || ps->_pSnake->y == 0 || ps->_pSnake->y == 26) ps->_Status = KILL_BY_WALL; } //蛇是否自杀 void KillBySelf(pSnake ps) { //遍历一遍蛇的坐标就行 pSnakeNode cur = ps->_pSnake->next; while (cur) { if (ps->_pSnake->x == cur->x && ps->_pSnake->y == cur->y) { //游戏状态为自杀 ps->_Status = KILL_BY_SELF; } //找下一个节点 cur = cur->next; } } //蛇的移动 void SnakeMove(pSnake ps) { //开辟蛇移动到下一个格子的空间 pSnakeNode pNext = (pSnakeNode)malloc(sizeof(SnakeNode)); //判断 if (pNext == NULL) { perror("SnakeMove()::malloc()"); exit(-1); } pNext->next = NULL; //蛇移动方向 switch (ps->_Dir) { case UP: pNext->x = ps->_pSnake->x; pNext->y = ps->_pSnake->y - 1; break; case DOWN: pNext->x = ps->_pSnake->x; pNext->y = ps->_pSnake->y + 1; break; case LEFT: pNext->x = ps->_pSnake->x - 2; pNext->y = ps->_pSnake->y; break; case RIGHT: pNext->x = ps->_pSnake->x + 2; pNext->y = ps->_pSnake->y; break; } //判断蛇头到达的坐标处是否是食物 if (NextIsFood(ps, pNext)) { //吃掉食物 EatFood(ps, pNext); } else { //不吃食物 NoFood(ps, pNext); } //蛇是否撞墙 KillByWall(ps); //蛇是否自杀 KillBySelf(ps); } //暂停蛇移动 void Pause() { while (1) { Sleep(100); if (KEY_PRESS(VK_SPACE)) { break; } } } //2. 游戏运行 - 游戏的正常运行过程 void GameRun(pSnake ps) { //打印帮助信息 PrintHelpInfo(); //循环方向,直到方向正确 do { //打印得分 SetPos(64, 10); printf("得分:%05d", ps->_Score); SetPos(64, 11); printf("每个食物的分数:%2d", ps->_FoodWeight); //判断蛇方向, if (KEY_PRESS(VK_UP) && ps->_Dir != DOWN) { ps->_Dir = UP; } else if (KEY_PRESS(VK_DOWN) && ps->_Dir != UP) { ps->_Dir = DOWN; } else if (KEY_PRESS(VK_LEFT) && ps->_Dir != RIGHT) { ps->_Dir = LEFT; } else if (KEY_PRESS(VK_RIGHT) && ps->_Dir != LEFT) { ps->_Dir = RIGHT; } else if (KEY_PRESS(VK_ESCAPE)) { ps->_Status = END_NORMAL; break; } else if (KEY_PRESS(VK_SPACE)) { //暂停蛇移动 Pause(); } else if (KEY_PRESS(VK_F3))//加速 { if (ps->_SleepTime >= 80) { ps->_SleepTime -= 30; ps->_FoodWeight += 2; } } else if (KEY_PRESS(VK_F4))//减速 { if (ps->_SleepTime < 320) { ps->_SleepTime += 30; ps->_FoodWeight -= 2; } } //蛇动的时间 Sleep(ps->_SleepTime); //蛇的移动 SnakeMove(ps); } while (ps->_Status == OK); } //3. 游戏结束 - 游戏善后(释放资源) void GameEnd(pSnake ps) { //判断游戏状态 SetPos(20, 12); switch (ps->_Status) { case END_NORMAL: printf("您主动退出游戏\n"); break; case KILL_BY_SELF: printf("自杀了,游戏结束\n"); break; case KILL_BY_WALL: printf("撞墙了,游戏结束\n"); break; } //释放蛇身的结点 pSnakeNode cur = ps->_pSnake; while (cur) { pSnakeNode del = cur; cur = cur->next; free(del); } ps->_pSnake = NULL; }
💧snake.h
#pragma once //包含头文件 #include <locale.h> #include<stdio.h> #include<assert.h> #include<stdlib.h> #include<windows.h> #include<time.h> #include<stdbool.h> //定义图案 #define WALL L'□' #define BODY L'●' #define FOOD L'★' //初始化蛇的位置 #define POS_X 24 #define POS_Y 5 //键位的移动 #define KEY_PRESS(VK) ( (GetAsyncKeyState(VK) & 0x1) ? 1 : 0 ) //枚举键位 enum DIRECTION { UP = 1,//上 DOWN,//下 LEFT,//左 RIGHT//右 }; //枚举游戏状态 enum GAME_STATUS { OK,//正常运行 END_NORMAL,//按ESC退出 KILL_BY_WALL, KILL_BY_SELF }; //贪吃蛇每个节点的描述 typedef struct SnakeNode { int x; int y; struct SnakeNode* next;//下个节点 }SnakeNode, * pSnakeNode; //贪吃蛇组成 typedef struct Snake { pSnakeNode _pSnake;//指向贪吃蛇头结点的指针 pSnakeNode _pFood;//指向食物结点的指针 int _Score;//贪吃蛇累计的总分 int _FoodWeight;//一个食物的分数 int _SleepTime;//每走一步休息的时间,时间越短,速度越快,时间越长,速度越慢 enum DIRECTION _Dir;//描述蛇的方向 enum GAME_STATUS _Status;//游戏的状态:正常、退出、撞墙、吃到自己 }Snake, * pSnake; //游戏开始 - 完成游戏的初始化动作 void GameStart(pSnake ps); //定位坐标 void SetPos(short x, short y); //游戏开始的欢迎界面 void WelComeToGame(); //打印地图 void CreateMap(); //初始化贪吃蛇 void InitSnake(pSnake ps); //创建食物 void CreateFood(pSnake ps); //游戏的正常运行 void GameRun(pSnake ps); //打印帮助信息 void PrintHelpInfo(); //游戏暂定和恢复 void Pause(); //蛇的移动 void SnakeMove(pSnake ps); //判断蛇头到达的坐标处是否是食物 int NextIsFood(pSnake ps, pSnakeNode pnext); //吃掉食物 void EatFood(pSnake ps, pSnakeNode pnext); //不吃食物 void NoFood(pSnake ps, pSnakeNode pnext); //蛇是否撞墙 void KillByWall(pSnake ps); //蛇是否自杀 void KillBySelf(pSnake ps); //游戏结束后的善后处理 void GameEnd(pSnake ps);
💧test.c
#define _CRT_SECURE_NO_WARNINGS 1 //包含头文件 #include"snake.h" void test() { int ch = 0; do { Snake snake = { 0 };//创建了贪吃蛇 //1. 游戏开始 - 初始化游戏 GameStart(&snake); //2. 游戏运行 - 游戏的正常运行过程 GameRun(&snake); //3. 游戏结束 - 游戏善后(释放资源) GameEnd(&snake); SetPos(20, 18); printf("再来一局吗?(Y/N):"); ch = getchar(); getchar();// 清理掉\n } while (ch == 'Y' || ch == 'y'); SetPos(0, 27); } int main() { //设置程序适应本地环境 setlocale(LC_ALL, "");//设置语言环境 srand((unsigned int)time(NULL));//游戏睡眠状态 //调用函数 test(); return 0; }
🌟结束语
今天内容就到这里啦,时间过得很快,大家沉下心来好好学习,会有一定的收获的,大家多多坚持,嘻嘻,成功路上注定孤独,因为坚持的人不多。那请大家举起自己的小说手给博主一键三连,有你们的支持是我最大的动力💞💞💞,回见。