使用C语言实现贪吃蛇(超详细)

目录

实现贪吃蛇我们要知道哪些?

Easyx图形库

Easyx的安装

游戏思路

游戏实现

头文件的使用

 ​编辑和食物以及控制方向的初始化

对于坐标的实现:

食物颜色的实现:

游戏数据的初始化

加载音乐

图形窗口的设置:

 蛇身节数的初始化:

食物的初始化

绘制游戏元素

移动蛇

贪吃蛇的穿墙实现

 通过键盘移动蛇和暂停游戏

_kbbit()函数作用

 蛇的移动判定

暂停游戏

判断蛇吃到食物

蛇吃到食物后的增长

 在main函数中调用各个函数

一些操作问题

键盘控制不了蛇的移动:


实现贪吃蛇我们要知道哪些?

Easyx图形库

我们需要安装Easyx配置到vs2022中,使游戏的背景颜色、蛇的颜色、食物的颜色可供我们随意设置,这样就不必再以前那个黑乎乎的终端命令窗口打开了

Easyx的安装

官网链接:https://easyx.cn/

在这个官网里不仅有简单的按照步骤还有关于一些库函数的介绍,教程通俗易懂,仅仅需要一点函数的知识。


游戏思路

我们需要知道蛇的移动是靠坐标的变换来实现的,食物的出现也是这样,这里的坐标系和我们日常数学中的有些区别(y轴的方向相反):

另外我们还需要知道:

是怎么移动的

是怎么吃东西的

吃到食物后节数怎么变化

如何通过键盘来实现蛇的移动


游戏实现

头文件的使用

#include<stdio.h>
#include<graphics.h>//图形库函数的头文件,下面的库函数是我们对背景、颜色、图形的生成等
#include<conio.h>//
#include<stdlib.h>
#include<windows.h>
#include<mmsystem.h>

对于这些头文件,下面使用包含其中的库函数时会讲到

 和食物以及控制方向的初始化

#define SNAKE_NUM 500 //蛇的最大节数
//蛇的移动方向
enum DIR
{UP,DOWN,LEFT,RIGHT,
};
//蛇的结构
struct Snake
{int size;//蛇的节数int dir;//蛇的方向int speed;//蛇的速度POINT coor[SNAKE_NUM];//坐标
}snake;
//食物结构
struct Food
{int x;int y;int r;//食物大小bool flag;//食物是否被吃DWORD color;//食物颜色
}food;
对于坐标的实现:

  选中POINT后转到定义查看

食物颜色的实现:

与查看坐标的实现方法是一样的,大家理解一下就行


游戏数据的初始化

//数据的初始化
void GameInit()
{//播放背景音乐mciSendString("open./res/mmm.mp3 alias BGM", NULL, 0, NULL);mciSendString("play BGM repeat", NULL, 0, NULL);//inint 初始化 graph 图形窗口 SHOWCONSOLE显示控制台initgraph(640, 480 /*SHOWCONSOLE*/);//蛇的初始化,起始有三节snake.size = 3;snake.speed = 10;snake.dir = RIGHT;for (int i = 0; i < snake.size; i++){snake.coor[i].x = 40 - 10 * i;snake.coor[i].y = 10;printf("?%d %d", snake.coor[i].x, snake.coor[i].y);}//设置随机数种子 GetTickCount获取系统开机经过的毫秒数srand(GetTickCount());//初始化食物 rand()随机产生一个整数,需要设置一个生产随机数的种子food.x = rand() % 640;food.y = rand() % 480;food.color = RGB(rand() % 256, rand() % 256, rand() % 256);food.r = rand() % 10 + 5;food.flag = true;
}

加载音乐

#include<mmsystem.h>
#pragma comment(lib, "winmm.lib")
//播放背景音乐
mciSendString("open./res/mmm.mp3 alias BGM", NULL, 0, NULL);
mciSendString("play BGM repeat", NULL, 0, NULL);

 这位博主关于mciSendString函数介绍的很详细:

https://blog.csdn.net/m0_73633088/article/details/128371136

图形窗口的设置:

initgraph(640, 480 /*SHOWCONSOLE*/);

initgraph(int x, int y, /*SHOWCONSOLE*/), /*SHOWCONSOLE*/用来显示终端窗口

 蛇身节数的初始化:

蛇头肯定是在蛇身前的,在初始化蛇身节数时节数要加在头后面,而各个节数之间其实是两个半径为5的圆形相切而成,下面这段代码就是对上面这句话的实现:

snake.coor[i].x = 40 - 10 * i;snake.coor[i].y = 10;

食物的初始化

由于食物是随机出现的,我们需要一个随机数来生成食物出现的坐标与半径,sand()是随机生成一个整数,但是此后这个整数不再变化,所以我们还需要srand(随机数)生成一个伪随机数(种子)。

对于如何判断食物是否被吃掉,我们使用布尔型的flag,定义ture的值为1(食物存在),定义false的值为0(食物消失)


绘制游戏元素

void GameDraw()
{//双缓冲绘图BeginBatchDraw();//设置背景颜色setbkcolor(RGB(28, 115, 119));cleardevice();//绘制蛇setfillcolor(GREEN);for (int i = 0; i < snake.size; i++){solidcircle(snake.coor[i].x, snake.coor[i].y, 5);}//绘制食物if (food.flag){solidcircle(food.x, food.y, food.r);}EndBatchDraw();
}

这段代码中的库函数都包含在#include<graphics.h>头文件中

这段代码,就是对上面布尔型数据的应用,如果food.flag = ture,则进行食物的绘制


移动蛇

//移动蛇
void snakeMove()
{//让身体跟着蛇头移动for (int i = snake.size - 1; i > 0; i--){snake.coor[i] = snake.coor[i - 1];}//移动是坐标发生改变switch (snake.dir){case UP:snake.coor[0].y -= snake.speed;if (snake.coor[0].y + 10 <= 0)snake.coor[0].y = 480;break;case DOWN:snake.coor[0].y += snake.speed;if (snake.coor[0].y - 10 >= 480)snake.coor[0].y = 0;break;case LEFT:snake.coor[0].x -= snake.speed;if (snake.coor[0].x + 10 <= 0)snake.coor[0].x = 640;break;case RIGHT:snake.coor[0].x += snake.speed;if (snake.coor[0].x - 10 >= 640)snake.coor[0].x = 0;break;}}

 我们在移动蛇时,肯定不能只让蛇头去移动,蛇的身子也必须跟着移动,所以我们要把上一节的坐标赋值给下一节,使它们连贯,实现如下:

 下面我们想一下,蛇移动是什么发生改变?是坐标!

那么蛇的移动速度呢?是在你设置的单位时间内移动的距离

 在坐标系内,向上移动是y值做减法,向下移动是y值做加法,向右移动是x值做加法,向左移动是做减法,实现如下:

贪吃蛇的穿墙实现

穿墙,本质上也是坐标的变化,比如向右走到有边界,右边界的坐标是(640,y),只有当蛇的尾部即最身子尾部的圆的左侧与其边界相切时,判断穿过,这时我们要把头部的坐标赋值为(0,y),实现如下:

 通过键盘移动蛇和暂停游戏

//通过按键改变蛇的移动方向
void keyControl()
{//判断有没有按键,有按键返回真if (_kbhit()){//72 80 75 77 上下左右键值switch (char ch = _getch()){case'w':case'W':case 72:if (snake.dir != UP){snake.dir = UP;}break;case's':case'S':case 80:if (snake.dir != UP){snake.dir = DOWN;}break;case'a':case'A':case 75:if (snake.dir != RIGHT){snake.dir = LEFT;}break;case'd':case'D':case 77:if (snake.dir != LEFT){snake.dir = RIGHT;}break;case ' ': system("pause");}}}

_kbbit()函数作用

_kbbit()函数的作用是检查控制台窗口的按键是否被按下。其格式为

int _kbhit( void );

如果在调用该函数时,有按键被按下,则返回一个非零值,否则该函数的返回值是0。需要注意的是,该函数是一个非阻塞函数,不管有没有按键被按下,该函数都会立即返回。_khbit()函数一般与_getch()函数与getche()函数组合使用获取按键信息。

参考自:https://blog.csdn.net/hou09tian/article/details/86668083

 蛇的移动判定

如果在向下移动蛇的过程,我们能让蛇不拐弯直接换个方向向上移动吗?显然是不行的。

当我们向右地动时,按“A”向左移动就不能让程序判定蛇向左移动,代码实现如下:

在这里我们也可以看到枚举类型的奇妙应用,是不是很有意思。

暂停游戏

 只需要点击空格就可以暂停游戏,其中system()包含在windows<stdio.h>头文件中


判断蛇吃到食物

//判断蛇吃到食物
void EatFood()
{if (food.flag && snake.coor[0].x >= food.x - food.r && snake.coor[0].x <= food.x + food.r && snake.coor[0].y >= food.y - food.r&& snake.coor[0].y <= food.y + food.r){food.flag = false;snake.size++;}//如果食物消失,重新生成if (!food.flag){//设置随机数种子 GetTickCount获取系统开机经过的毫秒数srand(GetTickCount());//初始化食物 rand()随机产生一个整数,需要设置一个生产随机数的种子food.x = rand() % 640;food.y = rand() % 480;food.color = RGB(rand() % 256, rand() % 256, rand() % 256);food.r = rand() % 10 + 5;food.flag = true;}}

蛇吃到食物后的增长

蛇可以从上下左右四个方向吃到食物,并且是当蛇的头部碰到食物时判定吃到,即蛇的头部的(圆)与食物(圆)相切。实现如下:

 在main函数中调用各个函数

int main()
{GameInit();GameDraw();while (1){GameDraw();snakeMove();keyControl();EatFood();Sleep(200);}return 0;
}

对于一些函数而言需要再while(1)中,防止一次就运行结束,得不到持续操作的效果。


一些操作问题

有时候即便写出程序也不能正常玩耍,解决方法在下面:

键盘控制不了蛇的移动:

打开 win11 的系统设置 -> 隐私和安全性 -> 开发者选项,找到“终端”项,里面有三项:“让 Windows 决定(默认)、Windows 控制台主机、Windows 终端”,改为“Windows 控制台主机”即可

然后键盘改为英文输入,鼠标点击终端命令窗口,确保是选中状态


制作不易,请点个呗!

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

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

相关文章

【动手学深度学习】多层感知机之暂退法问题研究详情

目录 &#x1f30a;问题研究1 &#x1f31e;问题研究2 &#x1f332;问题研究3 &#x1f30d;问题研究4 &#x1f333;问题研究5 &#x1f30c;问题研究6 &#x1f30a;问题研究1 如果更改第一层和第二层的暂退法概率&#xff0c;会发生什么情况&#xff1f;具体地说&am…

深入理解指针(4)--新手小白都能明白的指针解析

深入理解指针(4)–新手小白都能明白的指针解析 文章目录 深入理解指针(4)--新手小白都能明白的指针解析1. 回调函数2. qsort使用举例2.1 冒泡排序2.2 qsort函数介绍2.3 用冒泡排序实现qsort 结语 1. 回调函数 回调函数就是⼀个通过函数指针调用的函数 如果我们把函数的指针&a…

玩转微服务-GateWay

目录 一. 背景二. API网关1. 概念2. API网关定义3. API网关的四大职能4. API网关分类5. 开源API网关介绍6. 开源网关的选择 三. Spring Cloud Gateway1. 文档地址2. 三个核心概念3. 工作流程4. 运行原理4.1 路由原理4.2 RouteLocator 5. Predicate 断言6. 过滤器 Filter6.1. 过…

[图解]建模相关的基础知识-02

1 00:00:01,530 --> 00:00:05,200 第2个概念&#xff0c;谓词&#xff0c;Predicate 2 00:00:07,530 --> 00:00:10,800 或者叫断言&#xff0c;翻译各种各样都有 3 00:00:12,830 --> 00:00:15,050 实际上就是前面命题 4 00:00:15,060 --> 00:00:16,610 相当于常…

记录Nuxt 3 官网项目的一次部署

本来以为就是一次简单的部署&#xff0c;之前也是部署过几次nuxt项目了&#xff0c;所以&#xff0c;并没有要记录的想法。但是过程出现了很多问题&#xff0c;最后考虑还是写下来吧。留个记录&#xff08;完整的配置部署过程&#xff09; 这里我将要说明两种部署方式以供选择&…

开源网安软件安全国产化替代解决方案亮相2024澳门万讯论坛

近日&#xff0c;2024万讯论坛在澳门成功举办。本次论坛由万讯电脑科技主办&#xff0c;旨在引进国内尖端科技厂商&#xff0c;提供全方位的信创解决方案&#xff0c;分享信创化过程中所面临的挑战及阶段性转换经验。开源网安作为拥有软件安全领域全链条产品的厂商&#xff0c;…

Analytical Model(分析模型)和Compact model(紧凑模型)有什么不同

Analytical Model&#xff08;分析模型&#xff09; 和 Compact Model&#xff08;紧凑模型&#xff09; 在电子工程和半导体物理领域有着不同的应用和特点&#xff1a; Analytical Model&#xff08;分析模型&#xff09;: 理论基础&#xff1a;分析模型基于物理原理和数学公…

jeecg dictText字典值

前端列表的字典值回显&#xff0c;配置了数据字典后&#xff0c;在本地测试可以回显中文的数据&#xff0c; 但在线上服务器不能正常回显出来&#xff1b; 原因是在前端拿到records的列表值时可以拿到dictText的字典&#xff0c;但是线上服务器没有dictText的值&#xff1b; …

聚焦 Navicat 17 新特性 | 模型设计优化与创新

随着 Navicat 17 的正式发布&#xff0c;受到了广泛的关注和讨论。Navicat 产品力又一次大跃迁。新引入的特性显著增强了用户的数据库管理和数据分析体验&#xff0c;包括&#xff1a;模型设计与同步、数据字典、数据分析&#xff08;data profiling&#xff09;、用户体验、查…

共享门店模式:快速打造连锁实体店

在数字化浪潮的冲击下&#xff0c;许多线下实体店正面临前所未有的挑战。然而&#xff0c;在这个变革的时代&#xff0c;共享门店模式&#xff0c;也被称为“共享股东”&#xff0c;正以其独特的魅力&#xff0c;为实体店带来新的生机。 一、共享门店模式的崭新定义 共享门店…

​水经微图Web版1.8.0发布

让每一个人都有自己的地图&#xff01; 水经微图&#xff08;简称“微图”&#xff09;新版已上线&#xff0c;在该版本中主要新增了注册登录功能&#xff0c;线与面图层新增矩形、圆或军标等绘制功能&#xff0c;以及其它功能的优化。 现在&#xff0c;为你分享一下本轮迭代…

PostgreSQL调优工具:PGTune

PostgreSQL调优工具&#xff1a;PGTune 1&#xff0c;PGTune网址 https://pgtune.leopard.in.ua/#/ 参数解释&#xff1a; DB Version&#xff1a;数据库版本 OS Type&#xff1a;操作系统 DB Type&#xff1a;数据库类型&#xff0c;一般默认即可 Total Memory (RAM)&#x…

巨详细Linux安装MySQL

巨详细Linux安装MySQL 1、查看是否有自带数据库或残留数据库信息1.1检查残留mysql1.2检查并删除残留mysql依赖1.3检查是否自带mariadb库 2、下载所需MySQL版本&#xff0c;上传至系统指定位置2.1创建目录2.2下载MySQL压缩包 3、安装MySQL3.1创建目录3.2解压mysql压缩包3.3安装解…

2 - 力扣高频 SQL 50 题(基础版)

2.寻找用户推荐人 考点: sql里面的不等于&#xff0c;不包含null -- null 用数字判断筛选不出来 select name from Customer where referee_id !2 OR referee_id IS NULL;

UML行为图-状态图

概述 创建 UML 状态图的目的是研究类、角色、子系统或组件的实时行为。状态图不仅可用于描述用户接口、设备控制器和其他具有反馈的子系统&#xff0c;还可用于描述在生命期中跨越多个不同性质阶段的被动对象的行为&#xff0c;在每一阶段该对象都有自己特殊的行为。 一、状态…

2024最全软件测试面试八股文(答案+文档+视频讲解)

Part1 1、你的测试职业发展是什么&#xff1f; 测试经验越多&#xff0c;测试能力越高。所以我的职业发展是需要时间积累的&#xff0c;一步步向着高级测试工程师奔去。而且我也有初步的职业规划&#xff0c;前3年积累测试经验&#xff0c;按如何做好测试工程师的要点去要求自…

Python-3.12.0文档解读-内置函数zip()详细说明+记忆策略+常用场景+巧妙用法+综合技巧

一个认为一切根源都是“自己不够强”的INTJ 个人主页&#xff1a;用哲学编程-CSDN博客专栏&#xff1a;每日一题——举一反三Python编程学习Python内置函数 Python-3.12.0文档解读 目录 详细说明 基本用法 示例 特性 高级用法 注意事项 版本更新 示例代码 记忆策略…

UI的学习(一)

UI的学习(一) 文章目录 UI的学习(一)UIlabelUIButtonUIButton的两种形式UIButton的事件触发 UIView多个视图之间的关系 UIWindowUIViewController一个视图推出另一个视图 定时器和视图移动UISwitchUISlider和UIProgressSlid步进器与分栏控制器UITextFieldUIScrollView有关实现它…

个人笔记-随意记录

常见问题&#xff1f; 1.linux重启服务 端口被占用如何解决&#xff1f; 查看某个端口被占用的进程 netstat -tulnp | grep :23454 强制杀死进程 kill -9 1776 重启服务即可

解决Chat打开时Unable to load conversation 的问题

在开梯子的情况下打开chat依然很卡&#xff0c;这里选择edge的浏览器无痕模式&#xff08;新建InPrivate窗口&#xff09;&#xff0c;在无痕窗口下打开chat就可以了。