基于EasyX的扫雷游戏

Posted jzdnkbd

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了基于EasyX的扫雷游戏相关的知识,希望对你有一定的参考价值。

基于EasyX的扫雷游戏

一、预备知识

1.使用EasyX必须要知道的一些基础函数
2.选择结构 if , switch
3.循环结构 for, while
4.多维数组 arr1[N], arr2[N][N] , arr3[N][N][N]
5.函数封装

二、游戏逻辑

想要写出推箱子,首先要知道推箱子游戏都有哪些元素和规则

1.扫雷元素


主要可以分成三类
1.没有打开个格子
2.打开的格子
3.被标记的格子

有了素材,先来一个加载资源函数Loadimg()

#define SIZE 25//图片大小
IMAGE img[13];//存放图片数组

void Loadimg()
{
	loadimage(&img[0] ,L"./images/0.jpg",SIZE,SIZE);
	loadimage(&img[1] ,L"./images/1.jpg",SIZE,SIZE);
	loadimage(&img[2] ,L"./images/2.jpg",SIZE,SIZE);
	loadimage(&img[3] ,L"./images/3.jpg",SIZE,SIZE);
	loadimage(&img[4] ,L"./images/4.jpg",SIZE,SIZE);
	loadimage(&img[5] ,L"./images/5.jpg",SIZE,SIZE);
	loadimage(&img[6] ,L"./images/6.jpg",SIZE,SIZE);
	loadimage(&img[7] ,L"./images/7.jpg",SIZE,SIZE);
	loadimage(&img[8] ,L"./images/8.jpg",SIZE,SIZE);
	loadimage(&img[9] ,L"./images/9.jpg",SIZE,SIZE);
	loadimage(&img[10],L"./images/10.jpg",SIZE,SIZE);
	loadimage(&img[11],L"./images/11.jpg",SIZE,SIZE);
	loadimage(&img[12],L"./images/12.jpg",SIZE,SIZE);
}

2.扫雷规则

1.左键点击未被打开的格子,可以打开格子
2.右键点击未被打开的格子,可以标记格子
3.被标记的格子不能被打开
3.被标记的格子再右键时,可以解除标记
4.如果打开后格子是空的,则自动打开周围的八个格子
5.自动打开的格子也遵循规则4
6.如果打开的格子里面是雷,则游戏失败
7.如果所有非雷格子均被打开,则游戏获胜

(格子的数字代表它周围九宫格内雷的数量)

三、游戏设计

知道游戏元素和规则就可以开始设计游戏了

1.地图设计

地图内的数字代表它周围九宫格内雷的数量,看一下图就应该可以理解了,这次采用地图随机生成(为了避免大家记雷的位置),需要用<time.h>里的生成随机数,先写一个初始化地图函数Initmap()

#define NUM 10//地图行列数
int map[NUM][NUM]={0};

void Initmap()
{	
	//----------随机设置雷----------
	srand((unsigned)time(NULL));//根据时间设置随机数种子
	for(int k=0;k<NUM;  )
	{
		int x=rand()%NUM;//生成0 ~ 9的随机数
		int y=rand()%NUM;//为避免产生相同的坐标,采用下面的方式
		if(map[x][y]==0)
		{//如果格子为空,则设置雷
			map[x][y]=9;//雷是9
			k++;
		}
	}
	//如果是雷,则雷周围九宫格内数字均+1
	for(int i=0;i<NUM;i++)
	{
		for(int j=0;j<NUM;j++)
		{
			if(map[i][j]==9)
			{//如果是雷
				for(int a=i-1;a<=i+1;a++)
				{//遍历该雷周围的九宫格
					for(int b=j-1;b<=j+1;b++)
					{
						if(a>=0 && b>=0 && a<NUM && b<NUM && map[a][b]!=9)
						{//判断数组是否越界
							map[a][b]++;//不越界则+1
						}
					}
				}
			}
		}
	}
	//盖上格子 所有数字 +10,(点击打开格子 数字-10)
	for(int r=0;r<NUM;r++)
	{
		for(int c=0;c<NUM;c++)
		{
			map[r][c]+=10;
		}
	}
}

由上面的元素分类可以知道,游戏地图数据分为三类

  1. 被打开的格子 0 ~ 9
  2. 未被打开的格子 10 ~ 19
  3. 被标记的格子 20 ~ 29

也就是,对格子进行+10处理,+10代表另一种状态,现在可以根据状态绘制地图,写一个Drawmap()函数

void Drawmap()
{
	for(int i=0;i<NUM;i++)
	{
		for(int j=0;j<NUM;j++)
		{ 
			if(map[i][j]>=10 && map[i][j]<20)
			{//如果图还没点开,且未被标记,贴格子
				putimage(i*SIZE,j*SIZE,&img[10]);
			}
			else if(map[i][j]>=20)
			{//如果被标记,贴标记
				putimage(i*SIZE,j*SIZE,&img[11]);
			}
			else
			{//剩下的按数值贴图
				putimage(i*SIZE,j*SIZE,&img[ map[i][j] ]);
			}
		} 
	}
}

2.点击设计

上文提到

1.左键点击未被打开的格子,可以打开格子
2.右键点击未被打开的格子,可以标记格子
3.被标记的格子不能被打开
3.被标记的格子再右键时,可以解除标记

首先要找到鼠标位置,然后根据鼠标点击的情况,左键还是右键,分写不同的处理,所有内容写在Hitimg()函数内

ExMessage m;//消息全局变量
int ipos=-10,jpos=-10;//记录鼠标位置
int count=90;//总共非雷格子数

void Hitimg()
{
	m = getmessage(EM_MOUSE);//得到鼠标消息
	if(m.message == WM_LBUTTONDOWN)
	{//如果鼠标左键按下
		ipos=m.x/SIZE;//换算鼠标位置
		jpos=m.y/SIZE;
		if(map[ipos][jpos]>=10 && map[ipos][jpos]<20)
		{//如果格子还没被点开 且未被标记
			count--;//非雷格子数减少
			map[ipos][jpos]-=10;//打开
		}
	}
	if(m.message == WM_RBUTTONDOWN) 
	{//如果鼠标右键按下
		ipos=m.x/SIZE;//换算鼠标位置
		jpos=m.y/SIZE;
		if(map[ipos][jpos]>=10 && map[ipos][jpos]<20)
		{//如果格子还没被点开
			map[ipos][jpos]+=10;//标记
		}
		else if(map[ipos][jpos]>=20)
		{//如果格子被标记
			map[ipos][jpos]-=10;//去掉标记
		}
	}
}

上文提到

4.如果打开后格子是空的,则自动打开周围的八个格子
5.自动打开的格子也遵循规则4

这个就比较有趣了,仔细想想,这不就是递归吗,因为每次格子位置都不一样,需要传个 格子位置 参数,写个Openmap(int ipos,int jpos)函数

void Openmap(int ipos,int jpos)
{//如果是空白则打开别打空白
	if(map[ipos][jpos]==0)
	{//如果是空格子
		for(int a=ipos-1;a<=ipos+1;a++)
		{ //遍历格子周围的九宫格
			for(int b=jpos-1;b<=jpos+1;b++)
			{
				if(a>=0 && b>=0 && a<NUM && b<NUM)
				{//判断数组是否越界 
					if(map[a][b]>=10 && map[a][b]<=18)
					{//如果周围是没被打开的格子
						map[a][b]-=10;//打开格子
						count--;//非雷格子数减少
						Openmap(a,b);//递归打开 被打开格子周围的格子
					}
				}
			}
		}
	}
}

3.结束设计

上文提到

6.如果打开的格子里面是雷,则游戏失败
7.如果所有非雷格子均被打开,则游戏获胜

是不是很容易想到,
游戏失败条件就是map[ipos][jpos]==9
游戏获胜条件就是非雷格子数为灵count==0
如果游戏失败也把别的雷显示出来,并且点的雷是红的,写一个函数显示所有雷Showallmines()

void Showallmines()
{
	for(int i=0;i<NUM;i++)
	{
		for(int j=0;j<NUM;j++)
		{ 
			if(map[i][j]==19 && map[i][j]!=9)
				putimage(i*SIZE,j*SIZE,&img[12]);
			if(map[i][j]==9)
				putimage(i*SIZE,j*SIZE,&img[9]);
		}
	}
}

4.整体设计

下面把上面的函数组合起来就是推箱子游戏,再来点音乐

#include<stdio.h>
#include<time.h>
#include<easyx.h>//图形库
#include<mmsystem.h>//音乐
#pragma comment(lib,"winmm.lib")//库文件

int main()
{
	initgraph(SIZE*10,SIZE*10);//创建窗口
	Loadimg(); //加载图片资源
	Initmap();//初始化地图
	
	BeginBatchDraw();//批量绘图开始
	Drawmap();//贴图
	EndBatchDraw();//批量绘图结束

	//循环播放背景音乐
	mciSendString(L"open ./images/bg.mp3 ", 0, 0, 0);
	mciSendString(_T("play ./images/bg.mp3 repeat"), 0, 0, 0);
	while(true){
		if(map[ipos][jpos]==9)
		{//如果游戏失败
			Showallmines();//显示所有雷
			mciSendString(L"close ./images/bg.mp3 ", 0, 0, 0);//关闭背景音乐
			PlaySound(L"./images/over.wav", nullptr, SND_FILENAME | SND_ASYNC);//播放失败音乐
			MessageBox(NULL,_T("游戏结束,你输了,-_-||"),_T("游戏提示"),MB_OK);
			break;
		}
		else if(count==0)
		{//如果游戏胜利
			Drawmap();
			mciSendString(L"close ./images/bg.mp3 ", 0, 0, 0);//关闭背景音乐
			PlaySound(L"./images/success.wav", nullptr, SND_FILENAME | SND_ASYNC);//播放胜利音乐
			MessageBox(NULL,_T("游戏结束,恭喜你,你赢了!"),_T("游戏提示"),MB_OK) ;
			break;
		}
		Drawmap();//更新地图
		Hitimg();//点击格子图片
		Openmap(ipos,jpos);//递归打开空格子
	}
	system("pause");
	return 0;
}

把上面所有代码复制到一个cpp文件里,再把素材改好命名放到images文件夹里,images文件夹在cpp文件旁边,就可以编译运行了!

图片音乐素材及可执行程序都在我的资源中,需要的可以进行下载

以上是关于基于EasyX的扫雷游戏的主要内容,如果未能解决你的问题,请参考以下文章

C语言开发《扫雷》游戏,你从未体验过的联网对战版

C语言开发《扫雷》游戏,你从未体验过的联网对战版

基于EasyX 的五子棋小游戏。

基于EasyX 的五子棋小游戏。

基于C语言扫雷游戏的设计与实现

C++基于easyx图形库实现推箱子游戏