EasyX图形库实现贪吃蛇游戏
⭐大家好,我是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;elsebreak;}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;elsebreak;}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; }
相关文章:
EasyX图形库实现贪吃蛇游戏
⭐大家好,我是Dark Falme Masker,学习了动画制作及键盘交互之后,我们就可以开动利用图形库写一个简单的贪吃蛇小游戏,增加学习乐趣。 ⭐专栏:EasyX部分小游戏实现详细讲解 最终效果如下 首先包含头文件 #include<stdio.h> #…...
利用 Amazon CodeWhisperer 激发孩子的编程兴趣
我是一个程序员,也是一个父亲。工作之余我会经常和儿子聊他们小学信息技术课学习的 Scratch 和 Kitten 这两款图形化的少儿编程工具。 我儿子有一次指着书房里显示器上显示的 Visual Studio Code 问我,“为什么我们上课用的开发界面,和爸爸你…...
2023年中国分子筛稀土催化材料竞争格局及行业市场规模分析[图]
稀土催化材料能够起到提高催化剂热稳定性、催化剂活性、催化剂储氧能力,以及减少贵金属活性组分用量等作用,广泛应用于石油化工、汽车尾气净化、工业废气和人居环境净化、燃料电池等领域。 2015-2023年中国稀土催化材料规模及预测 资料来源:…...
vue3插件——vue-web-screen-shot——实现页面截图功能
最近在看前同事发我的vue3框架时,发现他们有个功能是要实现页面截图功能。 vue3插件——vue-web-screen-shot——实现页面截图功能 效果图如下:1.操作步骤1.1在项目中添加vvue-web-screen-shot组件1.2在项目入口文件导入组件——main.ts1.3在需要使用的页…...
简单总结Centos7安装Tomcat10.0版本
文章目录 前言JDK8安装部署Tomcat 前言 注意jdk与tomcat的兼容问题,其他的只要正确操作一般问题不大 Tomcat 是由 Apache 开发的一个 Servlet 容器,实现了对 Servlet 和 JSP 的支持,并提供了作为Web服务器的一些特有功能,如Tomca…...
ffmpeg中AVCodecContext和AVCodec的关系分析
怎么理解AVCodecContext和AVCodec的关系 AVCodecContext和AVCodec是FFmpeg库中两个相关的结构体,它们在音视频编解码中扮演着不同的角色。 AVCodecContext:是编解码器上下文结构体,用于存储音视频编解码器的参数和状态信息。它包含了进行音视…...
2023年中国门把手产量、销量及市场规模分析[图]
门把手行业是指专门从事门把手的设计、制造、销售和安装等相关业务的行业。门把手是门窗装饰硬件的一种,用于开启和关闭门窗,同时也具有装饰和美化门窗的作用。 门把手行业分类 资料来源:共研产业咨询(共研网) 随着消…...
HTML 核心技术点基础详细解析以及综合小案例
核心技术点 网页组成 排版标签 多媒体标签及属性 综合案例一 - 个人简介 综合案例二 - Vue 简介 02-标签语法 HTML 超文本标记语言——HyperText Markup Language。 超文本:链接 标记:标签,带尖括号的文本 标签结构 标签要成…...
BAT学习——批处理脚本(也称为BAT文件)常用语法元素与命令
批处理脚本(也称为BAT文件)使用Windows的批处理语言编写,它具有一些常用的语法元素和命令。以下是一些BAT编程的常用语法元素和命令: 命令行命令: 批处理脚本通常包含一系列Windows命令,例如echo࿰…...
AMD AFMF不但能用在游戏,也适用于视频
近期AMD发布了AMD Software Adrenalin Edition预览版驱动程序,增加了对平滑移动帧(AMD Fluid Motion Frames,AFMF)功能的支持,也就是AMD的“帧生成”技术,与DLSS 3类似,作为FidelityFX Super Re…...
CSS 常用样式浮动属性
一、概述 CSS 中,浮动属性的作用是让元素向左或向右浮动,使其他元素围绕它排布,常用的浮动属性有以下几种: float: left; 使元素向左浮动,其他元素从右侧包围它。 float: right; 使元素向右浮动,其他元素…...
Linux引导故障排除:从问题到解决方案的详细指南
1 BIOS初始化 通电->对硬件检测->初始化硬件时钟 2 磁盘引导及其修复 2.1 磁盘引导故障 磁盘主引导记录(MBR)是在0磁道1扇区位置,446字节。 MBR作用:记录grub2引导文件的位置 2.2 修复 步骤:1、光盘进…...
【vim 学习系列文章 6 -- vim 如何从上次退出的位置打开文件】
文章目录 1.1 vim 如何从上次退出的位置打开文件1.2 autogroup 命令学习1.2.1 augroup 基本语法 1.3 vim call 命令详细介绍 1.1 vim 如何从上次退出的位置打开文件 假设我打开了文件 test.c,然后我向下滚动到第 50 行,然后我做了一些修改并关闭了文件。…...
怎样学习C#上位机编程?
怎样学习C#上位机编程? 00001. 掌握C#编程和.NET框架基础。 00002. 学WinForm应用开发,了解控件使用和事件编程。 00003. 熟悉基本数据结构和算法,如链表、栈、队列。 00004. 理解串口通信协议和方法,用于与硬件交互。 00005…...
【算法-动态规划】两个字符串的删除操作-力扣 583
💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:kuan 的首页,持续学…...
【06】基础知识:typescript中的泛型
一、泛型的定义 在软件开发中,我们不仅要创建一致的定义良好的API,同时也要考虑可重用性。 组件不仅能支持当前数据类型,同时也能支持未来的数据类型,这在创建大型系统时提供了十分灵活的功能。 在像 C# 和 Java 这样的语言中&…...
flutter 绘制原理探究
文章目录 Widget1、简介2、源码分析Element1、简介2、源码分析RenderObjectWidget 渲染过程总结思考Flutter 的核心设计思想便是“一切皆 Widget”,Widget 是 Flutter 功能的抽象描述,是视图的配置信息,同样也是数据的映射,是 Flutter 开发框架中最基本的概念。 在 Flutter…...
[Java]SPI扩展功能
一、什么是SPI Java SPI(Service Provider Interface)是Java官方提供的一种服务发现机制。 它允许在运行时动态地加载实现特定接口的类,而不需要在代码中显式地指定该类,从而实现解耦和灵活性。 二、实现原理 基于 Java 类加载…...
机器人命令表设计
演算命令 CLEAR 将数据 1 上被指定的编号以后的变数的内容,以及数据 2 上仅被指定的个数都清除至 0。 INC 在被指定的变数内容上加上 1。 DEC 在被指定的变数内容上减掉 1。 SET 在数据 1 上设定数据 2。 ADD 将数据 1 和数据 2 相加,得出的结果保存在数…...
STM32--WDG看门狗
文章目录 WDG简介IWDGIWDG的超时计算WWDGWWDG超时和窗口值设定独立看门狗工程WWDG工程 WDG简介 WDG看门狗(Watchdog Timer)是一种常见的硬件设备,在STM32F10系列中,有两种看门狗,分别是独立看门狗和窗口看门狗&#x…...
OpenClaw+Qwen3-VL:30B:低成本搭建飞书多模态机器人
OpenClawQwen3-VL:30B:低成本搭建飞书多模态机器人 1. 为什么选择本地部署多模态助手? 去年我在团队内部尝试用商业API搭建了一个飞书机器人,用于处理日常的图片识别和文档分析需求。三个月后收到账单时,发现仅图片识别这一项功…...
避坑指南:Coze离线部署时bootstrap.sh文件丢失?Docker Compose卷映射的正确姿势
避坑指南:Coze离线部署时bootstrap.sh文件丢失?Docker Compose卷映射的正确姿势 当你第一次尝试在离线环境中部署Coze时,遇到bootstrap.sh文件丢失的问题可能会让你措手不及。这个看似简单的文件缺失背后,实际上隐藏着Docker数据…...
新手必看!用Python+OpenCV实现简易版视觉里程计(附车道线检测代码)
PythonOpenCV实战:从车道线检测到简易视觉里程计 在自动驾驶和机器人导航领域,视觉里程计(VO)是一项基础而关键的技术。它像是一双"数字眼睛",通过分析连续图像帧之间的变化来估算设备的运动轨迹。想象一下,当你闭着眼…...
4步精通OpenCore EFI制作:OpCore-Simplify智能配置引擎全解析
4步精通OpenCore EFI制作:OpCore-Simplify智能配置引擎全解析 【免费下载链接】OpCore-Simplify A tool designed to simplify the creation of OpenCore EFI 项目地址: https://gitcode.com/GitHub_Trending/op/OpCore-Simplify 在黑苹果技术领域࿰…...
ArcMap新手必看:5分钟搞定面要素的四至信息提取(附字段重命名技巧)
ArcMap新手实战:5分钟高效提取面要素四至信息与字段优化技巧 刚接触ArcMap的GIS实习生或规划人员,常常需要快速处理行政区划数据并生成规范的四至报告。面对属性表中密密麻麻的英文字段名和冗余数据,如何高效完成从数据加载到结果美化的全流程…...
palera1n 开发者贡献指南:如何快速参与iOS越狱项目开发 [特殊字符]
palera1n 开发者贡献指南:如何快速参与iOS越狱项目开发 🚀 【免费下载链接】palera1n Jailbreak for arm64 devices on iOS 15.0 项目地址: https://gitcode.com/GitHub_Trending/pa/palera1n palera1n是一款支持iOS 15.0系统的arm64设备越狱工具…...
告别显卡驱动残留困扰:Display Driver Uninstaller的深度清理全解析
告别显卡驱动残留困扰:Display Driver Uninstaller的深度清理全解析 【免费下载链接】display-drivers-uninstaller Display Driver Uninstaller (DDU) a driver removal utility / cleaner utility 项目地址: https://gitcode.com/gh_mirrors/di/display-drivers…...
Rolify 项目部署指南:从开发环境到生产环境的完整迁移流程
Rolify 项目部署指南:从开发环境到生产环境的完整迁移流程 【免费下载链接】rolify Role management library with resource scoping 项目地址: https://gitcode.com/gh_mirrors/ro/rolify Rolify 是一款功能强大的角色管理库,支持资源范围的权限…...
嵌入式开发必备:rootfs.img镜像修改的5个常见问题与解决方案
嵌入式开发实战:rootfs.img镜像修改的五大核心问题与深度解决方案 在嵌入式Linux开发中,rootfs.img镜像的修改是每个开发者绕不开的关键环节。无论是添加自定义配置文件、更新系统组件,还是优化启动流程,对根文件系统的调整都直接…...
memory-lancedb-pro混合检索揭秘:向量搜索+BM25如何提升AI记忆准确率300%
memory-lancedb-pro混合检索揭秘:向量搜索BM25如何提升AI记忆准确率300% 【免费下载链接】memory-lancedb-pro Enhanced LanceDB memory plugin for OpenClaw — Hybrid Retrieval (Vector BM25), Cross-Encoder Rerank, Multi-Scope Isolation, Management CLI …...
