当前位置: 首页 > news >正文

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图形库实现贪吃蛇游戏

⭐大家好&#xff0c;我是Dark Falme Masker,学习了动画制作及键盘交互之后&#xff0c;我们就可以开动利用图形库写一个简单的贪吃蛇小游戏&#xff0c;增加学习乐趣。 ⭐专栏&#xff1a;EasyX部分小游戏实现详细讲解 最终效果如下 首先包含头文件 #include<stdio.h> #…...

利用 Amazon CodeWhisperer 激发孩子的编程兴趣

我是一个程序员&#xff0c;也是一个父亲。工作之余我会经常和儿子聊他们小学信息技术课学习的 Scratch 和 Kitten 这两款图形化的少儿编程工具。 我儿子有一次指着书房里显示器上显示的 Visual Studio Code 问我&#xff0c;“为什么我们上课用的开发界面&#xff0c;和爸爸你…...

2023年中国分子筛稀土催化材料竞争格局及行业市场规模分析[图]

稀土催化材料能够起到提高催化剂热稳定性、催化剂活性、催化剂储氧能力&#xff0c;以及减少贵金属活性组分用量等作用&#xff0c;广泛应用于石油化工、汽车尾气净化、工业废气和人居环境净化、燃料电池等领域。 2015-2023年中国稀土催化材料规模及预测 资料来源&#xff1a;…...

vue3插件——vue-web-screen-shot——实现页面截图功能

最近在看前同事发我的vue3框架时&#xff0c;发现他们有个功能是要实现页面截图功能。 vue3插件——vue-web-screen-shot——实现页面截图功能 效果图如下&#xff1a;1.操作步骤1.1在项目中添加vvue-web-screen-shot组件1.2在项目入口文件导入组件——main.ts1.3在需要使用的页…...

简单总结Centos7安装Tomcat10.0版本

文章目录 前言JDK8安装部署Tomcat 前言 注意jdk与tomcat的兼容问题&#xff0c;其他的只要正确操作一般问题不大 Tomcat 是由 Apache 开发的一个 Servlet 容器&#xff0c;实现了对 Servlet 和 JSP 的支持&#xff0c;并提供了作为Web服务器的一些特有功能&#xff0c;如Tomca…...

ffmpeg中AVCodecContext和AVCodec的关系分析

怎么理解AVCodecContext和AVCodec的关系 AVCodecContext和AVCodec是FFmpeg库中两个相关的结构体&#xff0c;它们在音视频编解码中扮演着不同的角色。 AVCodecContext&#xff1a;是编解码器上下文结构体&#xff0c;用于存储音视频编解码器的参数和状态信息。它包含了进行音视…...

2023年中国门把手产量、销量及市场规模分析[图]

门把手行业是指专门从事门把手的设计、制造、销售和安装等相关业务的行业。门把手是门窗装饰硬件的一种&#xff0c;用于开启和关闭门窗&#xff0c;同时也具有装饰和美化门窗的作用。 门把手行业分类 资料来源&#xff1a;共研产业咨询&#xff08;共研网&#xff09; 随着消…...

HTML 核心技术点基础详细解析以及综合小案例

核心技术点 网页组成 排版标签 多媒体标签及属性 综合案例一 - 个人简介 综合案例二 - Vue 简介 02-标签语法 HTML 超文本标记语言——HyperText Markup Language。 超文本&#xff1a;链接 标记&#xff1a;标签&#xff0c;带尖括号的文本 标签结构 标签要成…...

BAT学习——批处理脚本(也称为BAT文件)常用语法元素与命令

批处理脚本&#xff08;也称为BAT文件&#xff09;使用Windows的批处理语言编写&#xff0c;它具有一些常用的语法元素和命令。以下是一些BAT编程的常用语法元素和命令&#xff1a; 命令行命令&#xff1a; 批处理脚本通常包含一系列Windows命令&#xff0c;例如echo&#xff0…...

AMD AFMF不但能用在游戏,也适用于视频

近期AMD发布了AMD Software Adrenalin Edition预览版驱动程序&#xff0c;增加了对平滑移动帧&#xff08;AMD Fluid Motion Frames&#xff0c;AFMF&#xff09;功能的支持&#xff0c;也就是AMD的“帧生成”技术&#xff0c;与DLSS 3类似&#xff0c;作为FidelityFX Super Re…...

CSS 常用样式浮动属性

一、概述 CSS 中&#xff0c;浮动属性的作用是让元素向左或向右浮动&#xff0c;使其他元素围绕它排布&#xff0c;常用的浮动属性有以下几种&#xff1a; float: left; 使元素向左浮动&#xff0c;其他元素从右侧包围它。 float: right; 使元素向右浮动&#xff0c;其他元素…...

Linux引导故障排除:从问题到解决方案的详细指南

1 BIOS初始化 通电->对硬件检测->初始化硬件时钟 2 磁盘引导及其修复 2.1 磁盘引导故障 磁盘主引导记录&#xff08;MBR&#xff09;是在0磁道1扇区位置&#xff0c;446字节。 MBR作用&#xff1a;记录grub2引导文件的位置 2.2 修复 步骤&#xff1a;1、光盘进…...

【vim 学习系列文章 6 -- vim 如何从上次退出的位置打开文件】

文章目录 1.1 vim 如何从上次退出的位置打开文件1.2 autogroup 命令学习1.2.1 augroup 基本语法 1.3 vim call 命令详细介绍 1.1 vim 如何从上次退出的位置打开文件 假设我打开了文件 test.c&#xff0c;然后我向下滚动到第 50 行&#xff0c;然后我做了一些修改并关闭了文件。…...

怎样学习C#上位机编程?

怎样学习C#上位机编程&#xff1f; 00001. 掌握C#编程和.NET框架基础。 00002. 学WinForm应用开发&#xff0c;了解控件使用和事件编程。 00003. 熟悉基本数据结构和算法&#xff0c;如链表、栈、队列。 00004. 理解串口通信协议和方法&#xff0c;用于与硬件交互。 00005…...

【算法-动态规划】两个字符串的删除操作-力扣 583

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kuan 的首页,持续学…...

【06】基础知识:typescript中的泛型

一、泛型的定义 在软件开发中&#xff0c;我们不仅要创建一致的定义良好的API&#xff0c;同时也要考虑可重用性。 组件不仅能支持当前数据类型&#xff0c;同时也能支持未来的数据类型&#xff0c;这在创建大型系统时提供了十分灵活的功能。 在像 C# 和 Java 这样的语言中&…...

flutter 绘制原理探究

文章目录 Widget1、简介2、源码分析Element1、简介2、源码分析RenderObjectWidget 渲染过程总结思考Flutter 的核心设计思想便是“一切皆 Widget”,Widget 是 Flutter 功能的抽象描述,是视图的配置信息,同样也是数据的映射,是 Flutter 开发框架中最基本的概念。 在 Flutter…...

[Java]SPI扩展功能

一、什么是SPI Java SPI&#xff08;Service Provider Interface&#xff09;是Java官方提供的一种服务发现机制。 它允许在运行时动态地加载实现特定接口的类&#xff0c;而不需要在代码中显式地指定该类&#xff0c;从而实现解耦和灵活性。 二、实现原理 基于 Java 类加载…...

机器人命令表设计

演算命令 CLEAR 将数据 1 上被指定的编号以后的变数的内容&#xff0c;以及数据 2 上仅被指定的个数都清除至 0。 INC 在被指定的变数内容上加上 1。 DEC 在被指定的变数内容上减掉 1。 SET 在数据 1 上设定数据 2。 ADD 将数据 1 和数据 2 相加&#xff0c;得出的结果保存在数…...

STM32--WDG看门狗

文章目录 WDG简介IWDGIWDG的超时计算WWDGWWDG超时和窗口值设定独立看门狗工程WWDG工程 WDG简介 WDG看门狗&#xff08;Watchdog Timer&#xff09;是一种常见的硬件设备&#xff0c;在STM32F10系列中&#xff0c;有两种看门狗&#xff0c;分别是独立看门狗和窗口看门狗&#x…...

Java 语言特性(面试系列1)

一、面向对象编程 1. 封装&#xff08;Encapsulation&#xff09; 定义&#xff1a;将数据&#xff08;属性&#xff09;和操作数据的方法绑定在一起&#xff0c;通过访问控制符&#xff08;private、protected、public&#xff09;隐藏内部实现细节。示例&#xff1a; public …...

Debian系统简介

目录 Debian系统介绍 Debian版本介绍 Debian软件源介绍 软件包管理工具dpkg dpkg核心指令详解 安装软件包 卸载软件包 查询软件包状态 验证软件包完整性 手动处理依赖关系 dpkg vs apt Debian系统介绍 Debian 和 Ubuntu 都是基于 Debian内核 的 Linux 发行版&#xff…...

聊聊 Pulsar:Producer 源码解析

一、前言 Apache Pulsar 是一个企业级的开源分布式消息传递平台&#xff0c;以其高性能、可扩展性和存储计算分离架构在消息队列和流处理领域独树一帜。在 Pulsar 的核心架构中&#xff0c;Producer&#xff08;生产者&#xff09; 是连接客户端应用与消息队列的第一步。生产者…...

【SQL学习笔记1】增删改查+多表连接全解析(内附SQL免费在线练习工具)

可以使用Sqliteviz这个网站免费编写sql语句&#xff0c;它能够让用户直接在浏览器内练习SQL的语法&#xff0c;不需要安装任何软件。 链接如下&#xff1a; sqliteviz 注意&#xff1a; 在转写SQL语法时&#xff0c;关键字之间有一个特定的顺序&#xff0c;这个顺序会影响到…...

鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个医院查看报告小程序

一、开发环境准备 ​​工具安装​​&#xff1a; 下载安装DevEco Studio 4.0&#xff08;支持HarmonyOS 5&#xff09;配置HarmonyOS SDK 5.0确保Node.js版本≥14 ​​项目初始化​​&#xff1a; ohpm init harmony/hospital-report-app 二、核心功能模块实现 1. 报告列表…...

第一篇:Agent2Agent (A2A) 协议——协作式人工智能的黎明

AI 领域的快速发展正在催生一个新时代&#xff0c;智能代理&#xff08;agents&#xff09;不再是孤立的个体&#xff0c;而是能够像一个数字团队一样协作。然而&#xff0c;当前 AI 生态系统的碎片化阻碍了这一愿景的实现&#xff0c;导致了“AI 巴别塔问题”——不同代理之间…...

鸿蒙DevEco Studio HarmonyOS 5跑酷小游戏实现指南

1. 项目概述 本跑酷小游戏基于鸿蒙HarmonyOS 5开发&#xff0c;使用DevEco Studio作为开发工具&#xff0c;采用Java语言实现&#xff0c;包含角色控制、障碍物生成和分数计算系统。 2. 项目结构 /src/main/java/com/example/runner/├── MainAbilitySlice.java // 主界…...

rnn判断string中第一次出现a的下标

# coding:utf8 import torch import torch.nn as nn import numpy as np import random import json""" 基于pytorch的网络编写 实现一个RNN网络完成多分类任务 判断字符 a 第一次出现在字符串中的位置 """class TorchModel(nn.Module):def __in…...

Java求职者面试指南:计算机基础与源码原理深度解析

Java求职者面试指南&#xff1a;计算机基础与源码原理深度解析 第一轮提问&#xff1a;基础概念问题 1. 请解释什么是进程和线程的区别&#xff1f; 面试官&#xff1a;进程是程序的一次执行过程&#xff0c;是系统进行资源分配和调度的基本单位&#xff1b;而线程是进程中的…...

GitHub 趋势日报 (2025年06月06日)

&#x1f4ca; 由 TrendForge 系统生成 | &#x1f310; https://trendforge.devlive.org/ &#x1f310; 本日报中的项目描述已自动翻译为中文 &#x1f4c8; 今日获星趋势图 今日获星趋势图 590 cognee 551 onlook 399 project-based-learning 348 build-your-own-x 320 ne…...