文章目录
- 前言
- 一、贪吃蛇游戏代码
- test.c文件
- Snake.h文件
- Snake.c文件
- 二、相关函数的介绍
- 1.COORD
- 2.Win32 API的介绍
- 3.GetStdHandle
- 4.GetConsoleCursorInfo
- 5.CONSOLE_CURSOR_INFO
- 5.SetConsoleCursorInf
- 6.SetConsoleCursorPosition
- 7.GetAsyncKeyState
- 总结
前言
哈喽各位好呀。今天小编来给大家分享一个我们小时候都玩过的小游戏,叫做贪吃蛇。如果大家想玩的话,代码我就放下面啦。
一、贪吃蛇游戏代码
test.c文件
#include"Snake.h"
void test()//完成游戏的测试逻辑
{
int ch;
do
{
system("cls");//清理界面
//创建贪吃蛇
Snake snake = { 0 };
//初始化游戏
GameStart(&snake);
//运行游戏
GameRun(&snake);
//结束游戏---善后工作
GameEnd(&snake);
SetPut(22, 14);
printf("您是否需要再来一局? Y/N");
ch = getchar();
getchar();
} while (ch == 'y' || ch == 'Y');
SetPut(0, 27);
}
int main()
{
//设置适应本地环境
setlocale(LC_ALL, "");
srand((unsigned int)time(NULL));
test();
}
Snake.h文件
#include<locale.h>
#include<windows.h>
#include<stdbool.h>
#include<stdlib.h>
#include<stdio.h>
#include<time.h>
#define POS_X 24
#define POS_Y 5
#define WALL L'墙'
#define SNAKE L'蛇'
#define KEY_PRESS(VK) ((GetAsyncKeyState(VK)&0x1) ? 1 : 0)
enum DIRECTION//蛇的方向
{
UP=1,
DOWN,
LEFT,
RIGHT
};
enum GAME_STATUS//蛇的状态
{
OK,
KILL_BY_WALL,
KILL_BY_SELF,
END_NOMAL
};
//蛇身的节点
typedef struct SnakeNode
{
//坐标
int x;
int y;
//指向下一个节点的指针
struct SnakeNode* next;
}SnakeNode ,*pSnakeNode;
typedef struct Snake
{
pSnakeNode _pSanke;//指向蛇头的指针
pSnakeNode _pFood;//指向食物的指针
enum DIRECTION _dir;//蛇的方向
enum GAME_STATUS _stutus;//蛇的状态
int _food_weight;//一个食物的分数
int _score;//总成绩
int _sleep_time;//蛇的速度
}Snake,*pSnake;
void SetPut(short x, short y); //获取光标出现的位置
void GameStart(pSnake ps);//开始游戏
void WelcomeGame();//打印欢迎界面
void GreatMap();//打印地图
void InitGame(pSnake ps);//初始化蛇的相关信息
void CreateFood(pSnake ps);//食物节点
void GameRun(pSnake ps);//游戏运行
void PrintHelpInfo();//打印界面右侧信息
bool NextIsFood(pSnakeNode pn, pSnake ps);//判断转向的下一个位置是否有食物
void EatFood(pSnakeNode pn, pSnake ps);//有食物的情况
void NoFood(pSnakeNode pn, pSnake ps);//没有食物的情况
void KillWall(pSnake ps);//撞墙的情况
void KillSelf(pSnake ps);//撞自己的情况
void SnakeMove(pSnake ps);//移动蛇身一步
void GameEnd(pSnake ps);//游戏结束
Snake.c文件
#include"Snake.h"
void SetPut(short x, short y)//光标出现的位置
{
COORD pos = { x,y };
HANDLE hOutput = NULL;
hOutput = GetStdHandle(STD_OUTPUT_HANDLE);//句柄
//CONSOLE_CURSOR_INFO CursorInfo;
//GetConsoleCursorInfo(hOutput, &CursorInfo); //获取控制台的光标信息
//CursorInfo.bVisible = false;//隐藏光标
//SetConsoleCursorInfo(hOutput, &CursorInfo);//设置光标的状态
SetConsoleCursorPosition(hOutput, pos);//光标的位置位pos
}
void WelcomeGame()//欢迎界面的打印
{
SetPut(40,14);
printf("欢迎来到贪吃蛇小游戏\n");
SetPut(42, 20);
system("pause");
system("cls");
SetPut(25, 15);
wprintf(L"用↑..↓..←..→控制蛇的方向,F3加速,F4减速\n");
SetPut(25, 20);
wprintf(L"加速可以得到更高的分数\n");
SetPut(40, 20);
system("pause");
system("cls");
}
void GreatMap()//打印地图
{
for (int i = 0; i < 29; i++)
{
wprintf(L"%lc", WALL);
}
SetPut(0, 26);
for (int i = 0; i < 29; i++)
{
wprintf(L"%lc", WALL);
}
for (int i = 1; i <= 25; i++)
{
SetPut(0,i);
wprintf(L"%lc", WALL);
}
for (int i = 1; i <= 25; i++)
{
SetPut(56, i);
wprintf(L"%lc", WALL);
}
}
void InitGame(pSnake ps)//初始化蛇
{
pSnakeNode cur = NULL;
for (int i = 0; i < 5; i++)//申请5个节点
{
cur =(pSnakeNode)malloc(sizeof(SnakeNode));
if (cur == NULL)
{
perror("Snake::malloc fail\n");
exit(1);
}
cur->next = NULL;
cur->x = POS_X + 2 * i;
cur->y = POS_Y;
//头插连接
if (ps->_pSanke == NULL)
{
ps->_pSanke=cur;
}
else
{
cur->next = ps->_pSanke;
ps->_pSanke = cur;
}
}
cur = ps->_pSanke;
while (cur)//打印蛇身
{
SetPut(cur->x, cur->y);
wprintf(L"%lc", SNAKE);
cur = cur->next;
}
ps->_dir = RIGHT;
ps->_food_weight = 10;
ps->_score = 0;
ps->_stutus =OK;
ps->_sleep_time = 200;
}
void CreateFood(pSnake ps)//食物节点的创建
{
int x = 0;
int y = 0;
gain:
do
{
x = rand() % 53 + 2;
y = rand() % 25 + 1;
} while (x % 2 != 0);//食物的位置必须是2的倍数
pSnakeNode cur = ps->_pSanke ; //食物节点不能和蛇身重合
while (cur)
{
if (x == cur->x && y == cur->y)
goto gain;
cur = cur->next;
}
//创建食物节点
pSnakeNode pFood = (pSnakeNode)malloc(sizeof(SnakeNode));
if (pFood == NULL)
{
perror("malloc fail\n");
return;
}
pFood->x = x;
pFood->y = y;
pFood->next = NULL;
SetPut(pFood->x, pFood->y);
wprintf(L"%lc", L'食');
ps->_pFood = pFood;
}
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);//设置光标的状态
//打印欢迎界面,功能打印
WelcomeGame();
//地图的绘制
GreatMap();
//初始化蛇神
InitGame(ps);
//创建食物
CreateFood(ps);
}
void PrintHelpInfo()
{
//打印提⽰信息
SetPut(64, 15);
printf("不能穿墙,不能咬到⾃⼰\n");
SetPut(64, 16);
printf("⽤↑.↓.←.→分别控制蛇的移动.");
SetPut(64, 17);
printf("F3 为加速,F4 为减速\n");
SetPut(64, 18);
printf("ESC :退出游戏.space:暂停游戏.");
SetPut(64, 20);
printf("小贾制作\n");
}
void pause()//暂停
{
while (1)
{
Sleep(200);
if (KEY_PRESS(VK_SPACE))
break;
}
}
bool NextIsFood(pSnakeNode pn, pSnake ps)//判断转向的下一个位置是否有食物
{
return pn->x == ps->_pFood->x && pn->y == ps->_pFood->y;
}
void EatFood(pSnakeNode pn, pSnake ps)//有食物的情况
{
pn->next = ps->_pSanke;
ps->_pSanke = pn;
pSnakeNode cur = ps->_pSanke;
while (cur)
{
SetPut(cur->x, cur->y);
wprintf(L"%lc", SNAKE);
cur = cur->next;
}
ps->_score += ps->_food_weight;
free(ps->_pFood );
CreateFood(ps);
}
void NoFood(pSnakeNode pn, pSnake ps)//没有食物的情况
{
//头插法
pn->next = ps->_pSanke;
ps->_pSanke = pn;
//打印蛇
pSnakeNode cur = ps->_pSanke;
while (cur->next->next)
{
SetPut(cur->x, cur->y);
wprintf(L"%lc", SNAKE);
cur = cur->next;
}
//最后⼀个位置打印空格,然后释放节点
SetPut(cur->next->x, cur->next->y);
printf(" ");
free(cur->next);
cur->next = NULL;
}
void KillWall(pSnake ps)//撞墙的情况
{
if (ps->_pSanke->x == 0 || ps->_pSanke->y == 26 ||ps->_pSanke->x == 56 || ps->_pFood->y == 0)
ps->_stutus = KILL_BY_WALL;
}
void KillSelf(pSnake ps)//撞自己的情况
{
pSnakeNode cur = ps->_pSanke->next ;
while (cur)
{
if (ps->_pSanke->x == cur->x && ps->_pSanke->y == cur->y)
{
ps->_stutus = KILL_BY_SELF;
break;
}
cur = cur->next;
}
}
void SnakeMove(pSnake ps)//移动蛇身一步
{
//创建一个蛇身节点
pSnakeNode pNextNode = (pSnakeNode)malloc(sizeof(SnakeNode));
if (pNextNode == NULL)
{
perror("malloc fail\n");
return;
}
switch (ps->_dir)
{
case UP:
{
pNextNode->x = ps->_pSanke->x;
pNextNode->y = ps->_pSanke->y - 1;
}
break;
case DOWN:
{
pNextNode->x = ps->_pSanke->x;
pNextNode->y = ps->_pSanke->y + 1;
}
break;
case LEFT:
{
pNextNode->x = ps->_pSanke->x - 2;
pNextNode->y = ps->_pSanke->y;
}
break;
case RIGHT:
{
pNextNode->x = ps->_pSanke->x + 2;
pNextNode->y = ps->_pSanke->y;
}
break;
}
if (NextIsFood(pNextNode, ps))
{
EatFood(pNextNode, ps);
}
else//如果没有⻝物
{
NoFood(pNextNode, ps);
}
KillWall(ps);
KillSelf(ps);
}
void GameRun(pSnake ps)//游戏运行
{
//打印右侧帮助信息
PrintHelpInfo();
do
{
SetPut(64, 10);
printf("得分:%d ", ps->_score );
printf("每个⻝物得分:%d分", 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_LEFT) && ps->_dir != RIGHT)
{
ps->_dir = LEFT;
}
else if (KEY_PRESS(VK_RIGHT) && ps->_dir != LEFT)
{
ps->_dir = RIGHT;
}
else if (KEY_PRESS(VK_SPACE))
{
pause();
}
else if (KEY_PRESS(VK_ESCAPE))
{
ps->_stutus = END_NOMAL;
break;
}
else if (KEY_PRESS(VK_F3))
{
if (ps->_sleep_time >= 80)
{
ps->_sleep_time -= 30;
ps->_food_weight += 2;//⼀个⻝物分数最⾼是20分
}
}
else if (KEY_PRESS(VK_F4))
{
if (ps->_sleep_time < 320)
{
ps->_sleep_time += 30;
ps->_food_weight -= 2;//⼀个⻝物分数最低是2分
}
}
//蛇每次⼀定之间要休眠的时间,时间短,蛇移动速度就快
Sleep(ps->_sleep_time );
SnakeMove(ps);
} while (ps->_stutus == OK);
}
void GameEnd(pSnake ps)//游戏结束
{
SetPut(22, 12);
switch (ps->_stutus)
{
case END_NOMAL:
{
printf("正常退出\n");
}
break;
case KILL_BY_WALL:
{
printf("您撞墙了,游戏结束!\n");
}
break;
case KILL_BY_SELF:
{
printf("您撞上您自己了\n");
}
break;
}
pSnakeNode cur = ps->_pSanke;
while (cur)//清除蛇身
{
pSnakeNode dir = cur;
cur = cur->next;
free(dir);
}
}
二、相关函数的介绍
1.COORD
控制台屏幕上的坐标COORD
2.Win32 API的介绍
Windows 这个多作业系统除了协调应⽤程序的执⾏、分配内存、管理资源之外, 它同时也是⼀个很⼤的服务中⼼,调⽤这个服务中⼼的各种服务(每⼀种服务就是⼀个函数),可以帮应⽤程序达到开启视窗、描绘图形、使⽤周边设备等⽬的,由于这些函数服务的对象是应⽤程序(Application), 所以便称之为 Application Programming Interface,简称 API 函数。WIN32 API也就就是Microsoft Windows32位平台的应⽤程序编程接⼝。
3.GetStdHandle
GetStdHandle
GetStdHandle是⼀个Windows API函数。它⽤于从⼀个特定的标准设备(标准输⼊、标准输出或标准错误)中取得⼀个句柄(⽤来标识不同设备的数值),使⽤这个句柄可以操作设备。
HANDLE GetStdHandle(DWORD nStdHandle);
实例:
HANDLE hOutput = NULL;
//获取标准输出的句柄(⽤来标识不同设备的数值)
hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
4.GetConsoleCursorInfo
GetConsoleCursorInfo
检索有关指定控制台屏幕缓冲区的光标⼤⼩和可⻅性的信息
BOOL WINAPI GetConsoleCursorInfo(
HANDLE hConsoleOutput,
PCONSOLE_CURSOR_INFO lpConsoleCursorInfo
);
PCONSOLE_CURSOR_INFO 是指向 CONSOLE_CURSOR_INFO 结构的指针,该结构接收有关主机游标
实例:
HANDLE hOutput = NULL;
//获取标准输出的句柄(⽤来标识不同设备的数值)
hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
CONSOLE_CURSOR_INFO CursorInfo;
GetConsoleCursorInfo(hOutput, &CursorInfo);//获取控制台光标信息
5.CONSOLE_CURSOR_INFO
这个结构体,包含有关控制台光标的信息
typedef struct _CONSOLE_CURSOR_INFO {
DWORD dwSize;
BOOL bVisible;
} CONSOLE_CURSOR_INFO, *PCONSOLE_CURSOR_INFO;
- dwSize,由光标填充的字符单元格的百分⽐。 此值介于1到100之间。 光标外观会变化,范围从完全填充单元格到单元底部的⽔平线条。
- bVisible,游标的可⻅性。 如果光标可⻅,则此成员为 TRUE。
//示例:
CursorInfo.bVisible = false; //隐藏控制台光标
5.SetConsoleCursorInf
SetConsoleCursorInfo
设置指定控制台屏幕缓冲区的光标的⼤⼩和可⻅性
BOOL WINAPI SetConsoleCursorInfo(
HANDLE hConsoleOutput,
const CONSOLE_CURSOR_INFO *lpConsoleCursorInfo
);
实例:
HANDLE hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
//影藏光标操作
CONSOLE_CURSOR_INFO CursorInfo;
GetConsoleCursorInfo(hOutput, &CursorInfo);//获取控制台光标信息
CursorInfo.bVisible = false; //隐藏控制台光标
SetConsoleCursorInfo(hOutput, &CursorInfo);//设置控制台光标状态
6.SetConsoleCursorPosition
SetConsoleCursorPosition
设置指定控制台屏幕缓冲区中的光标位置,我们将想要设置的坐标信息放在COORD类型的pos中,调用SetConsoleCursorPosition函数将光标位置设置到指定的位置。
BOOL WINAPI SetConsoleCursorPosition(
HANDLE hConsoleOutput,
COORD pos
);
实例:
COORD pos = { 10, 5};
HANDLE hOutput = NULL;
//获取标准输出的句柄(⽤来标识不同设备的数值)
hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
//设置标准输出上光标的位置为pos
SetConsoleCursorPosition(hOutput, pos);
SetPut:封装一个设置光标位置的函数
//设置光标的坐标
void SetPos(short x, short y)
{
COORD pos = { x, y };
HANDLE hOutput = NULL;
//获取标准输出的句柄(⽤来标识不同设备的数值)
hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
//设置标准输出上光标的位置为pos
SetConsoleCursorPosition(hOutput, pos);
}
7.GetAsyncKeyState
GetAsyncKeyState
获取按键情况,GetAsyncKeyState的函数原型如下:
SHORT GetAsyncKeyState(
int vKey
);
将键盘上每个键的虚拟键值传递给函数,函数通过返回值来分辨按键的状态。GetAsyncKeyState 的返回值是short类型,在上⼀次调⽤ GetAsyncKeyState 函数后,如果返回的16位的short数据中,最⾼位是1,说明按键的状态是按下,如果最⾼是0,说明按键的状态是抬起;如果最低位被置为1则说明,该按键被按过,否则为0。
如果我们要判断⼀个键是否被按过,可以检测GetAsyncKeyState返回值的最低值是否为1
#define KEY_PRESS(VK) ( (GetAsyncKeyState(VK) & 0x1) ? 1 : 0 )
参考:虚拟键码 (Winuser.h) - Win32 apps
总结
以上就是贪吃蛇的全部代码以及一些关于.Win32 API的接口的介绍。当然代码还可以优化许多玩法。小编呢就写这么多了。咋们下一期再见吧!感谢您的参观!