Qt和C++实现可视化扫雷游戏
目录
一、游戏简介
二、整体框架
1.Qt设计师界面类:GameWindow
2.head.h和function.cpp
3.源文件main.cpp
4.Qt控件类:CustomButton
三、具体实现
1.界面设计
2.雷的布置
3.界面生成
4.实现扫雷
5.功能细化
(1)界面整改
(2)尺寸更改
(3)其他功能
四、效果展示
五、完整代码
1.CustomButton类
2.GameWindow类
3.head.h、function.cpp、main.cpp
4.gamewindow.ui
一、游戏简介
扫雷游戏大家应该都玩过,就是给你一个棋盘,让你点击棋盘上的格子,如果格子下有雷则游戏失败,否则显示一个数字,表示周围八格内含雷的数量,然后根据数字来推断哪些格子是安全的。当所有无雷的方格都被排查了,游戏胜利。
接下来,让我们用Qt和c++实现一个仿制版扫雷游戏。
二、整体框架
1.Qt设计师界面类:GameWindow
为了实现扫雷游戏的可视化界面,我们仿照原版游戏,在项目中添加Qt设计师界面类GameWindow(继承自QWidget)作为游戏的主界面。因为原版也只有一个界面,所以只需添加一个Qt界面类,便足以实现游戏全部的功能。
该类带来了gamewindow.h、gamewindow.cpp、gamewindow.ui三个文件,分别用于类的定义、类成员函数的具体实现、界面的图形化设计。
2.head.h和function.cpp
为了实现完整的游戏项目,我们还需要在类GameWindow的外部定义一些函数。因此添加源文件function.cpp用于包含这些函数的实现方法,并添加头文件head.h来进行库文件的包含和函数的声明,以便于在多个源文件中共享这些函数。
此外,项目中还可能会用到一些全局变量,为了实现源文件之间的共享,同样需要在head.h中进行这些变量的外部声明。
3.源文件main.cpp
编写main函数作为程序的入口。此外,我把全局变量的定义也放在了这个文件里。
4.Qt控件类:CustomButton
为什么需要定义这个类,后面会细讲。
三、具体实现
1.界面设计
由于图形化程序主要依靠用户的鼠标点击进行交互,因此在搭建项目时,我们先从游戏界面的设计入手,在GameWindow窗口中添加一些需要的控件。
原版扫雷游戏有一块大棋盘,左键点击棋盘中的方格即可进行排雷,右键点击则是标旗。考虑到方格具有点击效果,我们在Qt中使用QPushButton按钮来实现方格的功能,棋盘里有多少方格就创建多少个按钮,如16*16的棋盘则需要16*16个按钮。
而为了方便地给这么多按钮统一设计样式,这时候就需要定义一个CustomButton类了。我们让它继承自QPushButton,便能继承普通按钮的所有功能;而通过在类的构造函数中调用setStyleSheet方法,就可将所有这样的按钮设置为同一样式。
同时,还可为每个方格添加一个int类型的状态值condition,方便在后续的游戏过程中对方块的行为进行管理。
custombutton.h
#ifndef CUSTOMBUTTON_H #define CUSTOMBUTTON_H #include "head.h" #include class CustomButton : public QPushButton { public: CustomButton(QWidget *parent = nullptr); void mousePressEvent(QMouseEvent *event); int condition = 0; //表示方格的状态 0.未排查未标记 1.已排查 2.已标记 }; #endif // CUSTOMBUTTON_H
custombutton.cpp
这里除了设置按钮的一般样式,还设置了左键点击(排雷)和右键点击(标旗)后的样式改变效果。
需要注意的是,由于QPushButton类中好像没有专门用于右键点击事件的槽函数(至少我没找到),因此只能重写QPushButton的mousePressEvent事件,当判断为右键点击时就执行自定义的功能(标旗),左键点击则调用父类的方法。
样式表里需要用到旗子图标作为border-image,可以自己在网上找免费素材(自己画也行),然后添加到资源文件resourse.qrc中。
#include "custombutton.h" CustomButton::CustomButton(QWidget *parent): QPushButton(parent) { setStyleSheet("QPushButton{" "background-color: #8FE1F3;" "border-style: solid;" "border-width: 2px;" "border-color: #71B2D2;" "}"); } void CustomButton::mousePressEvent(QMouseEvent *event) { if (event->button() == Qt::RightButton) { // 如果是右键点击,则修改按钮的样式 if (condition == 1) return; //如果按钮已被排查,则不可标旗 if (condition == 0) //按钮未排查未标记 { setStyleSheet("QPushButton{" "background-color: #8FE1F3;" "border-style: solid;" "border-width: 2px;" "border-color: #71B2D2;" "border-image: url(:/icons/icons/note.png);" "}"); condition = 2; //改为标记态 } else //按钮已被标记 { setStyleSheet("QPushButton{" "background-color: #8FE1F3;" "border-style: solid;" "border-width: 2px;" "border-color: #71B2D2;" "}"); condition = 0; //改回未排查未标记状态 } } else { // 如果是其它的鼠标按键事件,则调用父类的鼠标按下事件处理方法 QPushButton::mousePressEvent(event); } }
2.雷的布置
由于游戏里的雷分布在二维的棋盘上,因此我们定义一个char类型的二维数组(mine)来存储雷的信息,让棋盘的长和宽与数组的尺寸相对应。这样,棋盘上的每一个方格都对应mine中的一个元素,如果有雷,则存放'1',无雷则存放‘0’。
因为游戏中随时可能改变棋盘的长宽,为了方便改变数组的大小,我们使用二维动态数组,将数组的行(ROW)和列(COL)定义为全局变量,这样在程序运行过程中也可进行大小的调整。
有了mine数组后,理论上棋盘的信息就完全确定下来了。但实际游戏里,棋盘上显示的信息却不能直接靠mine数组来生成。
回顾一下原版游戏:鼠标没按下时,方格是空白的,表示尚未进行排雷;左键点击后,若方格下不是雷,则显示一个数字,表示该方格周围八格的含雷数量;若是雷,则游戏失败,同时棋盘上显示出全部雷和数字的信息。
因此,我们可以再定义一个char类型的二维数组(showl)(ps:本来用的show作为数组名,表示该数组存放棋盘的实际显示信息,但似乎会与Qt某个类的自带函数重名而报错)。用‘*’将数组初始化,表明该方格还未被排查;如果某个元素对应的方格被右键点击,若不是雷,则在mine中查询该方块周围八格的含雷数,并在showl中将元素值改为数字。
在main.cpp中定义的全局变量
char **mine; char **showl; int ROW = 9; int COL = 9; //棋盘实际的行和列 int ROWS = ROW + 2; int COLS = COL + 2; //用于存放棋盘信息的数组的行和列 //因为扫雷时要对周围八格进行检索,为了使边缘的方格行为与内部一致,在数组棋盘的外围加一圈空白 int EASY_COUNT = 10;//雷的数量 int NOT_MINE = ROW * COL - EASY_COUNT;//不含雷的方格数量 int not_mine = 0;// 游戏中已排查的无雷方格数量
function.cpp中棋盘的初始化和雷的布置
//棋盘的初始化,set表示用于填充的值 void InitBoard(char**& board, int rows, int cols, char set) { board = new char*[rows]; for (int i=0;isetupUi(this); createBoard(); ui->label_2->hide(); ui->label_3->hide(); ui->label_4->hide(); ui->spinBox->hide(); ui->spinBox_2->hide(); ui->spinBox_3->hide(); ui->pushButton_7->hide(); } //游戏中按钮棋盘的生成 void GameWindow::createBoard() { for (int i=0;ishow(); buttons.append(button); //将创建的按钮指针添加到buttons数组中 QSignalMapper *mapper=new QSignalMapper(this); mapper->setMapping(button,i*COL+j); //将按钮对应的下标作为mapper传递的参数 connect(button, SIGNAL(clicked()), mapper, SLOT(map())); connect(mapper, SIGNAL(mapped(int)), this, SLOT(onButtonClicked(int))); } }
4.实现扫雷
注意:在原版游戏里,有时点击一个无雷的方格,只会显示出该格下面的数字,而有时则会显示出周围一大片数字。这是因为:如果点击的那个方格周围八格里至少有一个雷,那可直接将统计出的数字放进方格,让玩家来推断是周围哪几格中含雷;如果那个方格周围八格没有雷,要是仍按照刚才的做法,把数字0放进方格就结束,玩家自然知道周围八格也没有雷,但还得用鼠标一个个点击排查,徒增无用功。
所以,正确的处理方法是:如果被点击的某个方格周围八格没有雷,则程序应代替玩家对周围八格继续进行排查,直到排查到的某个方格周围八格有雷为止。这就需要用到函数递归的思想。
同时,为了美观,经过排查后的方格也应改变样式。是雷的方格还应换上地雷图标,同样自己去找,然后添加到项目的资源文件中。
扫雷过程中会涉及到游戏状态的改变(胜利或失败),失败即踩雷,调用一个自己写的reveal_All函数,将棋盘上所有信息显示出来;胜利则要通过已排查的方格数量来判断,这里用之前定义好的全局变量not_mine来更新排查过的方格数目,NOT_MINE保存所有无雷方格的数量,当not_mine与NOT_MINE相等时,判断为游戏胜利。
GameWindow.cpp中按钮点击的槽函数
void GameWindow::onButtonClicked(int index) //index为按钮在buttons中的下标 { if (buttons[index]->condition == 1 || buttons[index]->condition == 2)return; //如果按钮处于已排查或已标记状态,则点击不起作用 int x=0,y=0; x=index/COL; y=index-x*COL+1; x++; //将按钮在buttons中的一维下标转化为棋盘中的二维下标 if (mine[x][y] == '1') //踩到雷,则本局游戏结束,并调用reveal_All()显示棋盘全部信息 { not_mine=0; this->ui->label_result->setText("对不起,你踩雷了。"); this->ui->label_face->setStyleSheet("QLabel{" "border-image: url(:/icons/icons/sad.png);" "}"); //这里会用到表情图标,之后会提到 reveal_All(mine, showl, this, ROW, COL); return; } else //没踩雷,则调用Sweep函数进一步扫雷 { Sweep(mine, showl, this, ROW, COL, x, y); if (not_mine == NOT_MINE) //判断为游戏胜利 { not_mine=0; //将not_mine清空,方便进行下一局游戏 this->ui->label_result->setText("恭喜你,扫雷成功!"); this->ui->label_face->setStyleSheet("QLabel{" "border-image: url(:/icons/icons/cool.png);" "}"); return; } } }
function.cpp
//递归实现扫雷 void Sweep(char **mine, char **show, GameWindow* w,int row, int col, int x, int y) { //因为要在GameWindow窗口上更改按钮状态,而Sweep不是其成员函数,所以需要传递窗口指针过去 int index=y-1+(x-1)*COL; //将二维下标转化为一维下标 if (show[x][y]!='*')return; int count = mine[x - 1][y - 1] + mine[x - 1][y] + mine[x - 1][y + 1] + mine[x][y - 1] + mine[x][y + 1] + mine[x + 1][y - 1] + mine[x + 1][y] + mine[x + 1][y + 1] - '0' * 8; show[x][y]=count+'0'; w->buttons[index]->setText(QString::number(count)); w->buttons[index]->setStyleSheet("QPushButton{" "font-size: 20px;" "background-color: #89CDEF;" "border-style: solid;" "border-width: 1px;" "border-color: #71B2D2;" "}"); w->buttons[index]->condition = 1; //将按钮状态修改为已排查 not_mine++; if (count == 0) //周围八格没有雷,则递归地对八格进行扫雷 { Sweep(mine, show, w, row, col, x - 1, y - 1); Sweep(mine, show, w, row, col, x - 1, y); Sweep(mine, show, w, row, col, x - 1, y + 1); Sweep(mine, show, w, row, col, x, y - 1); Sweep(mine, show, w, row, col, x, y + 1); Sweep(mine, show, w, row, col, x + 1, y - 1); Sweep(mine, show, w, row, col, x + 1, y); Sweep(mine, show, w, row, col, x + 1, y + 1); } } //踩雷时,棋盘显示全部信息 void reveal_All(char **mine, char **show, GameWindow* w,int row, int col) { for (int i=1;icondition = 1; w->buttons[index]->setStyleSheet("QPushButton{" "background-color: #89CDEF;" "border-image: url(:/icons/icons/mine.png);" "border-style: solid;" "border-width: 1px;" "border-color: #71B2D2;" "}"); } else Sweep(mine, show, w, row, col, i, j); } }
5.功能细化
(1)界面整改
再回去看一眼原版游戏,发现上面有五个选项可以重新选择棋盘的尺寸;此外,还有一个表情图标,用于标识游戏的状态(胜利、失败、进行中)。
此外,玩家可能也需要在游戏中重置棋盘,或者选择退出游戏。
因此,我们在这里对游戏的UI进行一个整体的美化,这可以方便地在Qt的设计界面里通过改变控件的样式表来实现。同时增添改变棋盘尺寸的按钮、重新开始的按钮、退出游戏的按钮(均使用QPushButton),再用QLabel来放置表情图标。
这里我用的图标仍然来源于网上的免费素材。
(可能不算太美观,但我尽力了)
(2)尺寸更改
以“中级”(11*11的棋盘,15个雷)为示例,转到“中级”按钮的click槽函数,在该函数中实现按钮的功能。
由于旧棋盘的尺寸可能与新棋盘不一样,因此mine和showl数组的尺寸也需要更改。这里先清除掉旧的按钮棋盘(使用自定义的成员函数deleteBoard),然后将两个数组的空间全部释放,根据新的尺寸重新初始化,再重新生成新的按钮棋盘。之前定义好的ROW、COL、ROWS、COLS、EASY_COUNT、NOT_MINE这些全局变量在这里用于传递棋盘的尺寸和雷数量信息。
gamewindow.cpp中deleteBoard函数
遍历buttons,将每个按钮的空间释放,然后将buttons数组的空间释放。
void GameWindow::deleteBoard(int row_old, int col_old) { for (int i=0;ilabel_result->setText(""); ui->label_face->setStyleSheet("border-image: url(:/icons/icons/smile.png);"); createBoard(); }
在实现“自定义”按钮时,我原本希望点击按钮能显示一个可输入行、列、雷数据的附属窗口,但尝试实现时发现很麻烦,所以索性直接在GameWindow主窗口上添加了三个spinBox控件和一个按钮,将它们放在了“自定义”的下方。这些新控件在构造时调用hide()方式进行隐藏,点击“自定义”后使用show()显示,再点一下又进行隐藏。为了指示隐藏和显示的状态,在GameWindow类中添加bool类型成员is_custom(是否要进行自定义)
gamewindow.cpp中“自定义”按钮的槽函数
void GameWindow::on_pushButton_5_clicked() { if (is_custom) { ui->label_2->hide(); ui->label_3->hide(); ui->label_4->hide(); ui->spinBox->hide(); ui->spinBox_2->hide(); ui->spinBox_3->hide(); ui->pushButton_7->hide(); is_custom = false; return; } ui->label_2->show(); ui->label_3->show(); ui->label_4->show(); ui->spinBox->show(); ui->spinBox_2->show(); ui->spinBox_3->show(); ui->pushButton_7->show(); is_custom = true; }
自定义下“确定”按钮的槽函数
这里要保证棋盘格子的数量多于雷的数量,否则无法触发棋盘的重新生成。
void GameWindow::on_pushButton_7_clicked() { ui->label_5->setText(""); int Column = ui->spinBox->value(); //列数据,即棋盘的长 int Row = ui->spinBox_2->value(); //行数据,即棋盘的宽 int Mine = ui->spinBox_3->value(); //雷的数量 if ( Mine >= Column * Row) { ui->label_5->setText("雷的数量应该少于格子的数量!"); return; } is_custom = false; //后面的操作与尺寸更改按钮的基本一样 deleteBoard(ROW, COL); for (int i = 0; i label_result->setText(""); ui->label_face->setStyleSheet("border-image: url(:/icons/icons/smile.png);"); InitBoard(mine, ROWS, COLS, '0'); InitBoard(showl, ROWS, COLS, '*'); SetMine(mine, ROW, COL); createBoard(); ui->label_2->hide(); ui->label_3->hide(); ui->label_4->hide(); ui->spinBox->hide(); ui->spinBox_2->hide(); ui->spinBox_3->hide(); ui->pushButton_7->hide(); }
(3)其他功能
gamewindow.cpp中“重新开始”按钮的槽函数
和尺寸更改按钮的槽函数差不多,只不过不用修改ROW、COL等全局变量,重新生成数组和棋盘即可。
void GameWindow::on_pushButton_restart_clicked() { InitBoard(mine, ROWS, COLS, '0'); InitBoard(showl, ROWS, COLS, '*'); //由于棋盘的尺寸不变,无需释放数组的空间,调用Init进行初始化即可 SetMine(mine, ROW, COL); not_mine = 0; ui->label_result->setText(""); ui->label_face->setStyleSheet("border-image: url(:/icons/icons/smile.png);"); deleteBoard(ROW, COL); createBoard(); }
gamewindow.cpp中“退出游戏”按钮的槽函数
void GameWindow::on_pushButton_quit_clicked() { for (int i = 0; i另外,为了在Qt中能打出中文,还需要在项目文件.pro中加上这样一段代码:
minesweeper.pro
msvc { QMAKE_CFLAGS += /utf-8 QMAKE_CXXFLAGS += /utf-8 }四、效果展示
五、完整代码
1.CustomButton类
custombutton.h
#ifndef CUSTOMBUTTON_H #define CUSTOMBUTTON_H #include "head.h" #include class CustomButton : public QPushButton { public: CustomButton(QWidget *parent = nullptr); void mousePressEvent(QMouseEvent *event); int condition = 0; //表示方格的状态 0.未排查未标记 1.已排查 2.已标记 }; #endif // CUSTOMBUTTON_Hcustombutton.cpp
#include "custombutton.h" CustomButton::CustomButton(QWidget *parent): QPushButton(parent) { setStyleSheet("QPushButton{" "background-color: #8FE1F3;" "border-style: solid;" "border-width: 2px;" "border-color: #71B2D2;" "}"); } void CustomButton::mousePressEvent(QMouseEvent *event) { if (event->button() == Qt::RightButton) { // 如果是右键点击,则修改按钮的样式 if (condition == 1) return; if (condition == 0) { setStyleSheet("QPushButton{" "background-color: #8FE1F3;" "border-style: solid;" "border-width: 2px;" "border-color: #71B2D2;" "border-image: url(:/icons/icons/note.png);" "}"); condition = 2; } else { setStyleSheet("QPushButton{" "background-color: #8FE1F3;" "border-style: solid;" "border-width: 2px;" "border-color: #71B2D2;" "}"); condition = 0; } } else { // 如果是其它的鼠标按键事件,则调用父类的鼠标按下事件处理方法 QPushButton::mousePressEvent(event); } }2.GameWindow类
gamewindow.h
#ifndef GAMEWINDOW_H #define GAMEWINDOW_H #pragma execution_character_set("utf-8") #include #include "head.h" #include "custombutton.h" namespace Ui { class GameWindow; } class GameWindow : public QWidget { Q_OBJECT public: explicit GameWindow(QWidget *parent = nullptr); ~GameWindow(); QVector buttons; bool is_custom = false; void createBoard(); void GameWindow::deleteBoard(int row_old, int col_old); public slots: void onButtonClicked(int index); private slots: void on_pushButton_restart_clicked(); void on_pushButton_quit_clicked(); void on_pushButton_2_clicked(); void on_pushButton_clicked(); void on_pushButton_6_clicked(); void on_pushButton_3_clicked(); void on_pushButton_4_clicked(); void on_pushButton_5_clicked(); void on_pushButton_7_clicked(); private: Ui::GameWindow *ui; }; #endif // GAMEWINDOW_Hgamewindow.cpp
#include "gamewindow.h" #include "ui_GameWindow.h" #include GameWindow::GameWindow(QWidget *parent) : QWidget(parent), ui(new Ui::GameWindow) { ui->setupUi(this); createBoard(); ui->label_2->hide(); ui->label_3->hide(); ui->label_4->hide(); ui->spinBox->hide(); ui->spinBox_2->hide(); ui->spinBox_3->hide(); ui->pushButton_7->hide(); } void GameWindow::createBoard() { for (int i=0;ishow(); buttons.append(button); QSignalMapper *mapper=new QSignalMapper(this); mapper->setMapping(button,i*COL+j); connect(button, SIGNAL(clicked()), mapper, SLOT(map())); connect(mapper, SIGNAL(mapped(int)), this, SLOT(onButtonClicked(int))); } } void GameWindow::deleteBoard(int row_old, int col_old) { for (int i=0;icondition == 1 || buttons[index]->condition == 2)return; int x=0,y=0; x=index/COL; y=index-x*COL+1; x++; if (mine[x][y] == '1') { not_mine=0; this->ui->label_result->setText("对不起,你踩雷了。"); this->ui->label_face->setStyleSheet("QLabel{" "border-image: url(:/icons/icons/sad.png);" "}"); reveal_All(mine, showl, this, ROW, COL); return; } else { Sweep(mine, showl, this, ROW, COL, x, y); if (not_mine == NOT_MINE) { not_mine=0; this->ui->label_result->setText("恭喜你,扫雷成功!"); this->ui->label_face->setStyleSheet("QLabel{" "border-image: url(:/icons/icons/cool.png);" "}"); return; } } } GameWindow::~GameWindow() { delete ui; } void GameWindow::on_pushButton_restart_clicked() //重新开始 { InitBoard(mine, ROWS, COLS, '0'); InitBoard(showl, ROWS, COLS, '*'); SetMine(mine, ROW, COL); not_mine = 0; ui->label_result->setText(""); ui->label_face->setStyleSheet("border-image: url(:/icons/icons/smile.png);"); deleteBoard(ROW, COL); createBoard(); } void GameWindow::on_pushButton_quit_clicked() //退出游戏 { for (int i = 0; i label_result->setText(""); ui->label_face->setStyleSheet("border-image: url(:/icons/icons/smile.png);"); createBoard(); } void GameWindow::on_pushButton_clicked() //“基础”按钮 { deleteBoard(ROW, COL); for (int i = 0; i label_result->setText(""); ui->label_face->setStyleSheet("border-image: url(:/icons/icons/smile.png);"); createBoard(); } void GameWindow::on_pushButton_6_clicked() //“极简”按钮 { deleteBoard(ROW, COL); for (int i = 0; i label_result->setText(""); ui->label_face->setStyleSheet("border-image: url(:/icons/icons/smile.png);"); createBoard(); } void GameWindow::on_pushButton_3_clicked() //“专家”按钮 { deleteBoard(ROW, COL); for (int i = 0; i label_result->setText(""); ui->label_face->setStyleSheet("border-image: url(:/icons/icons/smile.png);"); createBoard(); } void GameWindow::on_pushButton_4_clicked() //“满屏”按钮 { deleteBoard(ROW, COL); for (int i = 0; i label_result->setText(""); ui->label_face->setStyleSheet("border-image: url(:/icons/icons/smile.png);"); createBoard(); } void GameWindow::on_pushButton_5_clicked() //“自定义”按钮 { if (is_custom) { ui->label_2->hide(); ui->label_3->hide(); ui->label_4->hide(); ui->spinBox->hide(); ui->spinBox_2->hide(); ui->spinBox_3->hide(); ui->pushButton_7->hide(); is_custom = false; return; } ui->label_2->show(); ui->label_3->show(); ui->label_4->show(); ui->spinBox->show(); ui->spinBox_2->show(); ui->spinBox_3->show(); ui->pushButton_7->show(); is_custom = true; } void GameWindow::on_pushButton_7_clicked() //自定义下的“确定”按钮 { ui->label_5->setText(""); int Column = ui->spinBox->value(); int Row = ui->spinBox_2->value(); int Mine = ui->spinBox_3->value(); if ( Mine >= Column * Row) { ui->label_5->setText("雷的数量应该少于格子的数量!"); return; } is_custom = false; deleteBoard(ROW, COL); for (int i = 0; i label_result->setText(""); ui->label_face->setStyleSheet("border-image: url(:/icons/icons/smile.png);"); InitBoard(mine, ROWS, COLS, '0'); InitBoard(showl, ROWS, COLS, '*'); SetMine(mine, ROW, COL); createBoard(); ui->label_2->hide(); ui->label_3->hide(); ui->label_4->hide(); ui->spinBox->hide(); ui->spinBox_2->hide(); ui->spinBox_3->hide(); ui->pushButton_7->hide(); }3.head.h、function.cpp、main.cpp
head.h
#define _CRT_SECURE_NO_WARNINGS 1 extern int ROW; extern int COL; extern int ROWS; extern int COLS; extern int EASY_COUNT; extern int NOT_MINE; #include #include #include #include #include extern int not_mine; extern char **mine; extern char **showl; class GameWindow; void InitBoard(char **&board, int rows, int cols, char set); void SetMine(char **board, int row, int col); void Sweep(char **mine, char **showl, GameWindow* w, int row, int col, int x, int y); void reveal_All(char **mine, char **show, GameWindow* w,int row, int col);function.cpp
#include "head.h" #include "gamewindow.h" //棋盘的初始化,set表示用于填充的值 void InitBoard(char**& board, int rows, int cols, char set) { board = new char*[rows]; for (int i=0;ibuttons[index]->setText(QString::number(count)); w->buttons[index]->setStyleSheet("QPushButton{" "font-size: 20px;" "background-color: #89CDEF;" "border-style: solid;" "border-width: 1px;" "border-color: #71B2D2;" "}"); w->buttons[index]->condition = 1; not_mine++; qDebug()buttons[index]->setStyleSheet("QPushButton{" "background-color: #89CDEF;" "border-image: url(:/icons/icons/mine.png);" "border-style: solid;" "border-width: 1px;" "border-color: #71B2D2;" "}"); } else Sweep(mine, show, w, row, col, i, j); } }main.cpp
#include "gamewindow.h" #include #include "head.h" char **mine; char **showl; int ROW = 9; int COL = 9; //棋盘实际的行和列 int ROWS = ROW + 2; int COLS = COL + 2; //用于存放棋盘信息的数组的行和列 //因为扫雷时要对周围八格进行检索,为了使边缘的方格行为与内部的一致,在数组棋盘的外围加一圈空白 int EASY_COUNT = 10;//雷的数量 int NOT_MINE = ROW * COL - EASY_COUNT;//不含雷的方格数量 int not_mine = 0;// 已排查的无雷方格数量 int main(int argc, char *argv[]) { QApplication a(argc, argv); srand((unsigned int)time(NULL)); InitBoard(mine, ROWS, COLS, '0'); InitBoard(showl, ROWS, COLS, '*'); //棋盘的初始化 SetMine(mine, ROW, COL);//雷的布置 GameWindow *w=new GameWindow; w->show(); return a.exec(); }4.gamewindow.ui
可以使用Layout和Spacer将界面规划得更工整,但是我不太会用。