逆风微光 |
2018-05-20 13:11 |
《Qt实战小项目》4,扫雷游戏
这是我的CSDN博客专栏《Qt实战小项目》的系列文章第4篇。 专栏地址:https://blog.csdn.net/dpsying/category_9267660.html 目前专栏内有这些文章: QT项目一:俄罗斯方块游戏 QT项目二:五子棋游戏 QT项目三:中国象棋 QT项目四:扫雷游戏 QT项目五--计算器 QT项目六:简易客户信息管理系统 1,简介 QT开发的扫雷小游戏,这个相对比较简单,用了几个小时。
2,效果 [attachment=18984]
3,设计思路 背景:一个灰色大矩形 游戏区:默认是初级难度,9*9的矩形阵。可变成16*16,16*30。 每个小矩形元素类 Item.h:
[cpp] view plain copy
- #ifndef ITEM_H
- #define ITEM_H
-
- #include <QPoint>
-
- class Item
- {
- public:
- Item();
- Item(QPoint pos);
-
- QPoint m_pos; //位置
-
- bool m_bIsMine; <span style="white-space:pre;"> </span>//是否是雷
- bool m_bMarked; <span style="white-space:pre;"> </span>//是否已标记为雷
-
- int m_nNumber; <span style="white-space:pre;"> </span>//数字
- bool m_bOpen; //是否已打开,且非雷
- };
-
- #endif // ITEM_H
MainWindow.h:
[cpp] view plain copy
- #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;<span style="white-space:pre;"> </span>//爆炸图片
-
- int m_nRows; //行数
- int m_nColumes; //列数
- int m_nMineCount; //雷数
- QVector<QPoint> m_Mines; <span style="white-space:pre;"> </span>//雷点
- QVector<QVector<Item*>> m_items; <span style="white-space:pre;"> </span>//所有元素
- bool m_bGameFail; //是否是游戏失败,失败了需要显示雷
- };
-
- #endif // MAINWINDOW_H
随机初始化雷点:
[cpp] view plain copy
- 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[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[j]->m_nNumber = nCountMines;
- }
- }
- }
核心函数,鼠标点击处理:
[cpp] view plain copy
- 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函数,可能会打开空白一大片:
[cpp] view plain copy
- //运气好时点到一个空白元素,可能打开挨着的一大片
- 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;
- 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);
- }
- }
- }
[cpp] view plain copy
- //是否找完
- 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.size(); j++)
- {
- //只要存在一个雷没被标记,或存在一个非雷被没打开,都不算找完
- Item* pItem = m_items[j];
- if (pItem->m_bIsMine)
- {
- if (!pItem->m_bMarked)
- {
- bFindAll = false;
- }
- }
- else
- {
- if (!pItem->m_bOpen)
- {
- bFindAll = false;
- }
- }
- }
- }
- return bFindAll;
- }
4,源码 源码可在学习群免费下载! 群号码:1149411109 群名称:Qt实战派学习群
|
|