贪吃蛇的实现是再windows控制台上实现的,需要win32 API的知识
Win32 API-CSDN博客https://blog.csdn.net/bkmoo/article/details/138698452?spm=1001.2014.3001.5501
游戏说明
●地图的构建
●蛇身的移动(使用↑ . ↓ . ← . → 分别控制蛇的移动)
●F3加速,F4减速
●吃食物加分(加速可获得更高的分数,减速食物分数下降)
●蛇撞墙(游戏结束)
●蛇咬到自己(游戏退出)
●游戏暂停(空格操作)
●游戏退出(ESC正常退出)
下面这张图片是游戏细化的实现过程
头文件的声明
创建Snake.h文件存放游戏函数的声明,蛇的结构,需要的头文件。
#include <stdlib.h>
#include <stdio.h>
#include <windows.h>
#include <locale.h>
#include <windows.h>
#include <stdbool.h>#define KEY_PRESS(vk) ((GetAsyncKeyState(vk) & 0x1) ? 1 : 0)#define WALL L'□'
#define BOOY L'●'
#define FOOD L'★'//蛇的初始位置
#define POS_X 24
#define POS_Y 5//游戏状态
enum GAME_STATUS
{OK = 1,ESC,KILL_BY_WALL, //撞墙wallKILL_BY_SELF //撞到自己self
};//方向:上下左右
enum DIRECTION
{UP = 1,DOWN,LEFT,RIGHT
};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 GAME_STATUS status;//游戏当前的状态enum DIRECTION dir;//蛇当前的走向
}Snake, *pSnake;//光标位置的定位
void Setpos(int x, int y);//一 游戏开始前的准备(初始化)
void GameStark(pSnake ps);//游戏欢迎界面
void welcometoGame();//绘制地图
void CreateMap();//初始化蛇
void InitSnake(pSnake ps);//生成食物
void CreateFood(pSnake ps);//二 游戏运行的整个逻辑
void GameRun(pSnake ps);//贪吃蛇移动函数, 每次一步
void SnakeMove(pSnake ps);//蛇的下一步的位置是食物,吃掉
void EntFood(pSnake ps, pSnakeNode pnext);//蛇的下一步的位置不是食物
void NotEntFood(pSnake ps, pSnakeNode pnext);//检测是否撞墙
void KillByWall(pSnake ps);//检测是否撞到自己
void KillBySelf(pSnake ps);//三 游戏结束
void GameEnd(pSnake ps);
游戏实现
将游戏的实现分割成三个大块,分别是
1、游戏开始前的初始化
GameStark(&snake);
2、游戏过程的实现
GameRun(&snake);
3、游戏结束的善后工作
GameEnd(&snake);
一、游戏地图的实现
想要实现地图,这里就要控制台窗⼝的⼀些知识,如果想在控制台的窗⼝中指定位置输出信息,我们得知道 该位置的坐标,所以⾸先介绍⼀下控制台窗⼝的坐标知识。 控制台窗⼝的坐标如下所⽰,横向的是X轴,从左向右依次增⻓,纵向是Y轴,从上到下依次增⻓。
在游戏地图上,我们打印墙体使⽤宽字符:□,打印蛇使⽤宽字符●,打印⻝物使⽤宽字符★ ,普通的字符是占⼀个字节的,这类宽字符是占⽤2个字节
注意:打印的中文符号的宽字符,因此在使用之前需要本地化
附加:
C语⾔的标准中不断加⼊了国际化的⽀持。⽐如:加⼊和宽字符的类型wchar_t 和宽字符的输⼊和输出函数,加⼊和<locale.h>头⽂件,其中提供了允许程序员针对特定 地区(通常是国家或者说某种特定语⾔的地理区域)调整程序⾏为的函数。
<locale.h>本地化
类项:
setlocale函数
1 setlocale (LC_ALL, "C" );
1 setlocale (LC_ALL, " " ); // 切换到本地环境
地图的构建
//修改适配中文环境
setlocale(LC_ALL, "");
void CreateMap()
{int i = 0;//地图的上Setpos(0, 0);for (i = 0; i <= 56; i += 2){wprintf(L"%lc", WALL);//打印宽字符用%lc,wprintf(L"%lc", L'□');}//下Setpos(0, 25);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);}
}
二、蛇身和食物
蛇身的构建
typedef struct SnakeNode
{
int x;
int y;
struct SnakeNode* next;
}SnakeNode, * pSnakeNode;
关于整条贪吃蛇的维护
要管理SnakeNode就还要创建一个Snake结构体,里面包含整个贪吃蛇的信息。
typedef struct Snake
{
pSnakeNode pSnake;//维护贪吃蛇的指针,指向贪吃蛇的指针
pSnakeNode pFood;//指向食物的指针
int score; //当前累计的分数
int FoodWeight; //当前食物的分数
int SleepTime;//蛇休眠的时间
enum GAME_STATUS status;//游戏当前的状态
enum DIRECTION dir;//蛇当前的走向
}Snake, *pSnake;
这里使用了枚举,分别是GAME_STATUS表示游戏状态,DIRECTION表示蛇当前的走向。
//游戏状态
enum GAME_STATUS
{
OK = 1,
ESC,
KILL_BY_WALL, //撞墙wall
KILL_BY_SELF //撞到自己self
};
//方向:上下左右
enum DIRECTION
{
UP = 1,
DOWN,
LEFT,
RIGHT
};
三、游戏开始界面
有了前面的准备工作后,就可以开始包装游戏了。进入游戏要有开始界面吧,这里就用到了Win32 API。
创建贪吃蛇Snake snake
创建游戏的初始化函数 GameStark();
1.GameStark()
void GameStark(pSnake ps)
{
//设置控制台窗⼝的⻓宽:设置控制台窗⼝的⼤⼩,30⾏,100列 设置cmd窗⼝名称
system("mode con cols=100 lines=30");
system("title 贪吃蛇");//隐藏光标
CONSOLE_CURSOR_INFO cursor_info = { 0 };
HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
GetConsoleCursorInfo(handle, &cursor_info);//获取控制台光标信息
cursor_info.bVisible = false;//false是bool类型 头文件 stdbool.h,隐藏光标.
SetConsoleCursorInfo(handle, &cursor_info);//打印欢迎信息
welcometoGame();//绘制游戏地图
CreateMap();
//初始化蛇
InitSnake(ps);
//生成食物
CreateFood(ps);
}
welcometoGame();
创建函数 welcometoGame();打印欢迎信息
void welcometoGame()
{
Setpos(35, 15);
printf("欢迎来到贪吃蛇小游戏\n");
Setpos(36, 25);
system("pause");
system("cls");//清理屏幕!!!Setpos(15, 10);
printf("使用↑ . ↓ . ← . → 分别控制蛇的移动, F3为加速,F4为减速\n");
Setpos(30, 11);
printf("加速获得更高的分数\n");
Setpos(36, 25);
system("pause");
system("cls");
}
Setpos()
在欢迎信息函数里有Setpos函数,这个是位置定位函数,可以将光标定位到需要的位置
//定位光标信息
void Setpos(int x, int y)
{HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
COORD pos = { x, y };
//设置光标位置
SetConsoleCursorPosition(handle, pos);
}
InitSnake()
创建蛇的初始化函数
void InitSnake(pSnake ps)
{int i = 0;
for (i = 0; i < 5; i++)
{
//创建蛇身的五个节点
pSnakeNode cur = (pSnakeNode)malloc(sizeof(SnakeNode));
if (cur == NULL)
{
perror("InitSnake()::malloc");
return;
}
cur->x = POS_X + 2 * i;
cur->y = POS_Y;
cur->next = NULL;//头插法
if (ps->pSnake == NULL)
{
ps->pSnake = cur;
}
else
{
cur->next = ps->pSnake;
ps->pSnake = cur;
}
}
//打印蛇身
pSnakeNode cur = ps->pSnake;
while (cur)
{
Setpos(cur->x, cur->y);
wprintf(L"%lc", BOOY);
cur = cur->next;
}
//蛇的其他信息初始化
ps->dir = RIGHT;//初始化方向为右
ps->FoodWeight = 10;//初始化食物
ps->pFood = NULL;
ps->SleepTime = 200;//时间间隔200毫秒
ps->status = OK;//游戏状态OK
ps->score = 0; //当前累计的分数
}
使用头插法创建链表,使得从头节点开始可以依次向后找到每个节点。然后初始化其他信息和答应你蛇身。
CreateFood()
创建食物生成函数
void CreateFood(pSnake ps)
{
int x = 0;
int y = 0;again:
do
{
x = rand() % 53 + 2;
y = rand() % 24 + 1;
} while (x % 2 != 0);pSnakeNode cur = ps->pSnake;
while (cur)
{
if (cur->x == x && cur->y == y)
{
goto again;
}
cur = cur->next;
}//创建食物
pSnakeNode pFood = (pSnakeNode)malloc(sizeof(pSnakeNode));
if (pFood == NULL)
{
perror("CreateFood()::malloc");
return;
}
pFood->x = x;
pFood->y = y;//打印食物
Setpos(pFood->x, pFood->y);
wprintf(L"%lc", FOOD);
ps->pFood = pFood;
}
需要考虑到食物不能创建在地图外,不能创建在蛇身上。这里用到了goto语句,遍历蛇身节点。
2.游戏过程的实现
创建GameRun()函数,用来实现游戏的逻辑,里面有帮助信息,按键的检测,蛇的下一步
GameRun()
//二 游戏逻辑实现
void GameRun(pSnake ps)
{
//打印帮助信息
PrintHelpInfo();//循环
do
{
Setpos(60, 10);
printf("总分:%5d", ps->score);
Setpos(60, 11);
printf("当前食物的分值:%.2d", ps->FoodWeight);
//检测按键
//上下左右 ESC 空格 F3 F4
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 != RIGHT)
{
ps->dir = RIGHT;
}
else if (KEY_PRESS(VK_ESCAPE))
{
ps->status = ESC;
}
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->FoodWeight > 2)
{
ps->SleepTime += 30;
ps->FoodWeight -= 2;
}
}//睡眠一下
Sleep(ps->SleepTime);//下一步
SnakeMove(ps);} while (ps->status == OK);
}
在Win32 API这篇文章中已经介绍了GetAsyncKeyState()函数,并且宏定义,这里直接使用
#define KEY_PRESS(vk) ((GetAsyncKeyState(vk) & 0x1) ? 1 : 0)
使用游戏状态GAME_STATUS来判定循环是否继续,以此来实现只要游戏正常进行,按键就一直检测。
并且在进行F3 F4按键检测后,需要实现加速和食物分数增加,减速和食物分数减少。
故用 ps->SleepTime 加减30,ps->FoodWeight 减加2实现。同时还要考虑,睡眠时间不能为0,食物分数不能为0。
PrintHelpInfo()
创建帮助信息函数,打印帮助信息
void PrintHelpInfo()
{
Setpos(60, 14);
printf("1. 不能穿墙,不能咬到自己");
Setpos(60, 15);
printf("2. 使用↑ ↓ ← → 分别控制蛇的移动");
Setpos(60, 16);
printf("3. F3为加速,F4为减速");
Setpos(60, 17);
}
pause();
创建暂停函数,使用空格游戏暂停,再次按下游戏继续。
void pause()
{
while (1)
{
Sleep(100);
if (KEY_PRESS(VK_SPACE))
{
break;
}
}
}
这里使用死循环Sleep来实现游戏的暂停,并且使用按键检测,一旦检测到空格,就会break退出。
SnakeMove()
此时到了一个非常关键的地方,就是蛇的移动函数,也就是蛇的下一步。
创建一个新的节点pnext作为下一步。
考虑蛇的下一步时需要考虑方向的问题,比如当你向上走时不能向下走吧,就是不能向与当前蛇的走向的相反放向。
还要考虑蛇的下一步是否是食物这个问题,如果是食物就要吃掉,吃掉后就要加分,蛇身加长
下一步不是食物,就要维持原长。
还要考虑下一步是否撞墙,否则游戏结束。
下一步是否咬到自己,否则游戏结束。
void SnakeMove(pSnake ps)
{
//创建下一个节点
pSnakeNode pnext = (pSnakeNode)malloc(sizeof(SnakeNode));
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 (pnext->x == ps->pFood->x && pnext->y == ps->pFood->y)
{
EntFood(ps, pnext);
}
else
{
NotEntFood(ps, pnext);
}//检测是否撞墙
KillByWall(ps);//检测是否撞到自己
KillBySelf(ps);}
EntFood()
下一步是食物
void EntFood(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", BOOY);
cur = cur->next;
}//释放被吃掉的食物
free(ps->pFood);
//新生成食物
CreateFood(ps);//吃到了食物,更新总分数
ps->score += ps->FoodWeight;}
这里直接将下一步的节点,进行头插。
食物被吃掉后要释放旧的食物节点,还要生成新的食物,吃掉食物后要更新分数。
NotEntFood()
下一步不是食物
void NotEntFood(pSnake ps, pSnakeNode pnext)
{
//头插
pnext->next = ps->pSnake;
ps->pSnake = pnext;//因为要保持原长度,所以要释放尾节点,找到倒数第二个节点
pSnakeNode cur = ps->pSnake;
while (cur->next->next)
{
cur = cur->next;
}
//找到最后一个节点的位置,将旧的信息(图标)覆盖,
Setpos(cur->next->x, cur->next->y);
//两个空格(宽字符)
printf(" ");//释放尾节点
free(cur->next);
cur->next = NULL;
//打印蛇身
cur = ps->pSnake;
while (cur)
{
Setpos(cur->x, cur->y);
wprintf(L"%lc", BOOY);
cur = cur->next;
}}//蛇移动的过程就是把最后一个节点释放掉,再插入到头部,打印出来
下一步不是食物,头插下一步的节点,因为要维持原长度,所以要释放最后一个节点,使用cur->next->next找到倒数第二个节点,来操作尾节点。
尾节点要释放,还要再尾节点的位置进行覆盖,要打印两个空格覆盖释放的蛇身图标,避免拖尾
KillByWall()
检测蛇是否撞墙,如果撞墙就更新游戏状态,ps->status = KILL_BY_WALL
void KillByWall(pSnake ps)
{
if (ps->pSnake->x == 0 ||
ps->pSnake->x == 56 ||
ps->pSnake->y == 0 ||
ps->pSnake->y == 25)
{
ps->status = KILL_BY_WALL;
return;
}
}
只要判断蛇头的坐标即可,头节点的x坐标是否0或56,y坐标是否0或25
KillBySelf()
是否咬到自己,咬到自己更新游戏状态ps->status = KILL_BY_SELF
void KillBySelf(pSnake ps)
{
pSnakeNode cur = ps->pSnake->next;
while (cur)
{
if (cur->x == ps->pSnake->x && cur->y == ps->pSnake->y)
{
ps->status = KILL_BY_SELF;
return;
}
cur = cur->next;
}
}
遍历节点坐标是否相同即可。
3.游戏结束的善后工作
游戏结束了,总要有些结束语吧。而且使用malloc动态开辟的空间要释放吧。
GameEnd()
void GameEnd(pSnake ps)
{
Setpos(15, 12);
switch (ps->status)
{
case ESC:
printf("主动退出游戏,游戏结束");
break;
case KILL_BY_WALL:
printf("很遗憾,撞墙了,游戏结束");
break;
case KILL_BY_SELF:
printf("很遗憾,咬到自己了,游戏结束");
break;
}//游戏结束要释放内存
pSnakeNode cur = ps->pSnake;
pSnakeNode prv = NULL;while(cur)
{
prv = cur;
cur = cur->next;
free(prv);
}
free(ps->pFood);
ps->pFood = NULL;
ps->pSnake = NULL;
}
别忘了指向食物的节点也要释放。
游戏代码的实现
创建Snake.c文件实现函数的功能
#include "Snake.h"//定位光标信息
void Setpos(int x, int y)
{HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);COORD pos = { x, y };//设置光标位置SetConsoleCursorPosition(handle, pos);
}void CreateMap()
{int i = 0;//地图的上Setpos(0, 0);for (i = 0; i <= 56; i += 2){wprintf(L"%lc", WALL);//打印宽字符用%lc,wprintf(L"%lc", L'□');}//下Setpos(0, 25);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 welcometoGame()
{Setpos(35, 15);printf("欢迎来到贪吃蛇小游戏\n");Setpos(36, 25);system("pause");system("cls");//清理屏幕!!!Setpos(15, 10);printf("使用↑ . ↓ . ← . → 分别控制蛇的移动, F3为加速,F4为减速\n");Setpos(30, 11);printf("加速获得更高的分数\n");Setpos(36, 25);system("pause");system("cls");
}void InitSnake(pSnake ps)
{int i = 0;for (i = 0; i < 5; i++){//创建蛇身的五个节点pSnakeNode cur = (pSnakeNode)malloc(sizeof(SnakeNode));if (cur == NULL){perror("InitSnake()::malloc");return;}cur->x = POS_X + 2 * i;cur->y = POS_Y;cur->next = NULL;//头插法if (ps->pSnake == NULL){ps->pSnake = cur;}else{cur->next = ps->pSnake;ps->pSnake = cur;}}//打印蛇身pSnakeNode cur = ps->pSnake;while (cur){Setpos(cur->x, cur->y);wprintf(L"%lc", BOOY);cur = cur->next;}//蛇的其他信息初始化ps->dir = RIGHT;//初始化方向为右ps->FoodWeight = 10;//初始化食物ps->pFood = NULL;ps->SleepTime = 200;//时间间隔200毫秒ps->status = OK;//游戏状态OKps->score = 0; //当前累计的分数
}void CreateFood(pSnake ps)
{int x = 0;int y = 0;again:do{x = rand() % 53 + 2;y = rand() % 24 + 1;} while (x % 2 != 0);pSnakeNode cur = ps->pSnake;while (cur){if (cur->x == x && cur->y == y){goto again;}cur = cur->next;}//创建食物pSnakeNode pFood = (pSnakeNode)malloc(sizeof(pSnakeNode));if (pFood == NULL){perror("CreateFood()::malloc");return;}pFood->x = x;pFood->y = y;//打印食物Setpos(pFood->x, pFood->y);wprintf(L"%lc", FOOD);ps->pFood = pFood;
}void GameStark(pSnake ps)
{//设置控制台窗⼝的⻓宽:设置控制台窗⼝的⼤⼩,30⾏,100列 设置cmd窗⼝名称system("mode con cols=100 lines=30");system("title 贪吃蛇");//隐藏光标CONSOLE_CURSOR_INFO cursor_info = { 0 };HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);GetConsoleCursorInfo(handle, &cursor_info);//获取控制台光标信息cursor_info.bVisible = false;//false是bool类型 头文件 stdbool.h,隐藏光标.SetConsoleCursorInfo(handle, &cursor_info);//打印欢迎信息welcometoGame();//绘制游戏地图CreateMap();//初始化蛇InitSnake(ps);//生成食物CreateFood(ps);
}void PrintHelpInfo()
{Setpos(60, 14);printf("1. 不能穿墙,不能咬到自己");Setpos(60, 15);printf("2. 使用↑ ↓ ← → 分别控制蛇的移动");Setpos(60, 16);printf("3. F3为加速,F4为减速");Setpos(60, 17);
}void pause()
{while (1){Sleep(100);if (KEY_PRESS(VK_SPACE)){break;}}
}void EntFood(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", BOOY);cur = cur->next;}//释放被吃掉的食物free(ps->pFood);//新生成食物CreateFood(ps);//吃到了食物,更新总分数ps->score += ps->FoodWeight;}void NotEntFood(pSnake ps, pSnakeNode pnext)
{//头插pnext->next = ps->pSnake;ps->pSnake = pnext;//因为要保持原长度,所以要释放尾节点,找到倒数第二个节点pSnakeNode cur = ps->pSnake;while (cur->next->next){cur = cur->next;}//找到最后一个节点的位置,将旧的信息(图标)覆盖, Setpos(cur->next->x, cur->next->y);//两个空格(宽字符)printf(" ");//释放尾节点free(cur->next);cur->next = NULL;//打印蛇身cur = ps->pSnake;while (cur){Setpos(cur->x, cur->y);wprintf(L"%lc", BOOY);cur = cur->next;}}//蛇移动的过程就是把最后一个节点释放掉,再插入到头部,打印出来void KillByWall(pSnake ps)
{if (ps->pSnake->x == 0 ||ps->pSnake->x == 56 ||ps->pSnake->y == 0 ||ps->pSnake->y == 25){ps->status = KILL_BY_WALL;return;}
}void KillBySelf(pSnake ps)
{pSnakeNode cur = ps->pSnake->next;while (cur){if (cur->x == ps->pSnake->x && cur->y == ps->pSnake->y){ps->status = KILL_BY_SELF;return;}cur = cur->next;}
}void SnakeMove(pSnake ps)
{//创建下一个节点pSnakeNode pnext = (pSnakeNode)malloc(sizeof(SnakeNode));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 (pnext->x == ps->pFood->x && pnext->y == ps->pFood->y){EntFood(ps, pnext);}else{NotEntFood(ps, pnext);}//检测是否撞墙KillByWall(ps);//检测是否撞到自己KillBySelf(ps);}//二 游戏逻辑实现
void GameRun(pSnake ps)
{//打印帮助信息PrintHelpInfo();//循环do{Setpos(60, 10);printf("总分:%5d", ps->score);Setpos(60, 11);printf("当前食物的分值:%.2d", ps->FoodWeight);//检测按键//上下左右 ESC 空格 F3 F4if (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 != RIGHT){ps->dir = RIGHT;}else if (KEY_PRESS(VK_ESCAPE)){ps->status = ESC;}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->FoodWeight > 2){ps->SleepTime += 30;ps->FoodWeight -= 2;}}//睡眠一下Sleep(ps->SleepTime);//下一步SnakeMove(ps);} while (ps->status == OK);}void GameEnd(pSnake ps)
{Setpos(15, 12);switch (ps->status){case ESC:printf("主动退出游戏,游戏结束");break;case KILL_BY_WALL:printf("很遗憾,撞墙了,游戏结束");break;case KILL_BY_SELF:printf("很遗憾,咬到自己了,游戏结束");break;}//游戏结束要释放内存pSnakeNode cur = ps->pSnake;pSnakeNode prv = NULL;while(cur){prv = cur;cur = cur->next;free(prv);}free(ps->pFood);ps->pFood = NULL;ps->pSnake = NULL;
}
test.c
#include "Snake.h"void test()
{int ch = 0;//创建贪吃蛇Snake snake = { 0 };do{//游戏开始前的初始化GameStark(&snake);//游戏过程的实现GameRun(&snake);//游戏结束的善后工作GameEnd(&snake);Setpos(18, 15);printf("是否再来一局:Y/N");ch = getchar();} while (ch == 'Y' || ch == 'y');
}int main()
{//修改适配中文环境setlocale(LC_ALL, "");test();Setpos(0, 26);return 0;
}