【c语言】贪吃蛇

news2025/1/17 0:11:37

当我们不想学习新知识的时候,并且特别无聊,就会突然先看看别人怎么写游戏的,今天给大家分享的是贪吃蛇,所需要的知识有结构体,枚举,以及easy-x图形库的一些基本函数就完全够用了,本来我想插入游戏的音乐,但是没找到贪吃蛇的背景音乐,所以就没加,大家可以参考我之前的博文里面有如何加音乐的教程

包含的头文件

#include <stdio.h>
#include <graphics.h>//图形库头文件
#include <conio.h>//_kbhit()函数头文件
#include <stdlib.h>//srand函数头文件
#include <time.h>//time()函数头文件

初始化界面

int main()
{
	initgraph(640,480);//初始化界面
	while (1);
	
	return 0;

}

初始化长640,宽480的界面,while(1);使得界面一直出现
在这里插入图片描述

设置背景颜色

int main()
{
	initgraph(640,480);
	setbkcolor(RGB(31, 29, 66));//设置背景颜色
	cleardevice();//清空绘图设备
	while (1);
	
	return 0;

}

setbkcolor中的RGB(31, 29, 66),打开微信,同时按下Alt键,A,可获取你截图中颜色的RGB;
在这里插入图片描述

定义蛇的信息结构体,枚举定义蛇的方向

#define SNAKE_NUM 500// 最大有500节
struct Snake
{
	int size;//蛇的节数
	int dir;//蛇的方向
	int speed;//蛇的速度
	POINT coor[SNAKE_NUM];//蛇的坐标



}snake;
enum DIR  //表示蛇的方向
{
	UP,//向上
	DOWN,//向下
	LEFT,//向左
	RIGHT,//向右
};

数据初始化

void gameinit()
{    
	snake.size = 3;//初始化蛇刚开始为三节
	snake.speed = 8;//初始速度是每按一次方向键改变八个像素的位置
	snake.dir = RIGHT;//初始蛇的方向是向右
	for (int i = 0; i < snake.size; i++)
	{
		snake.coor[i].x = 40 - 10 * i;
		snake.coor[i].y = 10;


	}




}

在for循环内定义好每一节的圆心坐标,记snake.coor[0].x为蛇头的圆心坐标的横坐标,记snake.coor[0].y为蛇头的圆心坐标的纵坐标,初始化三节,for循环定义这三节的坐标,头的圆心坐标为(40,10)中间为(30,10),最后为(20,10);初始化蛇应该是横着的。

整合函数

函数写的太乱,我们可以将相同的功能放在一个函数里面,比如说将initgraph(640, 480);放在gameinit()里面,将画的都放在gamedraw()函数里面。

//数据初始化
void gameinit()
{
	initgraph(640, 480);
	snake.size = 3;
	snake.speed = 8;
	snake.dir = RIGHT;
	for (int i = 0; i < snake.size; i++)
	{
		snake.coor[i].x = 40 - 10 * i;
		snake.coor[i].y = 10;


	}




}
void gamedraw()
{
	BeginBatchDraw();//双缓冲消除闪屏
	setbkcolor(RGB(31, 29, 66));//设置背景颜色

	cleardevice();//清空绘图设备


	
	//绘制蛇
	setfillcolor(RED);//设置蛇的颜色
	for (int i = 0; i < snake.size; i++)
	{
	solidcircle(snake.coor[i].x, snake.coor[i].y, 5);//循环绘制蛇的三节身体

	}
	EndBatchDraw();//双缓冲消除闪屏


}
int main()
{
	
	gameinit();
	gamedraw();

	while (1);
	
	return 0;

}

在绘制蛇那里setfillcolor(RED);//设置蛇的颜色,在设置蛇的颜色下面的循环中是画初始化好的三节蛇的身体,solidcircle(snake.coor[i].x, snake.coor[i].y, 5);这个是画实心圆的函数,前两个参数为圆的横纵坐标,第三个参数是圆的半径
在这里插入图片描述

键盘控制移动方向

void keycontrol()
{
	//72 80 75 77
	
	if (_kbhit())
	{
		switch (_getch())
		{
		case 'w':
		case 'W':
		case 72:
			if (snake.dir != DOWN)
			{
				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;











		}
	}




}

_kbhit()是一个C和C++函数,用于非阻塞地响应键盘输入事件,即键盘敲击。它检查当前是否有键盘输入,如果有则返回一个非0值,否则返回0。
w,s,a,d对应的虚拟键值为72,80,75,77.当_kbhit()返回不为0,则有键按下,_getch 是 C 语言中常用的一个函数,它可以获取用户在控制台输入的一个字符,并且不需要回车。当按下方向键后,_getch()读取按下的字符,将蛇的方向改变成对应的方向键。为什么这里有条件判断语句if,是因为蛇在向一个方向走的时候不能直接往相反的方向走.比如说按下w,往上走,除了向下走外,向左走,和向右走的时候按向上走就可以向上走。

蛇的移动

void snakemove()
{
	for (int i = snake.size-1; i>0; i--)//身体跟着头移动
	{
		snake.coor[i] = snake.coor[i - 1];
		
	}
	switch (snake.dir)//头移动
	{
	case RIGHT:
		snake.coor[0].x+=snake.speed;
		if (snake.coor[0].x > 640)
		{
			snake.coor[0].x = 0;
		}
		break;
	case UP:
		snake.coor[0].y-= snake.speed;
		if (snake.coor[0].y < 0)
		{
			snake.coor[0].y = 480;
		}
		break;
	case DOWN:
		snake.coor[0].y+=snake.speed;
		if (snake.coor[0].y > 480)
		{
			snake.coor[0].y = 0;


		}
		break;
	case LEFT:
		snake.coor[0].x-=snake.speed;
		if (snake.coor[0].x < 0)
		{
			snake.coor[0].x = 640;
		}
		break;







	}
	


}

每次调用snakemove函数,让后面每一节等于前面那一节的坐标,就能实现尾巴身体跟着头动,switch中是此时头移动的方向,向哪个方向移动,对应方向上的x坐标或者y坐标就+或者-snake.speed;比如说向左就是对应x-snake.speed,向右就是x+snake.speed;向上就是y-snake.speed;向下就是y+snake.speed;这里为什么有判断语句if,这里是因为当蛇头到达边界位置的话,就可以从相反的边界出来,界面的大小是640*480;比如说蛇向左走,遇到左边界,就把蛇头的x坐标置为640.

主函数变化

int main()
{
	
	gameinit();
	gamedraw();

	while (1)
	{
		gamedraw();
		keycontrol();
		snakemove();
		Sleep(20);
	
	}
	
	return 0;

}

将键盘控制函数和蛇的移动的函数放在while循环里面,因为要无时无刻地获取从键盘上读来地值和蛇的移动,当不加Sleep函数时,蛇跑的特别快,让程序休眠20ms
视频演示

20230921_173904

食物信息结构体定义

struct food
{
	int x;//食物的横坐标
	int y;//食物的纵坐标
	bool flag;//食物是否被吃
	int r;//食物半径
	DWORD color;//食物颜色



}food;

食物信息初始化(修改数据初始化函数)

//数据初始化
void gameinit()
{    //设置随机数种子
	srand((unsigned int)time(NULL));//时间不断变化,
	initgraph(640, 480);//初始化界面
	snake.size = 3;
	snake.speed = 8;
	snake.dir=RIGHT;
	for (int i = 0;i<snake.size;i++)
	{
		snake.coor[i].x = 40-10*i;
		snake.coor[i].y = 10;


	}
	//随机初始化食物的横纵坐标
	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;
	


}

因为食物的位置,颜色,以及食物的半径要随机生成,必须用到rand函数生成随机数,但rand生成的随机数是不变的,所以要通过srand函数生成随机数种子,食物必须在界面内,所以food.x=rand()%640的话,food.x的范围为0-639,同理food.y,以及RGB的三个参数都是0-255的,所以对256取余数,半径是在0-9之间,在加上5,食物半径就在5-14之间,food.flag 定义的是食物是否被吃掉,没被吃掉就为true,吃掉后food.flag变为false.

蛇吃食物

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)//食物消失重新生成一个
	{
		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;



	}




}

如果要吃掉食物用蛇头圆心的坐标等于食物圆心的坐标的话,是不容易吃到食物的,所以只要蛇头的圆心坐标落入以食物圆心做出的外切正方形的范围内的话,就算吃到,对应食物正方形的边界坐标为food.x-food.r(左边界),
food.x+food.r(右边界),food.y+food.r(上边界),food.y-food.r(下边界),只要蛇头圆心落入该范围内就算吃到,吃到的话,food.flag=false;表示食物被吃掉,并且蛇的长度snake.size++;当食物被吃掉后,food.flag=0,此时!food.flag=1;立即生成下一个食物坐标,如果这里判断蛇头是否落在食物范围内时,如果不加&&food.flag的话,蛇吃第一个食物,会由三节直接变成五节在这里插入图片描述

绘制食物(修改绘制函数)

void gamedraw()
{
	BeginBatchDraw();
	setbkcolor(RGB(31, 29, 66));//设置背景颜色

	cleardevice();//清空绘图设备


	
	//绘制蛇
	setfillcolor(RED);//设置蛇的颜色
	for (int i = 0; i < snake.size; i++)
	{
		solidcircle(snake.coor[i].x, snake.coor[i].y, 5);


	}
	//绘制食物
	if (food.flag)//如果food.flag==1的话就绘制食物
	{
		solidcircle(food.x, food.y, food.r);
	}

	EndBatchDraw();


}

主函数修改

int main()
{
	
	gameinit();
	gamedraw();

	while (1)
	{
		gamedraw();
		keycontrol();
		snakemove();
		eatfood();//增加的
		Sleep(20);
	
	}
	
	return 0;

}

游戏暂停函数

void gamestop()//空格,游戏暂停
{
	if (_kbhit())
	{
		if (_getch()== ' ')
		{
			while (_getch() != ' ');
		}



	}



}

按下空格游戏暂停,_kbhit函数作用上面解释过了,然后如果从键盘读取的空格后,缓冲区没东西,就在一直循环,在按一次就继续游戏,注意修改的主函数

修改主函数

int main()
{
	
	gameinit();
	gamedraw();

	while (1)
	{
		gamestop();
		gamedraw();
		keycontrol();
		
		snakemove();
		eatfood();
		Sleep(20);
	
	}
	
	return 0;

}

程序源码

#include <stdio.h>
#include <graphics.h>
#include <conio.h>
#include <stdlib.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;
	bool flag;//食物是否被吃
	int r;//食物半径
	DWORD color;//食物颜色



}food;
//数据初始化
void gameinit()
{
	initgraph(640, 480);
	snake.size = 3;
	snake.speed = 8;
	snake.dir = RIGHT;
	for (int i = 0; i < snake.size; i++)
	{
		snake.coor[i].x = 40 - 10 * i;
		snake.coor[i].y = 10;


	}




}
void gamestop()//空格,游戏暂停
{
	if (_kbhit())
	{
		if (_getch() == ' ')
		{
			while (_getch() != ' ');
		}



	}



}
void gamedraw()//画函数
{
	BeginBatchDraw();
	setbkcolor(RGB(31, 29, 66));//设置背景颜色

	cleardevice();//清空绘图设备


	
	//绘制蛇
	setfillcolor(RED);//设置蛇的颜色
	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();


}
void keycontrol()//键盘控制方向
{
	//72 80 75 77

	if (_kbhit())
	{
		switch (_getch())
		{
		case 'w':
		case 'W':
		case 72:
			if (snake.dir != DOWN)
			{
				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;











		}
	}




}
void snakemove()//蛇的移动
{
	for (int i = snake.size - 1; i > 0; i--)//身体跟着头移动
	{
		snake.coor[i] = snake.coor[i - 1];

	}
	switch (snake.dir)//头移动
	{
	case RIGHT:
		snake.coor[0].x += snake.speed;
		if (snake.coor[0].x > 640)
		{
			snake.coor[0].x = 0;
		}
		break;
	case UP:
		snake.coor[0].y -= snake.speed;
		if (snake.coor[0].y < 0)
		{
			snake.coor[0].y = 480;
		}
		break;
	case DOWN:
		snake.coor[0].y += snake.speed;
		if (snake.coor[0].y > 480)
		{
			snake.coor[0].y = 0;


		}
		break;
	case LEFT:
		snake.coor[0].x -= snake.speed;
		if (snake.coor[0].x < 0)
		{
			snake.coor[0].x = 640;
		}
		break;







	}



}
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)//食物消失重新生成一个
	{
		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;



	}




}
int main()
{
	
	gameinit();
	gamedraw();

	while (1)
	{
		gamestop();
		gamedraw();
		keycontrol();
		
		snakemove();
		eatfood();
		Sleep(20);
	
	}
	
	return 0;

}

程序演示

20230921_184525

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

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

相关文章

7.1 实现进程内存块枚举

在Windows操作系统中&#xff0c;每个进程的虚拟地址空间都被划分为若干内存块&#xff0c;每个内存块都具有一些属性&#xff0c;如内存大小、保护模式、类型等。这些属性可以通过VirtualQueryEx函数查询得到。 该函数可用于查询进程虚拟地址空间中的内存信息的函数。它的作用…

【C++】STL之适配器---用deque实现栈和队列

目录 前言 一、deque 1、deque 的原理介绍 2、deque 的底层结构 3、deque 的迭代器 4、deque 的优缺点 4.1、优点 4.2、缺点 二、stack 的介绍和使用 1、stack 的介绍 2、stack 的使用 3、stack 的模拟实现 三、queue 的介绍和使用 1、queue 的介绍 2、queue 的使用 3、qu…

​专业图像处理软件 Photoshop 2023 mac版本更新(ps2023中文)

​Photoshop 2023 mac是一款图像编辑和图形设计软件&#xff0c;广泛应用于专业人士和爱好者。它提供了许多工具和功能&#xff0c;用于创建、编辑和增强数字图像&#xff0c;包括图层、蒙版、滤镜和各种选择工具。Photoshop还支持多种文件格式&#xff0c;包括psD、JPEG、PNG和…

APP开发者如何运用积分墙广告,提升APP应用下载和用户留存?

“积分墙”移动广告通过在应用内展示各种积分任务&#xff0c;鼓励用户完成任务以获得积分奖励&#xff0c;从而增加应用的曝光度和下载量。 一、什么是积分墙&#xff1f; 积分墙是一种第三方移动广告平台。开发者可以在这类平台上发布任务&#xff08;如下载安装 App、注册…

JavaScript - canvas - 放大镜

效果 示例 项目结构&#xff1a; 源码&#xff1a; <!DOCTYPE html> <html><head><meta charset"utf-8" /><title>放大镜</title><style type"text/css">div {width: 200px;height: 200px;display: inline-bl…

Java集成支付宝沙箱支付,详细教程(SpringBoot完整版)

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 一、开发前准备&#xff1f;二、使用步骤1、引入库2、配置在 application.yml 里面进行配置&#xff1a;3、alipay的java配置&#xff1a;AplipayConfig.java4、支付…

不要再滥用可选链运算符(?.)啦!

可选链运算符(?.)&#xff0c;大家都很熟悉了&#xff0c;直接看个例子&#xff1a; const result obj?.a?.b?.c?.d 很简单例子&#xff0c;上面代码?前面的属性如果是空值&#xff08;null或undefined&#xff09;&#xff0c;则result值是undefined&#xff0c;反之…

企业架构LNMP学习笔记58

开始学习Tomcat&#xff1a; 学习目标和内容&#xff1a; 1&#xff09;能够描述Tomcat的使用场景&#xff1b; 2&#xff09;能够简单描述Tomcat的工作原理&#xff1b; 3&#xff09;能够实现部署安装Tomcat&#xff1b; 4&#xff09;能够实现和配置Tomcat的Server服务…

如何判断自己的服务器是不是中了勒索病毒,勒索病毒解密数据恢复

勒索病毒日益横行&#xff0c;给企业的生产经营和个人数据造成了极大的影响。但是对单一的企业和个人而言&#xff0c;遭受勒索病毒攻击毕竟是低频率事件&#xff0c;这也导致很多人中招之后&#xff0c;无法判断出具体的问题&#xff0c;进而没有及时采取应对措施&#xff0c;…

【谢希尔 计算机网络】第3章 数据链路层

数据链路层 数据链路层的地位 网络中的主机、路由器等都必须实现数据链路层局域网中的主机、交换机等都必须实现数据链路层不同链路层可能采用不同的数据链路层协议 数据链路层信道类型 点对点信道 使用一对一的点对点通信方式广播通信 必须使用专用的共享系电脑协议来协调这些…

【Java 基础篇】Java多线程实现文件上传详解

文件上传是Web应用程序中常见的功能之一&#xff0c;用户可以通过网页将文件从本地计算机上传到服务器。在处理大文件或多用户并发上传的情况下&#xff0c;为了提高性能和用户体验&#xff0c;常常使用多线程来实现文件上传功能。本文将详细介绍如何使用Java多线程实现文件上传…

【RabbitMQ实战】docker 安装RabbitMQ(bitnami)

一、搜索镜像 [rootlocalhost ~]# docker search rabbitmq NAME DESCRIPTION STARS OFFICIAL AUTOMATED rabbitmq RabbitMQ is an open source multi-pro…

携手走过四年,极智嘉(Geek+)赋能上海西门子开关智慧物流升级

日前&#xff0c;上海西门子开关有限公司迎来了工厂30周年庆典暨客户开放日活动&#xff0c;全球仓储机器人引领者极智嘉(Geek)荣幸作为优秀供应商代表受邀参会&#xff0c;见证其突破革新、数智飞跃的三十载辉煌历程。 客户开放日活动中&#xff0c;上海西门子开关有限公司开…

【SpringCloud】微服务技术栈入门2 - Nacos框架与Feign

目录 Nacos下载 Nacos 并运行配置 NacosNacos 集群Nacos 负载均衡Nacos 环境隔离Nacos 注册细节Nacos 更多配置项快速上手自动更新 Feign取代 RestTemplateFeign 自定义配置性能优化 Nacos 下载 Nacos 并运行 首先下载对应的 release 包&#xff0c;主要要选择已经打包编译好…

链表的回文判断

思路: 找中间节点–>逆置->比较 代码&#xff1a; /*** Definition for singly-linked list.* struct ListNode {* int val;* struct ListNode *next;* };*/struct ListNode* middleNode(struct ListNode* head) { struct ListNode*slowhead; struct ListNode*f…

maven多模块依赖包程序包xxx不存在

背景 rpc-common 被 rpc-server、rpc-client依赖 项目地址 https://github.com/pjmike/springboot-rpc-demo mvn clean install 打包时报错 报错信息 程序包xxxx不存在 找不到符号 原因分析 原因还不清楚&#xff0c;网友们帮解答一下 解决 主pom.xml 添加 <packaging…

23个销量最高的3D扫描仪【2023】

如果你可以 3D 扫描它&#xff0c;你就可以 3D 打印它。 市场上 3D 扫描仪的种类和质量非常丰富&#xff0c;机器尺寸、功能和价格各异。 这样的选择虽然本身是一件很棒的事情&#xff0c;但也会让从无用的东西中挑选出宝石成为一件苦差事。 推荐&#xff1a;用 NSDT编辑器 快速…

如何在linux定时备份opengauss数据库(linux核心至少在GLIBC_2.34及以上)

前提环境&#xff0c;linux的核心至少在GLIBC_2.34及以上才能使用。 查看linux的glibc版本的命令如下 strings /lib64/libc.so.6 | grep GLIBC 如下图 或者用ldd --version 如下图 在官网下载对应的依赖包&#xff0c; 只需要这个lib文件即可&#xff0c;将这个包放在lin…

拓世法宝|短视频带货风潮,数字人教育书单号成销售黑马

Z世代的爸妈&#xff0c;正在搞一种很新的育儿方式。 “躺平式”带娃、“用魔法打败魔法”等新时代育儿方式频频登上热搜&#xff0c;作为与互联网共同成长起来的一代&#xff0c;Z世代父母们更热衷于通过网络攻略获得和分享知识和经验&#xff0c;更注重个性的养育方式&#…

ChatGPT所引用的参考文献根本不存在?如何正确使用AI工具?

从ChatGPT推出以来&#xff0c;一直都是一个热点话题&#xff0c;尤其是在高等教育领域&#xff0c;大家展开了非常激烈的讨论。 巴斯大学对ChatGPT进行测试时发现&#xff0c;ChatGPT生成的论文&#xff0c;其参考文献的引用格式看起来很完美&#xff0c;有作者姓名和期刊名称…