查看完整版本: [-- 《Qt实战小项目》4,扫雷游戏 --]

QTCN开发网 -> Qt 作品展 -> 《Qt实战小项目》4,扫雷游戏 [打印本页] 登录 -> 注册 -> 回复主题 -> 发表主题

逆风微光 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
  1. #ifndef ITEM_H  
  2. #define ITEM_H  
  3.   
  4. #include <QPoint>  
  5.   
  6. class Item  
  7. {  
  8. public:  
  9.     Item();  
  10.     Item(QPoint pos);  
  11.   
  12.     QPoint m_pos;       //位置  
  13.   
  14.     bool m_bIsMine; <span style="white-space:pre;"> </span>//是否是雷  
  15.     bool m_bMarked; <span style="white-space:pre;"> </span>//是否已标记为雷  
  16.   
  17.     int m_nNumber;  <span style="white-space:pre;"> </span>//数字  
  18.     bool m_bOpen;       //是否已打开,且非雷  
  19. };  
  20.   
  21. #endif // ITEM_H  



MainWindow.h:


[cpp] view plain copy
  1. #ifndef MAINWINDOW_H  
  2. #define MAINWINDOW_H  
  3.   
  4. #include "item.h"  
  5. #include <QMainWindow>  
  6.   
  7. namespace Ui {  
  8. class MainWindow;  
  9. }  
  10.   
  11. #define RECT_WIDTH      30  
  12. #define RECT_HEIGHT     30  
  13.   
  14. #define START_X         100  
  15. #define START_Y         100  
  16.   
  17.   
  18. class MainWindow : public QMainWindow  
  19. {  
  20.     Q_OBJECT  
  21.   
  22. public:  
  23.     explicit MainWindow(QWidget *parent = 0);  
  24.     ~MainWindow();  
  25.   
  26.     void InitItems();  
  27.     void ReleaseItems();  
  28.   
  29.     void NewGame();                      
  30.     void GameSuccess();  
  31.     void GameFail();  
  32.   
  33.     void OpenEmptyItem(QPoint pt);          //点击空白元素(相邻雷数为0)时,递归查找相邻的空白元素,以及空白元素附近的数字元素(数字是雷数)  
  34.     bool FindAll();  
  35.     bool PointInGameArea(QPoint pt);        //判断坐标是否超过游戏区域  
  36. protected:  
  37.     void paintEvent(QPaintEvent *);  
  38.     void mousePressEvent(QMouseEvent *);  
  39.   
  40. private slots:  
  41.     void OnMenu_NewGame();  
  42.     void OnMenu_Settings();  
  43.     void OnMenu_Level1();  
  44.     void OnMenu_Level2();  
  45.     void OnMenu_Level3();  
  46. private:  
  47.     void DrawChessboard();  
  48.     void DrawItems();  
  49.     void DrawItem(QPainter& painter,Item* pItem);  
  50. private:  
  51.     Ui::MainWindow *ui;  
  52.     QPixmap m_FlagImage;                //小红旗图片  
  53.     QPixmap m_BombImage;<span style="white-space:pre;">             </span>//爆炸图片  
  54.   
  55.     int m_nRows;                    //行数  
  56.     int m_nColumes;                 //列数  
  57.     int m_nMineCount;               //雷数  
  58.     QVector<QPoint> m_Mines;          <span style="white-space:pre;"> </span>//雷点  
  59.     QVector<QVector<Item*>> m_items;    <span style="white-space:pre;"> </span>//所有元素  
  60.     bool m_bGameFail;               //是否是游戏失败,失败了需要显示雷  
  61. };  
  62.   
  63. #endif // MAINWINDOW_H  


随机初始化雷点:



[cpp] view plain copy
  1. void MainWindow::InitItems()  
  2. {  
  3.     //随机初始化雷  
  4.     m_Mines.clear();  
  5.     for(int i = 0; i<m_nMineCount; i++)  
  6.     {  
  7.         qsrand(QTime::currentTime().msec());  
  8.         int x = qrand()%m_nColumes;  
  9.         int y = qrand()%m_nRows;  
  10.         while(m_Mines.contains(QPoint(x,y)))  
  11.         {  
  12.             x = qrand()%m_nColumes;  
  13.             y = qrand()%m_nRows;  
  14.         }  
  15.         m_Mines.append(QPoint(x,y));  
  16.     }  
  17.     //建立2维数组保存所有元素位置,方便索引  
  18.     for(int i=0; i<m_nColumes; i++)  
  19.     {  
  20.         QVector<Item*> rowItems;  
  21.         for(int j=0; j<m_nRows; j++)  
  22.         {  
  23.             QPoint pos = QPoint(i,j);  
  24.             Item* pItem = new Item(pos);  
  25.             if(m_Mines.contains(pos))   //该位置是雷  
  26.             {  
  27.                 pItem->m_bIsMine = true;  
  28.             }  
  29.             rowItems.append(pItem);      
  30.         }  
  31.         m_items.append(rowItems);  
  32.     }  
  33.     //计算雷附近格子的数字  
  34.     for(int i=0; i<m_nColumes; i++)  
  35.     {  
  36.         for(int j=0; j<m_nRows; j++)  
  37.         {  
  38.             if (m_items[j]->m_bIsMine)  
  39.             {  
  40.                 continue;  
  41.             }  
  42.             int nCountMines = 0;  
  43.             //求每个点附近的8个点的是雷的总数  
  44.             for (int m=-1;m<=1;m++)  
  45.             {  
  46.                 for (int n=-1; n<=1;n++)  
  47.                 {  
  48.                     if (m==0 && n==0)  
  49.                     {  
  50.                         continue;  
  51.                     }  
  52.                     QPoint ptNew = QPoint(i+m,j+n);  
  53.                     if (!PointInGameArea(ptNew))  
  54.                     {  
  55.                         continue;  
  56.                     }  
  57.   
  58.                     if (m_items[i+m][j+n]->m_bIsMine)  
  59.                     {  
  60.                         nCountMines++;  
  61.                     }  
  62.                 }  
  63.             }  
  64.             m_items[j]->m_nNumber = nCountMines;  
  65.         }  
  66.     }  
  67. }  

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



[cpp] view plain copy
  1. void MainWindow::mousePressEvent(QMouseEvent * e)  
  2. {  
  3.     //得到鼠标处的格子坐标  
  4.     QPoint pt;  
  5.     pt.setX( (e->pos().x() - START_X ) / RECT_WIDTH);  
  6.     pt.setY( (e->pos().y() - START_X ) / RECT_HEIGHT);  
  7.     //是否点在游戏区域内  
  8.     if (!PointInGameArea(pt))  
  9.     {  
  10.         return;  
  11.     }  
  12.     //获取所点击矩形元素  
  13.     Item* pItem = m_items[pt.x()][pt.y()];  
  14.     //左键打开元素,右键插旗帜标记  
  15.     if(e->button()==Qt::LeftButton)  
  16.     {  
  17.         //不是已标记的或已打开的空白点,也就是未处理的  
  18.         if(!pItem->m_bMarked && !pItem->m_bOpen)  
  19.         {  
  20.             //如果是雷,就GAME OVER  
  21.             if (pItem->m_bIsMine)  
  22.             {  
  23.                 //QMessageBox::information(NULL,  "GAME OVER","FAIL!", QMessageBox::Yes , QMessageBox::Yes);  
  24.                 GameFail();  
  25.                 return;  
  26.             }  
  27.             else  
  28.             {  
  29.                 //打开  
  30.                 pItem->m_bOpen = true;  
  31.                 if (pItem->m_nNumber == 0)  
  32.                 {  
  33.                     //如果数字是0,也就是不含任何相邻雷的元素,那么递归打开所有的相邻数字是0的元素  
  34.                     //也就是点到一个空白处,一下打开一大片的效果  
  35.                     OpenEmptyItem(pt);  
  36.                 }  
  37.                 //如果已找到所有雷  
  38.                 if (FindAll())  
  39.                 {  
  40.                     QMessageBox::information(NULL,  "GAME OVER","SUCCESS!", QMessageBox::Yes , QMessageBox::Yes);  
  41.                     //GameSuccess();  
  42.                     return;  
  43.                 }  
  44.             }  
  45.         }  
  46.     }  
  47.     else if(e->button()==Qt::RightButton)  
  48.     {  
  49.         //已标记过的,取消标记  
  50.         if (pItem->m_bMarked)  
  51.         {  
  52.             pItem->m_bMarked = false;  
  53.         }  
  54.         else if (!pItem->m_bOpen)  
  55.         {  
  56.             //没标记也没打开,就是未处理的,就插旗帜标记上  
  57.             pItem->m_bMarked = true;  
  58.             if (FindAll())  
  59.             {  
  60.                 QMessageBox::information(NULL,  "GAME OVER","SUCCESS!", QMessageBox::Yes , QMessageBox::Yes);  
  61.                 //GameSuccess();  
  62.                 return;  
  63.             }  
  64.         }  
  65.     }  
  66. }  

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



[cpp] view plain copy
  1. //运气好时点到一个空白元素,可能打开挨着的一大片  
  2. void MainWindow::OpenEmptyItem(QPoint pt)  
  3. {  
  4.     //对于空白元素,有上下左右4个方向挨着的空白元素,就打开并继续查找空白元素  
  5.     QVector<QPoint> directions;  
  6.     directions.push_back(QPoint(-1,0));  
  7.     directions.push_back(QPoint(1,0));  
  8.     directions.push_back(QPoint(0,-1));  
  9.     directions.push_back(QPoint(0,1));  
  10.     for (int i=0; i<directions.size(); i++)  
  11.     {  
  12.         QPoint ptNew = pt + directions;  
  13.         if (!PointInGameArea(ptNew))  
  14.         {  
  15.             continue;  
  16.         }  
  17.         Item* pItem = m_items[ptNew.x()][ptNew.y()];  
  18.         if (!pItem->m_bIsMine && !pItem->m_bOpen && !pItem->m_bMarked && pItem->m_nNumber == 0)  
  19.         {  
  20.             pItem->m_bOpen = true;  
  21.   
  22.             //对于找到的空白元素,在它的8个方向上有数字元素就打开  
  23.             QVector<QPoint> directions2 = directions;  
  24.             directions2.push_back(QPoint(-1,-1));  
  25.             directions2.push_back(QPoint(1,1));  
  26.             directions2.push_back(QPoint(1,-1));  
  27.             directions2.push_back(QPoint(-1,1));  
  28.             for (int j=0; j<directions2.size(); j++)  
  29.             {  
  30.                 QPoint ptNew2 = ptNew + directions2[j];  
  31.                 if(!PointInGameArea(ptNew2))  
  32.                 {  
  33.                     continue;  
  34.                 }  
  35.                 Item* pItem2 = m_items[ptNew2.x()][ptNew2.y()];  
  36.                 if (!pItem2->m_bIsMine && !pItem2->m_bOpen && !pItem2->m_bMarked && pItem2->m_nNumber > 0)  
  37.                 {  
  38.                     pItem2->m_bOpen = true;  
  39.                 }  
  40.             }  
  41.             //递归查找上下左右4个方向的空白元素  
  42.             OpenEmptyItem(ptNew);  
  43.         }  
  44.     }  
  45. }  



[cpp] view plain copy
  1. //是否找完  
  2. bool MainWindow::FindAll()  
  3. {  
  4.     bool bFindAll = true;  
  5.     //遍历二维数组 QVector<QVector<Item*>> m_items  
  6.     for (int i=0; i<m_items.size(); i++)  
  7.     {  
  8.         for (int j=0;j<m_items.size(); j++)  
  9.         {  
  10.             //只要存在一个雷没被标记,或存在一个非雷被没打开,都不算找完  
  11.             Item* pItem = m_items[j];  
  12.             if (pItem->m_bIsMine)  
  13.             {  
  14.                 if (!pItem->m_bMarked)  
  15.                 {  
  16.                     bFindAll = false;  
  17.                 }  
  18.             }  
  19.             else  
  20.             {  
  21.                 if (!pItem->m_bOpen)  
  22.                 {  
  23.                     bFindAll = false;  
  24.                 }  
  25.             }  
  26.         }  
  27.     }  
  28.     return bFindAll;  
  29. }  

4,源码


源码可在学习群免费下载!
群号码:1149411109
群名称:Qt实战派学习群








liuchangyin 2018-05-21 09:40

逆风微光 2018-05-25 12:21
liuchangyin:[表情]  (2018-05-21 09:40) 

谢谢老哥!看帖回复好习惯!


查看完整版本: [-- 《Qt实战小项目》4,扫雷游戏 --] [-- top --]



Powered by phpwind v8.7 Code ©2003-2011 phpwind
Gzip disabled