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
#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 100class MainWindow : public QMainWindow{ Q_OBJECTpublic: 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
随机初始化雷点:
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; directions.push_back(QPoint(-1,0)); directions.push_back(QPoint(1,0)); directions.push_back(QPoint(0,-1)); directions.push_back(QPoint(0,1)); for (int i=0; i<directions.size(); i++) { QPoint ptNew = pt + directions[i]; 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,源码
百度云:https://pan.baidu.com/s/1I3y4gADYnasWfncj_kBYzA