贪吃蛇小游戏(C语言)

写在前面:

贪吃蛇,相信大家都玩过,我在学习了C语言后也想自己动手写一个贪吃蛇小游戏,然后就查了一些资料,最后是以B站一个视频为基础,加入自己思考,设计,改进创作了自己的贪吃蛇小游戏,在这里分享给大家。(原视频链接:https://www.bilibili.com/video/BV1oW411G7Ag

相信不少同学(我说的是小白)在学完C语言课程后都有会有一个疑惑:课程中学习的,练习的程序都是在一个黑框中(也就是控制台)运行,还都是些文本,离我们平时使用的软件差了不止一点半点,同样是用C语言,怎么能够写出可视化界面呢?我当时也是同样的疑惑,然后就去查了一些资料,才了解了什么是GUI之类的。言归正传,对于C语言,要写出可视化界面,需要用到对应的函数库:EasyX库(我对这些了解得也不多,官方也有类似的函数库,但是我这里就只介绍我用到的,也是上面视频中老师用到的)。

EasyX库简介:

EasyX 是针对 C++ 的图形库,可以帮助 C/C++ 初学者快速上手图形和游戏编程。比如,可以基于 EasyX 图形库很快的用几何图形画一个房子,或者一辆移动的小车,可以编写俄罗斯方块、贪吃蛇、黑白棋等小游戏,可以练习图形学的各种算法,等等。EasyX库中提供了绘制窗口、绘制各种图形、文字输出、图像处理、鼠标捕获等诸多函数,功能齐全且强大。其次EasyX库的帮助文档中对于EasyX的安装说明、基本概念、函数说明、常见问题等都做出了详细的解释说明,使用起来较为方便,对于小白较友好。

EasyX库官方网站(提供免费下载):https://easyx.cn/

EasyX库百度云:easyX库百度云  提取码:easx

游戏设计概述:

我在写这个游戏的时候,开始由于对EasyX库及C语言编程都不太熟悉,所以一开始是跟着视频老师写的,所以思路也就和视频中差不多。随着逐步学习,然后加入了自己的设计,主要几个想法如下:

1.我希望我的游戏能够像以前玩过的那样,具有不同的地图(墙体)

2.我希望我的游戏可以有不同的难度选择

3.我希望的游戏可以有不同的背景音乐,可以随机切换;在蛇吃到食物时也会有音效

4.我希望将我学到的C语言文件操作运用起来,添加一个刷新新记录的功能

部分代码:

1.所有结构体,枚举类型定义

#ifndef HEAD_H
#define HEAD_H
#endif

struct Coor      //10*10小格
{
	int x;
	int y;
};

typedef enum ch   //蛇的方向,是否暂停
{
	up = 119,
	down = 115,
	left = 97,
	right = 100,
	space = 32,
	enter = 13
}CH;

struct Snake       //蛇
{
	struct Coor scr[2464];     //包含最多100格
	int n;                    //蛇的总格数
	CH ch;                    //蛇的方向
};

struct Food         //食物
{
	int x;
	int y;
};

struct Wall
{
	struct Coor wall[2400];
	int n;
};

typedef struct record
{
	int sleep;
	int mapnumber;
	int score;
	struct record* next;
}Record;

2.控制蛇移动及暂停游戏函数

void ChangeCh()
{
	int input;
	int input1;
	input=_getch();
	switch (input)
	{
	case up:
		if (snake.ch != down)
		{
			snake.ch = up;
			rotateimage(&head, &snakehead, 0, RGB(167, 225, 225));
		}
		break;
	case down:
		if (snake.ch != up)
		{
			snake.ch = down;
			rotateimage(&head, &snakehead, PI, RGB(167, 225, 225));
		}
		break;
	case left:
		if (snake.ch != right)
		{
			snake.ch = left;
			rotateimage(&head, &snakehead, PI / 2, RGB(167, 225, 225));
		}
		break;
	case right:
		if (snake.ch != left)
		{
			snake.ch = right;
			rotateimage(&head, &snakehead, 3 * PI / 2, RGB(167, 225, 225));
		}
		break;
	case enter:             //暂停或继续游戏
		setcolor(RED);    //设置颜色
		settextstyle(40, 0, L"黑体");   //设置字体风格
		setbkmode(OPAQUE);               //设置背景
		outtextxy(150, 180, L"暂停中!");      //输出文字
		settextstyle(30, 0, L"黑体");   //设置字体风格
		outtextxy(150, 230, L"enter:继续游戏");
		outtextxy(150, 260, L"空格: 重新开始");
		do
		{
			input1 = _getch();
			if (input1 == enter)
			{
				clearrectangle(20,20,580,460);
				DrawMap();
				DrawSnake();
				DrawFood();
				break;
			}
			if (input1 == space)
			{
				for (int i = 0; i < snake.n; i++)
				{
					snake.scr[i].x = 20;
					snake.scr[i].y = 20;
				}
				clearcliprgn();
				closegraph();
				CloseMusic();
				Game();
				break;
			}
		} while (1);
		break;
	default:
		break;
	}
}

3.文件操作

#include<stdio.h>  
#include<stdlib.h>
#include<string.h>
#include<graphics.h>       //图形库头文件
#include<mmsystem.h>       //多媒体设备接口
#include<time.h>
#include<conio.h>
#pragma comment(lib,"winmm.lib")    
#include"head.h"

#pragma comment(lib,"winmm.lib")  

#define pPath "record.txt"

#define FALSE 0
#define TRUE 1
#define TXTNUMBER 12
 
Record*Head;
int Mn;

int IfChangeRecord(int sleep, int mapnumber, int score)
{
	Record* head;
	head = (Record*)malloc(sizeof(Record));
	if (head != NULL)
	{
		head->next = NULL;
		Head = head;
	}
	Record*r;
	FILE* fp = NULL;
	errno_t err = 0;
	char aBuf[TXTNUMBER];
	//打开文件
	err = fopen_s(&fp, pPath, "r");
	if (fp == NULL)
	{
		return FALSE;
	}
	//读取文件
	while (feof(fp) == 0)
	{
		memset(aBuf, 0, TXTNUMBER);
		if (fgets(aBuf, TXTNUMBER, fp) != NULL)
		{
			if (strlen(aBuf) > 0)
			{
				const char* delims = "##";
				char* str[4];
				char* p = NULL;
				int i = 0;
				str[0] = strtok_s(aBuf, delims, &p);
				while (str[i])
				{
					i++;
					str[i] = strtok_s(NULL, delims, &p);
				}
				r = (Record*)malloc(sizeof(Record));
				if (r == NULL)
				{
					return FALSE;
				}
				r->sleep=atoi(str[0]);
				r->mapnumber = atoi(str[1]);
				r->score = atoi(str[2]);
				head->next = r;
				r->next = NULL;
				head = r;
				r = r->next;
			}
		}
	}
	//关闭文件
	fclose(fp);
	//判断并更新记录
	int n=0;
	head = Head->next;
	while (head != NULL)
	{
		if (sleep == head->sleep && mapnumber == head->mapnumber&&score>head->score)
		{
			head->score = score;
			n = 1;
		}
		head = head->next;
	}
	if (n == 1)
	{
		//删除文件中所有原有信息
		err = fopen_s(&fp, pPath, "w");
		if (fp == NULL)
		{
			return FALSE;
		}
		fclose(fp);
		//将更新后记录写入文件
		err = fopen_s(&fp, pPath, "a");
		if (fp == NULL)
		{
			return FALSE;
		}
		head = Head->next;
		while (head != NULL)
		{
			fprintf(fp, "%d##%d##%d\n", head->sleep, head->mapnumber, head->score);
			head = head->next;
		}
		//关闭文件
		fclose(fp);
	}
	return n;
}

/*
	MA:All Falls Down.mp3
	MB:All The Things She Said.mp3
	MC:Alone.mp3
	MD:(吞咽音效)
	ME:Faded.mp3
	MF:Ignite.mp3
	MH:Reality.mp3
	MI:YELLOW.mp3
	MJ:YYCY.mp3  (云烟成雨)
	*/

void SetBackgroundMusic()
{
	mciSendString(L"open MD.mp3 alias MD", 0, 0, 0);
	srand((unsigned)(time(NULL)));
	Mn = rand() % 8;
	switch (Mn)
	{
	case 0:
		mciSendString(L"open MA.mp3 alias MA", 0, 0, 0);
		mciSendString(L"play MA repeat", 0, 0, 0);
		break;
	case 1:
		mciSendString(L"open MB.mp3 alias MB", 0, 0, 0);
		mciSendString(L"play MB repeat", 0, 0, 0);
		break;
	case 2:
		mciSendString(L"open MC.mp3 alias MC", 0, 0, 0);
		mciSendString(L"play MC repeat", 0, 0, 0);
		break;
	case 3:
		mciSendString(L"open ME.mp3 alias ME", 0, 0, 0);
		mciSendString(L"play ME repeat", 0, 0, 0);
		break;
	case 4:
		mciSendString(L"open MF.mp3 alias MF", 0, 0, 0);
		mciSendString(L"play MF repeat", 0, 0, 0);
		break;
	case 5:
		mciSendString(L"open MH.mp3 alias MH", 0, 0, 0);
		mciSendString(L"play MH repeat", 0, 0, 0);
		break;
	case 6:
		mciSendString(L"open MI.mp3 alias MI", 0, 0, 0);
		mciSendString(L"play MI repeat", 0, 0, 0);
		break;
	case 7:
		mciSendString(L"open MJ.mp3 alias MJ", 0, 0, 0);
		mciSendString(L"play MJ repeat", 0, 0, 0);
		break;
	}
}

void CloseMusic()
{
	mciSendString(L"close MD", 0, 0, 0);
	switch (Mn)
	{
	case 0:
		mciSendString(L"close MA", 0, 0, 0);
		break;
	case 1:
		mciSendString(L"close MB", 0, 0, 0);
		break;
	case 2:
		mciSendString(L"close MC", 0, 0, 0);
		break;
	case 3:
		mciSendString(L"close ME", 0, 0, 0);
		break;
	case 4:
		mciSendString(L"close MF", 0, 0, 0);
		break;
	case 5:
		mciSendString(L"close MH", 0, 0, 0);
		break;
	case 6:
		mciSendString(L"close MI", 0, 0, 0);
		break;
	case 7:
		mciSendString(L"close MJ", 0, 0, 0);
		break;
	}
}

void ChangeMidMusic()
{
	mciSendString(L"play MD from 0", 0, 0, 0);
}

完整源代码:

完整源代码:https://pan.baidu.com/s/1OdZDuyTQBvCKl3bwkaHLuA 提取码:tcs1 

整个项目文件:https://pan.baidu.com/s/1XVGd59jmMy-aJhceFktWaw 提取码:tcs2 

游戏安装包:https://pan.baidu.com/s/1pD7dlNfl4SWpNdOorK1ZZw 提取码:tcs3 

游戏最终效果展示:

贪吃蛇2.0

问题补充:

针对在这个游戏编写过程中遇到的“怎么获取电脑键盘键值问题”做一个分享补充:

我在遇到这个问题时也是去各种百度各种查,然后发现搜索的结果竟然有所不同,实在是难受,不过最后还是让我找到了一个好东西:一段可以获取键值的C语言代码,我觉得挺好用的,在这里分享给大家

#include <stdio.h>

#include <windows.h>

#include <conio.h>

int main()

{
    int ch;//保存从键盘读取的值
    char c;//保存从键盘读取的值
    while (1)

    {
        if (_kbhit())//有击键发生

        {
            c= ch = _getch();//获取键盘值,不需要回车

            printf("%c键值是:%d\n",c, ch);

        }

        Sleep(100);//避免忙等待

    }
}

 不过实在是时间久了(这个小游戏我是2020年10月份左右写的),当时也没想太多,忘记了代码的出处,理解万岁,感谢原作者!

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

YFFlame

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值