#前言
关于贪吃蛇游戏这种学生时代,用来应付一个期末设计的东西。网上各式各样用不同方式实现的核心代码已经烂大街了。
用esayX这种图形库,可以简单进行蛇体,游戏界面的绘制。
我是通过确定了坐标后,直接插入的外部图片(图都是自己用画的)
主要增加了一下分数排行,分数显示,播放音乐等功能。都是些很简单的小玩意
以下是源代码
#pragma warning(disable:4996)
#include
#include //调用EasyX的图形库
#include
#include
#include
#include
#include
#pragma comment(lib, "Winmm.lib")//调用有关多媒体的动态连接库
#define GAMEWIDTH 640 //游戏窗口的宽
#define GAMEHIGH 480 //游戏窗口的高
#define N 20 //蛇每节的大小20x20
#define SNAKELEN 1000 //蛇的最大长度1000
#define SPEED 180 //定义初始的移动速度
#define PATHFILE "rank.txt"//分数存储的文件
//自定义函数
//——————————————————————————————
void menu(); //进入游戏的主菜单界面
void about(); //关于游戏界面
void initgame(); //初始化游戏界面
void OverGame(); //游戏失败界面
void gameegg(); //游戏彩蛋界面
void snakepic(); //加载蛇头蛇身的图片
void showsnake(); //生成蛇
void showfood(); //加载食物图片
void snakepicmove(); //控制蛇移动
void setfoodcrd(); //食物的生成坐标
void EatFood(); //吃到食物
void GameEnd(); //游戏退出和触发彩蛋的条件
void SnakeDirection(); //蛇头的方向
void addscore(); //显示分数
int rank(); //储存排行榜
int readrank(); //输出排行榜
void speedup(); //游戏加速
//自定义结构体和全局变量
//——————————————————————————————
MOUSEMSG m; //定义鼠标,引用Exasy库
int s; //定义游戏分数
// 方便以后在程序中改变速度
int speed = SPEED;
//定义使用的外部图片
IMAGE foods, snakeup, snakedown, snakeleft, snakerigth, snakebody, background, gameoverbk, reback, scorebk, gameeggbk;
typedef struct inf //定义一个结构体存放分数
{
int score;
}info;
enum DIR //运动方向的结构体
{
up = 72, //上、下、左、右、空格键ASCII码
down = 80,
left = 75,
right = 77,
space = 32
};
typedef struct COOR //表示坐标的结构体
{
int x; //横坐标
int y; //纵坐标
};
struct SNAKE //定义结构体,来存储蛇的相关信息
{
COOR crd[SNAKELEN]; //保存蛇的每个结数的坐标,最多有1000节
int node; //保存蛇当前节数
DIR dir; //当前蛇行进的方向
}snake;
struct FOOD //食物的结构体
{
COOR crd; //食物坐标
int flag; //表示有没有食物 1代表没有食物,没有食物需要生成食物
}food;
//=================================================================
//主函数
int main()
{
initgraph(GAMEWIDTH, GAMEHIGH); //创建一个680X480窗口
menu(); //主菜单函数
return 0; //程序正常结束
}
//主菜单界面模块
void menu()
{
loadimage(&background, _T("pic\\bk.jpg"), 640, 480); //从文件中读取图像
putimage(0, 0, &background); //重载用于在当前设备上绘制指定图像
setbkmode(TRANSPARENT); //字体背景透明
while (true)
{
while (MouseHit()) //检测当前是否有鼠标消息
{
m = GetMouseMsg(); //函数用于获取一个鼠标消息
if (m.uMsg == WM_LBUTTONDOWN) //左键按下消息
{
if (m.x >= 60 && m.x <= 280) //鼠标点击的区域横坐标为60-280
{
if (m.y >= 160 && m.y <= 215) //鼠标点击的区域纵坐标为160-215
{
mciSendString(_T("open pic\\Sushi.mp3 alias mysong"), NULL, 0, NULL); //从指定目录调用音乐
mciSendString(_T("play MySong repeat"), NULL, 0, NULL); //播放音乐
initgame(); //进入游戏游戏界面
}
if (m.y >= 260 && m.y <= 315)
{
//分数排行榜
readrank();
}
if (m.y >= 360 && m.y <= 415)
{
//关于游戏
about();
}
}
}
}
}
}
//游戏说明界面
void about()
{
cleardevice(); //清除屏幕内容
loadimage(&reback, _T("pic\\aboutbk.jpg"), 640,480);//背景图片
putimage(0, 0 ,&reback);
while (true) //鼠标点击,返回主菜单界面
{
if (MouseHit())
{
m = GetMouseMsg();
if (m.uMsg == WM_LBUTTONDOWN)
{
if (m.x >=540 && m.x <= 660 && m.y >=417 && m.y <= 460)
{
menu(); //主菜单函数
}
}
}
}
}
//载入蛇身,蛇头的图片
void snakepic()
{
setbkcolor(WHITE); //背景窗口设置为白色
loadimage(&snakeup, _T("pic\\蛇头上.jpg"), N, N);
loadimage(&snakedown, _T("pic\\蛇头下.jpg"), N, N);
loadimage(&snakeleft, _T("pic\\蛇头左.jpg"), N, N);
loadimage(&snakerigth, _T("pic\\蛇头右.jpg"), N, N);
loadimage(&snakebody, _T("pic\\贪吃蛇身体.jpg"), N, N);
snake.crd[0].x = 0; //蛇出现的横坐标
snake.crd[0].y = 0; //蛇出现的纵坐标
snake.node = 1; //蛇出生的节数
snake.dir = down; //蛇头运动的初始方向
food.flag = 0; //没有食物
cleardevice(); //清除屏幕内容
}
//初始化游戏的界面,运行游戏
void initgame()
{
srand(time(NULL)); //设置一个随机种子,每次运行都能保证随机种子不同。
snakepic(); //载入蛇身,蛇头的图片
s = -10; //循环开始时,将分数设置为负数
while (1)
{
while (!_kbhit())
{
if (!food.flag) //食物不存在,就生成食物
{
setfoodcrd();
s += 10; //每生产一次食物加10分
}
cleardevice();
showfood(); //食物图片
addscore(); //左上角显示分数
snakepicmove(); //
showsnake(); //
Sleep(speed); //移动速度
EatFood(); //吃到食物
GameEnd(); //游戏结束条件
}
SnakeDirection();
}
}
//随机生成食物的坐标
void setfoodcrd()
{
food.crd.x = rand() % (640 /N) *N;
food.crd.y = rand() % (480 /N) *N;
food.flag = 1;
}
//放置食物的图片
void showfood()
{
loadimage(&foods, _T("pic\\食物.jpg"), N, N);
putimage(food.crd.x, food.crd.y, &foods);
}
//放置蛇头以及蛇身的图片
void showsnake()
{
//蛇头
switch (snake.dir)//获取运动方向,改变蛇头图片
{
case up:
putimage(snake.crd[0].x, snake.crd[0].y, &snakeup);
break;
case down:
putimage(snake.crd[0].x, snake.crd[0].y, &snakedown);
break;
case left:
putimage(snake.crd[0].x, snake.crd[0].y, &snakeleft);
break;
case right:
putimage(snake.crd[0].x, snake.crd[0].y, &snakerigth);
break;
default:
break;
}
//蛇身
//通过一个从(node-1)到1的循环,生成蛇身,进行贴图
for (int i = snake.node - 1; i > 0; i--)
{
putimage(snake.crd[i].x, snake.crd[i].y, &snakebody);
}
}
//蛇体贴图坐标的移动
void snakepicmove()
{
//倒数第一节蛇身的贴图坐标等于倒数第二节
for (int i = snake.node - 1; i > 0; i--)
{
snake.crd[i].x = snake.crd[i - 1].x;
snake.crd[i].y = snake.crd[i - 1].y;
}
switch (snake.dir)
{
case up:
snake.crd[0].y -= N;
break;
case down:
snake.crd[0].y += N;
break;
case left:
snake.crd[0].x -= N;
break;
case right:
snake.crd[0].x += N;
break;
default:
break;
}
}
//键盘操作控制蛇移动方向
void SnakeDirection()
{
switch (_getch())
{
case 72:
if (snake.dir != down)
snake.dir = up;
break;
case 80:
if (snake.dir != up)
snake.dir = down;
break;
case 75:
if (snake.dir != right)
snake.dir = left;
break;
case 77:
if (snake.dir != left)
snake.dir = right;
break;
case 32://空格暂停
system("pause");
default:
break;
}
}
//吃到食物:食物的坐标和蛇头坐标的重合
void EatFood()
{
if (snake.crd[0].x == food.crd.x && snake.crd[0].y == food.crd.y)//食物的坐标和蛇头坐标(snake.crd.[0])的重合
{
snake.node++; //节数增加
food.flag = 0; //生成食物的标志变为0
speedup(); //每次吃到食物后,加速
}
}
//判断游戏退出
void GameEnd()
{
//撞墙
if (snake.crd[0].y < -1 || snake.crd[0].y>471 || snake.crd[0].x < -1 || snake.crd[0].x>631)
{
//退出
OverGame();
}
//吃到自己
for (int i = snake.node - 1; i > 0; i--)
{
if (snake.crd[0].x == snake.crd[i].x && snake.crd[0].y == snake.crd[i].y)
{
//退出
OverGame();
}
}
//分数到达520
if (s ==520)
{
//彩蛋界面
gameegg();
}
}
//游戏结束画面及相关操作
void OverGame()
{
mciSendString(_T("close MySong"), NULL, 0, NULL); //游戏结束,关闭背景音乐
loadimage(&gameoverbk, _T("pic\\gameoverbk.jpg"),640,480);
putimage(0, 0, &gameoverbk);
setbkmode(TRANSPARENT); //字体背景透明
settextcolor(BLACK); //黑色字体
settextstyle(31,31,_T("字魂27号-布丁体")); //字体大小,样式
TCHAR myscore[20]; //定义一个数组用来显示分数
swprintf_s(myscore, _T("你的分数:%d"), s); //输出数组
outtextxy(226,246, myscore); //文本输出的位置
//鼠标操作
while (true)
{
while (MouseHit())
{
m = GetMouseMsg();
if (m.uMsg == WM_LBUTTONDOWN)
{
if (m.x >= 220 && m.x <= 420)
{
if (m.y >= 250&& m.y <=295 )
{
rank();
}
if (m.y >= 320 && m.y <= 365)
{
rank();
menu();
}
if (m.y >= 390 && m.y <= 435)
{
rank();
exit(0);
}
}
}
}
}
}
//游戏分数的显示
void addscore()
{
TCHAR myscore[20];
swprintf_s(myscore, _T("score:%d"), s);// 输出数值,先将数字格式化输出为字符串
settextcolor(BLACK);
outtextxy(0,0,myscore);//输出分数的文本内容
}
//创建一个文件储存分数
int rank()
{
info tmp;
FILE* fp;
tmp.score = s;
fp = fopen(PATHFILE, "a");//追加 向文本文件尾添加数据
if (fp == NULL)
{
return -1;
}
fwrite(&tmp, sizeof(struct inf), 1, fp);
fclose(fp);
return 0;
}
//分数排行榜
int readrank()
{ //载入分数界面的背景图片------------------------------
loadimage(&scorebk, _T("pic\\scorebk.jpg"), 640, 480);
putimage(0, 0, &scorebk);
//读取分数进行排序------------------------------------
info tmp[N];
info temp; //空的结构体用于交换
int n = 0, j = 0, i = 0,k=0;
FILE* fp;
fp = fopen(PATHFILE, "a+");
if (fp == NULL)
{
printf("cannot open the files\n");
}
while (!feof(fp)) //数组tmp读取文件,未到达文件尾
{
fread(&tmp[i], sizeof(struct inf), 1, fp);
i++; //每读取一条数据,i+1
}
n = i - 1; //记录排行榜人数
for (i = 0; i < n; i++) //冒泡排序对分数排序
for (j = i + 1; j < n; j++)
if (tmp[i].score < tmp[j].score)
{
temp =tmp[i];
tmp[i] = tmp[j];
tmp[j] = temp;
}
//显示排序后的分数--------------------------------------
TCHAR myscore[20];
if(i>=10) //当记录的分数数据超过10条
{
for (i = 0; i < 10; i++) //只显示前10个分数
{
swprintf_s(myscore, _T("%d %d"), i + 1, tmp[i].score);
settextcolor(BLACK);
settextstyle(25, 25, _T("字魂75号-柳叶竹眉体"));
outtextxy(185, 120 + i * 20, myscore);//输出分数的文本内容
}
fclose(fp);
}
if (i < 10) //当记录的分数数据不超过10条
{
for (k = 0; k < i; k++) //只显示前i条分数
{
swprintf_s(myscore, _T("%d %d"), k + 1, tmp[k].score);
settextcolor(BLACK);
settextstyle(25, 25, _T("字魂75号-柳叶竹眉体"));
outtextxy(185, 120 + k * 20, myscore);//输出分数的文本内容
}
fclose(fp);//关闭文件
}
//鼠标操作返回主菜单--------------------------------------
while (true)
{
while (MouseHit())
{
m = GetMouseMsg();
if (m.uMsg == WM_LBUTTONDOWN)
{
if (m.x >= 540 && m.x <= 660 && m.y >= 417 && m.y <= 460)//返回主菜单界面
{
menu();
}
if (m.x >= 460 && m.x <=500 && m.y >= 417 && m.y <= 460)
{
remove(PATHFILE);
//清空记录
}
}
}
}
}
//游戏彩蛋
void gameegg()
{
mciSendString(_T("close MySong"), NULL, 0, NULL);
mciSendString(_T("open pic\\InsideStory.mp3 alias mysong2"), NULL, 0, NULL);
mciSendString(_T("play MySong2 repeat"), NULL, 0, NULL);
loadimage(&gameeggbk, _T("pic\\gameeggbk.jpg"),640,480);
putimage(0, 0, &gameeggbk);
//返回菜单
while (true)
{
while (MouseHit())
{
m = GetMouseMsg();
if (m.uMsg == WM_LBUTTONDOWN)
{
if (m.x >= 540 && m.x <= 660 && m.y >= 417 && m.y <= 460)//返回主菜单界面
{
mciSendString(_T("close MySong2"), NULL, 0, NULL); //游戏结束,关闭背景音乐
rank();
menu();
}
}
}
}
}
//游戏加速
void speedup()
{
if (speed >= 80)
{
speed= speed - 10;
}
}
下面是整个文件的链接
链接:https://pan.baidu.com/s/1Vp7khf_UfUR-b1fU1PQzkQ
提取码:jk71
仅供学习参考