写在前面:
贪吃蛇,相信大家都玩过,我在学习了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月份左右写的),当时也没想太多,忘记了代码的出处,理解万岁,感谢原作者!