小工具知识网

当前位置: 首页 > 行业知识 > 【C语言】打造你的专属贪吃蛇

行业知识

【C语言】打造你的专属贪吃蛇

2026-10-31 11:50:48 来源:互联网转载

前言

hello hello~ ,这里是大耳朵土土垚~ ,欢迎大家点赞关注收藏

个人主页:大耳朵土土垚的博客

所属专栏:C语言学习笔记、C/C++小游戏

对于C语言学习疑问的都可以在上面的专栏进行学习哦~ 有问题可以写在评论区或者私信我哦~

一、游戏实现效果展示

二、游戏实现源码

#define _CRT_SECURE_NO_WARNINGS 1#include#include#include#include#include#include #define KEY_PRESS(VK)  ( (GetAsyncKeyState(VK) & 0x1) ? 1 : 0 )#define WALL L'■'#define SNAKE L'●'#define SNAKE_HEAD L'●'#define FOOD L'★'//蛇最开始的坐标#define POS_X 50#define POS_Y 10#define SCORE 5//设置一次加几分#define FOODQUALITY 3//设置一次性开辟食物个数为3//定义贪吃蛇蛇身节点(使用链表)typedef struct SnakeNode{//定义x y记录蛇身节点坐标int x;int y;//记录下一个节点struct SnakeNode* next;}SnakeNode, * pSnakeNode;//定义食物typedef struct Food{//定义x y 记录食物节点坐标int x;int y;//记录下一个节点struct Food* next;}Food, * pFood;//游戏状态enum GAME_STATUS{OK = 1,ESC,KILL_BY_WALL,KILL_BY_SELF};//蛇移动方向enum DIRECTION{UP = 1,DOWN,LEFT,RIGHT};//定义贪吃蛇typedef struct Snake{pSnakeNode snakehead; //记录蛇头节点的指针,这样就可以找到整条蛇int snake_size;//记录蛇的节点个数int score;  //记录当前得分pFood food;//记录指向食物的指针int food_size; //记录食物个数int FoodWeight;//食物权重enum GAME_STATUS status;//记录游戏当前状态enum DIRECTION dir; //记录当前蛇移动方向int SleepTime;//记录蛇休眠时间}Snake, * pSnake;//函数声明void GameStart(pSnake psnake);//游戏开始前的初始化void Setpos(int x, int y);//定位光标void GameRun(pSnake psnake); //游戏运行void GameEnd(pSnake psnake);//游戏结束,清理工作//颜色设置void color(int c){SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), c); //颜色设置//注:SetConsoleTextAttribute是一个API(应用程序编程接口)}//定位光标void Setpos(int x, int y){HANDLE hanlde = GetStdHandle(STD_OUTPUT_HANDLE);//从标准输出设备中获得句柄COORD pos = { x, y };//控制台上的坐标SetConsoleCursorPosition(hanlde, pos);//根据句柄设置控制台光标的位置}//打印欢迎信息void Welcom_Print_Info(){//1.先隐藏光标HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);//获取句柄CONSOLE_CURSOR_INFO CursorInfo;GetConsoleCursorInfo(handle, &CursorInfo);//获取控制台光标信息CursorInfo.bVisible = false;//隐藏光标SetConsoleCursorInfo(handle, &CursorInfo);//2.定位光标Setpos(40,15);//3.打印欢迎信息color(11);//设置颜色为蓝色printf("欢迎来到贪吃蛇小游戏~\n");//定位光标,打印其他信息Setpos(40, 17);printf("ARE YOU READY?\n");//4.请按任意键继续...切换页面Setpos(50, 30);system("pause");system("cls");//清空屏幕//5.下一页功能信息介绍Setpos(40, 15);printf("请用↑ ↓ ← →来控制蛇的移动,F3加速,F4减速");Setpos(40, 17);printf("加速能获得更高的分数哦~");//6.请按任意键继续...切换页面Setpos(50, 30);system("pause");system("cls");}//绘制地图  cols=100 lines=35void Map_Print(){color(11);//设置颜色为蓝//1.打印第一行for (int i = 0; i x = POS_X+2*i;cur->y = POS_Y;cur->next = NULL;//3.连接节点if (psnake->snakehead == NULL){psnake->snakehead = cur;}else{cur->next = psnake->snakehead;psnake->snakehead = cur;}}//4.打印蛇pSnakeNode cur = psnake->snakehead;while (cur){if (cur == psnake->snakehead){//将蛇头颜色设为红色color(12);Setpos(cur->x, cur->y);//定位wprintf(L"%lc", SNAKE_HEAD);}else {//将蛇身颜色设为绿色color(10);Setpos(cur->x, cur->y);//定位wprintf(L"%lc", SNAKE);}cur = cur->next;}psnake->snake_size = 5;psnake->dir = RIGHT;psnake->FoodWeight = SCORE;psnake->food = NULL;psnake->food_size = 0;psnake->SleepTime = 300;psnake->status = OK;}//初始化食物void Init_Food(pSnake psnake){//创建食物j个节点int j = rand() % 5 + 1;psnake->food_size = j;;for (int i = 0; i x = rand() % 99+1;//不能为0cur->y = rand() % 34+1;//不能为0cur->next = NULL;} while (cur->x % 2 != 0);//判断坐标是否与蛇相同//要用循环遍历蛇节点pSnakeNode ps = psnake->snakehead;while (ps){if (ps->x == cur->x && ps->y == cur->y){goto again;}ps = ps->next;}//3.连接节点if (psnake->food == NULL){psnake->food = cur;}else{cur->next = psnake->food;psnake->food = cur;}}//4.打印食物pFood cur = psnake->food;while (cur){//将食物颜色设为黄色color(14);Setpos(cur->x, cur->y);//定位wprintf(L"%lc", FOOD);cur = cur->next;}}//游戏开始前的初始化void GameStart(pSnake psnake){//1.设置控制台信息system("title 贪吃蛇");//设置控制台标题为贪吃蛇system("mode con cols=150 lines=44");  //设置控制台大小//2.打印欢迎信息Welcom_Print_Info();//3.绘制地图 cols=100 lines=35Map_Print();//4.打印游戏说明信息Info_Print();//5.初始化蛇Init_Snake(psnake);//6.初始化食物Init_Food(psnake);}//暂停恢复函数void pause(){while (1){Sleep(100);if (KEY_PRESS(VK_SPACE))//如果再次按下空格就结束暂停状态{break;}}}////判断新节点也就是下一个位置是不是食物bool IsFood(pSnakeNode newnode,pSnake psnake){//遍历食物节点来判断pFood cur = psnake->food;while (cur){if (cur->x == newnode->x && cur->y == newnode->y){psnake->food_size--;if (psnake->food_size == 0){//如果没有食物就重新初始化食物//释放食物节点if (psnake->food != NULL){pFood tmp = psnake->food;pFood rem = tmp;while (tmp){tmp = tmp->next;free(rem);rem = tmp;}psnake->food = NULL;}Init_Food(psnake);}return true;}cur = cur->next;}return false;}//判断新节点也就是下一个位置是不是墙bool IsWall(pSnakeNode newnode){//墙的x坐标是0和100,y坐标是0和35if (newnode->x == 0 ||newnode->x == 100 ||newnode->y == 0 ||newnode->y == 35){return true;}return false;}//判断新节点也就是下一个位置是不是自己bool IsSelf(pSnakeNode newnode, pSnake psnake){//遍历蛇的节点,注意最后一个不需要遍历,因为新增了一个pSnakeNode cur = psnake->snakehead;while (cur->next){if (newnode->x == cur->x && newnode->y == cur->y){return true;}cur = cur->next;}return false;}//蛇移动的函数- 每次走一步void SnakeMove(pSnake psnake){//1.先开辟一个新的节点pSnakeNode newnode = (pSnakeNode)malloc(sizeof(SnakeNode));if (newnode == NULL){perror("SnakeMove() malloc fail");return;}//2.根据蛇当前前进的状态来确定新节点的位置坐标if (psnake->dir == UP){newnode->x = psnake->snakehead->x;newnode->y = psnake->snakehead->y - 1;//向上y坐标-1}if (psnake->dir == DOWN){newnode->x = psnake->snakehead->x;newnode->y = psnake->snakehead->y + 1;//向下y坐标+1}if (psnake->dir == LEFT){newnode->x = psnake->snakehead->x-2;//向上x坐标-2newnode->y = psnake->snakehead->y ;}if (psnake->dir == RIGHT){newnode->x = psnake->snakehead->x + 2;//向下x坐标+2newnode->y = psnake->snakehead->y;}//3.判断新节点位置if (IsWall(newnode)){//如果是墙//撞墙,游戏结束psnake->status = KILL_BY_WALL;return;}else if (IsSelf(newnode, psnake)){//如果是自己//咬到自己,游戏结束psnake->status = KILL_BY_SELF;return;}else if (IsFood(newnode, psnake)){//如果是食物//先+分数psnake->score +=SCORE;//蛇的节点个数+1psnake->snake_size++;}//然后连接新节点,头插法newnode->next = psnake->snakehead;psnake->snakehead = newnode;//打印,注意如果是食物就多打印一个,如果不是食物就打印原来的个数个,并且释放掉尾节点//可以通过蛇节点个数控制int size = psnake->snake_size;pSnakeNode cur = psnake->snakehead;pSnakeNode last = psnake->snakehead;while (size){if (size == psnake->snake_size){color(12);//蛇头是红色Setpos(cur->x, cur->y);wprintf(L"%lc", SNAKE_HEAD);}else{color(10);//身体是绿色色Setpos(cur->x, cur->y);wprintf(L"%lc", SNAKE_HEAD);}size--;last = cur;cur = cur->next;}//如果不是食物,将最后一个节点的位置打印空字符,并且释放该节点if (cur != NULL){Setpos(cur->x, cur->y);printf("  ");free(cur);cur = NULL;last->next = NULL;//防止越界访问}}//运行游戏void GameRun(pSnake psnake){do{//打印分数Setpos(40, 38);color(14);//设置颜色为黄色printf("总分:%5d", psnake->score);Setpos(40, 40);printf("当前食物分值:%02d", psnake->FoodWeight);if (KEY_PRESS(VK_UP) && psnake->dir != DOWN){psnake->dir = UP;}else if (KEY_PRESS(VK_DOWN) && psnake->dir != UP){psnake->dir = DOWN;}else if (KEY_PRESS(VK_LEFT) && psnake->dir != RIGHT){psnake->dir = LEFT;}else if (KEY_PRESS(VK_RIGHT) && psnake->dir != LEFT){psnake->dir = RIGHT;}else if (KEY_PRESS(VK_ESCAPE)){psnake->status = ESC;//退出游戏break;}else if (KEY_PRESS(VK_SPACE)){//游戏要暂定pause();//暂定和回复暂定}else if (KEY_PRESS(VK_F3))//加速,休眠时间减少,食物分数增加{if (psnake->SleepTime >= 80){psnake->SleepTime -= 30;psnake->FoodWeight += SCORE;}}else if (KEY_PRESS(VK_F4))//减速,休眠时间增加,食物分数减少{if (psnake->FoodWeight > SCORE){psnake->SleepTime += 30;psnake->FoodWeight -= SCORE;}}//走一步SnakeMove(psnake);//睡眠一下Sleep(psnake->SleepTime);} while (psnake->status == OK);}//游戏结束,清理工作void GameEnd(pSnake psnake){system("cls");color(11);//颜色设置为蓝色Setpos(40, 15);switch (psnake->status){case ESC:printf("你已主动退出游戏@-@");break;case KILL_BY_SELF:printf("很遗憾,你咬到自己了*^*,游戏结束");break;case KILL_BY_WALL:printf("很遗憾,你撞墙了*^*,游戏结束");break;}Setpos(50, 18);//释放蛇的节点pSnakeNode cur = psnake->snakehead;pSnakeNode del = cur;while (cur){cur = cur->next;free(del);del = cur;}//释放食物节点if (psnake->food != NULL){pFood tmp = psnake->food;pFood rem = tmp;while (tmp){tmp = tmp->next;free(rem);rem = tmp;}}psnake = NULL;}void test(){int  start = 'y';do{//创建贪吃蛇Snake snake = { 0 };//游戏开始前的初始化GameStart(&snake);//运行游戏GameRun(&snake);//游戏结束GameEnd(&snake);Setpos(50, 17);system("pause");system("cls");Setpos(40, 15);printf("是否要继续游戏?y/n\n");Setpos(60, 15);start = getchar();getchar();//清理\n} while (start == 'y' || start == 'Y');}int main(){//修改适配本地的中文环境setlocale(LC_ALL, "");//贪吃蛇游戏测试test();Setpos(50, 19);return 0;}

三、游戏实现逻辑

1.游戏框架

void test(){int  start = 'y';do{//创建贪吃蛇Snake snake = { 0 };//游戏开始前的初始化GameStart(&snake);//运行游戏GameRun(&snake);//游戏结束GameEnd(&snake);Setpos(50, 17);//设置控制台打印位置system("pause");//请按任意键继续system("cls");Setpos(40, 15);printf("是否要继续游戏?y/n\n");Setpos(60, 15);start = getchar();getchar();//清理\n} while (start == 'y' || start == 'Y');}int main(){//修改适配本地的中文环境setlocale(LC_ALL, "");//贪吃蛇游戏测试test();Setpos(50, 19);return 0;}

我们实现贪吃蛇需要实现创建贪吃蛇、创建好后进行初始化、运行游戏以及游戏结束这几个模块,并且在最开始以及结束之后通过输入y/n(yes/no)来确认是否开始游戏或者退出游戏,可以通过do while循环搭配输入getchar()从键盘上获取字符来实现

Setpos函数通过接受两个整型参数来定位控制台,控制台可以看成一个坐标系:

//定位光标void Setpos(int x, int y){HANDLE hanlde = GetStdHandle(STD_OUTPUT_HANDLE);//从标准输出设备中获得句柄COORD pos = { x, y };//控制台上的坐标SetConsoleCursorPosition(hanlde, pos);//根据句柄设置控制台光标的位置}//这是Windows提供的函数使用时要包含头文件#include

我们如果要在控制台上打印宽字符’●’或者’★’,汉字也是宽字符,就需要修改本地适配环境,在main函数中://修改适配本地的中文环境 setlocale(LC_ALL, "");,使用该函数要包含#include 头文件

#define _CRT_SECURE_NO_WARNINGS 1#include#include#include#include#include#include #define KEY_PRESS(VK)  ( (GetAsyncKeyState(VK) & 0x1) ? 1 : 0 )//控制按键#define WALL L'■'#define SNAKE L'●'#define SNAKE_HEAD L'●'//这里可以自己定义蛇头#define FOOD L'★'//蛇最开始的坐标#define POS_X 50#define POS_Y 10#define SCORE 5//设置一次加几分#define FOODQUALITY 3//设置一次性开辟食物个数为3//定义贪吃蛇蛇身节点(使用链表)typedef struct SnakeNode{//定义x y记录蛇身节点坐标int x;int y;//记录下一个节点struct SnakeNode* next;}SnakeNode,* pSnakeNode;//定义食物typedef struct Food{//定义x y 记录食物节点坐标int x;int y;//记录下一个节点struct Food* next;}Food, *pFood;//游戏状态enum GAME_STATUS{OK = 1,ESC,KILL_BY_WALL,KILL_BY_SELF};//蛇移动方向enum DIRECTION{UP = 1,DOWN,LEFT,RIGHT};//定义贪吃蛇typedef struct Snake{pSnakeNode snakehead; //记录蛇头节点的指针,这样就可以找到整条蛇int snake_size;//记录蛇的节点个数int score;  //记录当前得分pFood food;//记录指向食物的指针int food_size; //记录食物个数int FoodWeight;//食物权重enum GAME_STATUS status;//记录游戏当前状态enum DIRECTION dir; //记录当前蛇移动方向int SleepTime;//记录蛇休眠时间}Snake,*pSnake;//函数声明void GameStart(pSnake psnake);//游戏开始前的初始化void Setpos(int x, int y);//定位光标void GameRun(pSnake psnake); //游戏运行void GameEnd(pSnake psnake);//游戏结束,清理工作

实现贪吃蛇我们首先要定义一个结构体来确定蛇身节点,里面要包括蛇的坐标,x,y还有下一个节点的位置:

//定义贪吃蛇蛇身节点(使用链表)typedef struct SnakeNode{//定义x y记录蛇身节点坐标int x;int y;//记录下一个节点struct SnakeNode* next;}SnakeNode,* pSnakeNode;

同理如果你要实现多个食物,我们也需要开辟食物节点,与蛇身节点类似:

//定义食物typedef struct Food{//定义x y 记录食物节点坐标int x;int y;//记录下一个节点struct Food* next;}Food, *pFood;

其实这里也可以不定义,之间使用蛇身节点就行

然后是整条蛇:

//定义贪吃蛇typedef struct Snake{pSnakeNode snakehead; //记录蛇头节点的指针,这样就可以找到整条蛇int snake_size;//记录蛇的节点个数int score;  //记录当前得分pFood food;//记录指向食物的指针int food_size; //记录食物个数int FoodWeight;//食物权重enum GAME_STATUS status;//记录游戏当前状态enum DIRECTION dir; //记录当前蛇移动方向int SleepTime;//记录蛇休眠时间}Snake,*pSnake;

2.游戏开始前初始化

//游戏开始前的初始化void GameStart(pSnake psnake){//1.设置控制台信息system("title 贪吃蛇");//设置控制台标题为贪吃蛇system("mode con cols=150 lines=44");  //设置控制台大小//2.打印欢迎信息Welcom_Print_Info();//3.绘制地图 cols=100 lines=35Map_Print();//4.打印游戏说明信息Info_Print();//5.初始化蛇Init_Snake(psnake);//6.初始化食物Init_Food(psnake);}

2.1修改控制台信息

//1.设置控制台信息system("title 贪吃蛇");//设置控制台标题为贪吃蛇system("mode con cols=150 lines=44");  //设置控制台大小

如果发现控制台没改变,我们就需要修改一下默认终端应用程序:

①找到设置

②修改默认终端应用程序

③选择Windows控制台主机

④点击保存

这样既可以按照我们想要的方式打印标题和设置控制台大小了:

2.2打印欢迎信息

//打印欢迎信息void Welcom_Print_Info(){//1.先隐藏光标HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);//获取句柄CONSOLE_CURSOR_INFO CursorInfo;GetConsoleCursorInfo(handle, &CursorInfo);//获取控制台光标信息CursorInfo.bVisible = false;//隐藏光标SetConsoleCursorInfo(handle, &CursorInfo);//2.定位光标Setpos(40,15);//3.打印欢迎信息color(11);//设置颜色为蓝色printf("欢迎来到贪吃蛇小游戏~\n");//定位光标,打印其他信息Setpos(40, 17);printf("ARE YOU READY?\n");//4.请按任意键继续...切换页面Setpos(50, 30);system("pause");system("cls");//清空屏幕//5.下一页功能信息介绍Setpos(40, 15);printf("请用↑ ↓ ← →来控制蛇的移动,F3加速,F4减速");Setpos(40, 17);printf("加速能获得更高的分数哦~");//6.请按任意键继续...切换页面Setpos(50, 30);system("pause");system("cls");}

效果展示:

打印欢迎信息时我们发现光标一直闪动,所以我们可以通过windows提供的函数来隐藏光标,注意使用这些函数时要包含头文件#include

✨颜色设置函数

//颜色设置void color(int c){SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), c); //颜色设置//注:SetConsoleTextAttribute是一个API(应用程序编程接口)}

提供了一个int类型的参数,通过接收不同颜色的十进制表示来输出不同颜色的字符,上面欢迎信息我们选择的是蓝色color(11);

十进制颜色参照表:

2.3绘制地图

//绘制地图  cols=100 lines=35//WALL就是'■',在头文件中被宏定义void Map_Print(){color(11);//设置颜色为蓝//1.打印第一行for (int i = 0; i  

因为我们设置的控制台大小是(150,40),所以根据自己的设计。我们将墙设计为(100,35),如下图所示:

效果如下:

大家可以根据自己的需要选择颜色还有大小,此外最好将x的大小设置为偶数,因为蛇节点以及食物还有中文都是宽字符,占两个位置,但高度和普通字符一样,所以y没有要求;

2.4打印游戏说明信息

//打印游戏说明信息void Info_Print(){color(11);//设置颜色为蓝色Setpos(110, 10);printf("1.用 ↑.↓.←.→ 来控制蛇的移动");Setpos(110, 12);printf("2.F3是加速,F4是减速");Setpos(110, 14);printf("3.不能穿墙,不能咬到自己");Setpos(115, 20);printf("@胡土土");//这里可以设置和自己有关的信息作为版权}

效果如下:

各部分对应坐标如下图:

2.5初始化蛇

//初始化蛇void Init_Snake(pSnake psnake){//创建蛇5个节点for (int i = 0; i x = POS_X+2*i;cur->y = POS_Y;cur->next = NULL;//3.连接节点if (psnake->snakehead == NULL){psnake->snakehead = cur;}else{cur->next = psnake->snakehead;psnake->snakehead = cur;}}//4.打印蛇pSnakeNode cur = psnake->snakehead;while (cur){if (cur == psnake->snakehead){//将蛇头颜色设为红色color(12);Setpos(cur->x, cur->y);//定位wprintf(L"%lc", SNAKE_HEAD);}else {//将蛇身颜色设为绿色color(10);Setpos(cur->x, cur->y);//定位wprintf(L"%lc", SNAKE);}cur = cur->next;}//蛇剩下部分初始化psnake->snake_size = 5;psnake->dir = RIGHT;psnake->FoodWeight = SCORE;//宏定义,是5psnake->food = NULL;psnake->food_size = 0;psnake->SleepTime = 300;//休眠时间300mspsnake->status = OK;}

这里首先需要创建五个节点(可以根据自己的需求调整),将它们连接在一起,并将它们的坐标初始化好之后就可以在控制台打印啦~ ,然后记得将整条蛇其他部分都初始化一下哦 ~

效果如下:

我们可以通过控制蛇头节点来实现将蛇头打印与其他部分不一样的红色:

if (cur == psnake->snakehead){//将蛇头颜色设为红色color(12);Setpos(cur->x, cur->y);//定位wprintf(L"%lc", SNAKE_HEAD);}

2.6初始化食物

//初始化食物void Init_Food(pSnake psnake){//创建食物j个节点int j = rand() % 5 + 1;psnake->food_size = j;;for (int i = 0; i x = rand() % 99+1;//不能为0cur->y = rand() % 34+1;//不能为0cur->next = NULL;} while (cur->x % 2 != 0);//判断坐标是否与蛇相同//要用循环遍历蛇节点pSnakeNode ps = psnake->snakehead;while (ps){if (ps->x == cur->x && ps->y == cur->y){goto again;}ps = ps->next;}//3.连接节点if (psnake->food == NULL){psnake->food = cur;}else{cur->next = psnake->food;psnake->food = cur;}}//4.打印食物pFood cur = psnake->food;while (cur){//将食物颜色设为黄色color(14);Setpos(cur->x, cur->y);//定位wprintf(L"%lc", FOOD);cur = cur->next;}}

效果如下:

我们使用随机生成函数rand()%5+1来随机生成生成1~5个食物,上图随机生成了两个

与初始化蛇类似,我们要创建j个节点,然后赋予坐标,这里注意坐标不能在墙上以及外面(通过取模运算来控制),也不能与贪吃蛇蛇身节点重复(需要遍历蛇节点,每个节点都要判断一下),要判断一下,此外x坐标还不能是奇数,因为宽字符是x是两个一起的不然就可能出现蛇进入一半食物的情况,不好判断,可以使用goto again语句来实现一直生成随机数,直到合适的坐标出现

3.运行游戏

//运行游戏void GameRun(pSnake psnake){do{//打印分数Setpos(40, 38);color(14);//设置颜色为黄色printf("总分:%5d", psnake->score);Setpos(40, 40);printf("当前食物分值:%02d", psnake->FoodWeight);if (KEY_PRESS(VK_UP) && psnake->dir != DOWN){psnake->dir = UP;}else if (KEY_PRESS(VK_DOWN) && psnake->dir != UP){psnake->dir = DOWN;}else if (KEY_PRESS(VK_LEFT) && psnake->dir != RIGHT){psnake->dir = LEFT;}else if (KEY_PRESS(VK_RIGHT) && psnake->dir != LEFT){psnake->dir = RIGHT;}else if (KEY_PRESS(VK_ESCAPE)){psnake->status = ESC;//退出游戏break;}else if (KEY_PRESS(VK_SPACE)){//游戏要暂定pause();//暂定和回复暂定}else if (KEY_PRESS(VK_F3))//加速,休眠时间减少,食物分数增加{if (psnake->SleepTime >= 80){psnake->SleepTime -= 30;psnake->FoodWeight += SCORE;}}else if (KEY_PRESS(VK_F4))//减速,休眠时间增加,食物分数减少{if (psnake->FoodWeight > SCORE){psnake->SleepTime += 30;psnake->FoodWeight -= SCORE;}}//走一步SnakeMove(psnake);//睡眠一下Sleep(psnake->SleepTime);} while (psnake->status == OK);}

通过dowhile循环来实现不出意外时,蛇一直移动,平均休眠时间是最开始初始化的300ms,然后通过检测按键来实现不同的逻辑,当按下向上的键时,此时若蛇运动的方向不是向下那么我们就将蛇整个节点中的运动方向改为向上,其他同理;只要蛇运动方向与按键方向不是相反的情况,蛇运动方向就都可以改变

如果按下F3就让蛇休眠时间变短,注意不能为负数,要有一个最低限度,这样蛇运动的速度就变快,此时一个食物对应的分值就可以增加;与之相对,按下F4就让休眠时间边长,对应分值减少,同样要注意不能减为负数,要有一个最低限度

如果按下空格就暂停,再按一下就继续

如果按下Esc就将游戏状态改为ESC,退出游戏

这里的KEY_PRESS()是宏定义#define KEY_PRESS(VK) ( (GetAsyncKeyState(VK) & 0x1) ? 1 : 0 ) 如果按了指定按键就返回1

3.1打印分数

//打印分数Setpos(40, 38);color(14);//设置颜色为黄色printf("总分:%5d", psnake->score);Setpos(40, 40);printf("当前食物分值:%02d", psnake->FoodWeight);

和之前打印欢迎信息一样,确定位置,然后打印即可,这里要注意打印分数格式%02d%5d,这样每次打印都会打印2或5个会覆盖之前的值,这样就不会出现之前的值与现在的值一同打印的情况了,不然每次都要在之前的位置上打印空白字符

效果如下:

3.2游戏暂停

//暂停恢复函数void pause(){while (1){Sleep(100);if (KEY_PRESS(VK_SPACE))//如果再次按下空格就结束暂停状态{break;}}}

使用***循环让蛇一直休眠,直到再次按下空格键跳出***循环,继续游戏

3.3蛇移动函数

//蛇移动的函数- 每次走一步void SnakeMove(pSnake psnake){//1.先开辟一个新的节点pSnakeNode newnode = (pSnakeNode)malloc(sizeof(SnakeNode));if (newnode == NULL){perror("SnakeMove() malloc fail");return;}//2.根据蛇当前前进的状态来确定新节点的位置坐标if (psnake->dir == UP){newnode->x = psnake->snakehead->x;newnode->y = psnake->snakehead->y - 1;//向上y坐标-1}if (psnake->dir == DOWN){newnode->x = psnake->snakehead->x;newnode->y = psnake->snakehead->y + 1;//向下y坐标+1}if (psnake->dir == LEFT){newnode->x = psnake->snakehead->x-2;//向上x坐标-2newnode->y = psnake->snakehead->y ;}if (psnake->dir == RIGHT){newnode->x = psnake->snakehead->x + 2;//向下x坐标+2newnode->y = psnake->snakehead->y;}//3.判断新节点位置if (IsWall(newnode)){//如果是墙//撞墙,游戏结束psnake->status = KILL_BY_WALL;return;}else if (IsSelf(newnode, psnake)){//如果是自己//咬到自己,游戏结束psnake->status = KILL_BY_SELF;return;}else if (IsFood(newnode, psnake)){//如果是食物//先+分数psnake->score +=SCORE;//蛇的节点个数+1psnake->snake_size++;}//然后连接新节点,头插法newnode->next = psnake->snakehead;psnake->snakehead = newnode;//打印,注意如果是食物就多打印一个,如果不是食物就打印原来的个数个,并且释放掉尾节点//可以通过蛇节点个数控制int size = psnake->snake_size;pSnakeNode cur = psnake->snakehead;pSnakeNode last = psnake->snakehead;while (size){if (size == psnake->snake_size){color(12);//蛇头是红色Setpos(cur->x, cur->y);wprintf(L"%lc", SNAKE_HEAD);}else{color(10);//身体是绿色色Setpos(cur->x, cur->y);wprintf(L"%lc", SNAKE_HEAD);}size--;last = cur;cur = cur->next;}//如果不是食物,将最后一个节点的位置打印空字符,并且释放该节点if (cur != NULL){Setpos(cur->x, cur->y);printf("  ");free(cur);cur = NULL;last->next = NULL;//防止越界访问}}

这里每次移动都开辟一个新的节点,然后根据蛇移动的方向确定新节点的坐标,然后判断新节点的坐标是否是墙或者食物或者自己,如果是墙就撞墙了结束游戏,如果是自己就咬到自己,结束游戏,如果是食物就增加一个节点,并且得分增加,如果什么都不是就休眠一下继续走下一步

✨判断新节点是不是墙

//判断新节点也就是下一个位置是不是墙bool IsWall(pSnakeNode newnode){//墙的x坐标是0和100,y坐标是0和35if (newnode->x == 0||newnode->x == 100||newnode->y == 0||newnode->y == 35){return true;}return false;}

✨判断新节点是不是自己

//判断新节点也就是下一个位置是不是自己bool IsSelf(pSnakeNode newnode, pSnake psnake){//遍历蛇的节点,注意最后一个不需要遍历,因为新增了一个pSnakeNode cur = psnake->snakehead;while (cur->next){if (newnode->x == cur->x && newnode->y == cur->y){return true;}cur = cur->next;}return false;}

这里需要循环遍历自己,要注意,有了新节点,最后一个节点就不需要了,所以遍历到倒数第二个节点即可

✨判断新节点是不是食物

//判断新节点也就是下一个位置是不是食物bool IsFood(pSnakeNode newnode,pSnake psnake){//遍历食物节点来判断pFood cur = psnake->food;while (cur){if (cur->x == newnode->x && cur->y == newnode->y){psnake->food_size--;if (psnake->food_size == 0){//如果没有食物就重新初始化食物//释放食物节点if (psnake->food != NULL){pFood tmp = psnake->food;pFood rem = tmp;while (tmp){tmp = tmp->next;free(rem);rem = tmp;}psnake->food = NULL;}Init_Food(psnake);}return true;}cur = cur->next;}return false;}

因为食物有多个,所以要遍历食物,如果是食物,那么食物节点就少一个psnake->food_size--;,当食物节点个数为0时我们就要重新生成食物节点Init_Food(psnake);,在重新生成之前要记得将之前的食物节点都遍历释放掉

✨连接新节点

如果游戏没有结束,也就是蛇没有撞墙或者咬到自己,我们就需要连接新节点,和初始化蛇时连接节点一样使用头插法

//然后连接新节点,头插法newnode->next = psnake->snakehead;psnake->snakehead = newnode;

✨再次打印蛇

连接完节点之后就可以打印蛇了

当然如果不是食物,那么蛇的节点不会增加,连接了新节点,就需要释放尾节点,我们可以通过蛇的size来遍历打印:

while (size){if (size == psnake->snake_size){color(12);//蛇头是红色Setpos(cur->x, cur->y);wprintf(L"%lc", SNAKE_HEAD);}else{color(10);//身体是绿色色Setpos(cur->x, cur->y);wprintf(L"%lc", SNAKE_HEAD);}size--;last = cur;cur = cur->next;}

如果此时cur不为NULL说明还剩一个节点没打印,也就是说新节点不是食物,所以我们要释放尾节点,注意不要忘了将该位置打印空字符(两个哦~),不然蛇就会一直变大

//如果不是食物,将最后一个节点的位置打印空字符,并且释放该节点if (cur != NULL){Setpos(cur->x, cur->y);printf("  ");free(cur);cur = NULL;last->next = NULL;//防止越界访问}

4.游戏结束

//游戏结束,清理工作void GameEnd(pSnake psnake){system("cls");//清屏color(11);//颜色设置为蓝色Setpos(40, 15);switch (psnake->status){case ESC:printf("你已主动退出游戏@-@");break;case KILL_BY_SELF:printf("很遗憾,你咬到自己了*^*,游戏结束");break;case KILL_BY_WALL:printf("很遗憾,你撞墙了*^*,游戏结束");break;}Setpos(50, 18);//释放蛇的节点pSnakeNode cur = psnake->snakehead;pSnakeNode del = cur;while (cur){cur = cur->next;free(del);del = cur;}//释放食物节点if (psnake->food != NULL){pFood tmp = psnake->food;pFood rem = tmp;while (tmp){tmp = tmp->next;free(rem);rem = tmp;}}psnake = NULL;}

游戏结束后,清理屏幕system("cls");//清屏,并根据不同状态设置不同的结束语,比如撞墙的状态printf("很遗憾,你撞墙了*^*,游戏结束");,然后释放蛇的节点以及食物的节点,将整条蛇的指针置空

四、结语

上一篇:怎么连接巴西服务器网络?

下一篇:cfg怎么创建?