目录
void CreatSnake(pSnake ps);//创造蛇体
void CreatFood(pSnake ps);//食物创建
void SnakeMove(pSnake ps);//蛇体移动
int nextisfood(NeedSlink nextword,pSnake ps);//判断下一个是不是食物
void EatFood(NeedSlink nextword,pSnake ps);//吃掉食物
void NoFood(NeedSlink nextword, pSnake ps);//蛇体直接移动
void Killbywall(pSnake ps);//判断是否撞墙编辑
void Killbyself(pSnake ps);//判断是否撞到自己
前言
贪吃蛇游戏的实现需要的理论基础最重要的结构体、指针、链表,然后其他的就跟着作者的思路一起走下去吧。
一、结构体与枚举
这个结构体是用来创作蛇的身体的,在vs的实现中我们的windows控制台主机通过x,y以二维坐标的方式来定位我们蛇的位置然后在这个位置打印我们的蛇体即可。
这个结构体是为了方便我们后面对于蛇的一些参数的处理,以免后面创建多个变量。我们把蛇的参数通过结构体都列出来变量这样更方便后面的初始化和使用。
用于后面的蛇运动方向和游戏进行的状态。
二、游戏开始的初始化
首先我们要更改windows控制台主机的名字和控制台的界面大小就需要调用我们的system函数它的头文件是<stdlib.h>,使用的时候别忘了包含这个头文件,然后就是获得句柄,这里的句柄是指获得可以控制windows控制台主机的输入入口,这样我们才能获得控制台的光标,只有当把光标移动到我们设置的x和y坐标上我们才能对那个坐标进行打印蛇体。
后面有一个SetPos函数就是为了让光标在我需要的坐标上去,后面我们需要的时候直接调用,在GameStart函数中调用一次是为了让隐藏光标,SetPos函数是为了设置光标位置。
void WelcometoGame();//欢迎界面
这里的SetPos函数就是前文介绍的定位光标位置的函数,定位之后打印我们需要的界面就行了。别忘了调用system的pause和cls,pause是暂停按任意键就可以结束暂停这样的目的是防止游戏程序直接跑结束了,cls是清屏将我们打印的所有东西清屏这样的目的是为了让我们进入下一个界面的时候前面的东西都清理掉了。
void CreatMap();//地图创造
这里的地图就是指的四周的墙体,将这四周的墙体打印出来,需要参考你前面通过system函数设置的windows控制台主机的界面大小。wprintf是用来打印宽字符,即是一个字符占两个做个坐标,wall在前面已经用define定义了。
void CreatSnake(pSnake ps);//创造蛇体
蛇体主要是由链表创建,链表是由结构体实现的,前面我们在创建结构体的时候就创建了结构体指针NeedSlink,创建指针的目的是为了后面修改数据,然后创建5个链表意味着我们初始的蛇身体有五节,malloc是为了向内存中申请一块空间出来,后面就在以这五个链表的x和y来打印蛇的身体,body在前面已经定义了,统一定义的目的是为了后面修改的时候只需要修改一次即可。然后进行蛇参数的初始化。
void CreatFood(pSnake ps);//食物创建
因为我们的食物是随机生成的,所以需要调用我们的rand函数来生成随机数,同时生成的随机数要有范围限制,不能和蛇的身体重合同时不能和墙重合。
二、游戏的运行
首先在墙的右边打印分数,然后这里需要获取关键的键盘输入信息来控制贪吃蛇的走向,用KEY_PRESS在前面已经用宏定义了• 上:VK_UP • 下:VK_DOWN • 左:VK_LEFT • 右:VK_RIGHT • 空格:VK_SPACE • ESC:VK_ESCAPE • F3:VK_F3 • F4:VK_F4
当蛇的方向是向下的时候不能直接键盘输入向上的指令,当然其他方向也是一样的道理。
void HelpInfor();//提示信息打印
void pause();//游戏暂停
void SnakeMove(pSnake ps);//蛇体移动
这里要再申请一个蛇的空间来明确下一步蛇的运动方向,在前面的函数中我们从键盘中获取方向之后存进dire中然后判断不同的方向后面申请空间中的x和y不同,同时要判断下一个是不是食物如果是食物直接吃掉食物,不是食物的话就向dire的方向走一步,最后判断游戏的是否结束。
int nextisfood(NeedSlink nextword,pSnake ps);//判断下一个是不是食物
是食物的话返回1不是食物返回0。
void EatFood(NeedSlink nextword,pSnake ps);//吃掉食物
当返回为1的时候就吃掉食物,先释放我们前面为蛇下一个运动的位置申请的空间因为我们食物已经存在空间了,然后吃掉食物,把这个食物链表和我们蛇体的链表链接起来,之后再创建一个新的食物用CreatFood函数。
void NoFood(NeedSlink nextword, pSnake ps);//蛇体直接移动
用前面开辟的下一个位置的空间成为蛇头,然后释放掉蛇尾。
void Killbywall(pSnake ps);//判断是否撞墙

判断是否撞墙来改变游戏状态,判断游戏是否结束。
void Killbyself(pSnake ps);//判断是否撞到自己
撞到自己也会改变游戏状态导致游戏结束。
三、游戏结束以及主函数
打印游戏结束的界面,同时释放掉前面为蛇体释放的空间。
主函数和test函数主要是为了调用前面我所写的三个大部分以及多次进行游戏的循环,这里的本地化是为了让我们的宽字符能够使用,srand函数是为了后面x和y生成随机数调用rand函数做铺垫。
四、总结
由于作者水平有限,我尽可能讲清楚,相关函数有不理解的地方还请大家发挥自习能力,查阅相关资料进行学习,同时希望我的文章能够对大家有所帮助,如有错误,欢迎指出。谢谢大家的阅读!
五、源码
NOODSNAKE.C
#pragma once
#include "SNAKE.h"
int main()
{
setlocale(LC_ALL, "");//本地化
NeedSlink psnake = NULL;//创建蛇
srand((unsigned int)time(NULL));
test();
return 0;
}
void test()
{
int ch = 0;
do
{
system("cls");
//1.初始化蛇
Snake snake = { 0 };// 初始化蛇(蛇的头,蛇的食物,速度,方向,状态,食物得分,总得分)
//2.初始游戏界面
GameStart(&snake);
//2.进行游戏
GameRun(&snake);
//3.结束游戏
GameEnd(&snake);
SetPos(20, 15);
printf("想再来一局吗 ? (Y/N)");
ch = getchar();
getchar();
} while (ch == 'Y' || ch == 'y');
SetPos(0, 27);
}
SNAKE.H
#define _CRT_SECURE_NO_WARNINGS 1
#include<locale.h>
#include<Windows.h>
#include<stdbool.h>
#include<stdlib.h>
#include<stdio.h>
#include<malloc.h>
#include<time.h>
#define fix_x 24
#define fix_y 5
#define KEY_PRESS(VK) ((GetAsyncKeyState(VK)&0x1) ? 1 : 0)
#define wall L'□'
#define body L'●'
#define food L'◆'
enum DIRECTION
{
UP = 1,
DOWN,
LEFT,
RIGHT
};
enum GAME_STATUS
{
OK,
KILL_BY_WALL,
KILL_BY_YOURSELF,
END
};
typedef struct NoodSlink
{
int x;
int y;
struct NoodSlink* Next;
}NoodSlink, * NeedSlink;
typedef struct Snake
{
NeedSlink _psnake;//指向蛇头
NeedSlink _pfood;//指向食物
enum DIRECTION _dire; //蛇的方向
enum GAME_STATUS _status;//蛇的状态
int _food_weight;//食物的单分
int _score;//总分
int _speed;//速度
}Snake, * pSnake;
void GameStart(pSnake ps);
void WelcometoGame();
void SetPos(short x, short y);
void CreatMap();
void CreatSnake(pSnake ps);
void CreatFood(pSnake ps);
void CameRun(pSnake ps);
void HelpInfor();
void SnakeMove(pSnake ps);
int nextisfood(NeedSlink nextword,pSnake ps);
void EatFood(NeedSlink nextword,pSnake ps);
void pause();
void NoFood(NeedSlink nextword, pSnake ps);
void Killbywall(pSnake ps);
void Killbyself(pSnake ps);
void GameEnd(pSnake ps);
void test();
SNAKE.C
#define _CRT_SECURE_NO_WARNINGS 1
#include "SNAKE.h"
void GameStart(pSnake ps)
{
//改主界面名字,大小
system("mode con cols=100 lines=30 ");
system("title 贪吃蛇");
//获得句柄,隐藏光标
HANDLE houtput = NULL;
//获取标准输出的句柄(⽤来标识不同设备的数值)
houtput = GetStdHandle(STD_OUTPUT_HANDLE);
CONSOLE_CURSOR_INFO CursorInfo;
GetConsoleCursorInfo(houtput, &CursorInfo);//获取控制台光标信息
CursorInfo.bVisible = false; //隐藏控制台光标
SetConsoleCursorInfo(houtput, &CursorInfo);//设置控制台光标状态
WelcometoGame();//欢迎界面
CreatMap();//地图绘制
CreatSnake(ps);//蛇数据创建
CreatFood(ps);//食物创建
}
void CreatFood(pSnake ps)
{
int x = 0;
int y = 0;
again:
do
{
x = rand() % 53 + 2;
y = rand() % 25 + 1;
} while (x % 2 != 0);//x是2的倍数 x:2-54 y:1-25
NeedSlink cur = ps->_psnake;
while (cur)
{
if (x == cur->x && y == cur->y)
{
goto again;
}
cur = cur->Next;
}
NeedSlink pFood = (NeedSlink)malloc(sizeof(NoodSlink));
if (pFood == NULL)
{
perror("CreatFood()::malloc()");
return;
}
pFood->x = x;
pFood->y = y;
pFood ->Next = NULL;
ps->_pfood = pFood;
SetPos(x, y);
wprintf(L"%lc", food);
}
void CreatSnake(pSnake ps)
{
int i = 0;
NeedSlink cur = NULL;
for (i = 0; i < 5; i++)//创建5个链表
{
cur = (NeedSlink)malloc(sizeof(NoodSlink));
if (cur==NULL)
{
perror("CreatSnake::malloc");
return 1;
}
cur->Next = NULL;
cur->x = fix_x + (i * 2);
cur->y = fix_y;
if (ps->_psnake==NULL)
{
ps->_psnake = cur;
}
else
{
cur->Next = ps->_psnake;
ps->_psnake = cur;
}
}
while (cur)
{
SetPos(cur->x, cur->y);
wprintf(L"%lc", body);
cur = cur->Next;
}
ps->_dire = RIGHT;//右走
ps->_score = 0;//初始得分
ps->_food_weight = 10;//食物得分
ps->_speed = 200;//速度参数
ps->_status = OK;//蛇的状态
}
void WelcometoGame()
{
SetPos(34, 10);
printf("欢 迎 来 到 贪 吃 蛇 游 戏");
SetPos(38, 20);
system("pause");
system("cls");
SetPos(30, 10);
printf("用↑ ↓ ← →来控制蛇的方向,F3加速,F4减速");
SetPos(40, 12);
printf("注意加速分数更高");
SetPos(40, 20);
system("pause");
system("cls");
}
void CreatMap()
{
int i = 0;
for (i = 0; i < 58; i += 2)
{
wprintf(L"%lc", wall);
}
SetPos(0, 26);
for (i = 0; i < 58; i += 2)
{
wprintf(L"%lc", wall);
}
for (i = 1; i < 26; i++)
{
SetPos(0, i);
wprintf(L"%lc", wall);
}
for (i = 1; i < 26; i++)
{
SetPos(56, i);
wprintf(L"%lc", wall);
}
}
void SetPos(short x, short y)
{
COORD pos = { x, y };//设置光标的坐标
HANDLE houtput = NULL;
//获取标准输出的句柄(⽤来标识不同设备的数值)
houtput = GetStdHandle(STD_OUTPUT_HANDLE);
//设置标准输出上光标的位置为pos
SetConsoleCursorPosition(houtput, pos);
}
void GameRun(pSnake ps)
{
HelpInfor();
do
{
SetPos(64, 4);
printf("总分数:%d", ps->_score);
SetPos(64, 8);
printf("当前食物的分数:%2d", ps->_food_weight);
if (KEY_PRESS(VK_UP) && ps->_dire != DOWN)
{
ps->_dire = UP;
}
else if (KEY_PRESS(VK_DOWN) && ps->_dire != UP)
{
ps->_dire = DOWN;
}
else if (KEY_PRESS(VK_LEFT) && ps->_dire != RIGHT)
{
ps->_dire = LEFT;
}
else if (KEY_PRESS(VK_RIGHT) && ps->_dire != LEFT)
{
ps->_dire = RIGHT;
}
else if (KEY_PRESS(VK_SPACE))
{
pause();
}
else if (KEY_PRESS(VK_ESCAPE))
{
ps->_status = END;
break;
}
else if (KEY_PRESS(VK_F3))
{
if (ps->_speed > 80)
{
ps->_speed -= 30;
ps->_food_weight += 2;
}
}
else if (KEY_PRESS(VK_F4))
{
if (ps->_food_weight > 2)
{
ps->_speed += 30;
ps->_food_weight -= 2;
}
}
SnakeMove(ps);
Sleep (ps->_speed);
}while (ps->_status == OK);
}
void HelpInfor()
{
SetPos(60, 13);
printf("不能撞墙,不能咬到自己");
SetPos(60, 14);
printf("用↑ ↓ ← →来控制蛇的方向");
SetPos(60, 15);
printf("F3加速,F4减速");
SetPos(60, 16);
printf("ESC 退出游戏,空格 暂停游戏");
SetPos(60, 22);
printf("小叶学习中———");
}
void pause()
{
while (1)
{
Sleep(200);
if (KEY_PRESS(VK_SPACE))
{
break;
}
}
}
void SnakeMove(pSnake ps)
{
NeedSlink nextword = (NeedSlink)malloc(sizeof(NoodSlink));
if (nextword == NULL)
{
perror("nextword:");
return;
}
switch (ps->_dire)
{
case UP:
nextword->x = ps->_psnake->x;
nextword->y = ps->_psnake->y - 1;
break;
case DOWN:
nextword->x = ps->_psnake->x;
nextword->y = ps->_psnake->y + 1;
break;
case LEFT:
nextword->x = ps->_psnake->x - 2;
nextword->y = ps->_psnake->y;
break;
case RIGHT:
nextword->x = ps->_psnake->x + 2;
nextword->y = ps->_psnake->y ;
break;
}
//检测下一个坐标是不是食物
if (nextisfood(nextword,ps))
{
EatFood(nextword, ps);
}
else
{
NoFood(nextword, ps);
}
//检查蛇是否撞墙 撞到自己
Killbywall(ps);
Killbyself(ps);
}
int nextisfood(NeedSlink nextword, pSnake ps)
{
if ((ps->_pfood->x == nextword->x) && (ps->_pfood->y == nextword->y))
return 1;
else
return 0;
}
void EatFood(NeedSlink nextword, pSnake ps)
{
ps->_pfood->Next = ps->_psnake;
ps->_psnake = ps->_pfood;
free(nextword);
nextword = NULL;
NeedSlink cur = ps->_psnake;
while (cur)
{
SetPos(cur->x, cur->y);
wprintf(L"%lc", body);
cur = cur->Next;
}
ps->_score += ps->_food_weight;
CreatFood(ps);
}
void NoFood(NeedSlink nextword, pSnake ps)
{
nextword->Next = ps->_psnake;
ps->_psnake = nextword;
NeedSlink 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 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 Killbyself(pSnake ps)
{
NeedSlink cur = ps->_psnake->Next;
while (cur)
{
if (cur->x == ps->_psnake->x && cur->y == ps->_psnake->y)
{
ps->_status = KILL_BY_YOURSELF;
break;
}
cur = cur->Next;
}
}
void GameEnd(pSnake ps)
{
SetPos(24, 12);
switch (ps->_status)
{
case END:
printf("您主动结束游戏了\n");
break;
case KILL_BY_WALL:
printf("很遗憾,您撞墙了\n");
break;
case KILL_BY_YOURSELF:
printf("您吃到自己辣\n");
break;
}
SetPos(0, 27);
NeedSlink cur = ps->_psnake;
while (cur)
{
NeedSlink pn = cur;
cur = cur->Next;
free(pn);
}
}