一.游戏的形成
1.创建地图
2.初始化贪吃蛇
3.贪吃蛇的操作
4.善后操作
二.游戏开始之前的准备
1.创建地图
(1).游戏窗口的大小
我这里是需要将代码区域变成行30列100的大小,这里的话需要使用代码来进行创建。
int main()
{
setlocale(LC_ALL, " ");
system("mode con cols=100 lines=30");//设置控制台窗口的大小
system("title 贪吃蛇");//设置控制台窗口的名字
return 0;
}
(2).游戏的光标隐藏
这里我们的光标是需要关闭的,因为这是游戏进入之前的提示。这里就会使用到Win32 API的函数,这里要用到GetStdHandle 函数(获得特定的标准设备的句柄),GetConsoleCursorInfo函数(检索有关指定控制台屏幕缓冲区的光标⼤⼩和可⻅性的信息),CONSOLE_CURSOR_INFO结构体(这个结构体,包含有关控制台光标的信息),SetConsoleCursorInfo函数(设置指定控制台屏幕缓冲区的光标的⼤⼩和可⻅性。)和SetConsoleCursorPosition函数(设置指定控制台屏幕缓冲区中的光标位置,我们将想要设置的坐标信息放在COORD类型的pos中,调 ⽤SetConsoleCursorPosition函数将光标位置设置到指定的位置。)。
这些函数的信息都可以在这个网址上获取,有兴趣的读者可以去看看。
GetStdHandle 函数 - Windows Console | Microsoft Learn(这里是其中一个函数的网址,其他函数也可以在这上面找到哦)。
这里直接上代码写光标隐藏的函数
void GameStart()
{
HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);//获得该设备的句柄
CONSOLE_CURSOR_INFO Info;//初始化光标信息的结构体
GetConsoleCursorInfo(handle, &Info);//检索有关指定控制台屏幕缓冲区的光标
Info.bVisible = false;//隐藏光标
SetConsoleCursorInfo(handle, &Info);//设置指定控制台屏幕缓冲区的光标
}
(3).打印欢迎界面
界面的打印我们还是用一个函数进行封装,这里面也需要用的一些Win32 API的函数。这里我们又要用到SetConsoleCursorPosition(设置指定控制台屏幕缓冲区中的光标位置)和COORD这个结构体,这里的话我们直接就写代码来给大家展示.
void SetPos(int x, int y)
{
COORD pos = { x,y };
HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleCursorPosition(handle, pos);
}
//这是一个在光标中寻找位置的函数,是我们自己分装的,后续会继续用到,之后我就不在写出来了哦。
void WelcomeToGame()
{
SetPos(38, 12);
printf("欢迎来到贪吃蛇游戏\n");
SetPos(40, 25);
system("pause");
system("cls");
SetPos(27, 12);
printf("用↑.↓.←.→ 分别控制蛇的移动, F3为加速,F4为减速\n");
SetPos(40, 25);
system("pause");
}
//这就是一个简单的欢迎界面,pause只用来暂停一下滴,让界面更美观,
(4).创建游戏的地图
贪吃蛇的游戏地图需要墙体,和游戏提示,所以这里我们也是直接上代码,在代码里面给大家做出解释。
#define WALL L'□'
void CreateMap()
{
int i = 0;
SetPos(0, 0);
for (int i = 0; i < 58; i+=2)
{
wprintf(L"%lc", WALL);
}
SetPos(0, 26);
for (int i = 0; i < 58; i += 2)
{
wprintf(L"%lc", WALL);
}
for (int i = 1; i < 26; i++)
{
SetPos(0, i);
wprintf(L"%lc", WALL);
}
for (int i = 1; i < 26; i++)
{
SetPos(56, i);
wprintf(L"%lc", WALL);
}
SetPos(65, 15);
printf("用↑.↓.←.→ 分别控制蛇的移动\n");
SetPos(65, 16);
printf("F3为加速,F4为减速");
}
2.蛇的初始化
(1)蛇的结构
蛇是需要两个结构体的,因为我们知道贪吃蛇是需要使用单链表来组成一条蛇的,那么我们就需要创建两个结构体,一个结构体就是一个蛇身的结构体,还有一个结构体就是来维护整条贪吃蛇的。这里我们还是直接上代码,里面的代码也会在旁边进行注释。
enum STATUS
{
OK=1,
ESC,
KILL_BY_WALL,
KILL_BY_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 Food;//食物
int FoodWeight;//一个食物的得分
int score;//总分
int sleeptime;//休眠的时间
enum DIRECTION dir;// 蛇的方向
enum STATUS status;//蛇的状态
}Snake,*pSnake;
(2)创建蛇和食物
void CreateFood(pSnake ps)
{
pSnakeNode pFood = (pSnakeNode)malloc(sizeof(SnakeNode));
if (pFood == NULL)
{
perror("CreateFood():malloc():");
return;
}
int x = 0;
int y = 0;
do
{
x = rand() % 53 + 2;
y = rand() % 25 + 1;
} while (x % 2 != 0);//产生随机的食物
pSnakeNode cur = ps->pSnake;
while (cur)
{
if (cur->x == x && cur->y == y)
{
do
{
x = rand() % 53 + 2;
y = rand() % 25 + 1;
} while (x % 2 != 0);
}
cur = cur->next;
}
pFood->x = x;
pFood->y = y;
SetPos(pFood->x, pFood->y);//生成食物
wprintf(L"%lc", FOOD);
ps->Food = pFood;
}
void InitSnake(pSnake ps)
{
//蛇身有5个节点
pSnakeNode cur = NULL;
for (int i = 0; i < 5; i++)
{
cur = (pSnakeNode)malloc(sizeof(SnakeNode));
if (cur == NULL)
{
perror("InitSnake():malloc:");
return;
}
//初始化蛇的位置
cur->x = 24 + 2 * i;
cur->y = 5;
cur->next = NULL;
if (ps->pSnake == NULL)
ps->pSnake = cur;
else
{
//头插法
cur->next = ps->pSnake;
ps->pSnake = cur;
}
}
//打印蛇
cur = ps->pSnake;
for (int i = 0; i < 5; i++)
{
SetPos(cur->x, cur->y);
wprintf(L"%lc", BODY);
cur = cur->next;
}
//食物的创建
CreateFood(ps);
}
三.游戏的运作
1.打印帮助信息
void PrintHelp()
{
SetPos(65, 14);
printf("不能撞墙,不能咬到自己");
SetPos(65, 17);
printf("ESC:退出游戏,SPACE:暂停游戏");
getchar();
}
2.打印已获得的分数,每个食物的分数和按键情况
这里按键也要用到API的一个按键函数GetAsyncKeyState。
void PrintHelp()
{
SetPos(65, 14);
printf("不能撞墙,不能咬到自己");
SetPos(65, 17);
printf("ESC:退出游戏,SPACE:暂停游戏");
}
void pause()
{
while (1)
{
Sleep(100);
if (KEY_PRESS(VK_SPACE))
{
break;
}
}
}
void EatFood(pSnakeNode pNext,pSnake ps)
{
//头插
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;
}
ps->score += ps->FoodWeight;
free(ps->Food);
ps->Food = NULL;
CreateFood(ps);
}
void NotEatFood(pSnakeNode pNext, pSnake ps)
{
//头插
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;
}
void Kill_By_Wall(pSnake ps)
{
if (ps->pSnake->x == 0 ||
ps->pSnake->x == 56 ||
ps->pSnake->y == 0 ||
ps->pSnake->y == 25)
{
ps->status = KILL_BY_WALL;
}
}
void Kill_By_Self(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));
if (pNext == NULL)
{
perror("SnakeMove():malloc:");
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 (ps->Food->x == pNext->x && ps->Food->y == pNext->y)
{
EatFood(pNext,ps);
}
else
{
NotEatFood(pNext, ps);
}
//撞墙会死
Kill_By_Wall(ps);
//撞到自己会死
Kill_By_Self(ps);
}
void GameRun(pSnake ps)
{
//打印帮助信息
PrintHelp();
//打印已获得的分数和每个食物的分数
do
{
SetPos(65, 11);
printf("总分:%d", ps->score);
SetPos(65, 12);
printf("每个食物的分数:%2d", 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 = ESC;
break;
}
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)
{
pSnakeNode cur = ps->pSnake;
//游戏结束的方式
switch (ps->status)
{
case ESC:
SetPos(16, 12);
printf("游戏结束,正常退出\n");
break;
case KILL_BY_WALL:
SetPos(16, 12);
printf("很遗憾,撞墙了,游戏结束\n");
break;
case KILL_BY_SELF:
SetPos(16, 12);
printf("很遗憾,撞到自己了,游戏结束\n");
break;
}
//释放蛇
while (cur)
{
pSnakeNode del = cur;
cur = cur->next;
free(del);
}
}
这是收尾的工作,下面是循环的工作:
void test()
{
srand((unsigned int)time(NULL));
int ch = 0;
do
{
Snake snake = { 0 };
GameStart(&snake);//游戏开始前的准备
GameRun(&snake);//游戏的运作
GameEnd(&snake);//游戏的收尾
SetPos(16, 13);
printf("是否再玩一局:(Y/N)");
ch = getchar();
getchar();
} while (ch == 'Y' || ch == 'y');
SetPos(0, 26);
}
五.总结
以上就是贪吃蛇小游戏的简单编程,如果哪里有问题的话,也希望大家给我指出问题,谢谢大家的观看.