网站建设方案书怎么签字上传,做盗版网站,网站做熊掌号码,网站风格对比文章目录 前言1.了解Win32API相关知识1.1什么是Win32API1.2设置控制台的大小、名称1.3控制台上的光标1.4 GetStdHandle#xff08;获得控制台信息#xff09;1.5 SetConsoleCursorPosition#xff08;设置光标位置#xff09;1.6 GetConsoleCursorInfo#xff08;获得光标… 文章目录 前言1.了解Win32API相关知识1.1什么是Win32API1.2设置控制台的大小、名称1.3控制台上的光标1.4 GetStdHandle获得控制台信息1.5 SetConsoleCursorPosition设置光标位置1.6 GetConsoleCursorInfo获得光标信息1.7 SetConsoleCursorInfo设置光标信息1.8 GetAsyncKeyState获取按键信息 2.游戏设计与分析2.1地图设计2.2数据结构的设计2.2.1蛇身的设计2.2.2蛇的方向2.2.3游戏的状态2.2.4管理整条蛇 2.3.游戏流程 3. 功能的具体实现游戏开始前3.1 初始化蛇3.2 创建食物游戏运行3.3 检测按键3.4 蛇移动3.5 判断节点是否是食物3.6 吃食物3.7 不吃食物3.8 撞墙3.9 撞到自己3.10 游戏结束3.11 main函数 4.参考代码4.1 main4.2 Snake.h4.3 Snake.c 前言
彻底学习完C语言之后为巩固所学我们来实现一个贪吃蛇小游戏。 该游戏涉及到的C语言的知识有函数、枚举、结构体、动态内存管理、预处理指令。 另外还包含数据结构链表以及Win32API相关知识。
先看一下游戏效果吧
目前该游戏还有很多可以改进的地方由于蛇的移动使用的是Sleep函数看起来有点卡顿而且按键会有一点延迟。 代码仓库链接
1.了解Win32API相关知识
1.1什么是Win32API
Win32 API也就是Microsoft Windows32位平台的应用程序编程接口。调用各种接口可以帮应⽤程序达到开启视窗、描绘图形、使⽤周边设备等目的由于这些函数服务的对象是应用程序(Application) 所以便称之为 Application Programming Interface简称 API 函数。
1.2设置控制台的大小、名称
设置大小和名称的命令为
mode con cols列 lines行tiele 名称
如果想在C语言中设置窗口需要使用system函数。 1.3控制台上的光标
COORD 是Windows API中定义的⼀个结构体表示⼀个字符在控制台屏幕上的坐标坐标系00的原点位于缓冲区的左侧顶部的单元格。使用需要window.h的头文件
COORD类型的声明
typedef struct _COORD {SHORT X;//横坐标SHORT Y;//纵坐标
} COOR, *PCOORD;1.4 GetStdHandle获得控制台信息
GetStdHandle是⼀个Windows API函数。它⽤于从⼀个特定的标准设备标准输⼊、标准输出或标准错误中取得⼀个句柄(⽤来标识不同设备的数值使用这个句柄可以操作设备控制台。
HANDLE WINAPI GetStdHandle( _In_ DWORD nStdHandle);该函数需要一个参数参数如下
1.5 SetConsoleCursorPosition设置光标位置
设置指定控制台屏幕缓冲区中的光标位置我们将想要设置的坐标信息放在COORD类型的pos中调用SetConsoleCursorPosition函数将光标位置设置到指定的位置。
BOOL WINAPI SetConsoleCursorPosition(HANDLE hConsoleOutput, /要设置哪个控制台COORD pos /将光标设置的位置
);通过上述三个功能实现设置光标位置 1.6 GetConsoleCursorInfo获得光标信息
GetConsoleCursorInfo检索有关指定控制台屏幕缓冲区的光标大小和可见性的信息。然后将获得的信息放在一个结构体中
BOOL WINAPI GetConsoleCursorInfo(_In_ HANDLE hConsoleOutput,_Out_ PCONSOLE_CURSOR_INFO lpConsoleCursorInfo
);_ Out_ PCONSOLE_CURSOR_INFO 是指向 CONSOLE_CURSOR_INFO 结构体的指针 该结构体包含有关控制台光标的信息
typedef struct _CONSOLE_CURSOR_INFO {DWORD dwSize; BOOL bVisible;
} CONSOLE_CURSOR_INFO, *PCONSOLE_CURSOR_INFO;dwSize由光标填充的字符单元格的百分比。 此值介于1到100之间。 光标外观会变化范围从完全填充单元格到单元底部的⽔平线条。bVisible光标的可见性。 如果光标可见则此成员为 TRUE。 1.7 SetConsoleCursorInfo设置光标信息
SetConsoleCursorInfo设置指定控制台屏幕缓冲区的光标的大小和可见性。 其参数和get方法相同
BOOL WINAPI SetConsoleCursorInfo(HANDLE hConsoleOutput,const CONSOLE_CURSOR_INFO *lpConsoleCursorInfo
);设置前 设置后 隐藏光标 1.8 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 )#includeWindows.h
int main()
{while (1){if (KEY_PRESS(0x30))printf(0\n);else if (KEY_PRESS(0x31))printf(1\n);else if (KEY_PRESS(0x32))printf(2\n);else if (KEY_PRESS(0x33))printf(3\n);else if (KEY_PRESS(0x34))printf(4\n);else if (KEY_PRESS(0x35))printf(5\n);else if (KEY_PRESS(0x36))printf(6\n);else if (KEY_PRESS(0x37))printf(7\n);}return 0;
}2.游戏设计与分析
2.1地图设计
由于我打印墙体和蛇想使用 □但是它是宽字符该如何打印呢 C语言规定宽字符的类型wchar_t 和宽字符的输⼊和输出函数。 头文件为locale.h 宽字符与普通字符对比 由于墙体使用的是宽字符所以在打印时需要注意 以下函数实现打印墙体的功能
#define WALL L□
void DrawMap()
{SetPos(0, 0);//上for (int i 0; i 56; i 2)wprintf(L%lc, WALL);//左、右for (int i 1; i 25; i){SetPos(0, i);wprintf(L%lc, WALL);SetPos(56, i);wprintf(L%lc, WALL);}//下SetPos(0, 26);for (int i 0; i 56; i 2)wprintf(L%lc, WALL);
}再打印地图之前我们需要设置窗口的大小并且给玩家一些引导Win32API的使用已在上面讲过
//窗口相关设置
void ScreenPrepare()
{//设置窗体大小system(mode con cols100 lines40);system(title 贪吃蛇小游戏);//隐藏光标HideCurSor();//打印欢迎语Welcome();//打印地图DrawMap();//显示提示信息HelpInfo();
}具体函数的实现我们会在后面给出先看一下效果吧~ 2.2数据结构的设计
2.2.1蛇身的设计
在游戏运行的过程中蛇每次吃⼀个⻝物蛇的⾝体就会变长⼀节如果我们使用链表存储蛇的信息那么蛇的每⼀节其实就是链表的每个节点。每个节点只要记录好蛇身节点在地图上的坐标就行所以蛇节点结构如下
//蛇节点的定义
typedef struct SnakeNode
{int x;int y;struct SnakeNode* next;//指向下一个节点
}SnakeNode,*pSnakeNode;//同时定义一个指向该节点的指针2.2.2蛇的方向
蛇的方向可以一一列举使用枚举
//蛇的方向
//上、下、做、左、右
enum Direction
{UP 1,DOWN,LEFT,RIGHT
};2.2.3游戏的状态
游戏状态可以⼀⼀列举使用枚举
//游戏的状态
enum GameState
{OK,//正常KILL_BY_WALL,//撞墙KILL_BY_SELF,//撞到自己END//结束
};2.2.4管理整条蛇
我们再封装⼀个Snake的结构体来维护整条贪吃蛇。
//整条蛇
typedef struct Snake
{pSnakeNode pSnake; //指向整条蛇的指针pSnakeNode PFood; //指向食物的指针enum Direction Dire; //蛇的方向enum GameState State; //蛇的状态int SleepTime; //蛇的休眠时间休眠越短蛇移动的越快int Score;//游戏得分int FoodScore;//一个食物的分数
}Snake,* pSnake;2.3.游戏流程 3. 功能的具体实现
游戏开始前
3.1 初始化蛇
游戏开始蛇的长度默认是3。
创建节点头插法依次连接节点的纵坐标相同横坐标依次增加2产生串的效果蛇的方向默认为右打印蛇并且为蛇头设置颜色
此处颜色设置我们使用Win32API提供的函数
BOOL WINAPI SetConsoleTextAttribute(_In_ HANDLE hConsoleOutput,//当前设备句柄_In_ WORD wAttributes//颜色
);//设置颜色
void SetColor(int HeadColor)
{SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), HeadColor);
}//打印蛇
void PrintSnake(pSnake ps)
{pSnakeNode cur ps-pSnake;int flag 1;while (cur){if (flag 1){//为蛇头设置颜色SetColor(12);}SetPos(cur-x, cur-y);//设置每个节点的位置wprintf(L%lc, BODY);cur cur-next;flag 0;SetColor(10);}
}//初始化蛇
void InitSnake(pSnake ps)
{int i 0;pSnakeNode cur NULL;//蛇的长度开始为3for (i 0; i 3; i){cur (pSnakeNode)malloc(sizeof(SnakeNode));if (cur NULL){perror(InitSnake():malloc);return;}cur-next NULL;cur-x POS_X i * 2; //节点横坐标依次增加2cur-y POS_Y;//头插法将节点相连if (ps-pSnake NULL){ps-pSnake cur;}else{cur-next ps-pSnake;ps-pSnake cur;}}//打印蛇PrintSnake(ps);//初始化蛇的其它信息ps-Dire RIGHT;ps-FoodScore 10;ps-SleepTime 200;ps-State OK;ps-Score 0;
}3.2 创建食物
食物要随机生成有以下几个注意事项
食物应该在墙体内食物不可与蛇身重叠由于我们蛇的打印使用的是宽字符所以食物的坐标应该在2的倍数处
//创建食物
void CreatFood(pSnake ps)
{int x 0;int y 0;//食物坐标应该在墙体内
again:do{x rand() % 53 2;y rand() % 25 1;} while (x % 2 ! 0);//食物坐标不与蛇重叠pSnakeNode cur ps-pSnake;while (cur){if (x cur-x y cur-y){goto again;}cur cur-next;}//根据坐标创建食物pSnakeNode pFood (pSnakeNode)malloc(sizeof(SnakeNode));if (pFood NULL){perror(CreatFood():malloc());return;}//打印食物pFood-x x;pFood-y y;SetColor(14);SetPos(pFood-x, pFood-y);wprintf(L%lc, FOOD);SetColor(10);ps-pFood pFood;
}游戏运行
3.3 检测按键
玩家按上下左右键控制蛇的移动在检测时需要注意
玩家按的键若与蛇的方向相反则不做响应玩家按键符合则修改蛇的方向玩家按F1或F2修改蛇的移动速度蛇的速度应大于0食物的分数最低为1分按键的检测使用Win32API提供的功能GetAsyncKeyState
//玩游戏
void GameRun(pSnake ps)
{do{//显示分数SetColor(12);SetPos(65, 12);printf(目前得分%-5d,ps-Score);SetColor(10);SetPos(65, 13);printf(每个食物%2d分,ps-FoodScore);//检测按键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_ESCAPE)){//按ESC,退出游戏ps-State END;}else if (KEY_PRESS(VK_SPACE)){//按空格暂停pause();}else if (KEY_PRESS(VK_F1)){//按F1加速,即睡眠时间变短//休眠时间不能是负数,最快就是休眠30msif (ps-SleepTime 50){ps-SleepTime - 20;//速度变快食物的分数变高ps-FoodScore 2;}}else if (KEY_PRESS(VK_F2)){//F2减速睡眠时间变长//食物的分数不能减到负数,最多减为1分if (ps-FoodScore 3){ps-SleepTime 20;ps-FoodScore - 2;}}//按照蛇的睡眠时间真正实现休眠Sleep(ps-SleepTime);//休眠后蛇要移动SnakeMove(ps);} while (ps-State OK);//只有蛇的状态为OK才能继续检测按键
}3.4 蛇移动
蛇的移动就是根据蛇的方向产生节点判断节点是不是食物。
所产生的节点的位置根据蛇的方向而定节点是食物吃掉食物蛇身变长节点不是食物也吃掉食物但长度不变
//移动蛇
void SnakeMove(pSnake ps)
{//先产生节点pSnakeNode pNext (pSnakeNode)malloc(sizeof(SnakeNode));if (pNext NULL){perror(SnakeMove():malloc());return;}//根据蛇的方向设定节点的位置switch (ps-Dire){case UP://蛇的方向向上节点应该在当前蛇头的上方;横坐标不变纵坐标减1pNext-x ps-pSnake-x;pNext-y ps-pSnake-y - 1;break;case DOWN://蛇的方向向下节点应该在当前蛇头的下方;横坐标不变纵坐标加1pNext-x ps-pSnake-x;pNext-y ps-pSnake-y 1;break;case LEFT://蛇的方向向左节点应该在当前蛇头的左边;纵坐标不变横坐标减2pNext-x ps-pSnake-x - 2;pNext-y ps-pSnake-y;break;case RIGHT://蛇的方向向右节点应该在当前蛇头的右方;纵坐标不变横坐标加2pNext-x ps-pSnake-x 2;pNext-y ps-pSnake-y;break;}//判断下一个节点是否是食物if (JudgeNext(ps,pNext)){//是食物吃掉长度增加EatFood(ps, pNext);}else{//不是食物吃掉长度不增加NoFood(ps, pNext);}//未完//撞墙//撞自己
}3.5 判断节点是否是食物
将所产生的节点与食物的节点对比即可
//判断节点是否是食物
int JudgeNext(pSnake ps, pSnakeNode pNext)
{return ((ps-pFood-x pNext-x) (ps-pFood-y pNext-y));
}3.6 吃食物
吃掉食物很简单将所产生的节点与蛇想连连接后得分增加并释放所产生的节点再次产生食物。
//是食物吃掉长度增加
void EatFood(pSnake ps, pSnakeNode pNext)
{//吃掉食物头插法将节点插入pNext-next ps;ps pNext;//打印蛇PrintSnake(ps_-snake);//加分PrintSnake(ps-pSnake);//释放食物节点free(ps-pFood);//再次创建食物CreatFood(ps);
}3.7 不吃食物
不吃食物需要将产生的节点与蛇相连然后删除蛇尾将蛇尾打印尾空格并释放蛇尾节点
//不吃食物
void NoFood(pSnake ps, pSnakeNode pNext)
{//头插法连接pNext-next ps-pSnake;ps-pSnake pNext;//删除蛇尾pSnakeNode cur ps-pSnake;//找到蛇尾的前一个节点while (cur-next-next){cur cur-next;}pSnakeNode del cur-next;//将蛇尾打印尾空格SetPos(del-x, del-y);printf( );//释放蛇尾节点free(del);cur-next NULL;//打印蛇PrintSnake(ps);
}3.8 撞墙
若蛇头坐标与墙体坐标重合则说明撞墙了。
//撞墙
void KillByWall(pSnake ps)
{if (ps-pSnake-x 0 ||ps-pSnake-x 56 ||ps-pSnake-y 0 ||ps-pSnake-y 26){ps-State KillByWall;}
}3.9 撞到自己
若蛇头坐标与蛇身节点的坐标重合则说明撞到自己了。
//撞到自己
void KillBySelf(pSnake ps)
{pSnakeNode cur ps-pSnake-next;while (cur){if (cur-x ps-pSnake-x cur-y ps-pSnake-y){ps-State KILL_BY_SELF;}cur cur-next;}
}3.10 游戏结束
给出游戏结束的原因释放蛇身节点。
//游戏结束
void GameEnd(pSnake ps)
{SetPos(20, 13);SetColor(12);switch (ps-State){case END:printf(您结束了游戏);break;case KILL_BY_SELF:printf(很遗憾您撞到了自己);break;case KILL_BY_WALL:printf(很遗憾您撞墙了);break;}pSnakeNode cur ps-pSnake;while (cur){pSnakeNode del cur;cur cur-next;free(del);}ps NULL;SetColor(10);
}3.11 main函数
让玩家选择
int main()
{srand((unsigned int)time(NULL));setlocale(LC_ALL, );//适应本地中文环境int ch 0;do{Snake snake { 0 };//创建贪吃蛇//游戏开始前的初始化GameStart(snake);玩游戏GameRun(snake);游戏结束善后工作GameEnd(snake);SetPos(20, 15);printf(还要再来一局吗Y/N);ch getchar();getchar();//吸收换行} while (ch Y || ch y);SetPos(0, 30);return 0;
}4.参考代码
4.1 main
#define _CRT_SECURE_NO_WARNINGS 1
#includeSnake.hint main()
{srand((unsigned int)time(NULL));setlocale(LC_ALL, );//适应本地中文环境int ch 0;do{Snake snake { 0 };//创建贪吃蛇//游戏开始前的初始化GameStart(snake);玩游戏GameRun(snake);游戏结束善后工作GameEnd(snake);SetPos(20, 15);printf(还要再来一局吗Y/N);ch getchar();getchar();//吸收换行} while (ch Y || ch y);SetPos(0, 30);return 0;
}4.2 Snake.h
#define _CRT_SECURE_NO_WARNINGS 1#includelocale.h
#includestdio.h
#includeWindows.h
#includestdlib.h
#includestdbool.h
#includetime.h#define KEY_PRESS(VK) ((GetAsyncKeyState(VK)0x1) ? 1 : 0)#define WALL L□//墙体
#define BODY L●//蛇身
#define FOOD L★//食物//蛇初始位置
#define POS_X 24
#define POS_Y 12//蛇的方向
//上、下、左、右
enum Direction
{UP 1,DOWN,LEFT,RIGHT
};//游戏的状态
enum GameState
{OK,//正常KILL_BY_WALL,//撞墙KILL_BY_SELF,//撞到自己END//结束
};//蛇节点的定义
typedef struct SnakeNode
{int x;int y;struct SnakeNode* next;//指向下一个节点
}SnakeNode,*pSnakeNode;//同时定义一个指向该节点的指针//整条蛇
typedef struct Snake
{pSnakeNode pSnake; //指向整条蛇的指针pSnakeNode pFood; //指向食物的指针enum Direction Dire; //蛇的方向enum GameState State; //蛇的状态int SleepTime; //蛇的休眠时间休眠越短蛇移动的越快int Score;//游戏得分int FoodScore;//一个食物的分数
}Snake,* pSnake;//设置光标位置
void SetPos(int x, int y);//设置颜色
void SetColor(int HeadColor);//游戏准备
void GameStart(pSnake psnake);//初始化蛇
void InitSnake(pSnake psnake);//创建食物
void CreatFood(pSnake ps);//打印蛇
void PrintSnake(pSnake ps);//玩游戏
void GameRun(pSnake ps);//移动蛇
void SnakeMove(pSnake ps);//判断节点是否是食物
int JudgeNext(pSnake ps, pSnakeNode pNext);//是食物吃掉长度增加
void EatFood(pSnake ps, pSnakeNode pNext);//不是食物
void NoFood(pSnake ps, pSnakeNode pNext);//撞墙
void KillByWall(pSnake ps);//撞到自己
void KillBySelf(pSnake ps);//游戏结束
void GameEnd(pSnake ps);
4.3 Snake.c
#define _CRT_SECURE_NO_WARNINGS 1#includeSnake.h//隐藏光标
void HideCurSor()
{CONSOLE_CURSOR_INFO cursor { 0 };HANDLE handle GetStdHandle(STD_OUTPUT_HANDLE);GetConsoleCursorInfo(handle, cursor);cursor.bVisible false;SetConsoleCursorInfo(handle, cursor);
}//设置光标位置
void SetPos(int x, int y)
{COORD pos { x,y }; //要设置的位置HANDLE handle GetStdHandle(STD_OUTPUT_HANDLE);//哪个设备SetConsoleCursorPosition(handle, pos);//设置
}void Welcome()
{//显示欢迎语一SetPos(38, 15);printf(欢迎来到贪吃蛇小游戏);SetPos(40, 24);system(pause);system(cls);//清屏//提示语SetPos(25, 15);printf(按 ↑、↓、←、→ 控制蛇蛇的移动F1为加速F2为减速);SetPos(25, 16);printf(加速将能得到更高的分数);SetPos(40, 24);system(pause);
}//打印地图
void DrawMap()
{system(cls);SetColor(6);SetPos(0, 0);//上for (int i 0; i 56; i 2)wprintf(L%lc, WALL);//左、右for (int i 1; i 25; i){SetPos(0, i);wprintf(L%lc, WALL);SetPos(56, i);wprintf(L%lc, WALL);}//下SetPos(0, 26);for (int i 0; i 56; i 2)wprintf(L%lc, WALL); SetColor(10);
}//显示提示信息
void HelpInfo()
{SetPos(65, 18);printf(不能穿墙不能碰到自己,);SetPos(65, 19);printf(按 ↑、↓、←、→ 控制蛇蛇的移动,);SetPos(65, 21);printf(F1为加速F2为减速);SetPos(65, 22);printf(ESC退出游戏 SPACE暂停);
}//窗口相关设置
void ScreenPrepare()
{//设置窗体大小system(mode con cols100 lines40);system(title 贪吃蛇小游戏);//隐藏光标HideCurSor();//打印欢迎语Welcome();//打印地图DrawMap();//显示提示信息HelpInfo();
}//设置颜色
void SetColor(int HeadColor)
{SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), HeadColor);
}//打印蛇
void PrintSnake(pSnake ps)
{pSnakeNode cur ps-pSnake;int flag 1;while (cur){if (flag 1){//为蛇头设置颜色SetColor(12);}SetPos(cur-x, cur-y);//设置每个节点的位置wprintf(L%lc, BODY);cur cur-next;flag 0;SetColor(10);}
}//初始化蛇
void InitSnake(pSnake ps)
{int i 0;pSnakeNode cur NULL;//蛇的长度开始为3for (i 0; i 5; i){cur (pSnakeNode)malloc(sizeof(SnakeNode));if (cur NULL){perror(InitSnake():malloc);return;}cur-next NULL;cur-x POS_X i * 2; //节点横坐标依次增加2cur-y POS_Y;//头插法将节点相连if (ps-pSnake NULL){ps-pSnake cur;}else{cur-next ps-pSnake;ps-pSnake cur;}}//打印蛇PrintSnake(ps);//初始化蛇的其它信息ps-Dire RIGHT;ps-FoodScore 10;ps-SleepTime 200;ps-State OK;ps-Score 0;
}//创建食物
void CreatFood(pSnake ps)
{int x 0;int y 0;//食物坐标应该在墙体内
again:do{x rand() % 53 2;y rand() % 25 1;} while (x % 2 ! 0);//食物坐标不与蛇重叠pSnakeNode cur ps-pSnake;while (cur){if (x cur-x y cur-y){goto again;}cur cur-next;}//根据坐标创建食物pSnakeNode pFood (pSnakeNode)malloc(sizeof(SnakeNode));if (pFood NULL){perror(CreatFood():malloc());return;}//打印食物pFood-x x;pFood-y y;SetColor(14);SetPos(pFood-x, pFood-y);wprintf(L%lc, FOOD);SetColor(10);ps-pFood pFood;
}//游戏准备
void GameStart(pSnake psnake)
{//设置好窗口、地图ScreenPrepare();//初始化蛇InitSnake(psnake);//创建食物CreatFood(psnake);
}//暂停
void pause()
{while (1){//一直休眠直到再次按空格键Sleep(200);if (KEY_PRESS(VK_SPACE)){break;}}
}//玩游戏
void GameRun(pSnake ps)
{do{//显示分数SetColor(12);SetPos(65, 12);printf(目前得分%-5d,ps-Score);SetColor(10);SetPos(65, 13);printf(每个食物%2d分,ps-FoodScore);//检测按键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_ESCAPE)){//按ESC,退出游戏ps-State END;}else if (KEY_PRESS(VK_SPACE)){//按空格暂停pause();}else if (KEY_PRESS(VK_F1)){//按F1加速,即睡眠时间变短//休眠时间不能是负数,最快就是休眠30msif (ps-SleepTime 50){ps-SleepTime - 20;//速度变快食物的分数变高ps-FoodScore 2;}}else if (KEY_PRESS(VK_F2)){//F2减速睡眠时间变长//食物的分数不能减到负数,最多减为1分if (ps-FoodScore 3){ps-SleepTime 20;ps-FoodScore - 2;}}//按照蛇的睡眠时间真正实现休眠Sleep(ps-SleepTime);//休眠后蛇要移动SnakeMove(ps);} while (ps-State OK);//只有蛇的状态为OK才能继续检测按键
}//移动蛇
void SnakeMove(pSnake ps)
{//先产生节点pSnakeNode pNext (pSnakeNode)malloc(sizeof(SnakeNode));if (pNext NULL){perror(SnakeMove():malloc());return;}//根据蛇的方向设定节点的位置switch (ps-Dire){case UP://蛇的方向向上节点应该在当前蛇头的上方;横坐标不变纵坐标减1pNext-x ps-pSnake-x;pNext-y ps-pSnake-y - 1;break;case DOWN://蛇的方向向下节点应该在当前蛇头的下方;横坐标不变纵坐标加1pNext-x ps-pSnake-x;pNext-y ps-pSnake-y 1;break;case LEFT://蛇的方向向左节点应该在当前蛇头的左边;纵坐标不变横坐标减2pNext-x ps-pSnake-x - 2;pNext-y ps-pSnake-y;break;case RIGHT://蛇的方向向右节点应该在当前蛇头的右方;纵坐标不变横坐标加2pNext-x ps-pSnake-x 2;pNext-y ps-pSnake-y;break;}//判断下一个节点是否是食物if (JudgeNext(ps, pNext)){//是食物吃掉长度增加EatFood(ps, pNext);}else{//不是食物吃掉长度不增加NoFood(ps, pNext);}//撞墙KillByWall(ps);//撞自己KillBySelf(ps);
}//判断节点是否是食物
int JudgeNext(pSnake ps, pSnakeNode pNext)
{return ((ps-pFood-x pNext-x) (ps-pFood-y pNext-y));
}//是食物吃掉长度增加
void EatFood(pSnake ps, pSnakeNode pNext)
{//吃掉食物头插法将节点插入pNext-next ps-pSnake;ps-pSnake pNext;//打印蛇PrintSnake(ps);//加分ps-Score ps-FoodScore;//释放食物节点free(ps-pFood);//再次创建食物CreatFood(ps);
}//不是食物
void NoFood(pSnake ps, pSnakeNode pNext)
{//头插法连接pNext-next ps-pSnake;ps-pSnake pNext;//删除蛇尾pSnakeNode cur ps-pSnake;//找到蛇尾的前一个节点while (cur-next-next){cur cur-next;}pSnakeNode del cur-next;//将蛇尾打印为空格SetPos(del-x, del-y);printf( );//释放蛇尾节点free(del);cur-next NULL;//打印蛇PrintSnake(ps);
}//撞墙
void KillByWall(pSnake ps)
{if (ps-pSnake-x 0 ||ps-pSnake-x 56 ||ps-pSnake-y 0 ||ps-pSnake-y 26){ps-State KILL_BY_WALL;}
}//撞到自己
void KillBySelf(pSnake ps)
{pSnakeNode cur ps-pSnake-next;while (cur){if (cur-x ps-pSnake-x cur-y ps-pSnake-y){ps-State KILL_BY_SELF;}cur cur-next;}
}//游戏结束
void GameEnd(pSnake ps)
{SetPos(20, 13);SetColor(12);switch (ps-State){case END:printf(您结束了游戏);break;case KILL_BY_SELF:printf(很遗憾您撞到了自己);break;case KILL_BY_WALL:printf(很遗憾您撞墙了);break;}pSnakeNode cur ps-pSnake;while (cur){pSnakeNode del cur;cur cur-next;free(del);}ps NULL;SetColor(10);}