EasyX图形库实现贪吃蛇游戏

news2024/11/18 16:24:15

⭐大家好,我是Dark Falme Masker,学习了动画制作及键盘交互之后,我们就可以开动利用图形库写一个简单的贪吃蛇小游戏,增加学习乐趣。

⭐专栏:EasyX部分小游戏实现详细讲解

最终效果如下

首先包含头文件

#include<stdio.h>
#include<easyx.h>
#include<conio.h>
#include<time.h>

✅我们将会使用rand函数生成随机数,来控制食物的随机生成,设置时间戳srand(time(NULL)),所以要包含time.h

int main()
{
	initgraph(800, 600);
	setbkcolor(RGB(164, 225, 202));
	cleardevice();

	closegraph();
	return 0;
}

🏅创建窗体,更换背景颜色。

将窗体划分为各个边长为40的正方形小格,方便后续表示蛇和食物。

👉为了方便后续可以修改每个小格的边长,可以将其定义一下

#define NodeWidth 40

然后划分分界线,让后续绘制出蛇和食物位置更加清晰。

void paintGrid()
{
	for (int y = 0; y <= 600; y += NodeWidth)
	{
		line(0, y, 800, y);
	}
	for (int x = 0; x <= 800; x += NodeWidth)
	{
		line(x, 0, x, 600);
	}
}

运行代码观察是否存在问题

📚接下来绘制蛇,蛇的起始长度设置为5,蛇是由五个连续的正方形小块构成,食物由一个正方形小块构成。我们可以盛情一个结构体变量,存放每个正方形小格子的左上角的坐标。

typedef struct {
    int x;
    int y;
}Node;

📜创建一个结构体数组,装着蛇的坐标信息,在第七行绘制出蛇的各个节点,如果数组第一个元素作为头节点,那么其余节点的x坐标是递减NodeWidth的,直接传格数,在绘制时乘以小方块宽度即可。

Node snake[100] = { {5,7},{4,7},{3,7},{2,7},{1,7} };

绘制蛇的函数

void paintSnake(Node* snake, int n)
{
	int left, top, right, bottom;
	for (int i = 0; i < n; i++)
	{
		left = snake[i].x * NodeWidth;
		top = snake[i].y * NodeWidth;
		right = (snake[i].x + 1) * NodeWidth;
		bottom = (snake[i].y + 1) * NodeWidth;
		solidrectangle(left, top, right, bottom);
	}
}

💭传入结构体指针及社的节点个数,即可在画面中绘制出一个长度为5格的蛇。

但是蛇是会移动的,所以要加上键盘交互功能,控制蛇的方向移动。

创建枚举

enum direction
{
    eUp,
    eDown,
    eLeft,
    eRight
};

📑一共有三种状况,在蛇移动时判断键盘是否控制蛇的移位,通过控制结构体数组内x,y的值就可以实现蛇的移动。

默认direction为右

    enum direction d = eRight;

判断玩家按下键盘的函数,传入枚举指针,改变枚举值,从而根据改变后的值改变更新蛇的位置的x,y坐标,再次绘制时设就可以转向。

void changeDirection(enum direction* pD)
{
	if (_kbhit() != 0)
	{
		char c = _getch();
		switch (c)
		{
		case'w':
			if (*pD != eDown)
				*pD = eUp;
			break;
		case's':
			if (*pD != eUp)
				*pD = eDown;
			break;

		case'a':
			if (*pD != eRight)
				*pD = eLeft;
			break;

		case'd':
			if (*pD != eLeft)
				*pD = eRight;
			break;

		}
	}
}

🔥改变direction后就可以更改蛇节点的参数,要注意的是,蛇头不可以向正在移动的方向的相反方向移动,这里要进行判断。

👑在移动时,后续节点覆盖前边的节点的位置,根据蛇头位置及移动方向生成新的节点位置作为蛇头,设置这个函数的返回值为NODE类型,记录蛇尾节点。

✨返回蛇尾节点,利用一个结构体变量接收,在判断蛇头吃掉食物结点之后就可以将返回的节点续上,并且++蛇的长度。

Node snakeMove(Node* snake, int length, int direction)
{
	Node tail = snake[length - 1];
	for (int i = length - 1; i > 0; i--)
	{
		snake[i] = snake[i - 1];
	}
	Node NewHead;
	NewHead = snake[0];
	if (direction == eUp)
	{
		NewHead.y--;
	}
	else if (direction == eDown)
	{
		NewHead.y++;
	}
	else if (direction == eLeft)
	{
		NewHead.x--;
	}
	else if (direction == eRight)
	{
		NewHead.x++;
	}
	snake[0] = NewHead;
	return tail;
}

创建食物

🏅创建食物是随机生成坐标,不可以超出创建的窗体大小而且不可以生成到蛇的身体上,可以使用三子棋的思路,设置死循环,直到生成满足我们需要的位置然后break。

Node createFood(Node* snake, int length)
{
	Node food;

	while (1)
	{
		food.x = rand() % (800 / NodeWidth);
		food.y = rand() % (600 / NodeWidth);
		int i;
		for (i = 0; i < length; i++)
		{
			if ((food.x == snake[i].x) && (food.y == snake[i].y))
			{
				break;
			}
		}
		if (i < length)
			continue;
		else
			break;
	}
	return food;
}

生成完成后返回创建出的食物的结构体变量

然后绘制出食物,利用不同颜色作为区分。

void paintFood(Node food)
{
	int left, right, top, bottom;
	left = food.x * NodeWidth;
	top = food.y * NodeWidth;
	right = (food.x + 1) * NodeWidth;
	bottom = (food.y + 1) * NodeWidth;
	setfillcolor(YELLOW);
	solidrectangle(left, top, right, bottom);
	setfillcolor(WHITE);
}

👉我们绘制出的蛇的身体是白色的,如果在这里更改了填充颜色,再次绘制蛇就变成和食物一样的颜色,所以在绘制食物后还要将填充颜色还原为白色。

判断是否吃掉食物及节点的续接。

 Node lastTail = snakeMove(snake, length, d);
 if (snake[0].x == food.x && snake[0].y == food.y)
 {
        if (length < 100)
        {
           snake[length] = lastTail;
            length++;
         }
         food = createFood(snake, length);
 }

在循环中不断判断,吃掉食物后就创建出新的食物。

☁️判断游戏结束

如果蛇吃掉自己的身体或者蛇头越界了,就表示游戏失败

bool IsGameover(Node* snake, int length)
{
	if (snake[0].x<0 ||snake[0].x>(800 / NodeWidth))
		return true;
	if (snake[0].y<0 || snake[0].y>(600 / NodeWidth))
		return true;
	for (int i = 1; i < length; i++)//0改为1
	{
		if (snake[0].x == snake[i].x && snake[0].y == snake[i].y)
			return true;
	}
	return false;
}

💡如果没有碰到墙或者遍历过程中蛇头没有和某蛇节点重合,才返回false,否则返回true,表示游戏失败。

判断失败后,重置蛇的节点,重新生成新的食物。

讲解到这里就结束啦

代码如下,大家可以运行看一看效果(用于使格子更加明显的线可以画也可以不画)

#include<stdio.h>
#include<easyx.h>
#include<conio.h>
#include<time.h>
#define NodeWidth 40
typedef struct {
	int x;
	int y;
}Node;

void paintGrid()
{
	for (int y = 0; y <= 600; y += NodeWidth)
	{
		line(0, y, 800, y);
	}
	for (int x = 0; x <= 800; x += NodeWidth)
	{
		line(x, 0, x, 600);
	}
}
void paintSnake(Node* snake, int n)
{
	int left, top, right, bottom;
	for (int i = 0; i < n; i++)
	{
		left = snake[i].x * NodeWidth;
		top = snake[i].y * NodeWidth;
		right = (snake[i].x + 1) * NodeWidth;
		bottom = (snake[i].y + 1) * NodeWidth;
		solidrectangle(left, top, right, bottom);
	}
}
enum direction
{
	eUp,
	eDown,
	eLeft,
	eRight
};
Node snakeMove(Node* snake, int length, int direction)
{
	Node tail = snake[length - 1];
	for (int i = length - 1; i > 0; i--)
	{
		snake[i] = snake[i - 1];
	}
	Node NewHead;
	NewHead = snake[0];
	if (direction == eUp)
	{
		NewHead.y--;
	}
	else if (direction == eDown)
	{
		NewHead.y++;
	}
	else if (direction == eLeft)
	{
		NewHead.x--;
	}
	else if (direction == eRight)
	{
		NewHead.x++;
	}
	snake[0] = NewHead;
	return tail;
}
void changeDirection(enum direction* pD)
{
	if (_kbhit() != 0)
	{
		char c = _getch();
		switch (c)
		{
		case'w':
			if (*pD != eDown)
				*pD = eUp;
			break;
		case's':
			if (*pD != eUp)
				*pD = eDown;
			break;

		case'a':
			if (*pD != eRight)
				*pD = eLeft;
			break;

		case'd':
			if (*pD != eLeft)
				*pD = eRight;
			break;

		}

	}
}

Node createFood(Node* snake, int length)
{
	Node food;

	while (1)
	{
		food.x = rand() % (800 / NodeWidth);
		food.y = rand() % (600 / NodeWidth);
		int i;
		for (i = 0; i < length; i++)
		{
			if ((food.x == snake[i].x) && (food.y == snake[i].y))
			{
				break;
			}
		}
		if (i < length)
			continue;
		else
			break;
	}
	return food;
}

void paintFood(Node food)
{
	int left, right, top, bottom;
	left = food.x * NodeWidth;
	top = food.y * NodeWidth;
	right = (food.x + 1) * NodeWidth;
	bottom = (food.y + 1) * NodeWidth;
	setfillcolor(YELLOW);
	solidrectangle(left, top, right, bottom);
	setfillcolor(WHITE);
}
bool IsGameover(Node* snake, int length)
{
	if (snake[0].x<0 ||snake[0].x>(800 / NodeWidth))
		return true;
	if (snake[0].y<0 || snake[0].y>(600 / NodeWidth))
		return true;
	for (int i = 1; i < length; i++)//0改为1
	{
		if (snake[0].x == snake[i].x && snake[0].y == snake[i].y)
			return true;
	}
	return false;
}
void reset(Node* snake, int* length, enum direction* d)
{
	snake[0] = Node{ 5,7 };
	snake[1] = Node{ 4,7 };
	snake[2] = Node{ 3,7 };
	snake[3] = Node{ 2,7 };
	snake[4] = Node{ 1,7 };
	*length = 5;
	*d = eRight;
}

int main()
{
	initgraph(800, 600);
	setbkcolor(RGB(164, 25, 202));
	cleardevice();
	paintGrid();
	
	Node snake[100] = { {5,7},{4,7},{3,7},{2,7},{1,7} };
	int length = 5;
	enum direction d = eRight;
	srand(unsigned int(time(NULL)));
	Node food = createFood(snake, length);

	while (1)
	{
		cleardevice();
		paintGrid();
		paintSnake(snake, length);
		paintFood(food);
		Sleep(500);
		changeDirection(&d);
		Node lastTail = snakeMove(snake, length, d);
		if (snake[0].x == food.x && snake[0].y == food.y)
		{
			if (length < 100)
			{
				snake[length] = lastTail;
				length++;
			}
			food = createFood(snake, length);
		}
		if (IsGameover(snake, length) == true)
		{
			reset(snake, &length, &d);
			food = createFood(snake, length);
		}
	}

	closegraph();
	return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1089207.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C语言实现数据结构顺序查找和折半查找代码

文章目录 一、顺序查找二、折半查找 一、顺序查找 #define _CRT_SECURE_NO_WARNINGS #include<stdio.h> #include<stdlib.h> typedef struct {//查找表的数据结构int *data;//动态数组基址int TableLen;//表长 }SSTable;void InitTable(SSTable *L) {//初始化一个…

10Wqps网关接入层,LVS+Keepalived(DR模式)如何搭建?

说在前面 在40岁老架构师 尼恩的读者交流群(50)中&#xff0c;很多小伙伴拿高薪&#xff0c;完成架构的升级&#xff0c;进入架构师赛道&#xff0c;打开薪酬天花板。 最近有小伙伴拿到了一线互联网企业如京东、网易、微博、阿里、汽车之家、极兔、有赞、希音、百度、滴滴的架…

Redis的Java客户端-Jedis

目录 一、Jedis基本用法二、Jedis连接池 一、Jedis基本用法 二、Jedis连接池

C语言中的自定义类型详解(结构体 + 枚举 + 联合(共用体))

文章目录 1. 结构体1.1 结构体的声明1.2 结构体成员的访问1.3 匿名结构体1.4 结构体的自引用1.5 结构体内存对齐&#xff08;计算结构体的大小&#xff09;1.6 结构体传参1.6.1 传值传递1.6.2 传址传递&#xff08;使用指针&#xff09; 2. 位段2.1 什么是位段&#xff1f;2.2 …

Pytorch从零开始实战05

Pytorch从零开始实战——运动鞋识别 本系列来源于365天深度学习训练营 原作者K同学 文章目录 Pytorch从零开始实战——运动鞋识别环境准备数据集模型选择数据可视化模型预测总结 环境准备 本文基于Jupyter notebook&#xff0c;使用Python3.8&#xff0c;Pytorch2.0.1cu118…

五、OSPF动态路由实验

拓扑图&#xff1a; 基本ip的配置已经配置好了&#xff0c;接下来对两台路由器配置ospf协议&#xff0c;两台PC进行跨网段通讯 R1与R2构成单区域OSPF区域0&#xff0c;首先对R1进行配置 首先进入ospf 默认进程1&#xff0c;router id省略空缺&#xff0c;之后进入area 0区域&…

NodeMCU ESP8266 GPIO使用详解(图文并茂)

NodeMCU ESP8266 GPIO使用详解 文章目录 NodeMCU ESP8266 GPIO使用详解前言什么是GPIO&#xff1f;GPIO 的使用GPIO模式作为输出输出高电平输出低电平 作为输入上拉输入下拉输入读取输入值 总结 前言 前面的文章中我们已经学习了如何点亮一个LED灯&#xff0c;在嵌入式的世界里…

Web自动化测试入门 : 前端页面的组成分析详解

目前常见的前端页面是由HTMLcssJavaScript组成。 一、HTML&#xff1a; 作用&#xff1a;定义页面呈现的内容 HTML 是用来描述网页的一种语言。 HTML 指的是超文本标记语言 (Hyper Text Markup Language)HTML 不是一种编程语言&#xff0c;而是一种标记语言 (markup langua…

【问题思考】为什么SCAN CSCAN会导致磁臂黏着而FCFS不会导致磁臂黏着?

问题 这道18年的真题引起了我的疑惑&#xff0c;SCAN和CSCAN我认为应该也不会导致磁臂黏着&#xff0c;因为他们对于一个访问序列&#xff0c;比如19&#xff0c;24&#xff0c;52&#xff0c;现在正往外走&#xff0c;但是来了一个12的&#xff0c;不是早晚会往回走&#xff…

【数据结构】:队列的实现

队列 队列的概念及结构 队列&#xff1a;只允许在一端进行插入数据操作&#xff0c;在另一端进行删除数据操作的特殊线性表&#xff0c;队列具有先进先出 FIFO(First In First Out) 入队列&#xff1a;进行插入操作的一端称为队尾 出队列&#xff1a;进行删除操作的一端称为队…

AN基础工具——变形工具

【AN基础工具——变形工具】 基本使用方法&#xff1a;任意变形工具基础动画 本篇内容&#xff1a;学会使用变形工具 重点内容&#xff1a;变形工具 工 具&#xff1a;Adobe Animate 2022 基本使用方法&#xff1a; 任意变形工具 《任意变形工具&#xff08;快捷键Q&#xff0…

QT实现的截屏工具与录像功能

前言 目前实现了高仿微信的截屏工具&#xff0c;altx截屏&#xff0c;用户选取区域进行截屏确认&#xff0c;截屏完成后复制到了粘贴板&#xff0c;用全局按键监听按键&#xff0c;程序在最小化时也可以对按键进行监听&#xff0c;有截屏预览与保存按键。 大致流程&#xff1a…

统计学中箱型图的理解

一、箱形图的介绍 箱形图又称为盒须图、盒式图、盒状图或箱线图&#xff0c;是一种用作显示一组数据分散情况的统计图&#xff0c;因型状如箱子而得名。它是利用数据中的五个统计量&#xff1a;最小值、上四分位数、中位数、下四分位数与最大值来描述数据的一种统计图。 箱形图…

【MySQL入门到精通-黑马程序员】MySQL基础篇-函数

文章目录 前言一、字符串函数二、数值函数三、日期函数四、流程控制函数总结 前言 本专栏文章为观看黑马程序员《MySQL入门到精通》所做笔记&#xff0c;课程地址在这。如有侵权&#xff0c;立即删除。 函数 是指一段可以直接被另一段程序调用的程序或代码。 一、字符串函数 格…

C# AnimeGANv2 人像动漫化

效果 项目 下载 可执行程序exe下载 源码下载 其他 C# 人像卡通化 Onnx photo2cartoon-CSDN博客 C# AnimeGAN 漫画风格迁移 动漫风格迁移 图像卡通化 图像动漫化_天天代码码天天的博客-CSDN博客

复数的三角形式与指数形式

See https://blog.csdn.net/u011089570/article/details/102685877

C++ wpf自制软件打包安装更新源码实例

程序示例精选 C wpf自制软件打包安装更新源码实例 如需安装运行环境或远程调试&#xff0c;见文章底部个人QQ名片&#xff0c;由专业技术人员远程协助&#xff01; 前言 这篇博客针对《C wpf自制软件打包安装更新源码实例》编写代码&#xff0c;代码整洁&#xff0c;规则&…

JAVA设计模式-工厂模式(Factory Pattern)

一.概念 我们在创建对象时不会对客户端直接暴露创建逻辑&#xff0c;而是 通过使用一个共同的接口根据不同的条件来指向具体想要创建的对象。 二.工厂模式优点 1.解耦 &#xff1a;把对象的创建和使用的过程分开 2.降低代码重复&#xff1a; 如果创建某个对象的过程都很复杂…

HTML基础入门02

目录 1.格式化标签 2.图片标签: img 3.超链接标签: a 4.综合案例: 展示博客2 5.表格标签 5.1基本使用 5.2合并单元格 6.列表标签 1.格式化标签 加粗&#xff1a;strong标签和b标签 倾斜&#xff1a;em标签和i标签 删除线&#xff1a;del标签和s标签 下划线&#xff1a;i…

结构体,位段!

目录 1.什么是位段&#xff1f; 别急&#xff01;在下面第二点我和大家介绍。 2.位段的内存怎么分配&#xff1f; 还有一种情况就是两种类型夹杂在一起的位段 3.位段的跨平台问题 4.位段能干嘛&#xff1f;&#xff08;应用&#xff09; 5.位段的注意事项 1.什么是位段&…