建湖网站建设价格精品网站建设教程
建湖网站建设价格,精品网站建设教程,郑州英语网站建设,内容营销公司我们这篇会使用C语言在Windows环境的控制台中模拟实现经典小游戏贪吃蛇 实现基本的功能#xff1a;
结果如下: 1.一些Win32 API知识
本次实现呢我们会用到一些Win32 API的知识(WIN32 API也就是Microsoft Windows 32位平台的应用程序编程接口):
1)控制窗口大小 我们可以使用…我们这篇会使用C语言在Windows环境的控制台中模拟实现经典小游戏贪吃蛇 实现基本的功能
结果如下: 1.一些Win32 API知识
本次实现呢我们会用到一些Win32 API的知识(WIN32 API也就是Microsoft Windows 32位平台的应用程序编程接口):
1)控制窗口大小 我们可以使用cmd命令来设置控制台窗口的长宽设置控制台窗口的大小,设置控制台窗口的名字: mode con cols100 lines30//设置窗口大小为100列,30行
title 贪吃蛇//设置窗口名称为贪吃蛇我们在C语言中可以用函数system来执行,入上面这两个,我们可以这么写: system(mode con cols100 lines30);
system(title 贪吃蛇); 2)控制台屏幕上的坐标 Windows API中存在这么一个结构体,叫做COORD,表示⼀个字符在控制台屏幕上的坐标 typedef struct _COORD {SHORT X;SHORT Y;
} COORD, *PCOORD; 给坐标赋值 COORD pos { 10, 15 }; 3)GetStdHandle GetStdHandle是⼀个Windows API函数。它⽤于从⼀个特定的标准设备标准输入、标准输出或标准错误中取得⼀个句柄用来标识不同设备的数值),使用这个句柄可以操作设备。 HANDLE GetStdHandle(DWORD nStdHandle); 4)GetConsoleCursorInfo 检索有关指定控制台屏幕缓冲区的光标大小和可见性的信息 BOOL WINAPI GetConsoleCursorInfo(HANDLE hConsoleOutput,PCONSOLE_CURSOR_INFO lpConsoleCursorInfo
); 实例 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; //隐藏控制台光标 6)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);//设置控制台光标状态 7)SetConsoleCursorPosition 设置指定控制台屏幕缓冲区中的光标位置我们将想要设置的坐标信息放在COORD类型的pos中调用SetConsoleCursorPosition函数将光标位置设置到指定的位置 BOOL WINAPI SetConsoleCursorPosition(HANDLE hConsoleOutput,COORD pos
); 示例: COORD pos { 10, 5};HANDLE hOutput NULL;//获取标准输出的句柄(⽤来标识不同设备的数值)hOutput GetStdHandle(STD_OUTPUT_HANDLE);//设置标准输出上光标的位置为posSetConsoleCursorPosition(hOutput, pos); 用上面的这些函数我们就可以封装⼀个设置光标位置的函数 void SetPos(short x, short y)
{COORD pos { x, y };HANDLE hOutput NULL;//获取标准输出的句柄(⽤来标识不同设备的数值)hOutput GetStdHandle(STD_OUTPUT_HANDLE);//设置标准输出上光标的位置为posSetConsoleCursorPosition(hOutput, pos);
} 8)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 ) 2.游戏的逻辑 在做这个游戏前,我们应该先想好怎么做,游戏每个阶段该做什么. 这里我做了一个图: 3. 贪吃蛇的整体维护和地图绘制
整体维护我们创建一个结构体来维护整体运行
typedef struct Snake
{pSnakeNode pSnake;//维护整条蛇的指针是指向蛇头pSnakeNode pFood;//指向食物的指针int Score;//当前累积的分数int FoodWeight;//一个食物的分数int SleepTime;//蛇休眠的时间休眠的时间越短蛇的速度越快休眠的时间越长蛇的速度越慢enum GAME_STATUS status;//游戏当前的状态enum DIRECTION dir;//蛇当前走的方向
}Snake, * pSnake;我们顺便将蛇,目标,墙用宽字符定义了
#define WALL L□
#define BODY L●
#define FOOD L★窗口大小,名字,以及隐藏光标,我们都解决了,现在我们来解决打印贪吃蛇地图的问题 这里不得不讲⼀下控制台窗口的⼀些知识如果想在控制台的窗口中指定位置输出信息我们得知道该位置的坐标所以首先介绍⼀下控制台窗口的坐标知识.控制台窗口的坐标如下所示横向的是X轴从左向右依次增长纵向是Y轴从上到下依次增长。 在游戏地图上我们打印墙使用宽字符□打印蛇使用宽字符●打印食物使用宽字符★ 普通的字符是占一个字节的这类宽字符是占用2个字节。 过去C语言并不适合非英语国家地区使用。 C语言最初假定字符都是但自己的。但是这些假定并不是在世界的任何地方都适用。 C语言字符默认是采用ASCII编码的ASCII字符集采用的是单字节编码且只使用了单字节中的低7 位最高位是没有使用的可表示为0xxxxxxxx可以看到ASCII字符集共包含128个字符在英语国家中128个字符是基本够用的但是在其他国家语言中比如在法语中字母上方有注音符号它就无法用ASCII码表示。于是⼀些欧洲国家就决定利用字节中闲置的最高位编入新的符号。比如法语中的é的编码为130⼆进制10000010。这样⼀来这些欧洲国家使用的编码体系可以表示最多256个符号。但是这里又出现了新的问题。不同的国家有不同的字母因此哪怕它们都使用256个符号的编码方式代表的字母却不⼀样。比如130在法语编码中代表了é在希伯来语编码中却代表了字⺟Gimel在俄语编码中又会代表另⼀个符号。但是不管怎样所有这些编码方式中0--127表⽰的符号是⼀样的不⼀样的只是128--255的这⼀段。至于亚洲国家的文字使用的符号就更多了汉字就多达10万左右。⼀个字节只能表示256种符号肯定是不够的就必须使用多个字节表达⼀个符号。比如简体中文常见的编码方式是 GB2312使用两个字节表示⼀个汉字所以理论上最多可以表示 256 x 256 65536 个符号。 后来为了使C语言适应国际化C语⾔的标准中不断加入了国际化的支持。比如加入和宽字符的类型wchar_t 和宽字符的输入和输出函数加入locale.h头文件其中提供了允许程序员针对特定地区通常是国家或者说某种特定语言的地理区域调整程序行为的函数。 1)locale.h头文件 选自clocale locale.h - C参考 (cplusplus.com)
2)setlocale 函数 setlocale 函数用于修改当前地区可以针对⼀个类项修改也可以针对所有类项。 setlocale 的第一个参数可以是前⾯说明的类项中的⼀个那么每次只会影响⼀个类项如果第⼀个参数是LC_ALL就会影响所有的类项。 C标准给第二个参数仅定义了2种可能取值C和 。 在任意程序执行开始都会隐藏式执行调用: setlocale(LC_ALL, C); 当地区设置为C时库函数按正常方式执行小数点是⼀个点。 当程序运行起来后想改变地区就只能显示调用setlocale函数。用 作为第2个参数调用setlocale 函数就可以切换到本地模式这种模式下程序会适应本地环境。比如切换到我们的本地模式后就支持宽字符汉字的输出等。 setlocale(LC_ALL, );//切换到本地环境 在切换到我们的本地环境后就可以打印宽字符了 示例 #include stdio.h
#includelocale.h
int main()
{setlocale(LC_ALL, );wchar_t ch1 L●;char ch2 ;wprintf(L%c, ch1);printf(%c\n, ch2);printf(%c, ch2);return 0;
} 运行结果 经过对比我们发现⼀个普通字符占⼀个字符的位置 但是打印⼀个宽字符占用2个字符的位置那么我们如果要在贪吃蛇中使用宽字符就得处理好地图上坐标的计算。 3地图坐标 我们假设实现⼀个棋盘27行58列的棋盘行和列可以根据自己的情况修改再围绕地图画出墙 图在上面代码如下 #define WALL L□
void createmap()
{//上SetPos(0, 0);for (int i 0; i 56; i 2){wprintf(L%lc, WALL);}//下SetPos(0, 26);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);}//右for (int i 1; i 25; i){SetPos(56, i);wprintf(L%lc, WALL);}
} 4.有关蛇的定义
我们创建一个结构体来维护蛇身的节点
typedef struct SnakeNode
{int x;int y;struct SnakeNode* next;
}SnakeNode, * pSnakeNode;顺带我们枚举蛇的运动方向
enum DIRECTION
{UP,DOWN,LEFT,RIGHT
};
5.GameStart()
接下来我们开始按照流程来,现在是游戏前的准备
1设置窗口大小、设置窗口名字、隐藏光标
void setwindows()
{system(title 贪吃蛇);system(mode con cols100 lines30);
}
void hidcursor()
{//获得设备句柄HANDLE handle GetStdHandle(STD_OUTPUT_HANDLE);//定义控制台相关光标的结构体CONSOLE_CURSOR_INFO CursorInfo;GetConsoleCursorInfo(handle, CursorInfo);//获取控制台光标信息CursorInfo.bVisible false;//光标可见性关闭SetConsoleCursorInfo(handle, CursorInfo);//设置控制台光标状态
}void SetPos(int x, int y)
{//获得设备句柄HANDLE hanlde GetStdHandle(STD_OUTPUT_HANDLE);//根据句柄设置光标的位置COORD pos { x, y };//设置控制台光标状态SetConsoleCursorPosition(hanlde, pos);
} 2欢迎词
前面我们运行的时候都会有欢迎来到XXX,所以我们也做一个
void welcome()
{//欢迎信息SetPos(35, 10);printf(欢迎来到贪吃蛇小游戏\n);SetPos(38, 20);system(pause);system(cls);//功能介绍信息SetPos(15, 10);printf(用 ↑ . ↓ . ← . → 来控制蛇的移动F3是加速F4是减速);SetPos(15, 11);printf(加速能得到更高的分数);SetPos(38, 20);system(pause);system(cls);
}
(35,10)是我设计的打印时的光标位置你也可以自行修改。cls是清屏 3地图的创建与贪吃蛇的初始化
地图创建前面我讲过了这里就展示一下代码
void createmap()
{//上SetPos(0, 0);for (int i 0; i 56; i 2){wprintf(L%lc, WALL);}//下SetPos(0, 26);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);}//右for (int i 1; i 25; i){SetPos(56, i);wprintf(L%lc, WALL);}
}贪吃蛇的初始化包括蛇的出实话以及各种信息的初始化。
我们使用单链表将蛇的身体一节一节连起来最后用头插把蛇头接上蛇身
代码示例 pSnakeNode cur NULL;int i 0;for (i 0; i 3; i){cur (pSnakeNode)malloc(sizeof(SnakeNode));if (cur NULL){perror(InitSnake():malloc());return;}cur-x POS_X 2 * i;cur-y POS_Y;cur-next NULL;//头插法if (ps-pSnake NULL){ps-pSnake cur;}else{cur-next ps-pSnake;ps-pSnake cur;} }蛇写好了我们要打印出来所以我们可以遍历链表然后打印出整条蛇 //打印蛇身cur ps-pSnake;while (cur){SetPos(cur-x, cur-y);wprintf(L%lc, BODY);cur cur-next;}
这样我们的蛇就初始化完了接下来我们把其他信息也初始化
//贪吃蛇的其他信息初始化ps-dir RIGHT;ps-FoodWeight 10;ps-pFood NULL;ps-Score 0;ps-SleepTime 200;ps-status OK;
最后把这三段拼在一起就是贪吃蛇的初始化了
//初始化贪吃蛇
void InitSnake(pSnake ps)
{pSnakeNode cur NULL;int i 0;for (i 0; i 3; i){cur (pSnakeNode)malloc(sizeof(SnakeNode));if (cur NULL){perror(InitSnake():malloc());return;}cur-x POS_X 2 * i;cur-y POS_Y;cur-next NULL;//头插法if (ps-pSnake NULL){ps-pSnake cur;}else{cur-next ps-pSnake;ps-pSnake cur;} }//打印蛇身cur ps-pSnake;while (cur){SetPos(cur-x, cur-y);wprintf(L%lc, BODY);cur cur-next;}//贪吃蛇的其他信息初始化ps-dir RIGHT;ps-FoodWeight 10;ps-pFood NULL;ps-Score 0;ps-SleepTime 200;ps-status OK;
}
4食物的创建
既然我们完成了蛇的初始化在游戏没开始的时候食物不也是创建好了吗所以我们接下来写事物的创建但是我们食物的创建存在一定的条件对吧。
存在以下两个条件
1.食物不能与蛇重叠
2.食物不能生成在墙上
代码示例
//创建食物
void Createfood(pSnake ps)
{int x 0;int y 0;again:do{x rand() % 53 2;y rand() % 24 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(CreateFood()::malloc());return;}pFood-x x;pFood-y y;ps-pFood pFood;SetPos(x, y);wprintf(L%lc, FOOD);
}
注意考虑空间开辟失败的情况。 4打印帮助信息
我们在运行游戏时右边有一个帮助信息这个也是提前打好的
代码示例
//打印帮助信息
void PrintHelpInfo()
{SetPos(59, 15);printf(1.不要撞到墙);SetPos(59, 16);printf(2.不要撞到自己);SetPos(59, 17);printf(3.用 ↑ . ↓ . ← . → 来控制蛇的移动);SetPos(59, 18);printf(4.F3加速F4减速,加速能得到更高的分数);SetPos(59, 19);printf(5.ESC退出,space暂停);SetPos(59, 20);printf(Dream_Snowar所作);
}5整合
最后我们将代码整合起来就是我们的要的函数
//游戏初始化
void GameStart(pSnake ps)
{setwindows();welcome();hidcursor();createmap();InitSnake(ps);Createfood(ps);PrintHelpInfo();
}6.GameRun()
这里我们需要完成蛇的移动蛇吃食物各个按键的使用
首先我们写一下函数的结构以及判断各个按键
代码示例
void GameRun(pSnake ps)
{ do{//当前的分数SetPos(59, 10);printf(总分:%d\n, ps-Score);SetPos(59, 11);printf(分数倍率:%02d\n, ps-FoodWeight);//检测按键,↑ . ↓ . ← . → . ESC . F3 . F4 . spaceif (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_ESCAPE)){ps-status ESC;break;}else if (KEY_PRESS(VK_SPACE)){//游戏暂停pause();}else if (KEY_PRESS(VK_F3)){if (ps-SleepTime 80){ps-SleepTime - 30;ps-FoodWeight 2;}}else if (KEY_PRESS(VK_F4)){if (ps-FoodWeight 2){ps-SleepTime 30;ps-FoodWeight - 2;}}//休眠Sleep(ps-SleepTime);//走一步Snakemove(ps);KillBySelf(ps);KillByWall(ps);} while (ps-status OK);}按键检测我们前面已经定义过了
#define KEY_PRESS(VK) ( (GetAsyncKeyState(VK) 0x1) ? 1 : 0 )1暂停pause()
void pause()
{while (1){Sleep(100);if (KEY_PRESS(VK_SPACE)){break;}}
}2判断下一个坐标是否是食物
int Nextblock(pSnake ps, pSnakeNode pNext)
{if (ps-pFood-x pNext-x ps-pFood-y pNext-y){return 1;}else{return 0;}
}1是食物
void IsFood(pSnake ps, pSnakeNode pNext)
{pNext-next ps-pSnake;ps-pSnake pNext;pSnakeNode cur ps-pSnake;while (cur){SetPos(cur-x, cur-y);wprintf(L%lc, BODY);cur cur-next;}ps-Score ps-FoodWeight;free(ps-pFood);Createfood(ps);
} 2不是食物
void NotFood(pSnake ps, pSnakeNode pNext)
{pNext-next ps-pSnake;ps-pSnake pNext;pSnakeNode cur ps-pSnake;while (cur-next-next){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;
}3是否撞到墙
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;}
} 4是否撞到自己
//是否撞自己
void KillBySelf(pSnake ps)
{pSnakeNode cur ps-pSnake-next;//从第二个节点开始while (cur){if (cur-x ps-pSnake-x cur-y ps-pSnake-y){ps-status KILL_BY_SELF;return;}cur cur-next;}
} 5蛇移动
void Snakemove(pSnake ps)
{pSnakeNode pNext (pSnakeNode)malloc(sizeof(SnakeNode));if (pNext NULL){perror(CreateFood()::malloc());return;}pNext-next NULL;switch (ps-dir){case UP:pNext-x ps-pSnake-x;pNext-y ps-pSnake-y - 1;break;case DOWN:pNext-x ps-pSnake-x;pNext-y ps-pSnake-y 1;break;case LEFT:pNext-x ps-pSnake-x - 2;pNext-y ps-pSnake-y;break;case RIGHT:pNext-x ps-pSnake-x 2;pNext-y ps-pSnake-y;break;}//判断下一个坐标是都是食物if (Nextblock(ps, pNext)){IsFood(ps, pNext);}else{NotFood(ps, pNext);}
}7.GameEnd()
完成上面的内容游戏已经可以游玩了,只是缺少了一些结束的提示但是我们要记得最后将开辟的空间释放。
代码示例
void GameEnd(pSnake ps)
{SetPos(15, 12);switch (ps-status){case ESC:printf(主动退出游戏正常退出\n);break;case KILL_BY_WALL:printf(撞墙了游戏结束\n);break;case KILL_BY_SELF:printf(咬到自己了游戏结束\n);break;}//释放贪吃蛇的链表资源pSnakeNode cur ps-pSnake;pSnakeNode del NULL;while (cur){del cur;cur cur-next;free(del);}free(ps-pFood);ps NULL;
}
8.整合程序
void test()
{//创建贪吃蛇Snake snake { 0 };GameStart(snake);//游戏前的初始化GameRun(snake);GameEnd(snake);
}
int main()
{setlocale(LC_ALL, );test();//贪吃蛇游戏的测试SetPos(0, 27);return 0;
}
这样贪吃蛇就完成了 9.代码展示:
//snake.h
#pragma once
#define _CRT_SECURE_NO_WARNINGS
#includestdio.h
#include locale.h
#includestdlib.h
#includestdbool.h
#includeWindows.h
#define WALL L□
#define BODY L●
#define FOOD L★
//蛇默认的起始坐标
#define POS_X 24
#define POS_Y 5
//检测按键是否按了
#define KEY_PRESS(VK) ( (GetAsyncKeyState(VK) 0x1) ? 1 : 0 )//设置控制台窗口
void setwindows();
//隐藏光标
void hidcursor();
//欢迎内容
void welcome();
//创建地图
void createmap();
//光标位置
void SetPos(int x, int y);//游戏的状态
enum GAME_STATUS
{OK 1,//正常运行ESC, //按了ESC键退出正常退出KILL_BY_WALL,//撞墙KILL_BY_SELF //撞到自身
};//蛇行走的方向
enum DIRECTION
{UP,DOWN,LEFT,RIGHT
};//蛇身结点的定义
typedef struct SnakeNode
{int x;int y;struct SnakeNode* next;
}SnakeNode, * pSnakeNode;// 贪吃蛇
typedef struct Snake
{pSnakeNode pSnake;//维护整条蛇的指针是指向蛇头pSnakeNode pFood;//指向食物的指针int Score;//当前累积的分数int FoodWeight;//一个食物的分数int SleepTime;//蛇休眠的时间休眠的时间越短蛇的速度越快休眠的时间越长蛇的速度越慢enum GAME_STATUS status;//游戏当前的状态enum DIRECTION dir;//蛇当前走的方向
}Snake, * pSnake;//初始化贪吃蛇
void InitSnake(pSnake ps);
//创建食物
void Createfood(pSnake ps);
//打印帮助信息
void PrintHelpInfo();
//游戏初始化
void GameStart(pSnake ps);
//蛇移动
void Snakemove(pSnake ps);
//打印蛇
void PrintShake(pSnake ps);
//判断下一个坐标是否是食物
int Nextblock(pSnake ps, pSnakeNode pNext);
//是食物
void IsFood(pSnake ps,pSnakeNode pNext);
// 不是食物
void NotFood(pSnake ps, pSnakeNode pNext);
//是否撞自己
void KillBySelf(pSnake ps);
//是否撞墙
void KillByWall(pSnake ps);
//游戏进程
void GameRun(pSnake ps);
//游戏结束的资源释放
void GameEnd(pSnake ps);
//snake.c
#includeshake.h
void setwindows()
{system(title 贪吃蛇);system(mode con cols100 lines30);
}
void hidcursor()
{//获得设备句柄HANDLE handle GetStdHandle(STD_OUTPUT_HANDLE);//定义控制台相关光标的结构体CONSOLE_CURSOR_INFO CursorInfo;GetConsoleCursorInfo(handle, CursorInfo);//获取控制台光标信息CursorInfo.bVisible false;//光标可见性关闭SetConsoleCursorInfo(handle, CursorInfo);//设置控制台光标状态
}void SetPos(int x, int y)
{//获得设备句柄HANDLE hanlde GetStdHandle(STD_OUTPUT_HANDLE);//根据句柄设置光标的位置COORD pos { x, y };//设置控制台光标状态SetConsoleCursorPosition(hanlde, pos);
}void welcome()
{//欢迎信息SetPos(35, 10);printf(欢迎来到贪吃蛇小游戏\n);SetPos(38, 20);system(pause);system(cls);//功能介绍信息SetPos(15, 10);printf(用 ↑ . ↓ . ← . → 来控制蛇的移动F3是加速F4是减速);SetPos(15, 11);printf(加速能得到更高的分数);SetPos(38, 20);system(pause);system(cls);
}
//创建地图
void createmap()
{//上SetPos(0, 0);for (int i 0; i 56; i 2){wprintf(L%lc, WALL);}//下SetPos(0, 26);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);}//右for (int i 1; i 25; i){SetPos(56, i);wprintf(L%lc, WALL);}
}//初始化贪吃蛇
void InitSnake(pSnake ps)
{pSnakeNode cur NULL;int i 0;for (i 0; i 3; i){cur (pSnakeNode)malloc(sizeof(SnakeNode));if (cur NULL){perror(InitSnake():malloc());return;}cur-x POS_X 2 * i;cur-y POS_Y;cur-next NULL;//头插法if (ps-pSnake NULL){ps-pSnake cur;}else{cur-next ps-pSnake;ps-pSnake cur;} }//打印蛇身cur ps-pSnake;while (cur){SetPos(cur-x, cur-y);wprintf(L%lc, BODY);cur cur-next;}//贪吃蛇的其他信息初始化ps-dir RIGHT;ps-FoodWeight 10;ps-pFood NULL;ps-Score 0;ps-SleepTime 200;ps-status OK;
}
//创建食物
void Createfood(pSnake ps)
{int x 0;int y 0;again:do{x rand() % 53 2;y rand() % 24 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(CreateFood()::malloc());return;}pFood-x x;pFood-y y;ps-pFood pFood;SetPos(x, y);wprintf(L%lc, FOOD);
}//打印帮助信息
void PrintHelpInfo()
{SetPos(59, 15);printf(1.不要撞到墙);SetPos(59, 16);printf(2.不要撞到自己);SetPos(59, 17);printf(3.用 ↑ . ↓ . ← . → 来控制蛇的移动);SetPos(59, 18);printf(4.F3加速F4减速,加速能得到更高的分数);SetPos(59, 19);printf(5.ESC退出,space暂停);SetPos(59, 20);printf(Dream_Snowar所作);
}//游戏初始化
void GameStart(pSnake ps)
{setwindows();welcome();hidcursor();createmap();InitSnake(ps);Createfood(ps);PrintHelpInfo();
}//判断下一个坐标是否是食物
int Nextblock(pSnake ps, pSnakeNode pNext)
{if (ps-pFood-x pNext-x ps-pFood-y pNext-y){return 1;}else{return 0;}
}//space暂停
void pause()
{while (1){Sleep(100);if (KEY_PRESS(VK_SPACE)){break;}}
}
//打印蛇
void PrintShake(pSnake ps)
{pSnakeNode cur ps-pSnake;while (cur){SetPos(cur-x, cur-y);wprintf(L%lc, BODY);cur cur-next;}
}//是食物
void IsFood(pSnake ps, pSnakeNode pNext)
{pNext-next ps-pSnake;ps-pSnake pNext;PrintShake(ps);ps-Score ps-FoodWeight;free(ps-pFood);Createfood(ps);
}
//不是食物
void NotFood(pSnake ps, pSnakeNode pNext)
{pNext-next ps-pSnake;ps-pSnake pNext;pSnakeNode cur ps-pSnake;while (cur-next-next){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 KillBySelf(pSnake ps)
{pSnakeNode cur ps-pSnake-next;//从第二个节点开始while (cur){if (cur-x ps-pSnake-x cur-y ps-pSnake-y){ps-status KILL_BY_SELF;return;}cur cur-next;}
}
//是否撞墙
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 Snakemove(pSnake ps)
{pSnakeNode pNext (pSnakeNode)malloc(sizeof(SnakeNode));if (pNext NULL){perror(CreateFood()::malloc());return;}pNext-next NULL;switch (ps-dir){case UP:pNext-x ps-pSnake-x;pNext-y ps-pSnake-y - 1;break;case DOWN:pNext-x ps-pSnake-x;pNext-y ps-pSnake-y 1;break;case LEFT:pNext-x ps-pSnake-x - 2;pNext-y ps-pSnake-y;break;case RIGHT:pNext-x ps-pSnake-x 2;pNext-y ps-pSnake-y;break;}//判断下一个坐标是都是食物if (Nextblock(ps, pNext)){IsFood(ps, pNext);}else{NotFood(ps, pNext);}
}
//游戏运行
void GameRun(pSnake ps)
{ do{//当前的分数SetPos(59, 10);printf(总分:%d\n, ps-Score);SetPos(59, 11);printf(分数倍率:%02d\n, ps-FoodWeight);//检测按键,↑ . ↓ . ← . → . ESC . F3 . F4 . spaceif (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_ESCAPE)){ps-status ESC;break;}else if (KEY_PRESS(VK_SPACE)){//游戏暂停pause();}else if (KEY_PRESS(VK_F3)){if (ps-SleepTime 80){ps-SleepTime - 30;ps-FoodWeight 2;}}else if (KEY_PRESS(VK_F4)){if (ps-FoodWeight 2){ps-SleepTime 30;ps-FoodWeight - 2;}}//休眠Sleep(ps-SleepTime);//走一步Snakemove(ps);KillBySelf(ps);KillByWall(ps);} while (ps-status OK);}
void GameEnd(pSnake ps)
{SetPos(15, 12);switch (ps-status){case ESC:printf(主动退出游戏正常退出\n);break;case KILL_BY_WALL:printf(撞墙了游戏结束\n);break;case KILL_BY_SELF:printf(咬到自己了游戏结束\n);break;}//释放贪吃蛇的链表资源pSnakeNode cur ps-pSnake;pSnakeNode del NULL;while (cur){del cur;cur cur-next;free(del);}free(ps-pFood);ps NULL;
}
//test.c
#includeshake.h
void test()
{//创建贪吃蛇Snake snake { 0 };GameStart(snake);//游戏前的初始化GameRun(snake);GameEnd(snake);
}
int main()
{setlocale(LC_ALL, );test();//贪吃蛇游戏的测试SetPos(0, 27);return 0;
} C语言的学习结束了,把贪吃蛇作为最后的收官之作是我目前的想法,接下来我重点完成,C语言实践和数据结构的博客的撰写,希望大家多多支持.
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/89881.shtml
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!