EasyX图形库__C语言__贪吃蛇核心代码

本文分享了一段不到150行的C语言代码,使用EasyX图形库实现了贪吃蛇的基本移动和转向。通过动态内存分配和多物体移动原理,展示了蛇的自由移动和食物生成。重点在于代码结构和核心逻辑,适合熟悉单向链表的开发者阅读。
摘要由CSDN通过智能技术生成

EasyX图形库__C语言__贪吃蛇核心代码

前言:(第一次博客啊,格式不太会)

简介:
1.虽然用的是EasyX的图形库,但其实EGE应该也可以,函数功能好多都是相通的;
2.去掉注释,代码全长不到150行,不要担心太啰嗦;
3.这里只给了核心代码——没有加蛇撞墙会死的设定,这个很简单,想搞自己可以搞上;
4.写这个之前,我先跑去吸收了两个算是……一点算法思想吧,一个是多物体同时移动里面的移动方向控制(多物体同时移动代码链接),另一个是常见的“方块版”贪吃蛇怎么实现蛇移动
5.用的VS2019编译器,方便装EasyX图形库,不想用VS的可以拿DevCpp装EGE的改写;
6.适合对象:会单向链表就够了
先上效果

机制:按着A、D键可以左右拐弯,A左D右
![在这里插入图片描述](https://img-blog.csdnimg.cn/20210124031535725.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzUxNDcwNjM4,size_16,color_FFFFFF,t_70
这是直接修改代码初始长度得到的
在这里插入图片描述
也可以代码调加粗点
在这里插入图片描述

开始讲解(完整代码在最后面):

第一次搞,所以我准备一步一步地上代码,让大家看清过程;

第一步:“初始化”

1.我们肯定要画图,不管其他怎么搞,先把画布给定下来,所以用宏定义一下画布宽度和高度:
在这里插入图片描述
2.然后我们肯定要画蛇,那就一个个地画圆就OK了,连起来就是“蛇”了;
那么对于每一个圆都应该得到它的坐标,蛇的长度会变长,还会移动,所以我们用结构体+动态内存分配来搞它;蛇的粗细也要控制,这个用宏定义圆半径就OK;
在这里插入图片描述
这里坐标我用的double类型,虽然画图函数好像都是int类型;

3.搞完了全局main函数外的初始化,下来搞main函数里的;
首先,蛇初始的长度、蛇头初始坐标(有了蛇头身子就挨着朝一个方向搞就OK了)、然后蛇的速度(主要是速度方向);

先贴个图再解释:
在这里插入图片描述

dir就是方向direction,vx、vy就是水平和竖直方向速度,正余弦函数取dir做参数分别得到x、y速度,可以保证只用改变dir就能实现转向;
vx、vy的意义,想想物理的运动学吧:分解就OK了,蛇初始长度length=10;蛇头初始坐标x、y;

第二步:开始画蛇

1.先把初始化的蛇画出来,这个时候要构建链表了,我们有蛇头,所以身体部分的坐标可以沿着速度方向搞出来;
在这里插入图片描述

vx、vy的作用就是根据dir来控制移动方向,这里我们可以懒省事,拿来初始画蛇,不用深究,总之就是先随便画length个连在一起的圆
颜色我调出来的蓝色,自己选;

2.开始动态画蛇,这里要用到连续绘图(动态绘图)的三个XXXXBatchDraw()函数,不然屏幕会闪,不了解的去百度或者直接下载EasyX的help手册;

这里是画蛇的关键了,先上代码
在这里插入图片描述
死循环不解释,移动的自己看注释,到这里我们已经实现蛇的转向和自由移动了;
实在看不懂的也别着急,先去看看“方块版”的贪吃蛇怎么实现蛇移动的就懂了,方向控制不明白的,去看看前面给的EGE的那个链接;

第三步:食物和增长

前面的难的都搞定了,后面很容易了;
代码条不好截图,我直接先上全部代码了:

#include <stdio.h>
#include <math.h>
#include <time.h>
#include <ctype.h>
#include <graphics.h>
#include <windows.h>
#include <conio.h>
#include "resource.h"//你们的没资源放不了,注释掉运行;
#include <mmsystem.h>
#pragma comment(lib,"winmm.lib");
#define Width 1680
#define Height 980
//墙不想搞了,想搞自己加
/*#define wall_right
#define wall_left
#define wall_up
#define wall_down */
#define radius 10.0
#define PI 3.14159265358979
#define delay 100
typedef struct Snack Snack;
typedef struct Food Food;
struct Snack
{
	double x, y;
	Snack* pnext;
};
struct Food
{
	int x, y;
};
Food foods[10];
int foodnum = 10, times = 1;
void Make_Food();
int main(void)
{
//复制的代码没资源运行不过,把下面这一行注释掉就OK;
	PlaySoundA((LPCSTR)IDR_WAVE1, GetModuleHandle(NULL), SND_RESOURCE | SND_ASYNC | SND_LOOP);
	for (int i = 0; i < 10; i++)
	{//这里是我的函数设定了,-1表示这个结构体没有食物,初始的时候没食物全-1
		foods[i].x = -1;
		foods[i].y = -1;
	}
	int i;
	char keya, keyd;
	// Standard:初始化标准
	double x = 200, y = 200, length = 10, dir = PI / 2;
	double vx = sinf(dir), vy = cosf(dir), distance, temp1, temp2;
	// 链表构建与初始化 
	Snack* phead = (Snack*)calloc(sizeof(Snack), 1);
	Snack* pcurrent = NULL;
	phead->x = x, phead->y = y;
	phead->pnext = NULL;
	pcurrent = phead;
	for (i = 0; i < length; i++)
	{
		pcurrent->pnext = (Snack*)calloc(sizeof(Snack), 1);
		pcurrent = pcurrent->pnext;
		pcurrent->pnext = NULL;
		pcurrent->x = x - 1.5 * i * vx * radius, pcurrent->y = y - 1.5 * i * vy * radius;
	}
	initgraph(Width, Height, EW_SHOWCONSOLE);
	setcolor(RGB(255, 255, 0));
	setfillcolor(RGB(100, 150, 200));
	Make_Food();
	for (pcurrent = phead; pcurrent != NULL; pcurrent = pcurrent->pnext)
	{
		fillcircle(pcurrent->x, pcurrent->y, radius);
	}
	BeginBatchDraw();
	for (;;)
	{
		keya = GetKeyState('A');
		keyd = GetKeyState('D');
		if (keya < 0)
		{
			dir += 1.2f;//这里是转向度的大小,我定的1.2
			vx = sinf(dir), vy = cosf(dir);
		}
		else if (keyd < 0)
		{
			dir -= 1.2f;//这里是转向度的大小,我定的1.2
			vx = sinf(dir), vy = cosf(dir);
		}
		pcurrent = (Snack*)calloc(sizeof(Snack), 1);
		pcurrent->x = phead->x;
		pcurrent->y = phead->y;
		pcurrent->pnext = phead;
		phead = pcurrent;
		pcurrent->x += 1.5 * vx * radius;
		pcurrent->y += 1.5 * vy * radius;
		pcurrent = phead;
		setfillcolor(RGB(0, 255, 0));//蛇头单独挑出来加颜色,“高亮显示”
		fillcircle(pcurrent->x, pcurrent->y, radius);
		pcurrent = pcurrent->pnext;
		setfillcolor(RGB(100, 150, 200));
		for (; pcurrent->pnext->pnext != NULL; pcurrent = pcurrent->pnext)
		{
			fillcircle(pcurrent->x, pcurrent->y, radius);
		}
		for (i = 0; i < 10; i++)
		{
			distance = pow(phead->x - foods[i].x, 2) + pow(phead->y - foods[i].y, 2);
			if (distance < radius * radius)
			{
				foods[i].x = -1;
				foods[i].y = -1;
				foodnum--;
				Make_Food();
				foodnum++;
				for (pcurrent = phead; pcurrent->pnext->pnext != NULL;)
					pcurrent = pcurrent->pnext;
				temp1 = pcurrent->x, temp2 = pcurrent->y;
				pcurrent = pcurrent->pnext;
				pcurrent->pnext = (Snack*)calloc(sizeof(Snack), 1);
				pcurrent->pnext->x = 2 * temp1 - pcurrent->x;
			//数学:这里是中点坐标公式,中点就是原来的最后一个节点坐标,
			//倒数第二个点就是一个点,推出要添加的新节点的坐标
				pcurrent->pnext->y = 2 * temp2 - pcurrent->y;//
				pcurrent->pnext->pnext = NULL;
				pcurrent = pcurrent->pnext;
				fillcircle(pcurrent->x, pcurrent->y, radius);
			}
		}
		free(pcurrent->pnext);
		pcurrent->pnext = NULL;
		Make_Food();
		FlushBatchDraw();
		Sleep(delay);
		cleardevice();
	}
	EndBatchDraw();
	_getch();
}
void Make_Food()//产生食物函数
{
	int i;
	srand(time(NULL));
	if (foodnum < 10 || times == 1)
		for (i = 0; i < 10; i++)
		{
			if (foods[i].x == -1)
			{
				foods[i].x = rand() % (Width - 200);//限定范围
				foods[i].y = rand() % (Height - 200);//限定范围
				setfillcolor(RGB(rand(), rand(), rand()));
				fillcircle(foods[i].x, foods[i].y, 5);
				setfillcolor(RGB(100, 150, 200));
			}
			times++;
		}
	else
		for (i = 0; i < 10; i++)
		{
			setfillcolor(RGB(rand(), rand(), rand()));
			fillcircle(foods[i].x, foods[i].y, 5);
			setfillcolor(RGB(100, 150, 200));
		}
}

好了,下来开始慢慢说,食物的出现要随机出现,所以srand()+rand()+time() .(懂得都懂)

食物定义与产生

只有一个食物肯定太水,所以干脆搞个食物坐标结构体,这里我定义死了十个食物,全局变量foodnum,想改自己修改吧;
rand()%取余是保证食物的生成位置限定,不然生成到框框上,让人怎么吃???
关于食物会生成到蛇身上的,不要担心,这个是清屏重绘,一瞬间的事,和“方块版”的机制不一样;
食物这里我想快点结束了,所以有点草率,有的地方不精美,自己看着办吧;

蛇吃食物的判定与增长

蛇吃到食物就是用两点距离公式,看看蛇头的圆有没有包含食物的圆的圆心,包含了就吃掉,吃掉的食物坐标都改成-1,方便再生成食物的判定;

蛇的增长,和前面动态画蛇一样,就把链表再添一个圆,就OK了,*我的那个添加的代码放的位置 严格来说好像是顺序错误的但是清屏重绘,一瞬间的事,就不改了;

蛇增长时新节点位置的确定: 这里要添加一个新的节点,我们用数学的中点坐标公式即可,原先最后一个节点的坐标当中点,链表取到倒数第二个点,拿temp1、temp2装进去,推出新节点的坐标位置;

不足与缺陷:

蛇的视觉效果上,速度和蛇的大小(粗细)相挂钩,蛇的行进单位是百分比个蛇身圆,所以速度快慢其实是不可完全控制的,只能选定合适的蛇身大小来相对控制。
这个我想了好久也没有很合适的办法,只有过一个稍麻烦的想法(但是要推倒重来了,和前面画蛇思路不同)
把Snack结构体再加上成员vx,vy,dir,把每个蛇身的运动都记录下来,分别控制每个蛇身的运动;
再定义一个结构体Corners(成员应包括x,y,dir的变化量),用Corners数组记录下每个拐弯处的坐标,后续的蛇身圆行进到Corners数组坐标时就改变dir,若未遇到Corners,就接着原先各自的速度行进;
这样不简洁,而且起初用的浮点数坐标,这样判定感觉怪怪的……

(有大佬能改进这个的,希望能给我指点一下,感激不尽!)

结语

1.跑代码的时候把那两个说的注释掉,不然报错;
2.嫌蛇转向度不够或过多的可以改里面的数值,也可以添加函数专门控制;
3.我最后没有写释放内存的代码,因为死循环,所以偷个懒;
4.已经提过这里只是关键代码,所以没有搞界面美化
5.大家有想法的,评论区提一下;我的代码肯定能有很多拉胯的地方,欢迎指出;

  • 4
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

barbyQAQ

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值