贪吃蛇是久负盛名的游戏,是一款经典游戏
使用c语言在windows环境的控制台模拟实现小游戏贪吃蛇
基本的功能:
c语言函数、枚举、结构体、动态内存管理、预处理指令、链表。Win32API等
windows这个多作业系统除了协调应用程序的执行、分配内存、管理资源之外,同时也是一个很大的服务中心,调用这个服务中心的各种服务(每一种服务就是一个函数),可以帮助应用程序达到开启视窗、描绘图形、使用周边设备等目的,由于这些函数服务的对象是应用程序,所以便称之为 Application Programming Interface,简称API函数。WIN32 API也就是Microsoft Windows32位平台的应用程序编程接口
平常的黑框程序就是控制台程序
可以使用cmd命令来控制控制台窗口的长宽: 设置控制台窗口的大小,30行,100列
mode con cols=100 lines=30
也可以设置控制台窗口的名字
title 贪吃蛇
这些能在控制台窗口执行的命令,也可以调用c语言函数system执行
#include
int main()
{
//设置控制台窗⼝的⻓宽:设置控制台窗⼝的⼤⼩,30⾏,100列
system("mode con cols=100 lines=30");
//设置cmd窗⼝名称
system("title 贪吃蛇");
return 0;
}
COORD是windows api定义的一个结构体,表示一个字符在控制台屏幕上的坐标
typedef struct _COORD{
SHORT X;
SHORT Y;
} COORD,*PCOORD;
给坐标赋值:
COORD pos = { 10, 15 };
用于从一个特定的标准设备(标准输入、标准输出或标准错误)中取得一个句柄(用来标识不同设备的数值),使用这个句柄可以操作设备
HANDLE GetStdHandle (DWORD nStdHandle) ;
HANDLE hQutput = NULL;
//获取标准输出的句柄(用来标识不同设备的数值)
hQutput = GetStdHandle(STD_OUTPUT_HANDLE);
检测有关指定控制台屏幕缓冲区的光标大小和可见性的信息
BOOL WINAPI GetConsoleCursorInfo(
HANDLE hConsoleOutput,
PCONSOLE_CURSOR_INFO lpConsoleCursorInfo
)
实例:
HANDLE hOutput = NULL;
//获取标准输出的句柄(用来标识不同设备的数值)
hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
CONSOLE_CURSOR_INFO CursorInfo;
//获取控制台光标信息
GetConsoleCursorInfo(hOutput, &CursorInfo);
这个结构体,包含控制台光标的信息
typedef struct _CONSOLE_CURSOR_INFO {
DWORD dwSize;
BOOL bVisible;
} CONSOLE_CURSOR_INFO , *PCONSOLE_CURSOR_INFO;
CursorInfo.bVisible = false; //隐藏控制台光标
设置指定控制台屏幕缓冲区中的光标位置,我们将要设置的坐标信息放在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);
封装一个设置光标位置的函数
void SetPos(short x, short y)
{
COORD pos = { x , y };
HANDLE hOutput = NULL;
//获取标准输出的句柄(用来标识不同设备的数值)
hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
//设置标准输出上光标的位置pos
SetConsoleCursorPosition(hOutput, pos);
}
获取按键情况,GetAsyncKeyState的函数原型如下:
SHORT GetAsyncKeyState(
int vKey;
);
将键盘上每个键的虚拟键值传递给函数,函数通过返回值来分辨按键的状态
GetAsyncKeyState的返回值是short类型,在上一次调用GetAsyncKeyState函数后,如果返回的16位的short数据中,最高位是1,说明按键的状态是按下,如果最高位是0,说明按键的状态是抬起;如果最低位被置1说明,该按键被按过,否则是0
如果判断一个键是否被按过,可以检测这个函数的返回值的最低位是否为1
GetAsyncKeyState(VK) & 0x1 == 1
//宏定义
#define KEY_PRESS(VK) ((GetAsyncKeyState(VK) & 0x1) ? ! : 0)
想在控制台指定位置输出信息,必须知道该位置的坐标
如下图所示,横向的是x轴,从左右次依次增长,纵向是y轴,从上到下依次增长
在游戏地图上,打印墙体使用宽字符:□,蛇用宽字符:● 食物用宽字符:★
普通的字符是占一个字节的,这类宽字符是占用2个字节
c语言字符默认采用ASCII编码,ASCII字符集采用的是单字节编码,且只使用了单字节中的低7位,最高位是没有使用的,可表示为0xxxxxxxx;可以看到,ASCII字符集共包含了128个字符,在英语国家中,128个字符基本够用,但是在其他国家语言中,就无法表示。一些欧洲国家用最高位编入新的符号,比如,法语é的编码是130(二进制10000010)。这样一来,这些欧洲国家使用的编码体系,可以表示最多256个符号。但是,又出现了新的问题。不同的国家有不同的字母,哪怕都是用256个符号的编码方式,同一个码表示的字符确不一样,不管怎么样,0-127表示的符号是一样的,不一样的只是128-255这一段
至于亚洲国家的文字,需要的符号就更多了,汉字就多达10万左右,一个字节肯定不够,就必须使用多个字节表达一个符号。简体中文常见的编码方式是GB2312,使用后两个字节表示一个汉字,理论上最多可以表示256×256=65536个符号
后来为了使c语言国际化,不断加入了国家,用宽字节类型wchar_t和宽字节的输入和输出函数,加入和
在标准中,依赖地区的部分有以下几项:
通过修改地区,程序可以改变它的行为来适应世界的不同区域。但地区的改变可能会影响库的许多部分,其中一部分不希望修改,支持堆不同的类项修改,下面的宏指定类项
char* setlocale (int category, cosnt char* locale) ;
setlocale 函数用来修改当前地区,可以针对一个类项,也可以针对所有类项
setlocale 的第一个参数可以是前面说明的类型中的一个,那么每次只会影响一个类型,如果第一个参数是LC_ALL,就会影响所有的类项
c标准给第二个参数定义了2种可能取值: “C"和” ",任意程序执行开始,都会隐藏式执行调用
setlocale(LC_ALL, “C” ) ;
当地区设置为C,库函数按正常方式执行,小数点是一个点
想改变地区,只能显示调用函数,用“ ”作为第2个参数,调用setlocale函数就可以切换到本地模式,这种模式下程序会适应本地环境。比如:切换到我们的本地模式后就支持宽字符的输出
setlocale (LC_ALL, “”);
#include
#include
int main()
{
setlocale(LC_ALL, "");
wchar_t ch1 = L'●';
wchar_t ch2 = L'★';
printf("%c%c", 'a', 'b');
printf("%c%c\n", 'a', 'b');
wprintf(L"%c",ch1);
wprintf(L"%c",ch2);
}
从结果看出,一个普通字符占一个位置,汉字字符占两个位置,所以打印宽字符地图,需要计算好坐标
从左上角的00点开始,和控制台边框空出一段间隔,x从2开始打印,y从1开始打印,宽字符一个字符占两列,地图19行44列,从上面的起点算,x从2-45,y从1-19,四周最边的一面为墙
初始化状态,假设蛇的长度是3,蛇身的每个节点●,在固定的坐标处,y在第一列,横坐标放在地图的中间。需要注意,每个节点的x坐标必须是2的倍数,否则就会和墙体对不齐
食物是在墙体内随机生成一个坐标,x坐标也必须是2的倍数对齐,同时不能和蛇身重合,打印★
在游戏运行的过程中,蛇每次吃一个食物,蛇的身体就会变长一节,如果我们使用链表存储蛇的信息,那么蛇的每一节其实就是链表的每个节点。每个节点只要记录好蛇身节点在地图上的坐标和下一个结点就行
首先得有一个坐标的结构体
struct Point
{
int x;
int y;
};
蛇节点的结构体
//蛇身结点
typedef struct _SnakeNode
{
struct Point spoint; //坐标
struct _SnakeNode* next;
}SnakeNode, * pNode;
有了蛇身的结构,还需要管理整条蛇的属性信息,比如速度、蛇头等
//蛇属性
typedef struct _Snake
{
pNode phead; //蛇头
int score; //分数
int weight; //权重
int speed; //移动间隔
enum Dorection dre; //方向
enum STATUS sta; //状态
}* pSnake;
蛇的方向和状态用枚举出所有可能得结果
//方向
enum Dorection
{
UP,
DOWN,
LEFT,
RIGHT,
};
//状态
enum STATUS
{
OK = 1,
PAUSE,
KILL_WALL,
KILL_SELF,
ESC,
};
#define _CRT_SECURE_NO_WARNINGS 1
#include
#include
#include
#include "Sanke.h"
int main()
{
int ch = 0;
do
{
//创建蛇
struct _Snake snake;
//创建食物
struct Point food;
//初始化
GameInit(&snake, &food);
//游戏循环
GameRun(&snake, &food);
//游戏结束
GameEnd(&snake);
MoveCursor(16, 11);
printf("再来一局吗?(Y/N)");
while (_kbhit())
{
getch();
}
ch = getchar();
//读取换行
getchar();
} while (ch == 'Y' || ch == 'y');
MoveCursor(0, 21);
return 0;
}
void GameInit(pSnake snake, struct Point* food)
{
//控制台设置
system("mode con cols=82 lines=23");
system("title 贪吃蛇");
srand(time(0));
HideCursor();
菜单
Menu();
开始之前,做一些功能提醒
void Menu()
{
//欢迎
MoveCursor(30, 10);
puts("欢迎来到贪吃蛇游戏");
MoveCursor(31, 18);
system("pause");
system("cls");
//功能介绍
MoveCursor(15, 8);
puts("用 w . s . a . d 来控制蛇的移动,F3是加速,F4是减速");
MoveCursor(18, 10);
printf("加速能得到更高的分数");
MoveCursor(31, 18);
system("pause");
system("cls");
}
将墙打印出来,宽字节打印要用wprintf,字符串前面用L
关键是算好坐标
#define WALL L’□’
//地图大小
#define ROW 19
#define COL 44
void DrawMap()
{
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 0x6);
//四周
for (int i = 0; i < COL; i += 2)
{
MoveCursor(2 + i, 1);
wprintf(L"%lc", WALL);
MoveCursor(2 + i, 1 + ROW);
wprintf(L"%lc", WALL);
}
for (int i = 0; i < ROW; i++)
{
MoveCursor(2, 2 + i);
wprintf(L"%lc", WALL);
MoveCursor(COL, 2 + i);
wprintf(L"%lc", WALL);
}
printf("\r\n");
}
蛇的初始长度宏定义,每结对应一个节点,每个节点都有自己的坐标,将所有节点用蛇头节点管理,创建完后打印。设置蛇的速度,方向,分数等
蛇打印的宽字符
#define SNAKE L’●’
//初始化蛇
snake->phead = NULL;
for (int i = 0; i < LENGTH; i++)
{
pNode newnode = (pNode)malloc(sizeof(SnakeNode));
newnode->next = NULL;
newnode->spoint.x = SNAKE_X + i * 2;
newnode->spoint.y = SNAKE_Y;
//头插法
if (snake->phead == NULL)
{
snake->phead = newnode;
}
else
{
newnode->next = snake->phead;
snake->phead = newnode;
}
}
snake->score = 0;
snake->weight = 10;
snake->speed = 500;
snake->dre = RIGHT;
snake->sta = OK;
随机生成食物的坐标
创建食物对象,打印食物
食物打印的宽字符
#define FOOD L’★’
首先食物的坐标不能生成在墙上,所以x坐标从4-42,就是先生成0-38的随机数,再加4,y坐标就是2-18,先生成0-16的随机数再加2。再判断是不是在蛇身上,下面是食物坐标的生成
void FoodPoint(struct Point* food , pSnake snake)
{
pNode cur = snake->phead;
int flag = 0;
do
{
food->x = rand() % (COL - 5) + 4;
food->y = rand() % (ROW - 2) + 2;
flag = 1;
while (cur != NULL)
{
if (food->x == cur->spoint.x && food->y == cur->spoint.y)
{
flag = 0;
break;
}
cur = cur->next;
}
} while (food->x % 2 != 0 && flag == 1);
}
右侧打印帮助信息
根据游戏状态检查是否继续,如果状态OK则继续,否则游戏结束
如果继续就要检测按键情况,确定蛇下一步的方向和坐标,是否加速减速暂停退出等,就可以移动蛇了
void GameRun(pSnake snake, struct Point* food)
{
//打印帮助信息
PrintHelp();
DrawMap();
DrawSnake(snake);
DrawFood(food);
//循环
do
{
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 0x07);
//打印分数
MoveCursor(52, 6);
printf("总分:%3d\n", snake->score);
MoveCursor(51, 8);
printf("食物的分值:%4d\n", snake->weight);
//检测按键
//上 下 左 右 空格 F3
if (KEY_PRESS(0x57) && snake->dre != DOWN)
{
snake->dre = UP;
}
else if (KEY_PRESS(0x53) && snake->dre != UP)
{
snake->dre = DOWN;
}
else if (KEY_PRESS(0x41) && snake->dre != RIGHT)
{
snake->dre = LEFT;
}
else if (KEY_PRESS(0x44) && snake->dre != LEFT)
{
snake->dre = RIGHT;
}
else if (KEY_PRESS(VK_ESCAPE))
{
snake->sta = ESC;
}
else if (KEY_PRESS(VK_SPACE))
{
snake->sta = PAUSE;
//暂停
GamePause(snake);
}
else if (KEY_PRESS(VK_F3))
{
if (snake->speed >= 80)
{
snake->speed -= 100;
snake->weight += 2;
}
}
else if (KEY_PRESS(VK_F4))
{
if (snake->weight >= 2)
{
snake->speed += 100;
snake->weight -= 2;
}
}
//移动
MoveSnake(snake, food);
Sleep(snake->speed);
} while (snake->sta == OK);
}
检测按键,定义为宏
#define KEY_PRESS(VK) ( (GetAsyncKeyState(VK) & 0x1) ? 1 : 0 )
void PrintHelp()
{
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 0x07);
MoveCursor(49, 12);
printf("1.不能穿墙,不能咬到自己");
MoveCursor(49, 14);
printf("2.用 w.s.a.d 来控制蛇的移动");
MoveCursor(49, 16);
printf("3.F3是加速,F4是减速");
}
先创建下一个节点,根据移动的方向和蛇头的坐标,设置新蛇身的坐标
确定之后就要分下一个位置是不是食物,是食物就吃掉,刷新分数,重新生成食物不是就移动
将新节点连接蛇结构
移动后判断是否撞墙或撞自身,影响游戏状态结束游戏
void GameRun(pSnake snake, struct Point* food)
{
//打印帮助信息
PrintHelp();
DrawMap();
DrawSnake(snake);
DrawFood(food);
//循环
do
{
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 0x07);
//打印分数
MoveCursor(52, 6);
printf("总分:%3d\n", snake->score);
MoveCursor(51, 8);
printf("食物的分值:%4d\n", snake->weight);
//检测按键
//上 下 左 右 空格 F3
if (KEY_PRESS(0x57) && snake->dre != DOWN)
{
snake->dre = UP;
}
else if (KEY_PRESS(0x53) && snake->dre != UP)
{
snake->dre = DOWN;
}
else if (KEY_PRESS(0x41) && snake->dre != RIGHT)
{
snake->dre = LEFT;
}
else if (KEY_PRESS(0x44) && snake->dre != LEFT)
{
snake->dre = RIGHT;
}
else if (KEY_PRESS(VK_ESCAPE))
{
snake->sta = ESC;
}
else if (KEY_PRESS(VK_SPACE))
{
snake->sta = PAUSE;
//暂停
GamePause(snake);
}
else if (KEY_PRESS(VK_F3))
{
if (snake->speed >= 80)
{
snake->speed -= 100;
snake->weight += 2;
}
}
else if (KEY_PRESS(VK_F4))
{
if (snake->weight >= 2)
{
snake->speed += 100;
snake->weight -= 2;
}
}
//移动
MoveSnake(snake, food);
Sleep(snake->speed);
} while (snake->sta == OK);
}
void MoveSnake(pSnake snake, struct Point* food)
{
pNode newnode = (pNode)malloc(sizeof(SnakeNode));
newnode->next = NULL;
switch (snake->dre)
{
case UP:
newnode->spoint.x = snake->phead->spoint.x;
newnode->spoint.y = snake->phead->spoint.y - 1;
break;
case DOWN:
newnode->spoint.x = snake->phead->spoint.x;
newnode->spoint.y = snake->phead->spoint.y + 1;
break;
case LEFT:
newnode->spoint.x = snake->phead->spoint.x - 2;
newnode->spoint.y = snake->phead->spoint.y;
break;
case RIGHT:
newnode->spoint.x = snake->phead->spoint.x + 2;
newnode->spoint.y = snake->phead->spoint.y;
break;
}
//下一个坐标是不是食物
if (newnode->spoint.x == food->x && newnode->spoint.y == food->y)
{
EatFood(snake, food);
}
else
{
//释放尾结点
pNode cur = snake->phead;
while(cur->next->next != NULL)
{
cur = cur->next;
}
//尾结点处打印空
MoveCursor(cur->next->spoint.x, cur->next->spoint.y);
printf(" ");
free(cur->next);
cur->next = NULL;
}
newnode->next = snake->phead;
snake->phead = newnode;
//检测撞
KillWall(snake);
KillSelf(snake);
DrawSnake(snake);
}
吃掉食物更新分数,重新生成食物
void EatFood(pSnake snake, struct Point* food)
{
FoodPoint(food, snake);
snake->score += snake->weight;
DrawFood(food);
}
void KillWall(pSnake snake)
{
int x = snake->phead->spoint.x;
int y = snake->phead->spoint.y;
if (x == 2 || x == 44 || y == 1 || y == 19)
{
snake->sta = KILL_WALL;
return;
}
}
void KillSelf(pSnake snake)
{
pNode cur = snake->phead->next;
while (cur != NULL)
{
if (cur->spoint.x == snake->phead->spoint.x && cur->spoint.y == snake->phead->spoint.y)
{
snake->sta = KILL_SELF;
return;
}
cur = cur->next;
}
}
当游戏状态不再是OK或PAUSE的时候,游戏结束,告知结束的原因,并释放蛇身节点
void GameEnd(pSnake snake)
{
MoveCursor(18, 9);
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 0x07);
switch (snake->sta)
{
case ESC:
printf("退出游戏");
break;
case KILL_WALL:
printf("撞墙死亡");
break;
case KILL_SELF:
printf("撞自己死亡");
break;
}
//释放资源
pNode cur = snake->phead;
while (cur)
{
pNode del = cur;
cur = cur->next;
free(del);
}
}
源.c
#define _CRT_SECURE_NO_WARNINGS 1
#include
#include
#include
#include "Sanke.h"
int main()
{
int ch = 0;
do
{
//创建蛇
struct _Snake snake;
//创建食物
struct Point food;
//初始化
GameInit(&snake, &food);
//游戏循环
GameRun(&snake, &food);
//游戏结束
GameEnd(&snake);
MoveCursor(16, 11);
printf("再来一局吗?(Y/N)");
while (_kbhit())
{
getch();
}
ch = getchar();
//读取换行
getchar();
} while (ch == 'Y' || ch == 'y');
MoveCursor(0, 21);
return 0;
}
Sanke.h
#pragma once
//地图大小
#define ROW 19
#define COL 44
//各种物体形状
#define WALL L'□'
#define SNAKE L'●'
#define FOOD L'★'
//蛇的初始长度
#define LENGTH 3
//蛇初始位置
#define SNAKE_X COL / 2 + 2
#define SNAKE_Y 4
//按键
#define KEY_PRESS(VK) ( (GetAsyncKeyState(VK) & 0x1) ? 1 : 0 )
//方向
enum Dorection
{
UP,
DOWN,
LEFT,
RIGHT,
};
//状态
enum STATUS
{
OK = 1,
PAUSE,
KILL_WALL,
KILL_SELF,
ESC,
};
struct Point
{
int x;
int y;
};
//蛇身结点
typedef struct _SnakeNode
{
struct Point spoint; //坐标
struct _SnakeNode* next;
}SnakeNode, * pNode;
//蛇属性
typedef struct _Snake
{
pNode phead; //蛇头
int score; //分数
int weight; //权重
int speed; //移动间隔
enum Dorection dre; //方向
enum STATUS sta; //状态
}* pSnake;
//隐藏光标
void HideCursor();
//移动光标
void MoveCursor(int x, int y);
//菜单界面
void Menu();
//食物坐标随机检测
void FoodPoint(struct Point* food, pSnake snake);
//游戏初始化
void GameInit(pSnake snake, struct Point* food);
//绘制地图
void DrawMap();
//绘制蛇
void DrawSnake(pSnake snake);
//绘制食物
void DrawFood();
//游戏暂停
void GamePause(pSnake snake);
//游戏循环
void GameRun(pSnake snake, struct Point* food);
//移动
void MoveSnake(pSnake snake, struct Point* food);
//吃食物
void EatFood(pSnake snake, struct Point* food);
//打印帮助信息
void PrintHelp();
//检测撞墙
void KillWall(pSnake snake);
//检测撞自己
void KillSelf(pSnake snake);
//游戏结束释放资源
void GameEnd(pSnake snake);
Sanke.c
#include "Sanke.h"
#include
#include
#include
#include
#include
void HideCursor()
{
CONSOLE_CURSOR_INFO info;
HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
GetConsoleCursorInfo(handle, &info);
info.bVisible = false;
SetConsoleCursorInfo(handle, &info);
}
void MoveCursor(int x, int y)
{
HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
COORD coord = { x , y };
SetConsoleCursorPosition(handle, coord);
}
void Menu()
{
//欢迎
MoveCursor(30, 10);
puts("欢迎来到贪吃蛇游戏");
MoveCursor(31, 18);
system("pause");
system("cls");
//功能介绍
MoveCursor(15, 8);
puts("用 w . s . a . d 来控制蛇的移动,F3是加速,F4是减速");
MoveCursor(18, 10);
printf("加速能得到更高的分数");
MoveCursor(31, 18);
system("pause");
system("cls");
}
void FoodPoint(struct Point* food , pSnake snake)
{
pNode cur = snake->phead;
int flag = 0;
do
{
food->x = rand() % (COL - 5) + 4;
food->y = rand() % (ROW - 2) + 2;
flag = 1;
while (cur != NULL)
{
if (food->x == cur->spoint.x && food->y == cur->spoint.y)
{
flag = 0;
break;
}
cur = cur->next;
}
} while (food->x % 2 != 0 && flag == 1);
}
void GameInit(pSnake snake, struct Point* food)
{
//控制台设置
system("mode con cols=82 lines=23");
system("title 贪吃蛇");
srand(time(0));
HideCursor();
菜单
Menu();
//初始化蛇
snake->phead = NULL;
for (int i = 0; i < LENGTH; i++)
{
pNode newnode = (pNode)malloc(sizeof(SnakeNode));
newnode->next = NULL;
newnode->spoint.x = SNAKE_X + i * 2;
newnode->spoint.y = SNAKE_Y;
//头插法
if (snake->phead == NULL)
{
snake->phead = newnode;
}
else
{
newnode->next = snake->phead;
snake->phead = newnode;
}
}
snake->score = 0;
snake->weight = 10;
snake->speed = 500;
snake->dre = RIGHT;
snake->sta = OK;
//初始化食物
FoodPoint(food, snake);
//设置地区
setlocale(LC_ALL, "");
}
void DrawMap()
{
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 0x6);
//四周
for (int i = 0; i < COL; i += 2)
{
MoveCursor(2 + i, 1);
wprintf(L"%lc", WALL);
MoveCursor(2 + i, 1 + ROW);
wprintf(L"%lc", WALL);
}
for (int i = 0; i < ROW; i++)
{
MoveCursor(2, 2 + i);
wprintf(L"%lc", WALL);
MoveCursor(COL, 2 + i);
wprintf(L"%lc", WALL);
}
printf("\r\n");
}
void DrawSnake(pSnake snake)
{
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_GREEN);
pNode cur = snake->phead;
while (cur != NULL)
{
MoveCursor(cur->spoint.x, cur->spoint.y);
wprintf(L"%lc", SNAKE);
cur = cur->next;
}
}
void DrawFood(struct Point* food)
{
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_RED);
MoveCursor(food->x, food->y);
wprintf(L"%lc", FOOD);
}
void GamePause(pSnake snake)
{
while (snake->sta == PAUSE)
{
if (KEY_PRESS(VK_SPACE))
{
snake->sta = OK;
}
Sleep(100);
}
}
void GameRun(pSnake snake, struct Point* food)
{
//打印帮助信息
PrintHelp();
DrawMap();
DrawSnake(snake);
DrawFood(food);
//循环
do
{
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 0x07);
//打印分数
MoveCursor(52, 6);
printf("总分:%3d\n", snake->score);
MoveCursor(51, 8);
printf("食物的分值:%4d\n", snake->weight);
//检测按键
//上 下 左 右 空格 F3
if (KEY_PRESS(0x57) && snake->dre != DOWN)
{
snake->dre = UP;
}
else if (KEY_PRESS(0x53) && snake->dre != UP)
{
snake->dre = DOWN;
}
else if (KEY_PRESS(0x41) && snake->dre != RIGHT)
{
snake->dre = LEFT;
}
else if (KEY_PRESS(0x44) && snake->dre != LEFT)
{
snake->dre = RIGHT;
}
else if (KEY_PRESS(VK_ESCAPE))
{
snake->sta = ESC;
}
else if (KEY_PRESS(VK_SPACE))
{
snake->sta = PAUSE;
//暂停
GamePause(snake);
}
else if (KEY_PRESS(VK_F3))
{
if (snake->speed >= 80)
{
snake->speed -= 100;
snake->weight += 2;
}
}
else if (KEY_PRESS(VK_F4))
{
if (snake->weight >= 2)
{
snake->speed += 100;
snake->weight -= 2;
}
}
//移动
MoveSnake(snake, food);
Sleep(snake->speed);
} while (snake->sta == OK);
}
void MoveSnake(pSnake snake, struct Point* food)
{
pNode newnode = (pNode)malloc(sizeof(SnakeNode));
newnode->next = NULL;
switch (snake->dre)
{
case UP:
newnode->spoint.x = snake->phead->spoint.x;
newnode->spoint.y = snake->phead->spoint.y - 1;
break;
case DOWN:
newnode->spoint.x = snake->phead->spoint.x;
newnode->spoint.y = snake->phead->spoint.y + 1;
break;
case LEFT:
newnode->spoint.x = snake->phead->spoint.x - 2;
newnode->spoint.y = snake->phead->spoint.y;
break;
case RIGHT:
newnode->spoint.x = snake->phead->spoint.x + 2;
newnode->spoint.y = snake->phead->spoint.y;
break;
}
//下一个坐标是不是食物
if (newnode->spoint.x == food->x && newnode->spoint.y == food->y)
{
EatFood(snake, food);
}
else
{
//释放尾结点
pNode cur = snake->phead;
while(cur->next->next != NULL)
{
cur = cur->next;
}
//尾结点处打印空
MoveCursor(cur->next->spoint.x, cur->next->spoint.y);
printf(" ");
free(cur->next);
cur->next = NULL;
}
newnode->next = snake->phead;
snake->phead = newnode;
//检测撞
KillWall(snake);
KillSelf(snake);
DrawSnake(snake);
}
void EatFood(pSnake snake, struct Point* food)
{
FoodPoint(food, snake);
snake->score += snake->weight;
DrawFood(food);
}
void PrintHelp()
{
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 0x07);
MoveCursor(49, 12);
printf("1.不能穿墙,不能咬到自己");
MoveCursor(49, 14);
printf("2.用 w.s.a.d 来控制蛇的移动");
MoveCursor(49, 16);
printf("3.F3是加速,F4是减速");
}
void KillWall(pSnake snake)
{
int x = snake->phead->spoint.x;
int y = snake->phead->spoint.y;
if (x == 2 || x == 44 || y == 1 || y == 19)
{
snake->sta = KILL_WALL;
return;
}
}
void KillSelf(pSnake snake)
{
pNode cur = snake->phead->next;
while (cur != NULL)
{
if (cur->spoint.x == snake->phead->spoint.x && cur->spoint.y == snake->phead->spoint.y)
{
snake->sta = KILL_SELF;
return;
}
cur = cur->next;
}
}
void GameEnd(pSnake snake)
{
MoveCursor(18, 9);
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 0x07);
switch (snake->sta)
{
case ESC:
printf("退出游戏");
break;
case KILL_WALL:
printf("撞墙死亡");
break;
case KILL_SELF:
printf("撞自己死亡");
break;
}
//释放资源
pNode cur = snake->phead;
while (cur)
{
pNode del = cur;
cur = cur->next;
free(del);
}
}
参考:汉字字符集编码查询;中⽂字符集编码:GB2312、BIG5、GBK、GB18030、Unicode