【EasyX图形库】c语言贪吃蛇-音乐-图片-界面比较美观

前面的话:

首次写这个,如有不足,欢迎指正。

代码中的注释解释的很详细!所以说阅读起来应该问题不大,有帮助就最好啦!( ̄︶ ̄)↗ 

2022/11/2更新:

贪吃蛇C++_2021: 大一作业,使用EasyX图形库制作的贪吃蛇游戏 (gitee.com)

我在Gitee上传了自己的代码,大家喜欢可以支持一下啊!


EasyX库官网https://easyx.cn/

                需要先下好哦!

图像音频资源在这里找的 爱给网(现在也不好了,下载资源需要什么币,虽然可以免费获得但是很麻烦,有好的资源网站麻烦推荐一波)☺

作为c语言的练手项目算是非常经典的了,在完成的过程中也回忆了一波童年,所有代码可以运行。图片,音乐使用自己的就可以了。

有人要过游戏图片音频资源,我就在这里放出来了。

图片音频资源https://download.csdn.net/download/m0_63288666/86506592?spm=1001.2014.3001.5503PS:很抱歉没有看消息的习惯(主要是关注的人略微多些,每次都99+,随后就不再去管了)

那个朋友被晾了3,4个月,说声抱歉哈。


运行实例 

【EasyX图形库】c语言贪吃蛇-音乐-图片-界面比较美观_第1张图片

【EasyX图形库】c语言贪吃蛇-音乐-图片-界面比较美观_第2张图片

【EasyX图形库】c语言贪吃蛇-音乐-图片-界面比较美观_第3张图片

头文件 MYHEAD.h
        包裹了依赖的库和全局定义的变量,全局的函数。

1.首先完成基本功能的搭建。
2.实现变色功能。
3.计分版功能。
4.插入音乐。

大概按这个顺序做的,但是实话实说也不是很严格。

#pragma once
#include
#include
#include
#include
#include

#pragma comment(lib,"winmm.lib")//引用Windows Multimedia 
#pragma warning(disable:4996)
/*
开发日志:
1.首先完成基本功能的搭建。
2.实现变色功能。
3.计分版功能。
4.插入音乐。
*/
#define WIDTH 1200//UNIT*600
#define HIGH 660//UNIT*33
#define UNIT 20//这是每个蛇身矩形的单位长度
IMAGE img_bk;//设置背景图片
IMAGE img_frame, img_pause, img_manual, img_bkimg_rank;//边框图片,暂停图片,说明图片,排行榜背景
enum game//枚举设置空地,蛇身,蛇头,墙,食物所处的状态
{
	SPACE, BODY, HEAD, WALL, FOOD//0,1,2,3,4
};
struct SNAKE//蛇身的结构体
{
	int x;
	int y;
};
struct SNAKE snake[1024];//[0][0]储存蛇头的位置
int snakelong;//蛇身的长度
struct _FOOD//食物
{
	int color;
	int x;
	int y;
}food;
struct record//这是记录排行榜的数组
{
	int score = 0;//分数
	int hour;
	int min;
	int sec;
	int year;
	int mon;
	int day;
}rec[10];

/*————————————————定义全局变量————————*/
int h_num;//纵横的边框个数,只在绘制边框时有用
int w_num;
int canvas[HIGH][WIDTH - 120];//设置画布,用来存储当前的状态
int state;//状态,用来标识选项:选择的是 1 OR 2 OR 3 OR 4的选项状态
int snakeDir;//方向,用来标识蛇的移动方向
int snakecolor;//蛇的颜色
int score;//得分
int sleeptime;//调节速度用
int mode;//模式选择//普通,无敌
/*———————————————————————————————————*/
/*———————————————定义函数—————————————*/
void startup();//初始化函数
void chose();//选择函数
int setColor(int a);//设置填充颜色
void menu();//菜单函数
void pauseMenu();//暂停游戏菜单
void show();//展示函数
void updateWithoutuser();//与用户输入无关的更新
void updateWithuser();//与用户输入有关的更新
void gameover();//游戏结束
void regame();//重开游戏
void addFood();//随机设置食物
void speed_mode_menu();//速度,模式选择菜单。四种速度,三种模式
int writerank();
int showrank();
void autofind();//自动寻路
/*——————————————————————————————————————*/

源文件  TCS.cpp

        #include"MYHEAD.h" 使用双引号是在当前目录下先找,之后再到其它目录下找,我想这对于速度也许有一点的提升吧。实际的话尖括号也行。

#include"MYHEAD.h"

int main()
{

	startup();//初始化函数
	LABLE: menu();//菜单函数
	chose();//选择函数
	if (state == 1)//开始游戏
	{
		speed_mode_menu();//速度,模式选择菜单。四种速度,2种模式
		
		while (1)//游戏循环主题
		{
			show();//展示函数
			updateWithoutuser();//与用户输入无关的更新
			updateWithuser();//与用户输入有关的更新
		}
	}
	else if (state == 2)//游戏说明
	{
		cleardevice();
		putimage(0, 0, &img_manual);//输出说明图片
		settextstyle(80, 0, L"楷体");
		outtextxy(5 * UNIT, 2 * UNIT, L"游戏说明");
		settextstyle(30, 0, L"隶书");
		outtextxy(5 * UNIT, 7 * UNIT, L"1.游戏有4种速度,2种模式。");
		outtextxy(5 * UNIT, 9 * UNIT, L"2.速度决定难度和分数,模式分有“普通”和“无敌”,无敌模式下");
		outtextxy(5 * UNIT, 11 * UNIT, L"不会死亡,且可以穿墙。");
		outtextxy(5 * UNIT, 13 * UNIT, L"3.由“W”“S”“A”“D”分别控制上、下、左、右的方向。");
		outtextxy(5 * UNIT, 15 * UNIT, L"4.空格键暂停游戏。");
		outtextxy(5 * UNIT, 17 * UNIT, L"5.Esc键游戏退出,返回主页面。");
		outtextxy(5 * UNIT, 19* UNIT, L"6.当蛇首撞墙或咬到蛇身时游戏结束,自动存档并返回主页面。");
		outtextxy(5 * UNIT, 21 * UNIT, L"7.小蛇每吃到一个食物,分数就会增加。初始分数为零。");
		outtextxy(5 * UNIT, 23 * UNIT, L"8.游戏目标是尽可能多的得分,打破记录!");
		outtextxy(5 * UNIT, 25 * UNIT, L"按任意键返回……");
		system("pause");
		goto LABLE;
	}
	else if (state == 3)//排行榜
	{
		showrank();//排行榜显示
		system("pause");
		goto LABLE;
	}
	else if(state == 4)//游戏结束
	{
		gameover();
		exit(1);
	}
	return 0;
}

void startup()//初始化函数
{
	srand(GetTickCount());//随机数种子

	mciSendString(L"open .\\background_music.mp3 alias bkmusic",NULL,0,NULL);
	mciSendString(L"play bkmusic repeat", NULL, 0, NULL);

	initgraph(WIDTH, HIGH);//创建画布

	HWND hwnd = GetHWnd();//获取窗口句柄
	SetWindowText(hwnd, L"我的贪吃蛇 v1.0");//设置窗口标题文字

	loadimage(&img_bk, L".\\img_bk (2).png", WIDTH, HIGH);//加载背景图片
	loadimage(&img_frame, L".\\walls.gif");//加载边框图片
	loadimage(&img_pause, L".\\img_pause.JPG",WIDTH,HIGH);//加载暂停图片
	loadimage(&img_manual, L".\\manual.png", WIDTH, HIGH);//加载说明图片
	loadimage(&img_bkimg_rank, L".\\bkimg.JPG", WIDTH, HIGH);
	h_num = HIGH / 60;//高WALL图片有几个
	w_num = (WIDTH-120) / 60;//宽WALL图片有几个
//====================================分割线=====================================//
	memset(canvas, SPACE, sizeof(canvas));//全部初始化为空地
	for (int i = 0; i < 60; i++)//初始化墙壁所在的位置,四周 60 像素的边框
	{
		for (int j = 0; j < WIDTH-120; j++)
		{
			canvas[i][j] = canvas[HIGH - i - 1][j] = WALL;
		}
	}
	for (int i = 0; i < HIGH; i++)//初始化墙壁所在的位置,四周 60 像素的边框
	{
		for (int j = 0; j < 60; j++)
		{
			canvas[i][j] = canvas[i][WIDTH-120 - j - 1] = WALL;
		}
	}
	canvas[60 + UNIT*5][60 + UNIT*5] = HEAD;//初始化头
	canvas[60 + UNIT * 5][60 + UNIT * 4] = canvas[60 + UNIT * 5][60 + UNIT * 3] = BODY;//初始化身体
	addFood();//随机设置食物
	state = 1;//菜单选择状态
		snakelong = 3;//蛇长3
		snake[0].x = 60 + UNIT * 5; snake[0].y = 60 + UNIT * 5;
		snake[1].x = 60 + UNIT * 4; snake[1].y = 60 + UNIT * 5;
		snake[2].x = 60 + UNIT * 3; snake[2].y = 60 + UNIT * 5;
		snakeDir = 0x44;//D方向,虚拟键码,是默认的移动方向
		snakecolor = 1;//蛇初始颜色 1 —— 蓝色
	score = 0;
	sleeptime = 100;
	mode = 0;
}
void chose()//选择函数
{
	while (1)
	{
		switch (_getch())
		{
		case '1':
			menu();
			outtextxy(WIDTH / 2 - 150, HIGH / 2 - 60, L"->");
			state = 1;
			break;
		case '2':
			menu();
			outtextxy(WIDTH / 2 - 150, HIGH / 2 - 15, L"->");
			state = 2;
			break;
		case '3':
			menu();
			outtextxy(WIDTH / 2 - 150, HIGH / 2 + 30, L"->");
			state = 3;
			break;
		case '4':
			menu();
			outtextxy(WIDTH / 2 - 150, HIGH / 2 + 75, L"->");
			state = 4;
			break;
		case VK_RETURN:
			return;
		}
	}
}
void menu()//菜单函数
{
	putimage(0, 0, &img_bk);//显示背景
	setbkmode(TRANSPARENT);//设置背景格式为透明
	settextcolor(BLACK);//字体颜色是黑色
	//输出
	settextstyle(80, 0, _T("隶书"));
	outtextxy(WIDTH / 2 - 300, HIGH / 6, _T("贪吃蛇小游戏"));
	settextstyle(30, 0, _T("隶书"),0,0,0,0,0,0);
	outtextxy(WIDTH / 2 -120, HIGH / 2 - 60, _T("1:开始游戏"));
	outtextxy(WIDTH / 2 - 120, HIGH / 2 -15, _T("2:游戏说明"));
	outtextxy(WIDTH / 2 - 120, HIGH / 2 + 30, _T("3:排行榜"));
	outtextxy(WIDTH / 2 - 120, HIGH / 2 + 75, _T("4:退出游戏"));
	outtextxy(WIDTH / 2 - 300, HIGH / 2 + 125, L"数字键 1,2,3,4 选择,Enter键进入  “->”标识当前选中的位置");
}
void pauseMenu()//暂停游戏菜单
{
	system("pause");
}
void show()//展示函数
{
	BeginBatchDraw();
	setbkcolor(RGB(130,130,130));//设置当前背景色为 黑
	cleardevice();//用该背景色清空屏幕
	settextstyle(35, 0, L"华文彩云",0,0,FW_LIGHT,0,0,0);
	setcolor(YELLOW);
	TCHAR sco[5],snlong[6];
	_stprintf_s(sco, _T("%d"), score);		//  _stprintf_s 函数
	_stprintf_s(snlong, _T("%d"), snakelong);
	outtextxy(WIDTH - 100, 2 * UNIT, L"得分:");
	outtextxy(WIDTH - 100, 4 * UNIT, sco);
	outtextxy(WIDTH - 100, 6 * UNIT, L"蛇长:");
	outtextxy(WIDTH - 100, 8 * UNIT, snlong);
	int i = 0,j = 0;
	for (i = 0,j = 0; i < h_num || j < w_num; i++,j++)//输出边框
	{
		putimage(0, i * 60, &img_frame);
		putimage(WIDTH -120- 60, i * 60, &img_frame);
		putimage(j * 60, 0, &img_frame);
		putimage(j * 60, HIGH - 60, &img_frame);
	}
		setColor(rand()%7);//炫彩蛇头
		fillrectangle(snake[0].x, snake[0].y, snake[0].x + UNIT, snake[0].y + UNIT);
	for (i = 1; i < snakelong; i++)//输出蛇的矩形
	{
		setColor(snakecolor);
		fillrectangle(snake[i].x, snake[i].y, snake[i].x + UNIT, snake[i].y + UNIT);
	}
	setColor(food.color);
	fillrectangle(food.x, food.y, food.x + UNIT, food.y + UNIT);
	FlushBatchDraw();
}
void updateWithoutuser()//与用户输入无关的更新
{
	SNAKE temp{};//用来暂存蛇头的下一个位置
	//autofind();
	switch (snakeDir)
	{
	case 0x57://w
		temp.x = snake[0].x;
		temp.y = snake[0].y -UNIT;
		break;
	case 0x41://a
		temp.x = snake[0].x - UNIT;
		temp.y = snake[0].y;
		break;
	case 0x53://s
		temp.x = snake[0].x;
		temp.y = snake[0].y + UNIT;
		break;
	case 0x44://d
		temp.x = snake[0].x + UNIT;
		temp.y = snake[0].y;
		break;
	}
	switch (canvas[temp.y][temp.x])
	{
	case SPACE://是空地,可以继续前进
		canvas[snake[snakelong - 1].y][snake[snakelong - 1].x] = SPACE;//蛇尾消除
		for (int i = snakelong - 1; i > 0; i--)//蛇整体做个移动
		{
			snake[i] = snake[i-1];
		}
		canvas[snake[0].y][snake[0].x] = BODY;//地图上老蛇头的位置赋成蛇身
		snake[0] = temp;//重新赋值蛇头的坐标
		canvas[snake[0].y][snake[0].x] = HEAD;//地图标识蛇头位置
		break;
	case FOOD://食物,增长身体,重新生成
		mciSendString(L"close eatmusic", NULL, 0, NULL);
		mciSendString(L"open .\\eat.mp3 alias eatmusic", NULL, 0, NULL);
		mciSendString(L"play eatmusic", NULL, 0, NULL);	
		fillrectangle(food.x, food.y, food.x + UNIT, food.y + UNIT);
		for (int i = snakelong; i > 0; i--)//蛇整体做个移动
		{
			snake[i] = snake[i - 1];
		}
		canvas[snake[0].y][snake[0].x] = BODY;//地图上老蛇头的位置赋成蛇身
		snake[0] = temp;//重新赋值蛇头的坐标
		canvas[snake[0].y][snake[0].x] = HEAD;//地图标识蛇头位置
		snakelong++;//长度 + 1
		snakecolor = setColor(food.color);//颜色更该为原来食物的颜色
		addFood();//重新随机一个食物的颜色
		if (sleeptime == 200)
			score++;//得分加1
		else if (sleeptime == 100)
			score += 2;
		else if (sleeptime == 60)
			score += 3;
		else if (sleeptime == 40)
			score += 5;
		break;
	case BODY://身体,普通模式下死亡
		if (mode)//是模式2:无敌
			break;
		writerank();
		Sleep(800);
		regame();
		break;
	case WALL://墙,普通模式下死亡
		if (mode)//是模式2:无敌穿墙
		{
			canvas[snake[snakelong - 1].y][snake[snakelong - 1].x] = SPACE;//蛇尾消除
			for (int i = snakelong - 1; i > 0; i--)//蛇整体做个移动
			{
				snake[i] = snake[i - 1];
			}
			canvas[snake[0].y][snake[0].x] = BODY;//地图上老蛇头的位置赋成蛇身
	
			switch (snakeDir)
			{
			case 0x57://w
				temp.y = HIGH -20 - snake[0].y;
				break;
			case 0x41://a
				temp.x = WIDTH - 140 - snake[0].x;
				break;
			case 0x53://s
				temp.y = HIGH - 20 - snake[0].y;
				break;
			case 0x44://d
				temp.x = WIDTH - 140 - snake[0].x;
				break;
			}
			snake[0] = temp;//重新赋值蛇头的坐标
			canvas[snake[0].y][snake[0].x] = HEAD;//地图标识蛇头位置
			break;
		}
		writerank();
		Sleep(800);
		regame();
		break;
	}
	Sleep(sleeptime);
}
void updateWithuser()//与用户输入有关的更新
{
	if (GetAsyncKeyState(0x41) && snakeDir != 0x44)//a
		snakeDir = 0x41;
	if (GetAsyncKeyState(0x44) && snakeDir != 0x41)//d
		snakeDir = 0x44;
	if (GetAsyncKeyState(0x57) && snakeDir != 0x53)//w
		snakeDir = 0x57;
	if (GetAsyncKeyState(0x53) && snakeDir != 0x57)//s
		snakeDir = 0x53;
	if (GetAsyncKeyState(VK_SPACE))//空格建暂停
		pauseMenu();
	if (GetAsyncKeyState(VK_ESCAPE))//esc键退出游戏返回主菜单
		regame();
}
void gameover()//游戏结束
{
	EndBatchDraw();
	closegraph();
}
void addFood()//随机设置食物
{
	int x = 0, y = 0;
	do
	{
		x = (rand() % (int)((WIDTH-120 - 120) / UNIT)) * UNIT + 60;
		y = (rand() % (int)((HIGH - 120) / UNIT)) * UNIT + 60;
	} while (canvas[y][x] != SPACE);//随机的位置只能是空地
	canvas[y][x] = FOOD;//位置是食物
	food.x = x;
	food.y = y;
	food.color = rand() % 3;
}
int setColor(int a)//设置颜色
{
	switch (a)
	{
	case 0:
		setfillcolor(RGB(255, 0, 0));		//红色
		break;
	case 1:
		setfillcolor(RGB(0, 0, 255));		//蓝色
		break;
	case 2:
		setfillcolor(RGB(0, 255, 0));		//绿色
		break;
	case 3:
		setfillcolor(RGB(255, 165, 0));		//橙色
		break;
	case 4:
		setfillcolor(RGB(255, 255, 0));		//黄色
		break;
	case 5:
		setfillcolor(RGB(0, 255, 255));		//青色
		break;
	case 6:
		setfillcolor(RGB(160, 32, 240));	//紫色
		break;
	}
	return a;
}
void speed_mode_menu()//速度,模式选择菜单。四种速度,三种模式
{
	T1:cleardevice();
	wchar_t wspeed[5];

	InputBox(wspeed, 5, L"+请输入速度值+(输入数字1,2,3,4)\n\n1-超简单速度(一个食物得一分)\n2-普通速度(一个食物得两分)\n3-困难速度(一个食物三分)\n4-地狱速度(一个食物五分)\n\n★默认为普通速度★",L"这是一个选择速度等级的框框q(≧▽≦q)",L"输入吧( ̄︶ ̄)↗");
	int speed = _wtoi(wspeed);
	if (speed == 1)//超简单速度
		sleeptime = 200;
	else if (speed == 2)//普通模式
		sleeptime = 100;
	else if (speed == 3)//困难模式
		sleeptime = 60;
	else if (speed == 4)//地狱模式
		sleeptime = 40;
	else
		goto T1;
	T2:wchar_t tmode[2];
	InputBox(tmode, 2, L"+请输入1,2选择模式+\n\n1-普通模式\n2-无敌模式\n\n\n★默认为普通模式★", L"这是一个选择游戏模式的框框q(≧▽≦q)", L"输入吧( ̄︶ ̄)↗");
	int _mode = _wtoi(tmode);
	mode = _mode -1;//设置模式
	if (mode != 0 && mode != 1)
		goto T2;
}
int writerank()//输入存储排名
{
	while (_kbhit())//清空键盘缓冲区
	{
		_getch();
	}
	time_t timep;
	struct tm* ti;
	time(&timep);
	ti = localtime(&timep);
	record* temp = (record*)malloc(sizeof(record)*100);//申请一个空间
	int i = 0, n = 0;
	FILE* fp = NULL;
	errno_t err = fopen_s(&fp, ".\\rank.txt", "ab");//保存成绩用追加读写 
	if (err)
	{
		printf("cannot open the file\n");
		system("pause");
		return -1;//如果文件出现错误返回-1  
	}
	temp->score = score;//分数
	temp->year = ti->tm_year+1900;//年
	temp->mon = ti->tm_mon+1;  //月
	temp->day = ti->tm_mday; //日
	temp->hour = ti->tm_hour;//时
	temp->min = ti->tm_min;  //分
	temp->sec = ti->tm_sec;  //秒
	fwrite(temp, sizeof(record), 1, fp);
	fclose(fp);
	free(temp);
	return 0;
}

int showrank()//展示排名
{
	record _temp;
	int j = 0, i = 0, k = 0;
	FILE* fp = NULL;
	errno_t err = fopen_s(&fp, ".\\rank.txt", "rb");
	if (err)
	{
		printf("cannot open the file\n");
		system("pause");
		return -1;
	}
	if (fp == NULL)//这个貌似没有什么用处
	{
		printf_s("暂无数据!");
		//return 0;
	}
	rewind(fp);
	while (!feof(fp))
	{
		fread(&rec[i], sizeof(record), 1, fp);
		i++;
	}
	for (int j = 0; j < i; j++)//排序用
	{
		for (int k = 0; k < i; k++)
		{
			if (rec[k].score < rec[j].score)
			{
				_temp = rec[k];
				rec[k] = rec[j];
				rec[j] = _temp;
			}
		}
	}
	if (i >= 10)
		i = 10;
	cleardevice();
	putimage(0, 0, &img_bkimg_rank);
	settextcolor(BLACK);
	settextstyle(4 * UNIT, 0, L"华文新魏");
	outtextxy(WIDTH / 2 - 7*UNIT, 0, _T("排行榜"));
	settextstyle(3 * UNIT, 0, L"华文楷体");
	outtextxy(7 * UNIT, 4 * UNIT, L"得 分");
	outtextxy(20 * UNIT, 4*UNIT, L"年   月   日           时   分   秒");
	settextstyle( UNIT, 0, L"方正姚体");
	for (int j = 0; j < i ; j++)
	{
		TCHAR s1[6];
		_stprintf_s(s1,_T("%d"), rec[j].score);//分数
		outtextxy(10 * UNIT, j * (UNIT * 2) + 9 * UNIT, s1);
		TCHAR s2[6];
		_stprintf_s(s2, _T("%d"), rec[j].year);//年
		outtextxy(20 * UNIT, j * (UNIT * 2) + 9 * UNIT, s2);
		TCHAR s3[6];
		_stprintf_s(s3, _T("%d"), rec[j].mon);//月
		outtextxy(25 * UNIT, j * (UNIT * 2) + 9 * UNIT, s3);
		TCHAR s4[6];
		_stprintf_s(s4, _T("%d"), rec[j].day);//日
		outtextxy(30 * UNIT, j * (UNIT * 2) + 9 * UNIT, s4);
		TCHAR s5[6];
		_stprintf_s(s5, _T("%d"), rec[j].hour);//时
		outtextxy(40 * UNIT, j * (UNIT * 2) + 9 * UNIT, s5);
		TCHAR s6[6];
		_stprintf_s(s6, _T("%d"), rec[j].min);//分
		outtextxy(45 * UNIT, j * (UNIT * 2) + 9 * UNIT, s6);
		TCHAR s7[6];
		_stprintf_s(s7, _T("%d"), rec[j].sec);//秒
		outtextxy(50 * UNIT, j * (UNIT * 2) + 9 * UNIT, s7);
	}
	outtextxy(5 * UNIT, 30 * UNIT, L"按任意键返回……");
	fclose(fp);
	return 0;
}
void regame()
{
	clearrectangle(0,0,WIDTH,HIGH);
	gameover();
	main();
}
void autofind()
{
	snakeDir;
	int chax = snake[0].x - food.x;//蛇头与食物的坐标差
	int chay = snake[0].y - food.y;
	if (chax > 0 && snakeDir != 0x44)
	{
		snakeDir = 0x41;
	}
	if (chax < 0 && snakeDir != 0x41)
	{
		snakeDir = 0x44;
	}
	if (chay > 0 && snakeDir != 0x53)
	{
		snakeDir = 0x57;
	}
	if (chay < 0 && snakeDir != 0x57)
	{
		snakeDir = 0x53;
	}
	if (chay == 0 && chax > 0 && snakeDir == 0x44)
	{
		snakeDir = 0x53;
	}
	if (chay == 0 && chax < 0 && snakeDir == 0x41)
	{
		snakeDir = 0x53;
	}
	if (chax == 0 && chay > 0 && snakeDir == 0x53)
	{
		snakeDir = 0x41;
	}
	if (chay == 0 && chax < 0 && snakeDir == 0x57)
	{
		snakeDir = 0x41;
	}

}	

        先简单这样子吧,以后再慢慢发详细的东西。

你可能感兴趣的:(c语言,c++,开发语言)