贪吃蛇的实现是再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, //撞墙wall
KILL_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;//游戏状态OK
ps->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 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);
}
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;
}