这个贪吃蛇是在比特特训营里学到的,同时我还写了用EasyX图形库实现的图形化贪吃蛇,含有每个函数的实现以及游戏中各种细节的讲解,感兴趣的可以去看一看。
实现效果
以下就是源码,感兴趣的小伙伴可以cv自己玩一玩改造改造,每个函数都有相应功能细节的注释,有用的话欢迎大家点赞
snake.h
#pragma once #include <locale.h> #include <Windows.h> #include <stdio.h> #include <stdlib.h> #include <stdbool.h> #include <time.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=1, END_NOMAL,//正常退出 KILL_BY_WALL, KILL_BY_SELF }; typedef struct SnakeNode { int x; int y; struct SnakeNode* next; }SnakeNode,* pSnakeNode; //相当于 //typedef struct SnakeNode* pSnakeNode;//结构体指针的重命名 //描述蛇的结构体 typedef struct snake { pSnakeNode _psnake;//指向贪吃蛇头结点的指针。 pSnakeNode _fFood;//假设食物也是蛇节点的指针,吃掉时改变其状态即可。 int _Score;//分数,到时候要打印 int _Foodweight; int SleepTime;//每走一步休息的时间,时间越短,速度越快 enum DIRECTION _Dir;//方向,用枚举常量给出 enum GAME_STATUS _status; }Snake,*psnake; //游戏开始 void GameStart(psnake); //欢迎界面 WecomeGame();//打印游戏界面 //创建地图 void CreatMap(); void InitSnake(psnake ps); void CreateFood(psnake ps); //游戏的正常运行 void GameRun(psnake ps); //打印帮助信息 void SetPos(short x, short y); int KillBySelf(psnake ps); void GameOver(psnake ps);
===============================
snake.c
#define _CRT_SECURE_NO_WARNINGS #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); } WecomeGame()//打印游戏界面 { //定位光标,打印欢迎语句 SetPos(40,15); printf("欢迎来到贪吃蛇游戏"); SetPos(37, 27); //printf("按任意键继续"); system("pause");//暂停程序,库函数的暂停命令 //清空屏幕 system("cls"); SetPos(20, 15); printf("上下左右为↑↓←→,F3为加速,F4为减速 "); system("pause");//暂停程序,库函数的暂停命令 system("cls"); CreatMap(); } void CreatMap() { int i = 0; //通过创建的终端大小打印地图 SetPos(0, 0); for (i = 0; i <= 56; i+=2) { wprintf(L"%c",wall); } SetPos(0, 26); for (i = 0; i <= 56; i += 2) { wprintf(L"%c", wall); } for (i = 1; i <= 25; i++) { SetPos(0, i); wprintf(L"%c", wall); } for (i = 1; i <= 25; i++) { SetPos(56, i); wprintf(L"%c", wall); } } 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;//将状态设置为fasle,隐藏 SetConsoleCursorInfo(hOutput, &CursorInfo);//设置控制台光标状态 //打印欢迎界面,提示 WecomeGame();//打印游戏界面 //初始化贪吃蛇 InitSnake(ps); //创建食物 CreateFood(ps); } void CreateFood(psnake ps)//创建食物 { //坐标范围内随机生成,且不可以生成在蛇身上。 int x = 0; int y = 0; again: do { x = rand() % 53 + 2; y = rand() % 25 + 1; } while (x % 2 != 0);//横坐标为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("malloc fail"); return; } pfood->x = x; pfood->y = y; ps->_fFood = pfood; //打印食物 SetPos(x, y); wprintf(L"%lc", food); //getchar();随时阻塞,判断效果 } void InitSnake(psnake ps)//初始化蛇 { int i = 0; for (i = 0; i < 5; i++) { pSnakeNode snk = (pSnakeNode)malloc(sizeof(SnakeNode)); if (snk == NULL)//问题检查 { perror("malloc fail"); return; } snk->x = POS_X + 2*i;//节点位置不同 snk->y = POS_Y; snk->next = NULL; if (ps->_psnake == NULL) { ps->_psnake = snk; } else//此时_psnake修饰的就是蛇节点的头结点 { snk->next = ps->_psnake; ps->_psnake = snk; } } //打印蛇的身体 pSnakeNode cur = ps->_psnake; while (cur) { SetPos(cur->x, cur->y); wprintf(L"% c", body); cur = cur->next; } //蛇的相关数据 ps->_status = OK; ps->_Score = 0; ps->_Foodweight = 10; ps->_fFood = NULL; ps->SleepTime = 200;//休眠时间关乎蛇移动的速度 ps->_Dir = RIGHT; } void PrintInform() { SetPos(60, 15); printf("1:不能穿墙,不能咬到自己"); SetPos(60, 17); printf("2:上下左右为↑↓←→,F3为加速,F4为减速"); SetPos(60, 19); printf("3:F3加速,F4减速,Esc退出,空格暂停"); //getchar(); } void Pause()//暂停游戏或者继续游戏 { while (1) { Sleep(100); if (KEY_PRESS(VK_SPACE)) { break; } } } //判断是否吃掉食物 int NextIsFood(psnake ps,pSnakeNode pnext) { if (ps->_fFood->x == pnext->x && ps->_fFood->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; } //清理食物节点(食物节点是malloc出来的,所以要清理,不然会造成内存浪费),加分 free(ps->_fFood); ps->_Score += ps->_Foodweight; //继续创建食物 CreateFood(ps); } void NotEatFood(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; } //蛇是否撞墙 int 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; return 1; } return 0; } //是否吃到自己 int 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;//改变状态 return 1; } cur = cur->next; } return 0; } //值得学习的地方,修改整个数组 void SnakeMove(psnake ps) { pSnakeNode pnext = (pSnakeNode)malloc(sizeof(SnakeNode)); if (pnext == NULL) { perror("malloc fail"); return; } //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 RIGHT: pnext->x = ps->_psnake->x+2;//减2,因为宽度为2. pnext->y = ps->_psnake->y; break; case LEFT: pnext->x = ps->_psnake->x-2; pnext->y = ps->_psnake->y; break; } //判断是否吃掉食物,如果吃掉食物,最后一个节点不清理,如果没有吃掉食物,就将其清理、 //蛇头坐标和食物坐标 if (NextIsFood(ps, pnext)) { //吃掉食物 EatFood(ps,pnext); } else { //没吃食物 NotEatFood(ps,pnext); } //蛇是否撞墙 KillByWall(ps); //是否吃到自己 KillBySelf(ps); } void GameRun(psnake ps) { PrintInform(); do { SetPos(64, 10); printf("得分:%0.5d", ps->_Score); SetPos(64, 12); printf("每个食物10分"); 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_NOMAL;//正常退出 break; } else if (KEY_PRESS(VK_SPACE)) { //暂停函数 Pause();//封装一个函数,按下一次暂停,再按就继续 } else if (KEY_PRESS(VK_F3))//加速 { //ps->SleepTime = 100;//如果一直按着,那就一直二倍加速 //ps->_Foodweight = 15; if (ps->SleepTime >= 80)//也可以逐渐加速 { ps->SleepTime -= 30; ps->_Foodweight += 2; } } else if (KEY_PRESS(VK_F4))//减速 { if (ps->SleepTime <= 300)//逐渐减速 { ps->SleepTime += 30; ps->_Foodweight -= 2; } } Sleep(ps->SleepTime); //蛇的移动 SnakeMove(ps);//继续封装成函数 } while (ps->_status==OK); } //善后处理,打印分数,清理贪吃蛇 void GameOver(psnake ps) { SetPos(20, 12); switch (ps->_status) { case END_NOMAL: printf("您主动退出游戏"); break; case KILL_BY_SELF: printf("自杀成功"); break; case KILL_BY_WALL: printf("撞墙啦"); break; } SetPos(0, 27); //释放蛇的节点 pSnakeNode cur = ps->_psnake; while (cur)//循环全部释放 { pSnakeNode Next = cur->next; free(cur); cur = Next; } ps->_psnake = NULL; }
=====================================
test.c
#define _CRT_SECURE_NO_WARNINGS #include "snake.h" //Win32,API,Windows 32位接口。 //宽字符类型,一个汉字占用两个字符。 // 一个字母宽一个字符,一个汉字占两个字符 //wchar_t宽字符类型□☆★¤◎㊣ //setlocale(LC_ALL,"");//适应中文环境 //宽字符的打印,前缀加上L //int main() //{ // SetPos(10, 10); // setlocale(LC_ALL, ""); // wchar_t ch1 = L'●'; // wprintf(L"%lc\n", ch1);//打印时printf前边加w,打印时前边大写L,类型为lc= // return 0; //} void test() { char ch = 0; do { Snake snake = { 0 };//创建贪吃蛇 //1,游戏开始——初始化游戏 GameStart(&snake); //getchar();//设置光标状态是否成功可以检查一下,用getchar阻塞程序运行 //2,游戏运行——正常运行 GameRun(&snake); //3,游戏结束——如何结束,释放资源 GameOver(&snake); SetPos(20, 15); printf("是否想再来一把?(Y/N):"); ch = getchar(); getchar();//清理‘/n’。 } while (ch == 'Y' || ch == 'y'); } int main() { srand((unsigned int)time(NULL)); //设置程序适应本地化 setlocale(LC_ALL, ""); test(); return 0; } //地图,长为宽的2倍