逆枫゛

Qt学习群:1149411109 群文件提供博客源码,定期答疑、更新学习资料。

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

1,简介

QT开发的扫雷小游戏,这个相对比较简单,用了几个小时。

 

2,效果

 

 

3,设计思路

 

背景:一个灰色大矩形

游戏区:默认是初级难度,9*9的矩形阵。可变成16*16,16*30。

每个小矩形元素类 Item.h:

 

#ifndef ITEM_H
#define ITEM_H

#include <QPoint>

class Item
{
public:
    Item();
    Item(QPoint pos);

    QPoint m_pos;		//位置

	bool m_bIsMine;		//是否是雷
	bool m_bMarked;		//是否已标记为雷

    int m_nNumber;		//数字
	bool m_bOpen;		//是否已打开,且非雷
};

#endif // ITEM_H
	//是否是雷
	bool m_bMarked;		//是否已标记为雷

    int m_nNumber;		//数字
	bool m_bOpen;		//是否已打开,且非雷
};

#endif // ITEM_H

 

 

MainWindow.h:

 

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include "item.h"
#include <QMainWindow>

namespace Ui {
class MainWindow;
}

#define RECT_WIDTH      30
#define RECT_HEIGHT     30

#define START_X         100
#define START_Y         100


class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();

	void InitItems();
	void ReleaseItems();

    void NewGame();						
	void GameSuccess();
	void GameFail();

	void OpenEmptyItem(QPoint pt);			//点击空白元素(相邻雷数为0)时,递归查找相邻的空白元素,以及空白元素附近的数字元素(数字是雷数)
	bool FindAll();
	bool PointInGameArea(QPoint pt);		//判断坐标是否超过游戏区域
protected:
    void paintEvent(QPaintEvent *);
    void mousePressEvent(QMouseEvent *);

private slots:
    void OnMenu_NewGame();
    void OnMenu_Settings();
	void OnMenu_Level1();
	void OnMenu_Level2();
	void OnMenu_Level3();
private:
    void DrawChessboard();
    void DrawItems();
    void DrawItem(QPainter& painter,Item* pItem);
private:
    Ui::MainWindow *ui;
	QPixmap m_FlagImage;				//小红旗图片
	QPixmap m_BombImage;				//爆炸图片

	int m_nRows;					//行数
	int m_nColumes;					//列数
	int m_nMineCount;				//雷数
    QVector<QPoint> m_Mines;				//雷点
	QVector<QVector<Item*>> m_items;		//所有元素
	bool m_bGameFail;				//是否是游戏失败,失败了需要显示雷
};

#endif // MAINWINDOW_H
				//爆炸图片

	int m_nRows;					//行数
	int m_nColumes;					//列数
	int m_nMineCount;				//雷数
    QVector<QPoint> m_Mines;				//雷点
	QVector<QVector<Item*>> m_items;		//所有元素
	bool m_bGameFail;				//是否是游戏失败,失败了需要显示雷
};

#endif // MAINWINDOW_H

 


随机初始化雷点:

 

 

 

void MainWindow::InitItems()
{
	//随机初始化雷
	m_Mines.clear();
	for(int i = 0; i<m_nMineCount; i++)
	{
		qsrand(QTime::currentTime().msec());
		int x = qrand()%m_nColumes;
		int y = qrand()%m_nRows;
		while(m_Mines.contains(QPoint(x,y)))
		{
			x = qrand()%m_nColumes;
			y = qrand()%m_nRows;
		}
		m_Mines.append(QPoint(x,y));
	}
	//建立2维数组保存所有元素位置,方便索引
	for(int i=0; i<m_nColumes; i++)
	{
		QVector<Item*> rowItems;
		for(int j=0; j<m_nRows; j++)
		{
			QPoint pos = QPoint(i,j);
			Item* pItem = new Item(pos);
			if(m_Mines.contains(pos))   //该位置是雷
			{
				pItem->m_bIsMine = true;
			}
			rowItems.append(pItem);		
		}
		m_items.append(rowItems);
	}
	//计算雷附近格子的数字
	for(int i=0; i<m_nColumes; i++)
	{
		for(int j=0; j<m_nRows; j++)
		{
			if (m_items[i][j]->m_bIsMine)
			{
				continue;
			}
			int nCountMines = 0;
			//求每个点附近的8个点的是雷的总数
			for (int m=-1;m<=1;m++)
			{
				for (int n=-1; n<=1;n++)
				{
					if (m==0 && n==0)
					{
						continue;
					}
					QPoint ptNew = QPoint(i+m,j+n);
					if (!PointInGameArea(ptNew))
					{
						continue;
					}

					if (m_items[i+m][j+n]->m_bIsMine)
					{
						nCountMines++;
					}
				}
			}
			m_items[i][j]->m_nNumber = nCountMines;
		}
	}
}


核心函数,鼠标点击处理:

 

 

void MainWindow::mousePressEvent(QMouseEvent * e)
{
	//得到鼠标处的格子坐标
	QPoint pt;
	pt.setX( (e->pos().x() - START_X ) / RECT_WIDTH);
	pt.setY( (e->pos().y() - START_X ) / RECT_HEIGHT);
	//是否点在游戏区域内
	if (!PointInGameArea(pt))
	{
		return;
	}
	//获取所点击矩形元素
	Item* pItem = m_items[pt.x()][pt.y()];
	//左键打开元素,右键插旗帜标记
	if(e->button()==Qt::LeftButton)
	{
		//不是已标记的或已打开的空白点,也就是未处理的
		if(!pItem->m_bMarked && !pItem->m_bOpen)
		{
			//如果是雷,就GAME OVER
			if (pItem->m_bIsMine)
			{
				//QMessageBox::information(NULL,  "GAME OVER","FAIL!", QMessageBox::Yes , QMessageBox::Yes);
				GameFail();
				return;
			}
			else
			{
				//打开
				pItem->m_bOpen = true;
				if (pItem->m_nNumber == 0)
				{
					//如果数字是0,也就是不含任何相邻雷的元素,那么递归打开所有的相邻数字是0的元素
					//也就是点到一个空白处,一下打开一大片的效果
					OpenEmptyItem(pt);
				}
				//如果已找到所有雷
				if (FindAll())
				{
					QMessageBox::information(NULL,  "GAME OVER","SUCCESS!", QMessageBox::Yes , QMessageBox::Yes);
					//GameSuccess();
					return;
				}
			}
		}
	}
	else if(e->button()==Qt::RightButton)
	{
		//已标记过的,取消标记
		if (pItem->m_bMarked)
		{
			pItem->m_bMarked = false;
		}
		else if (!pItem->m_bOpen)
		{
			//没标记也没打开,就是未处理的,就插旗帜标记上
			pItem->m_bMarked = true;
			if (FindAll())
			{
				QMessageBox::information(NULL,  "GAME OVER","SUCCESS!", QMessageBox::Yes , QMessageBox::Yes);
				//GameSuccess();
				return;
			}
		}
	}
}


其中OpenEmptyItem函数,可能会打开空白一大片:

 

 

//运气好时点到一个空白元素,可能打开挨着的一大片
void MainWindow::OpenEmptyItem(QPoint pt)
{
	//对于空白元素,有上下左右4个方向挨着的空白元素,就打开并继续查找空白元素
	QVector<QPoint> directions;            //新建一个空list,里面可以装QPoint类型元素
	directions.push_back(QPoint(-1,0));    //插入一个QPoint,代表左方向
	directions.push_back(QPoint(1,0));     //插入一个QPoint,代表右方向
	directions.push_back(QPoint(0,-1));    //插入一个QPoint,代表下方向
	directions.push_back(QPoint(0,1));     //插入一个QPoint,代表上方向
	for (int i=0; i<directions.size(); i++)//遍历directions,对4个方向处理
	{
		QPoint ptNew = pt + directions[i]; //原格子pt,加上上面的一个单位的方向值,就是这个方向相邻的一个格子
		if (!PointInGameArea(ptNew))
		{
			continue;
		}
		Item* pItem = m_items[ptNew.x()][ptNew.y()];
		if (!pItem->m_bIsMine && !pItem->m_bOpen && !pItem->m_bMarked && pItem->m_nNumber == 0)
		{
			pItem->m_bOpen = true;

			//对于找到的空白元素,在它的8个方向上有数字元素就打开
			QVector<QPoint> directions2 = directions;
			directions2.push_back(QPoint(-1,-1));
			directions2.push_back(QPoint(1,1));
			directions2.push_back(QPoint(1,-1));
			directions2.push_back(QPoint(-1,1));
			for (int j=0; j<directions2.size(); j++)
			{
				QPoint ptNew2 = ptNew + directions2[j];
				if(!PointInGameArea(ptNew2))
				{
					continue;
				}
				Item* pItem2 = m_items[ptNew2.x()][ptNew2.y()];
				if (!pItem2->m_bIsMine && !pItem2->m_bOpen && !pItem2->m_bMarked && pItem2->m_nNumber > 0)
				{
					pItem2->m_bOpen = true;
				}
			}
			//递归查找上下左右4个方向的空白元素
			OpenEmptyItem(ptNew);
		}
	}
}

 

//是否找完
bool MainWindow::FindAll()
{
	bool bFindAll = true;
	//遍历二维数组 QVector<QVector<Item*>> m_items
	for (int i=0; i<m_items.size(); i++)
	{
		for (int j=0;j<m_items[i].size(); j++)
		{
			//只要存在一个雷没被标记,或存在一个非雷被没打开,都不算找完
			Item* pItem = m_items[i][j];
			if (pItem->m_bIsMine)
			{
				if (!pItem->m_bMarked)
				{
					bFindAll = false;
				}
			}
			else
			{
				if (!pItem->m_bOpen)
				{
					bFindAll = false;
				}
			}
		}
	}
	return bFindAll;
}


 

4,源码

 

 

源码已上传至群文件,可在学习群免费下载!

群号码:1149411109

群名称:Qt实战派学习群

 

 

 

 

posted on 2016-12-21 21:57  逆枫゛  阅读(441)  评论(0编辑  收藏  举报