贪吃蛇作为一个游戏,肯定被大家所熟知,我相信大部分人都玩过这个游戏,所以规则我相信大家都明白,在这我就不过多讲述了。
然后今天我们就要基于C语言链表这个内容去实现这个小游戏,下面我们先来看一下这个我们将会实现的几个画面。
这就是我们这个游戏的几个画面,下面我们就要基于这几个画面去实现我们的贪吃蛇小游戏。
首先我们说一下这个小游戏的大致功能吧,他的功能有如下几个方面,
首先我们要能够去控制它的移动,有上下左右四个方向的移动,然后我们要设置蛇的速度,这里其实就是一个休息的时间,就是休息多少秒进行下一步,然后还有我们要生成食物,还有触碰到食物之后要加分,以及生成下一个食物。
下面我们就一步步的来实现一下这个代码
实现过程使用了WIN32的一些API,这里简单介绍一下这些API的功能。
设置控制台窗口大小,在windows界面的cmd中我们可以输入这样的指令来控制窗口的大小:
mode con cols=100 lines=30 #控制窗口,cols为行长度,lines为列行数
命令行窗口的名称也可以通过命令的方式来更改:
title 贪吃蛇#更改命令行窗口的名称
在C语言中,我们需要使用system接口来改变终端 窗口的大小 以及 窗口名称,使用system接口需要包含 stdlib.h 头文件,例如下面代码:
#include<stdio.h>
#include<stdlib.h//使用system接口的头文件
int main()
{
system("title 贪吃蛇");//将命令行窗口的名字更改为需要的名字
system("mode con cols=100 lines=30");//设置命令行窗口的大小
//其他操作
return 0;
}
通常,我们的终端也可看作坐标系,左上角为坐标原点,向右为x轴,向下位y轴,如下图所示:
我们在windows窗口上描述一个坐标需要使用一个windows API中定义的一个结构体 COORD,表示一个字符在控制台屏幕缓冲区上的坐标,在C语言中,我们需要包含 windows.h 头文件才能使用,使用实例如下:
#include<stdio.h>
#include<windows.h>//调用该api需要的头文件
#include<stdlib.h>
int main()
{
COORD pos = { 20, 20 };//使用第一个参数为行,第二参数为列
return 0;
}
实现光标隐藏,我们需要先调用 GetStdHandle 函数来获取标准输出句柄(什么是句柄可以看这个blogger的文章:戳我跳转),使用这个句柄可以操作设备。
HANDLE output = NULL;//HANDLE为结构体指针类型
//获取标准输出句柄来表示不同设备的数值
output = GetStdHandle(STD_OUTPUT_HANDLE);
要隐藏光标,我们就先要获得一个光标信息,上面我们已经获取了标准输出相关设备的句柄,接下来我们创建 CONSOLE_CORSOR_INFO 结构体对象(接收有关主机光标信息的结构体),再调用 GetConsoleCursorInfo 函数来获得光标信息:
#include<stdio.h>
#include<windows.h>//调用win32 api所需要的头文件
int main()
{
HANDLE output = NULL;
//获取标准输出句柄来表示不同设备的数值
output = GetStdHandle(STD_OUTPUT_HANDLE);
CONSOLE_CURSOR_INFO cursor_info;
GetConsoleCursorInfo(output, &cursor_info);//获取光标的信息
return 0;
}
设置终端光标输出位置,我们首先要获取想要输出位置的坐标,上面我们介绍了COORD结构体,用来设置位置坐标。获取完坐标之后,我们可以调用 SetConsoleCorsorPosition 函数将光标位置设置到获取的坐标位置。
void SetPos(short x, short y)
{
HANDLE houtput = NULL;
houtput = GetStdHandle(STD_OUTPUT_HANDLE);
COORD pos = { x,y };
SetConsoleCursorPosition(houtput, pos);
}
我们便可利用这个函数将光标放到我们需要的位置上
完贪吃蛇我们一定需要用键盘来控制一些功能,我们可以使用 GetAsyncKeyState 函数来获取按键情况,此函数函数原型如下:
SHORT GetAsyncKeyState(int vKey);
将键盘上的键值传给函数,通过函数返回值来判断按键的状态。GetAsyncKeyState 返回值是short类型,在上一次调用此函数后,如果返回的16位的short数据中,最高位是1,说明按键的状态是按下,如果最高位是0,说明按键的状态是抬起;如果最低位被置为1,则说明该按键被按过,否则位0。
如果我们要判断按键是否被按过,只需要判断返回值最低值是否为1即可,我们可以按位与上0x1来获取最低位的值,那么我们就可这样来编写函数:
#define KEY_PRESS(VK) ((GetAsyncKeyState(VK) & 0x1) ? 1 : 0)//返回1表示按过,返回0表示没有按过
我们在打印蛇身和墙体的时候,是需要特殊字符——宽字符 宽字符的长度为2字节,因为不同地区的语言不同,计算机中描述的方式也不太一样,普通的单字节字符并不适合我们的地区,因此C语言加入了宽字符(字符类型:wchar_t 需要包含 locale.h 头文件)允许程序员针对特定地区调整程序行为函数。
类项: 通过修改地区,程序可以改变它的行为来适应世界的不同区域。但是地区改变可能会影响库的许多部分,其中一部分可能是我们不希望修改的,所以C语言针对不同类型的类项进行修改,下面的一个宏指定一个类项:
LC_COLLATE:影响字符串比较函数 LC_CTYPE:影响字符处理函数行为 LC_MONETARY:影响货币格式 LC_NUMERIC:影响printf()函数 LC_TIME:影响时间格式 LC_ALL:针对所有类项修改,将以上所有类别设定为给定的语言环境
我们使用 setlocale 函数修改类项:
char* setlocale(int category, const char* locale);
函数的第一个参数可以是前面说明的类项中的一个,那么每次只会影响一个类项,如果第一个参数是LC_ALL,就会影响所有类项。C标准给第二个参数定义了2种可能取值:“C”(正常模式)和“”(本地模式) 在任意程序执行开始,默认隐式调用:
setlocale(LC_ALL, "C");
我们需要切换到本地环境输出字符,所以:
setlocale(LC_ALL, " ");//切换为本地环境
我们想要打印宽字符也是与普通打印不同的,宽字符字面量前必须加上L,否则C语言就会将其当为窄字符,且占位符应当为"%lc",和"%ls",才可正常打印宽字符。
#include<stdio.h>
#include<locale.h>
int main()
{
setlocale(LC_ALL, "");
wchar_t ch = L'蛇';
wprintf(L"%lc\n", ch);
return 0;
}
#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 GAEM_STATUS//蛇运行状态
{
RUNNING,//运行状态
EXIT_NORMAL,//正常退出状态
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 GAEM_STATUS _Status;//游戏状态
}Snake, *pSnake;
//设置光标
void SetPos(short x, short y);
//游戏初始化
void GameStart(pSnake ps);
//欢迎界面
void WelcomeToGame();
//创建蛇
void InitSnake(pSnake ps);
//创建食物
void CreateFood(pSnake ps);
//游戏运行逻辑
void GameRun(pSnake ps);
//蛇的移动
void SnakeMove(pSnake ps);
//检查下一个是否是食物
int NEXTFOOD(pSnakeNode pn, pSnake ps);
//是就吃掉它
void EATFOOD(pSnakeNode pn, pSnake ps);
//不是就往前走
void NOFOOD(pSnakeNode pn,pSnake ps);
//撞墙
void KillByWall(pSnake ps);
//撞自己
void KillBySelf(pSnake ps);
//游戏结束
void GameEnd(pSnake ps);
游戏开始的时候我们隐藏光标、设置窗口大小及名称,随后我们打印欢迎界面,打印地图,初始化贪吃蛇以及创建食物等操作。
需要注意的是,打印地图的时候,其实我们终端x轴的密度约是y轴的两倍,也就是说x轴两个单位约等于y轴一个单位,这里我给了一个合适的值:
void CreateMap()
{
//上
SetPos(0, 0);
for (int i = 0; i <= 56; i += 2)
{
wprintf(L"%lc", WALL);
}
//下
SetPos(0, 26);
for (int i = 0; i <= 56; i += 2)
{
wprintf(L"%lc", WALL);
}
//左
for (int i = 0; i <= 25; i++)
{
SetPos(0, i);
wprintf(L"%lc", WALL);
}
//右
for (int i = 0; i <= 25; i++)
{
SetPos(56, i);
wprintf(L"%lc", WALL);
}
return;
}
void InitSnake(pSnake ps)
{
pSnakeNode cur = NULL;
for (int i = 0; i < 5; i++)//蛇身五个节点
{
cur = (pSnakeNode)malloc(sizeof(pSnakeNode));
if (cur == NULL)//检测空指针
{
perror("InitSnake::alloc()");
return;
}
cur->x = POS_X + 2 * i;//初始化蛇,让蛇身在开始的时候是横着放的,一个蛇身字符是两个字节,所以要乘2
cur->y = POS_Y;//横着的蛇身,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 = RUNNING;//游戏状态置为运行中
ps->_score = 0;//游戏分数置为0
ps->_pFood = NULL;//食物节点先设置为空
ps->_SleepTime = 200;//刷新时间
ps->_FoodWeight = 10;//食物重量设置10
ps->_Dir = RIGHT;//蛇头方向
return;
}
int x = 0, y = 0;
again:
do
{
x = rand() % 53 + 2;//横坐标2-54范围刚好不会越界
y = rand() % 25 + 1;//纵坐标1-25范围也不会越界
} while (x % 2 != 0);//食物位置正确打印,保证与蛇在一条线上
pSnakeNode cur = ps->_psnake;
while (cur)//保证创建食物不在蛇身上
{
if (cur->x == x || cur->y == y)
{
goto again;
}
cur = cur->next;
}
游戏运行时,首先打印帮助信息,再打印食物的分数信息,然后根据按键按下的状态执行下一步的操作
这里要注意的是,如果是要控制蛇的方向,如果当前蛇头的位置朝右,那我们就不能向左走,同理,蛇头位置朝上,我们不能朝下走…
除此之外,还需要判断当前按键是不是退出、暂停、加速、减速等状态,如果对应了状态就做对用的事情,并且这些信息是需要不断刷新的,因此,将其放在循环中在合适不过,当游戏状态为RUNNING时,就一直循环:
do
{
SetPos(64, 3);
printf("Score: %05d", ps->_score);
SetPos(64, 4);
printf("Every food's score:%3d", 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 = EXIT_NORMAL;
break;
}
else if (KEY_PRESS(VK_SPACE))
{
TimeOut();
}
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 == RUNNING);//游戏运行时各个信息设置打印等
void SnakeMove(pSnake ps)
{
pSnakeNode pNext = (pSnakeNode)malloc(sizeof(pSnakeNode));//新节点接收
if (pNext == NULL)
{
perror("SnakeMove::alloc()");
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 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
{
//不是食物,为空地
Space(ps, pNext);
}
//是否撞墙
KillByWall(ps);
//是否咬到自己
KillBySelf(ps);
return;
}
void GameEnd(pSnake ps)
{
SetPos(20, 12);//在合适的位置打印提示信息
switch (ps->_Status)
{
case EXIT_NORMAL:
printf("Exit the game\n");
break;
case KILL_BY_WALL:
printf("Hit the wall, you are die\n");
break;
case KILL_BY_SELF:
printf("Kill yourself, gameover\n");
break;
}
SetPos(0, 27);//将进程退出信息放到最下面
pSnakeNode cur = ps->_psnake;
while (cur)//将蛇的节点全部销毁
{
pSnakeNode del = cur;
cur = cur->next;
free(del);
}
ps->_psnake = NULL;
system("pause");
return;
}
游戏结束后,我们可以安排是否再来一局,只需要在外层套上循环即可,输入一个提示信息,如果信息正确,则再来一局,否则退出。
void Test()
{
char ch = 0;
do
{
Snake snake = { 0 };//创建贪吃蛇对象
//开始游戏
GameStart(&snake);
//运行游戏
GameRun(&snake);
//游戏结束
GameEnd(&snake);
SetPos(20, 10);
printf("Another round?(Y/N)\n");
SetPos(40, 10);
ch = getchar();
getchar();
} while (ch == 'Y' || ch == 'y');
SetPos(0, 26);
return;
}
#pragma once
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<locale.h>
#include<stdlib.h>
#include<stdbool.h>
#include<Windows.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 DIERCTION
{
UP=1,
DOWN,
LEFT,
RIGHT
};
//蛇的状态
//正常,撞墙,装自己,正常退出
enum GAME_STATUS
{
OK,//正常
KILL_BY_WALL,//撞墙
KILL_BYSELF,//撞自己
END_NORMAL//正常退出
};
//蛇身的节点类型
typedef struct SnakeNode
{
//坐标
int x;
int y;
//指向下一个节点的指针
struct SnakeNode* next;
}SnakeNode,*pSnakeNode;
//贪吃蛇
typedef struct Snake
{
pSnakeNode _pSnake;//指向贪吃蛇蛇头的指针
pSnakeNode _pFood;//指向食物的指针
enum DIERCTION _dir;//方向
enum GAME_STATUS _status;//游戏状态
int _food_weight;//食物分数
int _score;//总成绩
int _sleep_time;//休息越短,速度越快
}Snake,*pSnake;
//设置光标
void SetPos(short x, short y);
//游戏初始化
void GameStart(pSnake ps);
//欢迎界面
void WelcomeToGame();
//创建蛇
void InitSnake(pSnake ps);
//创建食物
void CreateFood(pSnake ps);
//游戏运行逻辑
void GameRun(pSnake ps);
//蛇的移动
void SnakeMove(pSnake ps);
//检查下一个是否是食物
int NEXTFOOD(pSnakeNode pn, pSnake ps);
//是就吃掉它
void EATFOOD(pSnakeNode pn, pSnake ps);
//不是就往前走
void NOFOOD(pSnakeNode pn,pSnake ps);
//撞墙
void KillByWall(pSnake ps);
//撞自己
void KillBySelf(pSnake ps);
//游戏结束
void GameEnd(pSnake ps);
#include"snake.h"
void SetPos(short x, short y)
{
HANDLE houtput = NULL;
houtput = GetStdHandle(STD_OUTPUT_HANDLE);
COORD pos = { x,y };
SetConsoleCursorPosition(houtput, pos);
}
//创建食物
void CreateFood(pSnake ps)
{
int x = 0, y = 0;
again:
do
{
x = rand() % 53 + 2;//范围 2~54
y = rand() % 25 + 1;//范围1~65
} while (x % 2 != 0);
//不能和蛇身冲突
pSnakeNode cur = ps->_pSnake;
while (cur)
{
if (x == cur->x || y == cur->y)
{
goto again;
}
cur = cur->next;
}
pSnakeNode pFood = (pSnakeNode)malloc(sizeof(SnakeNode));
if (pFood == NULL)
{
perror("malloc failed!!!");
return;
}
pFood->x = x;
pFood->y = y;
pFood->next = NULL;
SetPos(x, y);
wprintf(L"%lc", FOOD);
ps->_pFood = pFood;
return;
}
void PrintfHelp()
{
SetPos(64, 10);
wprintf(L"%ls", L"不能穿墙,不能咬到自己");
SetPos(64, 11);
wprintf(L"%ls", L"使用 ↑.↓.←.→.分别控制蛇的移动");
SetPos(64, 12);
wprintf(L"%ls", L"按F3加速,F4减速");
SetPos(64, 13);
wprintf(L"%ls", L"按ESC退出游戏,按空格暂停游戏");
SetPos(64, 15);
wprintf(L"%ls", L"小章鱼制作");
}
int NEXTFOOD(pSnakeNode pn, pSnake ps)
{
return (ps->_pFood->x == pn->x && ps->_pFood->y == pn->y);
}
//有食物,吃掉它
void EATFOOD(pSnakeNode pn, pSnake ps)
{
ps->_pFood->next = ps->_pSnake;
ps->_pSnake = ps->_pFood;
free(pn);
pn = NULL;
pSnakeNode cur = ps->_pSnake;
//打印蛇
while (cur)
{
SetPos(cur->x, cur->y);
wprintf(L"%lc", BODY);
cur = cur->next;
}
ps->_score += ps->_food_weight;
//重新创建食物
CreateFood(ps);
}
//没有食物
void NOFOOD(pSnakeNode pn, pSnake ps)
{
//头插法
pn->next = ps->_pSnake;
ps->_pSnake = pn;
pSnakeNode cur = ps->_pSnake;
ps->_pSnake = pn;
cur = ps->_pSnake;
while (cur->next->next != NULL)
{
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 (cur->x == ps->_pSnake->x && cur->y == ps->_pSnake)
{
ps->_status = KILL_BYSELF;
break;
}
cur = cur->next;
}
}
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 SnakeMove(pSnake ps)
{
pSnakeNode pNextNode = (pSnakeNode)malloc(sizeof(SnakeNode));
if (pNextNode == NULL)
{
perror("failed");
return;
}
//往哪个方向走?
switch (ps->_dir)
{
case UP:
pNextNode->x = ps->_pSnake->x;
pNextNode->y = ps->_pSnake->y-1;
break;
case DOWN:
pNextNode->x = ps->_pSnake->x;
pNextNode->y = ps->_pSnake->y+1;
break;
case LEFT:
pNextNode->x = ps->_pSnake->x - 2;
pNextNode->y = ps->_pSnake->y;
break;
case RIGHT:
pNextNode->x = ps->_pSnake->x + 2;
pNextNode->y = ps->_pSnake->y;
break;
}
if (NEXTFOOD(pNextNode,ps))
{
EATFOOD(pNextNode, ps);
}
else
{
NOFOOD(pNextNode,ps);
}
KillByWall(ps);
KillBySelf(ps);
}
//游戏暂停
void Pause()
{
while (1)
{
Sleep(100);
if (KEY_PRESS(VK_SPACE))
{
break;
}
}
}
//游戏运行逻辑
void GameRun(pSnake ps)
{
//打印帮助信息
PrintfHelp();
do
{
//打印总分数和食物的分值
SetPos(64, 8);
printf("总分数:%d\n", ps->_score);
SetPos(64, 9);
printf("食物分数:%2d\n", ps->_food_weight);
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_RIGHT) && ps->_dir != LEFT)
{
ps->_dir = RIGHT;
}
else if (KEY_PRESS(VK_LEFT) && ps->_dir != RIGHT)
{
ps->_dir = LEFT;
}
else if (KEY_PRESS(VK_SPACE))
{
//暂停
Pause();
}
else if (KEY_PRESS(VK_ESCAPE))
{
//退出
ps->_status = END_NORMAL;
}
else if (KEY_PRESS(VK_F3))
{
//加速
if (ps->_sleep_time > 80)
{
ps->_sleep_time -= 30;
ps->_food_weight += 2;
}
}
else if (KEY_PRESS(VK_F4))
{
//减速
if (ps->_food_weight > 2)
{
ps->_sleep_time += 30;
ps->_food_weight -= 2;
}
}
//蛇的移动
SnakeMove(ps);
Sleep(ps->_sleep_time);
} while (ps->_status == OK);
}
//初始化贪吃蛇
void InitSnake(pSnake ps)
{
pSnakeNode cur = NULL;
int i = 0;
for (i = 0; i < 5; i++)
{
cur = (pSnakeNode)malloc(sizeof(SnakeNode));
if (cur == NULL)
{
perror("malloc failed!!!");
return;
}
cur->next = NULL;
cur->x = POS_X + 2 * i;
cur->y = POS_Y;
//头插法
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->_dir = RIGHT;
ps->_score = 0;
ps->_food_weight = 10;
ps->_sleep_time = 200;
ps->_status = OK;
}
void CreateMap()
{
//上
for (int i = 0;i < 29; i++)
{
wprintf(L"%lc", WALL);
}
//下
SetPos(0, 26);
for (int i = 0; i < 29; i++)
{
wprintf(L"%lc", WALL);
}
//左
for (int i = 1; i <= 25; i++)
{
SetPos(0, i);
wprintf(L"%lc", WALL);
}
//右
for (int i = 1; i <= 25; i++)
{
SetPos(56, i);
wprintf(L"%lc", WALL);
}
}
void WelcomeToGame()
{
SetPos(35, 14);
printf("欢迎来到贪吃蛇小游戏\n");
SetPos(37, 20);
system("pause");
system("cls");
SetPos(25, 13);
wprintf(L"使用 ↑.↓.←.→.分别控制蛇的移动, F3是加速,F4是减速\n");
SetPos(37, 20);
system("pause");
system("cls");
}
void GameEnd(pSnake ps)
{
SetPos(24, 12);
switch (ps->_status)
{
case END_NORMAL:
printf("您主动结束游戏\n");
break;
case KILL_BY_WALL:
printf("你撞墙死了\n");
break;
case KILL_BYSELF:
printf("你撞到自己死了\n");
break;
default :
printf("无效输入\n");
}
//释放链表
pSnakeNode cur = ps->_pSnake;
while (cur)
{
pSnakeNode del = cur;
cur = cur->next;
free(del);
}
cur = NULL;
}
void GameStart(pSnake ps)
{
//0.先设置窗口大小,然后光标隐藏
system("mode con cols=100 lines=30");
system("title 贪吃蛇之xzy");
HANDLE houtput = GetStdHandle(STD_OUTPUT_HANDLE);
//隐藏光标操作
CONSOLE_CURSOR_INFO cursor_info = { 0 };
GetConsoleCursorInfo(houtput, &cursor_info);
cursor_info.bVisible = false;
SetConsoleCursorInfo(houtput, &cursor_info);
//2.功能介绍
WelcomeToGame();
//3.绘制地图
CreateMap();
//4.创建蛇
InitSnake(ps);
//5.创建食物
CreateFood(ps);
}
#include"snake.h"
//游戏逻辑测试
void test()
{
int ch = 0;
do
{
system("cls");
//创建一个贪吃蛇
Snake snake = { 0 };
//游戏开始
GameStart(&snake);
//运行游戏
GameRun(&snake);
//游戏结束
GameEnd(&snake);
SetPos(20, 15);
printf("再来一局吗?(Y/N):");
ch = getchar();
getchar();
SetPos(0, 27);
} while (ch == 'Y' || ch == 'y');
}
int main()
{
//设置本地环境
setlocale(LC_ALL, "");
srand((unsigned int)time(NULL));
test();
return 0;
}
本篇博客我们介绍了关于贪吃蛇小游戏的创作,希望对大家有所帮助!