小游戏——C++扫雷开发(MFC)
博客专区 > Nyika 的博客 > 博客详情
小游戏——C++扫雷开发(MFC)
Nyika 发表于5个月前
小游戏——C++扫雷开发(MFC)
  • 发表于 5个月前
  • 阅读 19
  • 收藏 1
  • 点赞 0
  • 评论 0

腾讯云 新注册用户 域名抢购1元起>>>   

       扫雷游戏是Windows里面自带的一个小游戏,经典情怀,正好王者荣耀玩腻,便来编了一个扫雷,玩了一整天😊。

扫雷游戏规则:

        游戏开始有一片雷区,你可以左键单击雷区,揭开方格,没有雷就是空白,有雷就游戏结束,如果在周边的八个格子中有雷,那么当前你翻开的格子显示的是周围八个格子中有雷的个数。根据周围雷数的提示,你可以猜出哪个格子有雷,这时你可以鼠标右键单击做标记,雷区有20颗雷,你有25次右击机会,如果25次标记都没能完全标记所有雷,游戏结束,失败了。若准确标记出20颗雷,就算你赢了。

界面设计:

        在VS2017或者vc6.0中建立单文本文档,界面如下:

具体尺寸自己设计,觉得合适即可。

操作步骤:

1.删除工具栏:

删除标记部分即可删除工具栏,关于菜单栏的修改,只需要在资源里面手动修改即可。

2、修改窗口

BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
{
	if (!CFrameWnd::PreCreateWindow(cs))
		return FALSE;
	// TODO: 在此处通过修改
	//  CREATESTRUCT cs 来修改窗口类或样式
	cs.style = WS_SYSMENU | WS_OVERLAPPED | WS_MINIMIZEBOX;
	cs.cx = 400;
	cs.cy = 500;
	cs.lpszName = _T("扫雷1.0");
	return TRUE;
}

3、界面布置,在CxxView::OnDraw()函数里面可以对窗口的背景进行绘制,同时还可以画出雷区的框线

void CMineCleannerView::OnDraw(CDC* pDC)
{
	CMineCleannerDoc* pDoc = GetDocument();
	ASSERT_VALID(pDoc);
	if (!pDoc)
		return;
	// TODO: 在此处为本机数据添加绘制代码
	
	CBrush mybrush;
	mybrush.CreateSolidBrush(RGB(125, 125, 247));
	CRect myrect(0, 0, 500, 500); //添加背景的区域  
	pDC->FillRect(myrect, &mybrush);
	CPen mypen;
	CPen*myoldPen;
	mypen.CreatePen(PS_SOLID, 2, RGB(255, 255, 255));
	myoldPen = pDC->SelectObject(&mypen);
	for (int i = 0; i < m_ColCount; i++)
	{
		for (int j = 0; j < m_RowCount; j++)
		{
			pDC->MoveTo(35 + i * 30, 50 + j * 30 + 30);
			pDC->LineTo(35 + i * 30, 50 + j * 30);
			pDC->LineTo(35 + i * 30 + 30, 50 + j * 30);
		}
	}
	pDC->SelectObject(myoldPen);
	//右下角黑线  
	CPen mypen2;
	CPen*myoldPen2;
	mypen2.CreatePen(PS_SOLID, 1, RGB(0, 0, 0));
	myoldPen2 = pDC->SelectObject(&mypen2);
	for (int ii = 0; ii < m_ColCount; ii++)
	{
		for (int jj = 0; jj < m_RowCount; jj++)
		{
			pDC->MoveTo(35 + ii * 30, 50 + jj * 30 + 30);
			pDC->LineTo(35 + ii * 30 + 30, 50 + jj * 30 + 30);
			pDC->LineTo(35 + ii * 30 + 30, 50 + jj * 30);
		}
	}
	pDC->SelectObject(myoldPen2);
	
}

CDC是绘图类的成员对象,详细参见:https://msdn.microsoft.com/zh-cn/library/fxhhde73.aspx?f=255&MSPPError=-2147217396

4、正式进入雷区的处理,关于雷区的处理我们首先得建立雷的一个结构体

struct Mine {
		int iBitMap;//标记
		int iArondMineNum;//雷区的每个格子情况
	};

这里标记了雷的属性mine.IAroundMineNum表示周围雷的个数,-1就表示是雷,0就表示周围八个格子内都没有雷,如果是0……8的数字则表示周围的雷数目。

我们定义一个雷区的逻辑二维数组,Mine m_Mine[10][10],我们可以将这个数组称为逻辑雷区,然后就可以开始补雷和标记所有格子里的内容,这个过程我们写在初始化的函数里面。

void CMineCleannerView::OnInitializeMine(bool bIsSelfDefine)
{
	int aa = 0;
	srand((unsigned)time(0));
	//设置m_iMineNum个雷  
	do
	{
		//以当前秒数为产生随机算法  
		int k = (rand()) % m_ColCount;
		int l = (rand()) % m_RowCount;
		//为了避免一个位置同时算两个雷  
		//只允许当前位置不是雷时赋值为雷  
		if (m_Mine[k][l].iArondMineNum != -1)
		{
			//这个位置有雷  
			m_Mine[k][l].iArondMineNum = -1;
			aa++;
		}
	} while (aa != m_iMineNum);

	//给方格赋值,计算雷数  
	for (int a = 0; a < m_ColCount; a++)
	{
		for (int b = 0; b < m_RowCount; b++)
		{
			if (m_Mine[a][b].iArondMineNum == 0)
			{
				for (int c = a - 1; c < a + 2; c++)
				{
					for (int d = b - 1; d < b + 2; d++)
					{
						if (c >= 0 && c < m_ColCount && d >= 0 && d < m_RowCount)
						{
							if (m_Mine[c][d].iArondMineNum == -1)
							{
								m_Mine[a][b].iArondMineNum++;
							}
						}
					}
				}
			}
		}
	}
}

然后在构造函数里面要对自己写的变量进行赋值,并且还要调用这个初始化的函数,至此,逻辑布雷就已经完成了,然后就要进行下一步了,画图。

5、在OnDraw函数中我们已经将窗口的初始化问题解决了,然后我们要实现当鼠标左键点击一个方格时,这个方格就被翻开,被翻开的定义就是画出相应方格对应的逻辑块。这里面有两个函数,一个是鼠标响应另一个是画,先说鼠标响应吧。

首先到类视图,右键单击XXView类选择消息响应,然后添加代码即可,

void CMineCleannerView::OnLButtonDown(UINT nFlags, CPoint point)
{
	// TODO: 在此添加消息处理程序代码和/或调用默认值
	int ax, ay,ii;
	ax = (point.x - 35) / 30;
	ay = (point.y - 50) / 30;
	if (ay < 10 && ax < 10&&point.x>=35&&point.y>=50) {
		if (m_Mine[(point.x - 35) / 30][(point.y - 50) / 30].iArondMineNum != -1) {
			show(ax, ay);
		}
		else
			GameOver();
	}
	
	CView::OnLButtonDown(nFlags, point);
}

然后就是画图类show();

void CMineCleannerView::show(int tx, int ty)
{
	m_dc = this->GetDC();
	CDC memdc;
	if (m_Mine[tx][ty].iArondMineNum == 0) {
		memdc.CreateCompatibleDC(m_dc);
		memdc.SelectObject(&m_Bmp[0]);
		m_dc->BitBlt(tx * 30 + 35, ty * 30 + 50, 30, 30, &memdc, 0, 0, SRCCOPY);
		m_Mine[tx][ty].iBitMap = 1;
		for (int i = tx-1;i<tx+2&&i>=0&& i < m_ColCount; i++) 
			for (int j = ty - 1; j < ty + 2 && j >= 0 && j < m_RowCount; j++) {
				if(m_Mine[i][j].iBitMap != 1)
				show(i, j);
			}
	}
	else {
		memdc.CreateCompatibleDC(m_dc);
		memdc.SelectObject(&m_Bmp[m_Mine[tx][ty].iArondMineNum]);
		m_dc->BitBlt(tx * 30 + 35, ty * 30 + 50, 30, 30, &memdc, 0, 0, SRCCOPY);
		m_Mine[tx][ty].iBitMap = 1;
	}

绘画位图本来很简单,但是在这里我们用到了一个递归,主要是为了实现扩散的效果,在扫雷中,如果你点击了一个空白块,那么他会以你点击的空白快为中心扩散直到边缘都为数字块。

6、关于右键点击的响应事件与左键差不多,所以可以这样写。

void CMineCleannerView::OnRButtonDown(UINT nFlags, CPoint point)
{
	// TODO: 在此添加消息处理程序代码和/或调用默认
	int ax, ay, ii;
	m_dc = this->GetDC();
	ax = (point.x - 35) / 30;
	ay = (point.y - 50) / 30;
	CDC memdc;
	if (ay < 10 && ax < 10 && point.x >= 35 && point.y >= 50) {
			ii = m_Mine[(point.x - 35) / 30][(point.y - 50) / 30].iArondMineNum;
			memdc.CreateCompatibleDC(m_dc);
			memdc.SelectObject(&m_Bmp[11]);
			m_dc->BitBlt(ax * 30 + 35, ay * 30 + 50, 30, 30, &memdc, 0, 0, SRCCOPY);
			mine++;
			if (m_Mine[ax][ay].iArondMineNum == -1) right++;
		}
	if (right == m_iMineNum) youwin();
	if (mine >= 25) GameOver();
	CView::OnRButtonDown(nFlags, point);
}

7、点击开始的响应函数就是将构造函数的内容重新做一遍,然后清楚之前的内容,清楚之前的内容可以用Invalidate();

8、在此之后可以在窗口中添加一个按钮,添加按钮的操作如下。

    首先在资源视图中找到StringTable,在里面注册一个按钮,然后添加一个Create的消息响应函数,创建一个按钮代码如下

    CButton *B_begin=new CButton();	
	CRect rect_button(150, 10, 200, 40);   //控制按钮大小、位置
	B_begin->Create(_T("Start"), WS_CHILD | WS_VISIBLE | WS_BORDER, rect_button, this, ID_BUTTON);
	B_begin->ShowWindow(SW_SHOWNORMAL);

按钮创建好以后,要对按钮绑定一个按钮的响应函数

afx_msg void OnStart();

在view.h文件里面添加这句话,然后再转到view.cpp文件

ON_COMMAND(ID_Start, &CMineCleannerView::OnStart)

在文件的开头,将按钮的id和相应函数绑定,这样就可以在onstart里面去添加代码了,这里面的代码和菜单栏里面的代码一样。

结语

至此一个简单的扫雷小游戏就可以跑起来了,但是这里面原本是有一个计时的功能,可以在OnTimer();里面添加代码,但本人研究数日都未能将时间显示在文本框中,实在惭愧,所以就这样结束了。

在游戏中还有一个可能算难点吧,就是添加位图,首先你将位图资源文件导入到了你的资源文件,然后你不能直接用,你需要了解CBitmap类,去建立一个CBitmap类的对象数组来装这些图,然后按照数组的下标来使用这些图。

扫雷:链接:http://pan.baidu.com/s/1hsFwmXI 密码:s511

 

共有 人打赏支持
粉丝 2
博文 6
码字总数 5556
×
Nyika
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
* 金额(元)
¥1 ¥5 ¥10 ¥20 其他金额
打赏人
留言
* 支付类型
微信扫码支付
打赏金额:
已支付成功
打赏金额: