安徽建设工程实名制网站,广西建设职业技术学院教育网站,最经济 网站建设,长春网站建设多少钱文章目录 前言效果展示游戏用到的图片游戏思路一览游戏前准备一、贪吃蛇、食物、障碍物节点坐标的结构体二、枚举游戏状态、和贪吃蛇的方向三、维护运行的结构体 游戏开始前的初始化一、学习图形库相关知识二、设置背景三、欢迎界面四、初始化贪吃蛇五、生成障碍物六、生成食物… 文章目录 前言效果展示游戏用到的图片游戏思路一览游戏前准备一、贪吃蛇、食物、障碍物节点坐标的结构体二、枚举游戏状态、和贪吃蛇的方向三、维护运行的结构体 游戏开始前的初始化一、学习图形库相关知识二、设置背景三、欢迎界面四、初始化贪吃蛇五、生成障碍物六、生成食物七、游戏前的初始化 游戏运行过程一、打印分数和显示当前食物分数二、检测按键输入三、的移动过程四、游戏运行 游戏结束 前言 1、使用工具vs2022、EasyX图形库。 2、面向对象非常适用于刚学完c语言和学过单链表的小伙伴哦。 3、作用能够提高学习编程的兴趣、复习学过的c语言和单链表、提高编程的能力和逻辑能力。 效果展示 贪吃蛇 游戏用到的图片
背景
蛇节点 食物 障碍物 图片来自网络。 当然也可以改成其他图片哦。
游戏思路一览 游戏前准备
一、贪吃蛇、食物、障碍物节点坐标的结构体
1、因为要包含不同类型所以我们用结构体。 2、我们用单链表来将贪吃蛇连在一起不同位置的食物、障碍物也是通过单链表连在一起这样方便我们找到它们的位置。 3、结构体成员记录它们的坐标x,y和记录下一个位置的前驱结构体指针next。 4、参考代码
//蛇的节点和食物
typedef struct SnakesNode
{//坐标int x;int y;//链接struct SnakesNode* next;
}SnakesNode, *pSnakesNode;二、枚举游戏状态、和贪吃蛇的方向
1、游戏状态正常运行、正常结束、撞墙、咬到自己、撞到障碍物。 2、游戏状态的作用通过游戏状态判断蛇下一步应该怎么走。 3、参考代码
//游戏状态
enum GAME_STATUS
{OK,//正常运⾏KILL_BY_WALL,//撞墙KILL_BY_SELF,//咬到⾃⼰KILL_BY_BARR,//撞到障碍物了END_NOMAL//正常结束
};4、贪吃蛇的方向上下左右。 5、方向的作用判断贪吃蛇下一步的方向。 6、参考代码:
//⽅向
enum DIRECTION
{UP 1,//上DOWN,//下LEFT,//左RIGHT//右
};三、维护运行的结构体
结构体成员 维护整条贪吃蛇的指针、维护⻝物的指针、维护障碍物的指针、贪吃蛇头的方向默认是向右、游戏状态、当前获得分数、当前食物的分数、每走⼀步休眠时间。
//维护整个运行
typedef struct Snakes
{pSnakesNode _pSnake;//维护整条贪吃蛇的指针pSnakesNode _pFood;//维护⻝物的指针pSnakesNode _pBarrier;//维护障碍物的指针enum DIRECTION _Dir;//蛇头的⽅向默认是向右enum GAME_STATUS _Status;//游戏状态int _Socre;//当前获得分数int _foodWeight;//默认每个⻝物10分int _SleepTime;//每⾛⼀步休眠时间
}Snakes,*pSnakes;游戏开始前的初始化
一、学习图形库相关知识
在初始化之前我们先来了解一下图形库中的函数如果没有的话可以先下载EasyX这个直接在浏览器上搜索下载就行了这个图形库会自己适配vs2022并且里面有很多关于图形库的函数感兴趣的可以去了解一下哦 界面 关于坐标
我们现在来了解我们制作贪吃蛇用到的一些函数 使用下面函数都要包含头文件#includegraphics.h 1、loadimage函数 作用获取图片到 IMAGE图像对象的类型 指针里。
// 从图片文件获取图像(bmp/gif/jpg/png/tif/emf/wmf/ico)
void loadimage(IMAGE* pDstImg, // 保存图像的 IMAGE 对象指针LPCTSTR pImgFile, // 图片文件名int nWidth 0, // 图片的拉伸宽度int nHeight 0, // 图片的拉伸高度bool bResize false // 是否调整 IMAGE 的大小以适应图片
);参数说明 这个函数报错问题 更改以下设置就可以了
2、putimage函数 作用将图片加载到设备上。
// 绘制图像
void putimage(int dstX, // 绘制位置的 x 坐标int dstY, // 绘制位置的 y 坐标IMAGE *pSrcImg, // 要绘制的 IMAGE 对象指针DWORD dwRop SRCCOPY // 三元光栅操作码
);3、outtextxy函数 作用在指定坐标输出字符串。
void outtextxy(//坐标int x,int y,LPCTSTR str //字符串
);4、initgraph函数 作用初始化窗口大小。
HWND initgraph(int width,int height,int flag NULL
);二、设置背景
1、我们背景窗口设置为1000640游戏的范围为800640。 2、多出来的范围用于显示分数。
//绘制窗口大小
initgraph(1000, 640);
//图像对象
IMAGE bg_img;
//设置背景
loadimage(bg_img, blackground.bmp, 800, 640, true);
//加载到设备
putimage(0, 0, bg_img);效果
三、欢迎界面
1、我们先在中间打印然后再重新加载一个背景将前面的覆盖再打印按键的提示最后再覆盖一次为后面的游戏准备。
//欢迎界⾯
void WelcomeToGame(IMAGE *bg_img)
{//在中间打印outtextxy(340, 300, 欢迎进入游戏...);//在末尾提示outtextxy(0, 620, 请按任意键开始游戏...);//任意键继续的一个函数system(pause);//重新加载背景putimage(0, 0, bg_img);//游戏按键提示outtextxy(190, 300, ⽤ ↑ . ↓ . ← . → 分别控制蛇的移动 F3为加速F4为减速);outtextxy(0, 620, 请按任意键开始游戏...);system(pause);//再重新加载背景putimage(0, 0, bg_img);
}效果
四、初始化贪吃蛇
1、通过链表来将贪吃蛇连起来默认为5个节点。 2、贪吃蛇的初始位置00贪吃蛇一个节点的大小我们设置为20*20 用于个宏定义来设置方便以后更改
//贪吃蛇的初始位置
#define POS_X 0
#define POS_Y 0
//节点大小
#define SIZE 203、链接过程先创建一个头节点然后再用头插法将其他的节点头插进去最后将头节点赋给维护贪吃蛇的指针其他节点的位置就是与前一个节点的x隔着一个SIZE的大小因为默认向右。 4、再创建过程顺便将图片加载出来。 5、代码参考
//加载贪吃蛇节点的函数
void Node(pSnakesNode p)
{//函数对象IMAGE node;//获取图片信息loadimage(node, 屏幕截图 2024-01-28 140514.png, SIZE, SIZE, true);//加载到设备上putimage(p-x, p-y, node);
}//初始化贪吃蛇身
void InitSnake(pSnakes ps)
{//头节点pSnakesNode tmp (pSnakesNode)malloc(sizeof(SnakesNode));//判断是否成功assert(tmp);//初始化第一个节点tmp-next NULL;tmp-x POS_X;tmp-y POS_Y;//将这个节点加载到设备上Node(tmp);//有头插法将其他4个节点加入for (int i 1; i 4; i){pSnakesNode tmp1 (pSnakesNode)malloc(sizeof(SnakesNode));assert(tmp1); tmp1-x POS_Xi*SIZE;tmp1-y POS_Y;//新的接后的一个tmp1-next tmp;tmp tmp1;tmp1 NULL;Node(tmp);}//将结构体中维护蛇节点的指针赋值ps-_pSnake tmp;tmp NULL;//其他属性初始化ps-_Dir RIGHT;ps-_Socre 0;ps-_Status OK;ps-_SleepTime 150;ps-_foodWeight 10;ps-_pBarrier NULL;
}效果
五、生成障碍物
1、开始设置10个障碍物。 2、利用链表将所有障碍物连起来通过头插入这样方便找到它们的位置。 3、生成的位置不能在贪吃蛇的蛇身、要在游戏的地图范围内并且要是SIZE的倍数保证与贪吃蛇的位置在同一个水平上。 4、参考代码
//加载障碍物图片
void Barr(pSnakesNode p)
{IMAGE barrier;loadimage(barrier, 屏幕截图 2024-01-28 165936.png, SIZE, SIZE, true);putimage(p-x ,p-y, barrier);
}//生成障碍物
void barrier(pSnakes ps)
{//障碍物的坐标int x 0;int y 0;pSnakesNode p;//生成障碍物的坐标for (int i 0; i 10; i){ again://跳回do {//1、要在地图内x rand() % 780 1;y rand() % 620 1;//2、x和y要在SIZE的倍数上} while (x % SIZE ! 0 || y % SIZE ! 0);//3、不能出现在贪吃蛇的身上pSnakesNode tmp ps-_pSnake;while (tmp ! NULL){if (tmp-x x tmp-y y){//如果碰到是在贪吃蛇的身上的话就跳转到开始的位置goto again;}tmp tmp-next;}p (pSnakesNode)malloc(sizeof(SnakesNode));assert(p);p-next NULL;p-x x;p-y y;//加载障碍物图片Barr(p);//如果ps-_pBarrier NULL则将p赋给ps-_pBarrier其他的用头插插入if (ps-_pBarrier NULL){ps-_pBarrier p;}else{p-next ps-_pBarrier;ps-_pBarrier p;p NULL;} }
}六、生成食物
1、开始设置10个食物。 2、利用链表将所有食物连起来通过头插入这样方便找到它们的位置。 3、生成的位置不能在贪吃蛇的蛇身、要在游戏的地图范围内并且要是SIZE的倍数保证与贪吃蛇的位置在同一个水平上不要在障碍物上。 4、参考代码
//加载食物图片到设备上
void Foob(pSnakesNode p)
{IMAGE food;loadimage(food, 屏幕截图 2024-01-28 151002.png, SIZE, SIZE, true);putimage(p-x, p-y, food);
}//创建单个食物
pSnakesNode Create(pSnakes ps)
{
//食物坐标pSnakesNode p;int x 0;int y 0;
again:
in:do {//1、要在地图内x rand() % 780 1;y rand() % 620 1;//2、是SIZE的倍数} while (x % SIZE ! 0 || y % SIZE ! 0);//3、不能出现在贪吃蛇的身上pSnakesNode tmp ps-_pSnake;while (tmp ! NULL){if (tmp-x x tmp-y y){goto again;}tmp tmp-next;}//4、不能出现在障碍物身上pSnakesNode tmp1 ps-_pBarrier;while (tmp1 ! NULL){if (tmp1-x x tmp1-y y){goto in;}tmp1 tmp1-next;}p (pSnakesNode)malloc(sizeof(SnakesNode));assert(p);p-next NULL;p-x x;p-y y;//加载图片Foob(p);//返回这个食物节点return p;
}//生成10个食物
void CreateFood(pSnakes ps)
{pSnakesNode p;//通过循环进行头插for (int i 0; i 10; i){pSnakesNode p Create(ps);if (ps-_pFood NULL){ps-_pFood p;}else{p-next ps-_pFood;ps-_pFood p;p NULL;}}
}5、生成障碍物和食物效果
七、游戏前的初始化
void GameStart(pSnakes ps)
{//绘制窗口大小initgraph(1000, 640);//图像对象IMAGE bg_img;//设置背景loadimage(bg_img, blackground.bmp, 800, 640, true);//加载到设备putimage(0, 0, bg_img);//欢迎界面WelcomeToGame(bg_img);// 初始化InitSnake(ps);//生成障碍物barrier(ps);//创建⻝物CreateFood(ps);
}游戏运行过程
一、打印分数和显示当前食物分数
我们之前提到过在设置窗口的时候设置了比游戏地图大因此我们就在多的位置上显示获得的分数和当前食物的得分。
//记录的分
char str[10] { 0 };
//利用该函数将当前得分输入到str上
sprintf(str, %d, ps-_Socre);
//记录当前食物的分数
char str1[5] { 0 };
sprintf(str1, %d, ps-_foodWeight);
//在规定的位置上输出
outtextxy(810, 20, 当前得分);
outtextxy(940, 20, str);
outtextxy(810, 80, 当前食物得分);
outtextxy(940, 80, str1);效果
二、检测按键输入
1、利用GetAsyncKeyState函数检测按键 头文件#includeWindows.h 函数原型
SHORT GetAsyncKeyState([in] int vKey
);将键盘上每个键的虚拟键值传递给函数函数通过返回值来分辨按键的状态。 虚拟键值参考 GetAsyncKeyState 的返回值是short类型在上⼀次调用 GetAsyncKeyState 函数后如果返回的16位的short数据中最高位是1说明按键的状态是按下如果最⾼是0说明按键的状态是抬起如果最低位被置为1则说明该按键被按过否则为0。 如果我们要判断⼀个键是否被按过可以检测GetAsyncKeyState返回值的最低值是否为1。 因此我们可以定义一个宏来判断按键是否被按过
//按键
#define KEY_PRESS(VK) ((GetAsyncKeyState(VK)0x1) ? 1 : 0)这样我们就可以通过按键控制贪吃蛇的状态了。 2、按键 ⽤ ↑ . ↓ . ← . → 分别控制蛇的移动 F3为加速F4为减速空格为暂停ESC为正常退出 暂停 利用死循环来控制暂停通过Sleep函数来延迟当再次接受到空格键时直接打破。
//暂停
void pause()
{while (1){ //延迟函数Sleep(300);if (KEY_PRESS(VK_SPACE)){break;}}
}加速减速 通过减少延迟的时间就可以加速减少就反之当然加速每个食物的分数就会增加减速每个食物的分数就会减少。
通过选择语句来判断
//判断按键
//上走上就不能走下了防止重叠
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();
}
//ESC
else if (KEY_PRESS(VK_ESCAPE))
{ //改变游戏状态ps-_Status END_NOMAL;break;
}
//F3
else if (KEY_PRESS(VK_F3))
{//控制延迟的时间if (ps-_SleepTime 50){ps-_SleepTime - 20;//分数增加ps-_foodWeight 2;}
}
//F4
else if (KEY_PRESS(VK_F4))
{//控制延迟的时间if (ps-_SleepTime 350){ps-_SleepTime 20;//分数减少ps-_foodWeight - 2;//不能太慢if (ps-_SleepTime 350){ps-_foodWeight 1;}}
}三、的移动过程
第一步获得下一步的坐标
//计算下一个坐标的位置
// //下一个节点
pSnakesNode tmp (pSnakesNode)malloc(sizeof(SnakesNode));
assert(tmp);
tmp-nextNULL;
//上
if (ps-_Dir UP)
{tmp-y ps-_pSnake-y - SIZE;tmp-x ps-_pSnake-x;
}
//下
else if (ps-_Dir DOWN)
{tmp-y ps-_pSnake-y SIZE;tmp-x ps-_pSnake-x;
}
//左
else if (ps-_Dir LEFT)
{tmp-y ps-_pSnake-y;tmp-x ps-_pSnake-x-SIZE;
}
//右
else if (ps-_Dir RIGHT)
{tmp-y ps-_pSnake-y;tmp-x ps-_pSnake-xSIZE;
}第二步判断是不是食物 通过下一步的坐标和食物的坐标对比如果符合的话就证明是食物
//判断是不是食物
bool judgment(pSnakesNode psn, pSnakes ps)
{pSnakesNode tmp ps-_pFood;//遍历食物的所有坐标while (tmp ! NULL){if (tmp-x psn-x tmp-y psn-y){return true;}tmp tmp-next;}return false;
}第三步下一步是食物 1、将下一步的坐标和贪吃蛇链接并重新加载这个贪吃蛇节点图片。 2、重新生成一个新食物并重新加载食物节点图片。 3、利用前后指针的方法找到该食物的坐标和该食物的上一个坐标。 3、如果该食物是头节点的话保存下一个节点同时释放该食物节点用新食物做食物的头节点并链接保存的节点。 4、如果不是的话就保存该食物节点的下一个节点用新食物的next连这个节点再用保存的该食物的上一个节点的next连新食物最后释放该食物节点。 5、进行加分。
//下⼀个节点是⻝物
void EatFood(pSnakesNode psn, pSnakes ps)
{//直接将下一个坐标和当前的贪吃蛇链接并加载图片psn-next ps-_pSnake;ps-_pSnake psn;Node(psn);//找食物节点//前pSnakesNode p ps-_pFood;//后pSnakesNode p1 NULL;while (p ! NULL){if (p-x psn-x p-y psn-y){break;}p1 p;p p-next;}//生成一个食物pSnakesNode p2 Create(ps);//头if (p1 NULL){p2-next p-next;free(p);ps-_pFood p2;}else{p1-next p2;p2-next p-next;free(p);}//加分ps-_Socre ps-_foodWeight;
}第四步下一步不是食物 1、链接。 2、找到倒数第二个节点在这个过程顺便加载新的贪吃蛇节点图片。 3、通过倒数第二个节点将倒数第一个节点位置加载为背景颜色然后释放最后一个节点。
//下一个节点不是食物
void NoFood(pSnakesNode psn, pSnakes ps)
{//链接psn-next ps-_pSnake;ps-_pSnake psn;pSnakesNode tmp ps-_pSnake;//找倒数第二个节点while (tmp-next-next! NULL){Node(tmp);tmp tmp-next;}IMAGE b;//设置背景loadimage(b, blackground.bmp, SIZE, SIZE, true);putimage(tmp-next-x, tmp-next-y, b);//释放最后一个free(tmp-next);tmp-next NULL;
}第五步判断是否撞墙 通过判断蛇头的位置的坐标是否超出地图的范围超过了就改变蛇的状态。
//撞墙检测
int KillByWall(pSnakes ps)
{
//判断蛇头的位置是否超出地图的范围if (ps-_pSnake-x0 || ps-_pSnake-x780 || ps-_pSnake-y0|| ps-_pSnake-y620){//改变贪吃蛇的状态为撞墙ps-_Status KILL_BY_WALL;return 1;}else{return 0;}
}第六步判断是否撞到障碍物 通过遍历障碍物的坐标与蛇头坐标对比。
//撞到障碍物
int Hittheobstacles(pSnakes ps)
{pSnakesNode tmp ps-_pSnake;pSnakesNode tmp1 ps-_pBarrier;//遍历while (tmp1 ! NULL){if (tmp1-x tmp-x tmp1-y tmp-y){//改变游戏运行状态为撞到障碍物ps-_Status KILL_BY_BARR;return 1;}tmp1 tmp1-next;}return 0;
}第七步判断是否咬到自己了 通过从贪吃蛇的第二个节点开始遍历与蛇头坐标对比。
/撞自⾝检测
int KillBySelf(pSnakes ps)
{pSnakesNode tmp ps-_pSnake-next;while (tmp){//判断if (tmp-x ps-_pSnake-x tmp-y ps-_pSnake-y){//改变游戏运行状态ps-_Status KILL_BY_SELF;return 1;}tmp tmp-next;}return 0;
}最后总的贪吃蛇移动的代码
//贪吃蛇的移动
void SnakeMove(pSnakes ps)
{//计算下一个坐标的位置// //下一个节点pSnakesNode tmp (pSnakesNode)malloc(sizeof(SnakesNode));assert(tmp);tmp-nextNULL;//上if (ps-_Dir UP){tmp-y ps-_pSnake-y - SIZE;tmp-x ps-_pSnake-x;}//下else if (ps-_Dir DOWN){tmp-y ps-_pSnake-y SIZE;tmp-x ps-_pSnake-x;}//左else if (ps-_Dir LEFT){tmp-y ps-_pSnake-y;tmp-x ps-_pSnake-x-SIZE;}//右else if (ps-_Dir RIGHT){tmp-y ps-_pSnake-y;tmp-x ps-_pSnake-xSIZE;}//判断是否是食物//是食物if (judgment(tmp,ps)){ EatFood(tmp, ps);}//不是食物else{NoFood(tmp, ps);}//是否撞墙KillByWall(ps);//是否咬到自己KillBySelf(ps);//是否撞到障碍物Hittheobstacles(ps);
}四、游戏运行
1、通过do while循环至少有一次的按键机会。 2、while来判断游戏状态如果不是OK就代表结束游戏了。 3、rhythm是全局变量用于控制游戏节奏每走一次就加1默认为0当每走到一定步数时速度就会加快同时每个食物的分数增加还有当走到一定步数时障碍物会重新刷新。
//游戏运⾏
void GameRun(pSnakes ps)
{do {//记录的分char str[10] { 0 };//利用该函数将当前得分输入到str上sprintf(str, %d, ps-_Socre);//记录当前食物的分数char str1[5] { 0 };sprintf(str1, %d, ps-_foodWeight);//在规定的位置上输出outtextxy(810, 20, 当前得分);outtextxy(940, 20, str);outtextxy(810, 80, 当前食物得分);outtextxy(940, 80, str1);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-_Status END_NOMAL;break;}else if (KEY_PRESS(VK_F3)){if (ps-_SleepTime 50){ps-_SleepTime - 20;ps-_foodWeight 2;}}else if (KEY_PRESS(VK_F4)){if (ps-_SleepTime 350){ps-_SleepTime 20;ps-_foodWeight - 2;if (ps-_SleepTime 350){ps-_foodWeight 1;}}}//暂停Sleep(ps-_SleepTime);//移动蛇SnakeMove(ps);//控制节奏rhythm;if (rhythm % 150 0rhythm600ps-_SleepTime45){ps-_SleepTime - 15;ps-_foodWeight 2;}//重新刷新障碍物if (rhythm%2000){IMAGE b;//设置背景loadimage(b, blackground.bmp, SIZE, SIZE, true);//释放之前的障碍物pSnakesNode tmp ps-_pBarrier;while (tmp ! NULL){putimage(tmp-x, tmp-y, b);pSnakesNode tmp1 tmp-next;free(tmp);tmp tmp1;}ps-_pBarrier NULL;barrier(ps);}} while (ps-_StatusOK);//判断游戏状态}游戏结束
1、游戏结束后通过判断游戏状态来告诉玩家是什么原因无的。 2、释放蛇、食物、障碍物最后结束程序。
/游戏结束
void GameEnd(pSnakes ps)
{
//判断是什么原因结束的if (ps-_Status END_NOMAL){outtextxy(340, 300, 正常结束游戏);}else if (ps-_Status KILL_BY_SELF){outtextxy(340, 300, 咬到自己了);}else if (ps-_Status KILL_BY_WALL){outtextxy(340, 300, 撞墙了);}else if (ps-_Status KILL_BY_BARR){outtextxy(340, 300, 撞到障碍物了);}//释放蛇pSnakesNode tmp ps-_pSnake;while (tmp ! NULL){pSnakesNode tmp1 tmp-next;free(tmp);tmp tmp1;}//释放障碍物tmp ps-_pBarrier;while (tmp ! NULL){pSnakesNode tmp1 tmp-next;free(tmp);tmp tmp1;}//最后释放食物tmp ps-_pFood;while (tmp ! NULL){pSnakesNode tmp1 tmp-next;free(tmp);tmp tmp1;}//按任意键system(pause);//结束程序exit(1);
}总参考代码 test.cpp
#includeSnakes.h
void test()
{srand((unsigned int)time(NULL));Snakes snake {0};GameStart(snake);GameRun(snake);GameEnd(snake);
}
int main()
{test();return 0;
}Snakes.h
#pragma once
#define _CRT_SECURE_NO_WARNINGS
#includestdio.h
#includestdlib.h
#includeWindows.h
#includelocale.h
#includetime.h
#includestdbool.h
#includeassert.h
#includegraphics.h
#include conio.h
#includestring//按键
#define KEY_PRESS(VK) ((GetAsyncKeyState(VK)0x1) ? 1 : 0)//蛇的初始位置
#define POS_X 0
#define POS_Y 0
#define SIZE 20
//⽅向
enum DIRECTION
{UP 1,DOWN,LEFT,RIGHT
};//游戏状态
enum GAME_STATUS
{OK,//正常运⾏KILL_BY_WALL,//撞墙KILL_BY_SELF,//咬到⾃⼰KILL_BY_BARR,//撞到障碍物了END_NOMAL//正常结束
};//蛇的节点和食物
typedef struct SnakesNode
{//坐标int x;int y;//链接struct SnakesNode* next;
}SnakesNode, *pSnakesNode;//维护整个运行
typedef struct Snakes
{pSnakesNode _pSnake;//维护整条蛇的指针pSnakesNode _pFood;//维护⻝物的指针pSnakesNode _pBarrier;//维护障碍物的指针enum DIRECTION _Dir;//蛇头的⽅向默认是向右enum GAME_STATUS _Status;//游戏状态int _Socre;//当前获得分数int _foodWeight;//默认每个⻝物10分int _SleepTime;//每⾛⼀步休眠时间
}Snakes,*pSnakes;//游戏开始前的初始化
void GameStart(pSnakes ps);//欢迎界⾯
//void WelcomeToGame();
//创建地图
void CreateMap();
//设置光标信息
void SetPos(short x, short y);
//初始化蛇
void InitSnake(pSnakes ps);
//生成障碍物void barrier(pSnakes ps);
//创建⻝物
void CreateFood(pSnakes ps);
// 暂停响应
void pause();
//下⼀个节点是⻝物
void EatFood(pSnakesNode psn, pSnakes ps);
//下一个节点不是食物
void NoFood(pSnakesNode psn, pSnakes ps);
//蛇的移动
void SnakeMove(pSnakes ps);
//撞墙检测
int KillByWall(pSnakes ps);
//撞⾃⾝检测
int KillBySelf(pSnakes ps);
//游戏运⾏
void GameRun(pSnakes ps);
//游戏结束
void GameEnd(pSnakes ps);Snakes.cpp
#includeSnakes.h//控制游戏节奏
int rhythm 0;
//设置光标的坐标
void SetPos(short x, short y)
{COORD pos { x, y };HANDLE hOutput NULL;//获取标准输出的句柄(⽤来标识不同设备的数值)hOutput GetStdHandle(STD_OUTPUT_HANDLE);//设置标准输出上光标的位置为posSetConsoleCursorPosition(hOutput, pos);
}//欢迎界⾯
void WelcomeToGame(IMAGE *bg_img)
{//在中间打印outtextxy(340, 300, 欢迎进入游戏...);//在末尾提示outtextxy(0, 620, 请按任意键开始游戏...);//任意键继续函数system(pause);//重新加载背景putimage(0, 0, bg_img);//游戏按键提示outtextxy(190, 300, ⽤ ↑ . ↓ . ← . → 分别控制蛇的移动 F3为加速F4为减速);outtextxy(0, 620, 请按任意键开始游戏...);system(pause);//再重新加载背景putimage(0, 0, bg_img);}void Node(pSnakesNode p)
{IMAGE node;loadimage(node, 屏幕截图 2024-01-28 140514.png, SIZE, SIZE, true);putimage(p-x, p-y, node);
}
void Barr(pSnakesNode p)
{IMAGE barrier;loadimage(barrier, 屏幕截图 2024-01-28 165936.png, SIZE, SIZE, true);putimage(p-x ,p-y, barrier);
}
void Foob(pSnakesNode p)
{IMAGE food;loadimage(food, 屏幕截图 2024-01-28 151002.png, SIZE, SIZE, true);putimage(p-x, p-y, food);
}//创建单个食物
pSnakesNode Create(pSnakes ps)
{pSnakesNode p;int x 0;int y 0;
again:
in:do {//1、要在地图内x rand() % 780 1;y rand() % 620 1;//2、x需要在奇数上} while (x % SIZE ! 0 || y % SIZE ! 0);//3、不能出现在蛇的身上pSnakesNode tmp ps-_pSnake;while (tmp ! NULL){if (tmp-x x tmp-y y){goto again;}tmp tmp-next;}//4、不能出现在障碍物身上pSnakesNode tmp1 ps-_pBarrier;while (tmp1 ! NULL){if (tmp1-x x tmp1-y y){goto in;}tmp1 tmp1-next;}p (pSnakesNode)malloc(sizeof(SnakesNode));assert(p);p-next NULL;p-x x;p-y y;Foob(p);return p;}//创建⻝物
void CreateFood(pSnakes ps)
{pSnakesNode p;int x 0;int y 0;for (int i 0; i 10; i){pSnakesNode p Create(ps);if (ps-_pFood NULL){ps-_pFood p;}else{p-next ps-_pFood;ps-_pFood p;p NULL;}}
}
//初始化蛇身
void InitSnake(pSnakes ps)
{//头节点pSnakesNode tmp (pSnakesNode)malloc(sizeof(SnakesNode));//判断是否成功assert(tmp);//初始化第一个节点tmp-next NULL;tmp-x POS_X;tmp-y POS_Y;//将这个节点加载到设备上Node(tmp);//有头插法将其他4个节点加入for (int i 1; i 4; i){pSnakesNode tmp1 (pSnakesNode)malloc(sizeof(SnakesNode));assert(tmp1); tmp1-x POS_Xi*SIZE;tmp1-y POS_Y;//新的接后的一个tmp1-next tmp;tmp tmp1;tmp1 NULL;Node(tmp);}//将结构体中维护蛇节点的指针赋值ps-_pSnake tmp;tmp NULL;//其他属性初始化ps-_Dir RIGHT;ps-_Socre 0;ps-_Status OK;ps-_SleepTime 150;ps-_foodWeight 10;ps-_pBarrier NULL;
}
//生成障碍物
void barrier(pSnakes ps)
{//障碍物的坐标int x 0;int y 0;pSnakesNode p;//生成障碍物的坐标for (int i 0; i 10; i){ again://跳回do {//1、要在地图内x rand() % 780 1;y rand() % 620 1;//2、x和y要在SIZE的倍数上} while (x % SIZE ! 0 || y % SIZE ! 0);//3、不能出现在蛇的身上pSnakesNode tmp ps-_pSnake;while (tmp ! NULL){if (tmp-x x tmp-y y){//如果碰到是在蛇的身上的话就跳转到开始的位置goto again;}tmp tmp-next;}p (pSnakesNode)malloc(sizeof(SnakesNode));assert(p);p-next NULL;p-x x;p-y y;//加载障碍物图片Barr(p);//如果ps-_pBarrier NULL则将p赋给ps-_pBarrier其他的用头插插入if (ps-_pBarrier NULL){ps-_pBarrier p;}else{p-next ps-_pBarrier;ps-_pBarrier p;p NULL;} }
}void GameStart(pSnakes ps)
{//绘制窗口大小initgraph(1000, 640);//图像对象IMAGE bg_img;//设置背景loadimage(bg_img, blackground.bmp, 800, 640, true);//加载到设备putimage(0, 0, bg_img);//欢迎界面WelcomeToGame(bg_img);//创造地图//CreateMap();//蛇身初始化InitSnake(ps);//生成障碍物barrier(ps);//创建⻝物CreateFood(ps);
}
//打印右侧帮助信息
void PrintHelpInfo()
{//打印提⽰信息SetPos(64, 15);printf(不能穿墙不能咬到⾃⼰\n);SetPos(64, 16);printf(⽤↑.↓.←.→分别控制蛇的移动.);SetPos(64, 17);printf(F1 为加速F2 为减速\n);SetPos(64, 18);printf(ESC 退出游戏.space暂停游戏.);
}
//暂停
void pause()
{while (1){Sleep(300);if (KEY_PRESS(VK_SPACE)){break;}}
}
//判断是不是食物
bool judgment(pSnakesNode psn, pSnakes ps)
{pSnakesNode tmp ps-_pFood;while (tmp ! NULL){if (tmp-x psn-x tmp-y psn-y){return true;}tmp tmp-next;}return false;
}
//下⼀个节点是⻝物
void EatFood(pSnakesNode psn, pSnakes ps)
{//直接将下一个坐标和当前的贪吃蛇链接并加载图片psn-next ps-_pSnake;ps-_pSnake psn;Node(psn);//找食物节点//前pSnakesNode p ps-_pFood;//后pSnakesNode p1 NULL;while (p ! NULL){if (p-x psn-x p-y psn-y){break;}p1 p;p p-next;}//生成一个食物pSnakesNode p2 Create(ps);//头if (p1 NULL){p2-next p-next;free(p);ps-_pFood p2;}else{p1-next p2;p2-next p-next;free(p);}//加分ps-_Socre ps-_foodWeight;
}//下一个节点不是食物
void NoFood(pSnakesNode psn, pSnakes ps)
{psn-next ps-_pSnake;ps-_pSnake psn;pSnakesNode tmp ps-_pSnake;//找倒数第二个节点while (tmp-next-next! NULL){Node(tmp);tmp tmp-next;}IMAGE b;//设置背景loadimage(b, blackground.bmp, SIZE, SIZE, true);putimage(tmp-next-x, tmp-next-y, b);//释放最后一个free(tmp-next);tmp-next NULL;
}
//撞墙检测
int KillByWall(pSnakes ps)
{if (ps-_pSnake-x0 || ps-_pSnake-x780 || ps-_pSnake-y0|| ps-_pSnake-y620){ps-_Status KILL_BY_WALL;return 1;}else{return 0;}
}
//撞⾃⾝检测
int KillBySelf(pSnakes ps)
{pSnakesNode tmp ps-_pSnake-next;while (tmp){if (tmp-x ps-_pSnake-x tmp-y ps-_pSnake-y){ps-_Status KILL_BY_SELF;return 1;}tmp tmp-next;}return 0;
}
//撞到障碍物
int Hittheobstacles(pSnakes ps)
{pSnakesNode tmp ps-_pSnake;pSnakesNode tmp1 ps-_pBarrier;while (tmp1 ! NULL){if (tmp1-x tmp-x tmp1-y tmp-y){ps-_Status KILL_BY_BARR;return 1;}tmp1 tmp1-next;}return 0;
}//蛇的移动
void SnakeMove(pSnakes ps)
{//计算下一个坐标的位置// //下一个节点pSnakesNode tmp (pSnakesNode)malloc(sizeof(SnakesNode));assert(tmp);tmp-nextNULL;//上if (ps-_Dir UP){tmp-y ps-_pSnake-y - SIZE;tmp-x ps-_pSnake-x;}//下else if (ps-_Dir DOWN){tmp-y ps-_pSnake-y SIZE;tmp-x ps-_pSnake-x;}//左else if (ps-_Dir LEFT){tmp-y ps-_pSnake-y;tmp-x ps-_pSnake-x-SIZE;}//右else if (ps-_Dir RIGHT){tmp-y ps-_pSnake-y;tmp-x ps-_pSnake-xSIZE;}//判断是否是食物//是食物if (judgment(tmp,ps)){ EatFood(tmp, ps);}//不是食物else{NoFood(tmp, ps);}KillByWall(ps);KillBySelf(ps);Hittheobstacles(ps);
}//游戏运⾏
void GameRun(pSnakes ps)
{do {//记录的分char str[10] { 0 };//利用该函数将当前得分输入到str上sprintf(str, %d, ps-_Socre);//记录当前食物的分数char str1[5] { 0 };sprintf(str1, %d, ps-_foodWeight);//在规定的位置上输出outtextxy(810, 20, 当前得分);outtextxy(940, 20, str);outtextxy(810, 80, 当前食物得分);outtextxy(940, 80, str1);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-_Status END_NOMAL;break;}else if (KEY_PRESS(VK_F3)){if (ps-_SleepTime 50){ps-_SleepTime - 20;ps-_foodWeight 2;}}else if (KEY_PRESS(VK_F4)){if (ps-_SleepTime 350){ps-_SleepTime 20;ps-_foodWeight - 2;if (ps-_SleepTime 350){ps-_foodWeight 1;}}}//暂停Sleep(ps-_SleepTime);//移动蛇SnakeMove(ps);rhythm;if (rhythm % 150 0rhythm600ps-_SleepTime45){ps-_SleepTime - 15;ps-_foodWeight 2;}if (rhythm%2000){IMAGE b;//设置背景loadimage(b, blackground.bmp, SIZE, SIZE, true);//释放pSnakesNode tmp ps-_pBarrier;while (tmp ! NULL){putimage(tmp-x, tmp-y, b);pSnakesNode tmp1 tmp-next;free(tmp);tmp tmp1;}ps-_pBarrier NULL;barrier(ps);}} while (ps-_StatusOK);}
//游戏结束
void GameEnd(pSnakes ps)
{if (ps-_Status END_NOMAL){outtextxy(340, 300, 正常结束游戏);}else if (ps-_Status KILL_BY_SELF){outtextxy(340, 300, 咬到自己了);}else if (ps-_Status KILL_BY_WALL){outtextxy(340, 300, 撞墙了);}else if (ps-_Status KILL_BY_BARR){outtextxy(340, 300, 撞到障碍物了);}//释放蛇pSnakesNode tmp ps-_pSnake;while (tmp ! NULL){pSnakesNode tmp1 tmp-next;free(tmp);tmp tmp1;}//释放障碍物tmp ps-_pBarrier;while (tmp ! NULL){pSnakesNode tmp1 tmp-next;free(tmp);tmp tmp1;}//最后释放食物tmp ps-_pFood;while (tmp ! NULL){pSnakesNode tmp1 tmp-next;free(tmp);tmp tmp1;}system(pause);exit(1);
}以上就是我的分享了如果有什么错误欢迎在评论区留言。 最后祝大家天天开心