c语言实战之贪吃蛇

02-26 1110阅读

文章目录

  • 前言
    • 效果展示
    • 游戏用到的图片
    • 游戏思路一览
    • 游戏前准备
      • 一、贪吃蛇、食物、障碍物节点坐标的结构体
      • 二、枚举游戏状态、和贪吃蛇的方向
      • 三、维护运行的结构体
      • 游戏开始前的初始化
        • 一、学习图形库相关知识
        • 二、设置背景
        • 三、欢迎界面
        • 四、初始化贪吃蛇
        • 五、生成障碍物
        • 六、生成食物
        • 七、游戏前的初始化
        • 游戏运行过程
          • 一、打印分数和显示当前食物分数
          • 二、检测按键输入
          • 三、的移动过程
          • 四、游戏运行
          • 游戏结束

            前言

            1、使用工具:vs2022、EasyX图形库。

            2、面向对象:非常适用于刚学完c语言和学过单链表的小伙伴哦。

            3、作用:能够提高学习编程的兴趣、复习学过的c语言和单链表、提高编程的能力和逻辑能力。

            效果展示

            贪吃蛇

            游戏用到的图片

            背景:

            c语言实战之贪吃蛇

            蛇节点:

            c语言实战之贪吃蛇

            食物:

            c语言实战之贪吃蛇

            障碍物:

            c语言实战之贪吃蛇

            图片来自网络。

            当然也可以改成其他图片哦。

            游戏思路一览

            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,并且里面有很多关于图形库的函数,感兴趣的可以去了解一下哦

            界面:

            c语言实战之贪吃蛇

            关于坐标:

            c语言实战之贪吃蛇

            我们现在来了解我们制作贪吃蛇用到的一些函数

            使用下面函数都要包含头文件:#include

            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 的大小以适应图片
            );
            

            参数说明:

            c语言实战之贪吃蛇

            这个函数报错问题:

            更改以下设置就可以了:

            c语言实战之贪吃蛇

            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、我们背景窗口设置为1000,640,游戏的范围为800,640。

            2、多出来的范围用于显示分数。

            //绘制窗口大小
            initgraph(1000, 640);
            //图像对象
            IMAGE bg_img;
            //设置背景
            loadimage(&bg_img, "blackground.bmp", 800, 640, true);
            //加载到设备
            putimage(0, 0, &bg_img);
            

            效果:

            c语言实战之贪吃蛇

            三、欢迎界面

            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);
            }
            

            效果:

            c语言实战之贪吃蛇

            四、初始化贪吃蛇

            1、通过链表来将贪吃蛇连起来,默认为5个节点。

            2、贪吃蛇的初始位置:(0,0),贪吃蛇一个节点的大小,我们设置为20*20

            用于个宏定义来设置,方便以后更改:

            //贪吃蛇的初始位置
            #define POS_X 0
            #define POS_Y 0
            //节点大小
            #define SIZE 20
            

            3、链接过程:先创建一个头节点然后再用头插法将其他的节点头插进去,最后将头节点赋给维护贪吃蛇的指针,其他节点的位置就是与前一个节点的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 
            		pSnakesNode tmp1 = (pSnakesNode)malloc(sizeof(SnakesNode));
            		assert(tmp1);		
            		tmp1-x = POS_X+i*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;
            }
            

            效果:

            c语言实战之贪吃蛇

            五、生成障碍物

            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 _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 _pFood == NULL)
            		{
            			ps->_pFood= p;
            		}
            		else
            		{
            			p->next = ps->_pFood;
            			ps->_pFood = p;
            			p = NULL;
            		}
            	}
            }
            

            5、生成障碍物和食物效果

            c语言实战之贪吃蛇

            七、游戏前的初始化

            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);
            

            效果:

            c语言实战之贪吃蛇

            二、检测按键输入

            1、利用GetAsyncKeyState函数检测按键

            头文件:#include

            函数原型:

            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 _SleepTime += 20;
            		//分数减少
            		ps->_foodWeight -= 2;
            		//不能太慢
            		if (ps->_SleepTime == 350)
            		{
            			ps->_foodWeight = 1;
            		}
            	}
            }
            

            三、的移动过程

            第一步获得下一步的坐标:

            //计算下一个坐标的位置
            // //下一个节点
            pSnakesNode tmp = (pSnakesNode)malloc(sizeof(SnakesNode));
            assert(tmp);
            tmp->next=NULL;
            //上
            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->x+SIZE;
            }
            

            第二步判断是不是食物:

            通过下一步的坐标和食物的坐标对比,如果符合的话就证明是食物

            //判断是不是食物
            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->x_pSnake->x>780 || ps->_pSnake->y_pSnake->y>620)
            	{
            	  //改变贪吃蛇的状态为撞墙
            		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->next=NULL;
                //上
            	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->x+SIZE;
            	}
            	//判断是否是食物
            	//是食物
            	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 _SleepTime += 20;
            				ps->_foodWeight -= 2;
            				if (ps->_SleepTime == 350)
            				{
            					ps->_foodWeight = 1;
            				}
            			}
            		}
            		//暂停
            		Sleep(ps->_SleepTime);
            		//移动蛇
            		SnakeMove(ps);
            		//控制节奏
            		rhythm++;
            		if (rhythm % 150 == 0&&rhythm_SleepTime>=45)
            		{
            			ps->_SleepTime -= 15;
            			ps->_foodWeight += 2;
            		}
            		//重新刷新障碍物
            		if (rhythm%200==0)
            		{
            			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->_Status==OK);//判断游戏状态
            }
            

            游戏结束

            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

            #include"Snakes.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
            #include
            #include
            #include
            #include
            #include
            #include
            #include
            #include	
            #include 
            #include
            //按键
            #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

            #include"Snakes.h"
            //控制游戏节奏
            int rhythm = 0;
            //设置光标的坐标
            void SetPos(short x, short y)
            {
            	COORD pos = { x, y };
            	HANDLE hOutput = NULL;
            	//获取标准输出的句柄(⽤来标识不同设备的数值)
            	hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
            	//设置标准输出上光标的位置为pos
            	SetConsoleCursorPosition(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 _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 
            		pSnakesNode tmp1 = (pSnakesNode)malloc(sizeof(SnakesNode));
            		assert(tmp1);		
            		tmp1-x = POS_X+i*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 _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->x_pSnake->x>780 || ps->_pSnake->y_pSnake->y>620)
            	{
            		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->next=NULL;
                //上
            	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->x+SIZE;
            	}
            	//判断是否是食物
            	//是食物
            	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 _SleepTime += 20;
            				ps->_foodWeight -= 2;
            				if (ps->_SleepTime == 350)
            				{
            					ps->_foodWeight = 1;
            				}
            			}
            		}
            		//暂停
            		Sleep(ps->_SleepTime);
            		//移动蛇
            		SnakeMove(ps);
            		rhythm++;
            		if (rhythm % 150 == 0&&rhythm_SleepTime>=45)
            		{
            			ps->_SleepTime -= 15;
            			ps->_foodWeight += 2;
            		}
            		if (rhythm%200==0)
            		{
            			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->_Status==OK);
            }
            //游戏结束
            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);
            }
            

            以上就是我的分享了,如果有什么错误,欢迎在评论区留言。

            最后,祝大家天天开心

VPS购买请点击我

文章版权声明:除非注明,否则均为主机测评原创文章,转载或复制请以超链接形式并注明出处。

目录[+]