目录
文章结尾有源码可自取
Win32API
此处首先对Win32API进行简单的介绍,在游戏中光标的隐藏,获得用户按键信息的都会用到;
以下操作要包含头文件<windows.h>
光标的隐藏
//隐藏光标
void Hide_cursor(void)
{
//获得输出控制台句柄
HANDLE houtput = NULL;
houtput = GetStdHandle(STD_OUTPUT_HANDLE);
//获得控制台光标信息
CONSOLE_CURSOR_INFO cursor_info;
GetConsoleCursorInfo(houtput, &cursor_info);
//将光标信息中的可见度改为0
cursor_info.bVisible = 0;
SetConsoleCursorInfo(houtput, &cursor_info);
}
获取按键信息
可以读取用户按下了哪一个键。
//获取按键信息
#define KEY_PRESS(vk) (GetAsyncKeyState(vk)?1:0)
//vk表示按键
//如:Key_Press(VK_UP)如果用户按下↓,则返回1,否则是0
控制光标位置
在有一些情况不希望将光标放在控制台(黑框)的开始默认位置,我们可能需要在控制台中央打印,此时就需要对控制台光标位置进行定位。
//定位光标位置
void SetPos(size_t x, size_t y)
{
//获得输出控制台句柄
HANDLE houtput = NULL;
houtput = GetStdHandle(STD_OUTPUT_HANDLE);
// 获取光标
COORD cursorPos = { 0, 0 };
//修改光标位置
cursorPos.X = x;
cursorPos.Y = y;
SetConsoleCursorPosition(houtput,cursorPos);
}
关于Win32API的知识不过多讲解,对于初学者可以直接使用上述代码实现功能。有兴趣可以看windows的控制台函数。控制台函数 - Windows Console | Microsoft Learnhttps://learn.microsoft.com/zh-cn/windows/console/console-functions
游戏开始前的准备
游戏进行时我们有时候想要打印图案字符,eg:■◯Ő以及一些汉字(宽字符)。我们就需要将程序修改为本地状态,可以理解为不仅仅能够打印外国的英文,还能够打印我们本地中文汉字。
为了有更好的游戏体验,需要对控制台的大小和名称进行修改。
#include<locale.h>
void Prepare(void)
{
//先将程序改成本地模式
setlocale(LC_ALL, "");
//先对控制台的大小进行修改
system("mode con cols=150 lines=40");
//对名称进行重命名
system("title 贪吃蛇");
}
游戏准备及介绍
隐藏完光标后,我们进行游戏开始之前的打印,范围两部分,打印加载和欢迎页面;打印游戏指南,方法。所以在游戏开始运行之前有两个页面需要打印。
加载和欢迎界面
先打印第一个封面,欢迎和加载界面。此处将代码封装成立三个函数:打印进度条函数;打印欢迎界面函数。效果如下。
进度条的实现主要是依靠一个数组,通过循环向数组里添加字符,打印数组从而动态的效果。
//加载界面的打印
void ProcessBar(void)
{
wchar_t bar[102] = { '\0' }; //设置一个字符串来当作进度条
int cnt = 0;
while (cnt <= 100)
{
SetPos(25, 21); //定位
wprintf(GREEN L"[%-101ls]" RESET"[%d%%]", bar, cnt); //打印进度条,并加上颜色
Sleep(100); //休眠100毫秒
bar[cnt] = L'█';
cnt++;
}
}
void Print_Page1(void)
{
//定位光标,打印欢迎语句
SetPos(65, 18);
wprintf(YELLOW L"欢迎进入贪吃蛇游戏" RESET);
//打印加载的进度条
ProcessBar();
}
可以看到,在上买了的代码中添加了控制输出字符串的颜色,定义如下可自取。
//定义一些颜色
#define RESET L"\033[0m" // 恢复默认
// 前景色 (文本颜色)
#define BLACK L"\033[30m"
#define RED L"\033[31m"
#define GREEN L"\033[32m"
#define YELLOW L"\033[33m"
#define BLUE L"\033[34m"
#define MAGENTA L"\033[35m"
#define CYAN L"\033[36m"
#define WHITE L"\033[37m"
打印游戏指南
为了让用户有更好的游戏体验,需要对玩法进行说明。第二张画面打印游戏指南。效果如下。
void Print_Page2(void)
{
system("cls");//清屏
SetPos(58, 17);
wprintf(MAGENTA L"用↑↓←→来控制方向,ESC退出,SPACE暂停");
SetPos(58, 18);
wprintf(L"F1加速,F2减速;");
SetPos(58, 19);
wprintf(L"加速可以获得更多积分,减速获得积分变慢;" RESET);
SetPos(58, 21);
system("pause");
}
将page1和page2结合。
void game_prepare(void)
{
//隐藏光标
Hide_cursor();
//设置控制台名称和大小
Prepare();
//打印第一个画面,进入画面
Print_Page1();
//打印游戏指南
Print_Page2();
}
运行游戏
打印墙体和说明
墙体的长和宽都可以自定义设置。效果如下。
打印墙体的时候要注意临界位置的讨论。
//打印墙体
void Print_Wall(void)
{
SetPos(0, 0);
//打印横向
for (int i = 0; i < LEN; i += 2) //注意此处是+2,因为宽字符的宽度是2不像普通字符一样
wprintf(L"%c", WALL);
SetPos(0, WIDTH);
for (int i = 0; i < LEN; i += 2)
wprintf(L"%c", WALL);
//打印纵向
for (int i = 1; i < WIDTH; i++)
{
SetPos(0, i);
wprintf(L"%c", WALL);
SetPos(LEN-2, i);
wprintf(L"%c", WALL);
}
}
//打印说明
void Print_RULE(void)
{
SetPos(100, 8);
wprintf(GREEN L"↑↓←→来控制方向");
SetPos(100, 9);
wprintf(L"ESC退出,SPACE暂停");
SetPos(100, 10);
wprintf(L"F1加速,F2减速");
SetPos(100, 11);
wprintf(L"加速可以获得更多积分,减速获得积分变慢" RESET);
}
设置蛇的各个信息
在打印蛇之前,需要考虑设置什么样的信息。此处首先就是蛇身的各个节点,还需要蛇的方向,状态,蛇的得分,以及食物信息,所以要创建多个结构体:蛇的各个信息,蛇的每个节点,关于食物的信息。
通过枚举将蛇的方向,状态一一列举出来。
//设置方向
typedef enum DIR
{
RIGHT,
LEFT,
UP,
DOWN
}DIR;
//设置蛇的状态
typedef enum STATE
{
FINE, //正常
DEAD_BYWALL, //撞墙
DEAD_BYBODY, //撞到蛇身
QUIT //退出
}STATE;
//设置蛇节点
typedef struct Snackbody
{
size_t _x;
size_t _y;
struct Snackbody* _next;
}Snackbody;
//食物信息
typedef struct Food
{
size_t x;
size_t y;
size_t _each_score;
}Food;
//设置结构体来存储蛇的各个信息
typedef struct Snack
{
Snackbody* _head;
size_t _speed;
size_t _score;
DIR dir;
STATE state;
Food* pf;
}Snack;
初始化及打印蛇
初始化打印蛇,主要分为两步:创建蛇身的各个节点并将其链接;初始化蛇的状态,得分等信息。
//创造节点
Snackbody* MakeBody()
{
Snackbody* newbody = (Snackbody*)malloc(sizeof(Snackbody));
newbody->_next = NULL;
newbody->_x = 0;
newbody->_y = 0;
return newbody;
}
//对蛇进行初始化
void Init_Snack(Snack* sn)
{
//对蛇的各个数据进行初始化
sn->_head = NULL;
sn->dir = RIGHT;
sn->state = FINE;
sn->_score = 0;
sn->_speed = 200;
//创建蛇身
Snackbody** snhead = &(sn->_head); //注意此处需要是二级指针,因为要对其地址进行修改
Snackbody* tail = *snhead;
for (int i = 0; i < 5; i++)
{
Snackbody* newbody = MakeBody();
newbody->_x = 20 - i * 2;
newbody->_y = 15;
if ((*snhead) == NULL)
{
*snhead = newbody;
tail = newbody;
}
else
{
(tail)->_next = newbody;
tail = (tail)->_next;
}
}
//打印
Snackbody* cur = sn->_head;
while (cur != NULL)
{
SetPos(cur->_x, cur->_y);
wprintf(L"%c", SNACK);
cur = cur->_next;
}
}
创造食物
此处通过伪随机数,时间戳来确定食物的位置。
注意:创造的食物必须在墙内部,并且不能创作到蛇身上。所以在创建完食物后,要对食物的位置进行检查。
#define FOOD L'豆'
//检查食物
bool CHECK_food(Snack* sn)
{
size_t x = sn->pf->x;
size_t y = sn->pf->y;
Snackbody* cur = sn->_head;
while (cur != NULL)
{
if (cur->_x == x && cur->_y == y)
return false;
cur = cur->_next;
}
return true;
}
//创造食物
void Make_food(Snack* sn)
{
sn->pf = (Food*)malloc(sizeof(Food));
sn->pf->_each_score = 10;
srand((unsigned int)time(NULL));
again:
sn->pf->x = rand()%65+2; //食物x范围是2-66
sn->pf->y = rand()%21+1; //y的范围是1-21
//检查食物是否符合要求
//x必须是偶数,食物不能出现在蛇身上
if (sn->pf->x % 2 != 0 || !CHECK_food(sn))
{
goto again;
}
SetPos(sn->pf->x, sn->pf->y);
wprintf(L"%c", FOOD);
}
运行游戏
游戏是要持续进行的,所以肯定要设置循环,此处采用do...while循环,在蛇没有死之前,游戏都要能正常运行,所以循环的条件就是蛇的状态是不是FINE。
在循环中,主要有4个部分需要处理:打印侧栏得分;获取按键信息;打印蛇的下一步;检查状态。具体实现目的如下。
1)打印得分情况
游戏运行过程中要保证实时打印总得分及当前每个食物的得分。如下图所示
SetPos(100, 6);
wprintf(L"总得分:%d", sn->_score);
SetPos(100, 7);
wprintf(L"当前食物分数:%2d", sn->pf->_each_score);
2)获取按键信息
关于按键信息如何获取,前面Win32API已经写了,此处直接使用其宏定义,获取完按键信息后要对蛇的方向进行修改。
对于不同的按键要有不同的响应:上下左右直接改变方向即可;F1需要添加分数,缩短睡眠时间;F2相反;Esc则需要改变蛇的状态,让循环停止;Space需要暂停游戏,这里使用一个死循环来模拟暂停。
//暂停
void SPACE()
{
while (1)
{
if (KEY_PRESS(VK_SPACE))
break;
}
}
//检查按键
if (KEY_PRESS(VK_UP) && sn->dir != DOWN)
sn->dir = UP;
if (KEY_PRESS(VK_DOWN) && sn->dir != UP)
sn->dir = DOWN;
if (KEY_PRESS(VK_LEFT) && sn->dir != RIGHT)
sn->dir = LEFT;
if (KEY_PRESS(VK_RIGHT) && sn->dir != LEFT)
sn->dir = RIGHT;
if (KEY_PRESS(VK_F1))
{
if (sn->_speed > 40)
{
sn->pf->_each_score += 2;
sn->_speed -= 40;
}
}
if (KEY_PRESS(VK_F2))
{
if (sn->pf->_each_score > 2)
{
sn->pf->_each_score -= 2;
sn->_speed += 40;
}
}
if (KEY_PRESS(VK_SPACE))
SPACE();
if (KEY_PRESS(VK_ESCAPE))
{
sn->state = QUIT;
break;
}
Sleep(sn->_speed); //休眠,准备打印下一张图片
3)蛇走的下一步
可以通过蛇的方向来预测蛇的下一个位置,将下一个位置打印成蛇即可实现移动。此处需要考虑下一个位置是不是食物。两种情况是不同的,不是食物的话,就需要将尾部变为空,将下一个位置打印成蛇;是食物的话,就不需要对尾部进行处理,但是需要重新创建食物。
//打印下一张图片
void Print_NEXT(Snack* sn)
{
//创建下一个位置的节点
Snackbody* pnext = (Snackbody*)malloc(sizeof(Snackbody));
switch(sn->dir)
{
case UP:
pnext->_x = sn->_head->_x;
pnext->_y = sn->_head->_y - 1;
break;
case DOWN:
pnext->_x = sn->_head->_x;
pnext->_y = sn->_head->_y + 1;
break;
case LEFT:
pnext->_x = sn->_head->_x - 2;
pnext->_y = sn->_head->_y;
break;
case RIGHT:
pnext->_x = sn->_head->_x + 2;
pnext->_y = sn->_head->_y;
break;
}
//下一个位置是食物
if (pnext->_x == sn->pf->x && pnext->_y == sn->pf->y)
{
IS_FOOD(sn,pnext);
}
else
NO_FOOD(sn,pnext);
}
下一步不是食物
不是食物的话,就需要将尾部变为空,将下一个位置打印成蛇;
//下一个位置不是食物
void NO_FOOD(Snack* sn, Snackbody* pnext)
{
//将下一个节点当成蛇头
pnext->_next = sn->_head;
sn->_head = pnext;
//找到最后一个节点并释放
Snackbody* cur = sn->_head;
while (cur->_next->_next != NULL)
cur = cur->_next;
//将蛇尾从原本的蛇改为空
SetPos(cur->_next->_x, cur->_next->_y);
wprintf(L" ");
free(cur->_next);
cur->_next = NULL;
//将蛇的下一个位置打印成蛇
SetPos(pnext->_x, pnext->_y);
wprintf(L"%c", SNACK);
}
下一步是食物
//下一个位置是食物
void IS_FOOD(Snack* sn,Snackbody* pnext)
{
//将食物直接当成蛇头
pnext->_next = sn->_head;
sn->_head = pnext;
SetPos(pnext->_x, pnext->_y);
wprintf(L"%c", SNACK);
//加分
sn->_score += sn->pf->_each_score;
//创建食物
Make_food(sn);
}
4)检查是否撞到墙或自己
此处直接通过坐标进行检查即可。
//检查是不是墙
void IF_WALL(Snack* sn)
{
if (sn->_head->_x >= LEN - 2 || sn->_head->_x == 0)
sn->state = DEAD_BYWALL;
if (sn->_head->_y == WIDTH || sn->_head->_y == 0)
sn->state = DEAD_BYWALL;
}
//检查是否撞到自己
void IF_SELF(Snack* sn)
{
int x = sn->_head->_x;
int y = sn->_head->_y;
Snackbody* cur = sn->_head->_next;
while (cur != NULL)
{
if (cur->_x == x && cur->_y == y)
sn->state = DEAD_BYBODY;
cur = cur->_next;
}
}
游戏结束
游戏可以正常运行了,但是还要对游戏结束进行处理,要将游戏结束的信息反馈给用户,还要对游戏运行过程中动态开辟的空间进行销毁。
//游戏结束
void Game_over(Snack* sn)
{
system("cls");
SetPos(65, 18);
switch (sn->state)
{
case DEAD_BYWALL:
wprintf(MAGENTA L"你撞到墙了!!!");
break;
case DEAD_BYBODY:
wprintf(L"你撞到自己了!!!");
break;
case QUIT:
wprintf(L"已退出!!!" RESET);
break;
}
SetPos(0, 35);
//释放蛇身各个节点,释放动态开辟的空间
Snackbody* cur = sn->_head;
while (cur != NULL)
{
Snackbody* next = cur->_next;
free(cur);
cur = next;
}
free(sn->pf);
}
各个文件代码统计
<Blog_Snack.h>头文件
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
#include<windows.h>
#include<locale.h>
#include<stdbool.h>
#include<time.h>
//定义一些颜色
#define RESET L"\033[0m" // 恢复默认
// 前景色 (文本颜色)
#define BLACK L"\033[30m"
#define RED L"\033[31m"
#define GREEN L"\033[32m"
#define YELLOW L"\033[33m"
#define BLUE L"\033[34m"
#define MAGENTA L"\033[35m"
#define CYAN L"\033[36m"
#define WHITE L"\033[37m"
#define WALL L'墙' //也可以用█,看自己。但是一定要是宽字符
#define SNACK L'蛇' //可以定义成其他宽字符
#define FOOD L'豆'
#define LEN 70
#define WIDTH 22
//获取按键信息
#define KEY_PRESS(vk) (GetAsyncKeyState(vk)?1:0)
//vk表示按键
//如:Key_Press(VK_UP)如果用户按下↓,则返回1,否则是0
//设置方向
typedef enum DIR
{
RIGHT,
LEFT,
UP,
DOWN
}DIR;
//设置蛇的状态
typedef enum STATE
{
FINE, //正常
DEAD_BYWALL, //撞墙
DEAD_BYBODY, //撞到蛇身
QUIT //退出
}STATE;
//设置蛇节点
typedef struct Snackbody
{
size_t _x;
size_t _y;
struct Snackbody* _next;
}Snackbody;
//食物信息
typedef struct Food
{
size_t x;
size_t y;
size_t _each_score;
}Food;
//设置结构体来存储蛇的各个信息
typedef struct Snack
{
Snackbody* _head;
size_t _speed;
size_t _score;
DIR dir;
STATE state;
Food* pf;
}Snack;
//设置控制台名称和大小
void Prepare(void);
//打印游戏指南
void Print_Page2(void);
//隐藏光标
void Hide_cursor(void);
//定位光标位置
void SetPos(size_t x, size_t y);
//开始前的准备
void game_prepare(void);
//第一个界面
void Print_Page1(void);
//加载界面的打印
void ProcessBar(void);
//运行游戏
void game_start(void);
//打印墙体
void Print_Wall(void);
//打印说明
void Print_RULE(void);
//打印蛇
void Print_Snack(Snack* sn);
//对蛇进行初始化
void Init_Snack(Snack* sn);
//创造节点
Snackbody* MakeBody();
//创造食物
void Make_food(Snack* sn);
//检查食物
bool CHECK_food(Snack* sn);
//游戏运行
void start(Snack* sn);
//打印下一张图片
void Print_NEXT(Snack* sn);
//暂停
void SPACE();
//下一个位置是食物
void IS_FOOD(Snack* sn,Snackbody* pnext);
//下一个位置不是食物
void NO_FOOD(Snack* sn, Snackbody* pnext);
//检查是不是墙
void IF_WALL(Snack* sn);
//检查是否撞到自己
void IF_SELF(Snack* sn);
//游戏结束
void Game_over(Snack* sn);
<Snack.c>源文件
#define _CRT_SECURE_NO_WARNINGS 1
#include"Blog_Snack.h"
//隐藏光标
void Hide_cursor(void)
{
//获得输出控制台句柄
HANDLE houtput = NULL;
houtput = GetStdHandle(STD_OUTPUT_HANDLE);
//获得控制台光标信息
CONSOLE_CURSOR_INFO cursor_info;
GetConsoleCursorInfo(houtput, &cursor_info);
//将光标信息中的可见度改为0
cursor_info.bVisible = 0;
SetConsoleCursorInfo(houtput, &cursor_info);
}
//定位光标位置
void SetPos(size_t x, size_t y)
{
//获得输出控制台句柄
HANDLE houtput = NULL;
houtput = GetStdHandle(STD_OUTPUT_HANDLE);
// 获取光标
COORD cursorPos = { 0, 0 };
//修改光标位置
cursorPos.X = x;
cursorPos.Y = y;
SetConsoleCursorPosition(houtput,cursorPos);
}
#include<locale.h>
void Prepare(void)
{
//先将程序改成本地模式
setlocale(LC_ALL, "");
//先对控制台的大小进行修改
system("mode con cols=150 lines=40");
//对名称进行重命名
system("title 贪吃蛇");
}
//加载界面的打印
void ProcessBar(void)
{
wchar_t bar[102] = { '\0' }; //设置一个字符串来当作进度条
int cnt = 0;
while (cnt <= 100)
{
SetPos(25, 21); //定位
wprintf(GREEN L"[%-101ls]" RESET"[%d%%]", bar, cnt); //打印进度条,并加上颜色
Sleep(1); //休眠100毫秒
bar[cnt] = L'█';
cnt++;
}
}
void Print_Page1(void)
{
//定位光标,打印欢迎语句
SetPos(65, 18);
wprintf(YELLOW L"欢迎进入贪吃蛇游戏" RESET);
//打印加载的进度条
ProcessBar();
}
void Print_Page2(void)
{
system("cls");//清屏
SetPos(58, 17);
wprintf(MAGENTA L"用↑↓←→来控制方向,ESC退出,SPACE暂停;");
SetPos(58, 18);
wprintf(L"F1加速,F2减速;");
SetPos(58, 19);
wprintf(L"加速可以获得更多积分,减速获得积分变慢;" RESET);
SetPos(58, 21);
system("pause");
}
//打印墙体
void Print_Wall(void)
{
SetPos(0, 0);
//打印横向
for (int i = 0; i < LEN; i += 2) //注意此处是+2,因为宽字符的宽度是2不像普通字符一样
wprintf(L"%c", WALL);
SetPos(0, WIDTH);
for (int i = 0; i < LEN; i += 2)
wprintf(L"%c", WALL);
//打印纵向
for (int i = 1; i < WIDTH; i++)
{
SetPos(0, i);
wprintf(L"%c", WALL);
SetPos(LEN-2, i);
wprintf(L"%c", WALL);
}
}
//打印说明
void Print_RULE(void)
{
SetPos(100, 8);
wprintf(GREEN L"↑↓←→来控制方向");
SetPos(100, 9);
wprintf(L"ESC退出,SPACE暂停");
SetPos(100, 10);
wprintf(L"F1加速,F2减速");
SetPos(100, 11);
wprintf(L"加速可以获得更多积分,减速获得积分变慢" RESET);
}
//创造节点
Snackbody* MakeBody()
{
Snackbody* newbody = (Snackbody*)malloc(sizeof(Snackbody));
newbody->_next = NULL;
newbody->_x = 0;
newbody->_y = 0;
return newbody;
}
//对蛇进行初始化
void Init_Snack(Snack* sn)
{
//对蛇的各个数据进行初始化
sn->_head = NULL;
sn->dir = RIGHT;
sn->state = FINE;
sn->_score = 0;
sn->_speed = 200;
//创建蛇身
Snackbody** snhead = &(sn->_head); //注意此处需要是二级指针,因为要对其地址进行修改
Snackbody* tail = *snhead;
for (int i = 0; i < 5; i++)
{
Snackbody* newbody = MakeBody();
newbody->_x = 20 - i * 2;
newbody->_y = 15;
if ((*snhead) == NULL)
{
*snhead = newbody;
tail = newbody;
}
else
{
(tail)->_next = newbody;
tail = (tail)->_next;
}
}
//打印
Snackbody* cur = sn->_head;
while (cur != NULL)
{
SetPos(cur->_x, cur->_y);
wprintf(L"%c", SNACK);
cur = cur->_next;
}
}
//检查食物
bool CHECK_food(Snack* sn)
{
size_t x = sn->pf->x;
size_t y = sn->pf->y;
Snackbody* cur = sn->_head;
while (cur != NULL)
{
if (cur->_x == x && cur->_y == y)
return false;
cur = cur->_next;
}
return true;
}
//创造食物
void Make_food(Snack* sn)
{
sn->pf = (Food*)malloc(sizeof(Food));
sn->pf->_each_score = 10;
srand((unsigned int)time(NULL));
again:
sn->pf->x = rand()%65+2; //食物x范围是2-66
sn->pf->y = rand()%21+1; //y的范围是1-21
//检查食物是否符合要求
//x必须是偶数,食物不能出现在蛇身上
if (sn->pf->x % 2 != 0 || !CHECK_food(sn))
{
goto again;
}
SetPos(sn->pf->x, sn->pf->y);
wprintf(L"%c", FOOD);
}
//下一个位置是食物
void IS_FOOD(Snack* sn,Snackbody* pnext)
{
//将食物直接当成蛇头
pnext->_next = sn->_head;
sn->_head = pnext;
SetPos(pnext->_x, pnext->_y);
wprintf(L"%c", SNACK);
//加分
sn->_score += sn->pf->_each_score;
//创建食物
Make_food(sn);
}
//下一个位置不是食物
void NO_FOOD(Snack* sn, Snackbody* pnext)
{
//将下一个节点当成蛇头
pnext->_next = sn->_head;
sn->_head = pnext;
//找到最后一个节点并释放
Snackbody* cur = sn->_head;
while (cur->_next->_next != NULL)
cur = cur->_next;
//将蛇尾从原本的蛇改为空
SetPos(cur->_next->_x, cur->_next->_y);
wprintf(L" ");
free(cur->_next);
cur->_next = NULL;
//将蛇的下一个位置打印成蛇
SetPos(pnext->_x, pnext->_y);
wprintf(L"%c", SNACK);
}
//打印下一张图片
void Print_NEXT(Snack* sn)
{
//创建下一个位置的节点
Snackbody* pnext = (Snackbody*)malloc(sizeof(Snackbody));
switch(sn->dir)
{
case UP:
pnext->_x = sn->_head->_x;
pnext->_y = sn->_head->_y - 1;
break;
case DOWN:
pnext->_x = sn->_head->_x;
pnext->_y = sn->_head->_y + 1;
break;
case LEFT:
pnext->_x = sn->_head->_x - 2;
pnext->_y = sn->_head->_y;
break;
case RIGHT:
pnext->_x = sn->_head->_x + 2;
pnext->_y = sn->_head->_y;
break;
}
//下一个位置是食物
if (pnext->_x == sn->pf->x && pnext->_y == sn->pf->y)
{
IS_FOOD(sn,pnext);
}
else
NO_FOOD(sn,pnext);
}
//暂停
void SPACE()
{
while (1)
{
if (KEY_PRESS(VK_SPACE))
break;
}
}
//检查是不是墙
void IF_WALL(Snack* sn)
{
if (sn->_head->_x >= LEN - 2 || sn->_head->_x == 0)
sn->state = DEAD_BYWALL;
if (sn->_head->_y == WIDTH || sn->_head->_y == 0)
sn->state = DEAD_BYWALL;
}
//检查是否撞到自己
void IF_SELF(Snack* sn)
{
int x = sn->_head->_x;
int y = sn->_head->_y;
Snackbody* cur = sn->_head->_next;
while (cur != NULL)
{
if (cur->_x == x && cur->_y == y)
sn->state = DEAD_BYBODY;
cur = cur->_next;
}
}
//游戏结束
void Game_over(Snack* sn)
{
system("cls");
SetPos(65, 18);
switch (sn->state)
{
case DEAD_BYWALL:
wprintf(MAGENTA L"你撞到墙了!!!");
break;
case DEAD_BYBODY:
wprintf(L"你撞到自己了!!!");
break;
case QUIT:
wprintf(L"已退出!!!" RESET);
break;
}
SetPos(0, 35);
//释放蛇身各个节点,释放动态开辟的空间
Snackbody* cur = sn->_head;
while (cur != NULL)
{
Snackbody* next = cur->_next;
free(cur);
cur = next;
}
free(sn->pf);
}
//游戏运行
void start(Snack* sn)
{
do {
SetPos(100, 6);
wprintf(L"总得分:%d", sn->_score);
SetPos(100, 7);
wprintf(L"当前食物分数:%2d", sn->pf->_each_score);
//检查按键
if (KEY_PRESS(VK_UP) && sn->dir != DOWN)
sn->dir = UP;
if (KEY_PRESS(VK_DOWN) && sn->dir != UP)
sn->dir = DOWN;
if (KEY_PRESS(VK_LEFT) && sn->dir != RIGHT)
sn->dir = LEFT;
if (KEY_PRESS(VK_RIGHT) && sn->dir != LEFT)
sn->dir = RIGHT;
if (KEY_PRESS(VK_F1))
{
if (sn->_speed > 40)
{
sn->pf->_each_score += 2;
sn->_speed -= 40;
}
}
if (KEY_PRESS(VK_F2))
{
if (sn->pf->_each_score > 2)
{
sn->pf->_each_score -= 2;
sn->_speed += 40;
}
}
if (KEY_PRESS(VK_SPACE))
SPACE();
if (KEY_PRESS(VK_ESCAPE))
{
sn->state = QUIT;
break;
}
Sleep(sn->_speed); //休眠,准备打印下一张图片
Print_NEXT(sn);
IF_WALL(sn);
IF_SELF(sn);
} while (sn->state == FINE);
//游戏结束
Game_over(sn);
}
<Snack_main.c>源文件
#define _CRT_SECURE_NO_WARNINGS 1
#include"Blog_Snack.h"
void game_prepare(void)
{
//隐藏光标
Hide_cursor();
//设置控制台名称和大小
Prepare();
//打印第一个画面,进入画面
Print_Page1();
//打印游戏指南
Print_Page2();
}
//运行游戏
void game_start(void)
{
system("cls");
//打印墙体
Print_Wall();
//打印说明
Print_RULE();
Snack sn;
//对蛇进行初始化
Init_Snack(&sn);
//创造食物
Make_food(&sn);
//游戏运行
start(&sn);
}
int main()
{
game_prepare();
game_start();
return 0;
}