文章目录
一、题目内容
实现一个N*M的扫雷游戏
扫雷游戏规则:
把所有非地雷的格子揭开即胜利;踩到地雷格子就算失败。开局随机左击一个方格,方格即被打开并显示出方格中的数字;方格中数字则表示其周围的8个方格隐藏了几颗雷。右击是标记雷。双击:当双击位置周围已标记雷数等于该位置数字时操作有效,相当于对该数字周围未打开的方块均进行一次左键单击操作。地雷未标记完全时使用双击无效。若数字周围有标错的地雷,则游戏结束,标错的地雷上会显示一个“×”。当打开的方格为空时,自动打开周围八个方格。
二、需求分析
三、界面设计
1.UI资源
**备注:**扫雷图标从网站上下载
2.主界面
3.自定义难度设置
四、功能实现
1.雷区实现
雷区本质:一个N*M的二维数组
数组元素为一个个的小格子,格子有很多不同状态
写一个格子类来带表格子,不同的状态用不同的参数来表示
在QT中将格子看做是一个个图元,通过将图元添加到场景中,再利用UI设置从而将格子显示出来,鼠标点击的时候就调用函数来改变被点击格子的状态。每次操作之后调用UpdateMate()更新函数检查格子状态的改变并进行重绘。
figuremate.h
class FigureMate : public QObject,public QGraphicsPixmapItem //继承
{
Q_OBJECT//宏定义,可以使用信号和槽机制
public:
FigureMate();
void setRowAndCol(int x,int y){row=x;col=y;} //设置行和列
void setOpened(bool s){opened=s;updateMate();}//模拟点击,打开
void setMine(bool m){mine=m;}
void setNumOfMines(int n){numOfMines=n;}//设置地雷个数
void setFlag(bool m){flag=m;updateMate();}//标记
void setOpenMine(bool s){openMine=s;}//设置打开雷,即踩雷
bool isOpened(){return opened;}//判断是否打开过
bool isMine(){return mine;}//判断是不是雷
bool isFlag(){return flag;}//判断是否已被标记
int getNumOfMines(){return numOfMines;}//得到雷数
void simulateLeftClick();//模拟左键点击
void updateMate();//更新显示
protected:
void mousePressEvent(QGraphicsSceneMouseEvent *event)override;
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override;
signals:
void openOnMine(int r,int c);//挖到了雷
void openOnNone(int r,int c);//挖到没有雷的地方
void doubleClickSignal(int r,int c);//左右键双击有效
void checkSignal();//每次点击后,发送信号给myscene检查游戏是否结束
void flagChangedSignal();//标记改变信号,用来numScene改变剩余地雷数
private:
int row;//行
int col;//列
bool opened=false;//是否已经打开,默认设置为未打开
bool mine=false;//有地雷吗?默认设置为没有
bool flag=false;//是否被旗帜标记了,默认设置为没有被标记
bool openMine=false;//是否打开了雷
int numOfMines=0;//周围地雷个数
};
2.获取所点击位置周围地雷数
本质:遍历周围八个格子,计数有地雷的格子数。
思路:若我们将被点击的格子放在二维坐标系的原点,则周围八个格子相对于中心格子的坐标不变,始终是(-1,0)、(1,0)…
则周围八个格子的真实坐标=中心格子的真实坐标+周围八个格子相对与中心格子的坐标
int nx,ny,numOfMines;//周围的八个点 坐标
int dx[8]={-1,-1,-1,0,0,1,1,1};
int dy[8]={-1,0,1,-1,1,-1,0,1};
//设置周围砖块数
for(int i=0;i<width;++i){
for(int j=0;j<height;++j){
numOfMines = 0;
for(int k=0;k<8;++k){//遍历它周围的八个格子
nx=i+dx[k];
ny=j+dy[k];
if(0<=nx&&nx<width&&0<=ny&&ny<height){//判断是在第一象限,在范围内,防止四周的格子判断失误
if(this->mates[nx][ny]->isMine()){
//这八个格子中如果有雷
numOfMines++;
}
}
}
this->mates[i][j]->setNumOfMines(numOfMines);//设置雷数
}
}
3.自动排雷
效果描述:当打开的格子为空时,自动打开周围的八个格子
效果展示:
实现:递归实现。
当打开的格子不为空时,递归停止。
//无雷时,自动打开实现
void SceneClass::findNoMines(int row,int col){
mates[row][col]->setOpened(true);//模拟点击
if(mates[row][col]->getNumOfMines()!=0)
return;//有数字的方块就不展开了
int nx,ny;
int dx[8]={-1,-1,-1,0,0,1,1,1};
int dy[8]={-1,0,1,-1,1,-1,0,1};
for(int i=0;i<8;++i){
nx=row+dx[i];
ny=col+dy[i];
if(0<=nx&&nx<width&&0<=ny&&ny<height&&!mates[nx][ny]->isOpened()){
findNoMines(nx,ny);
}
}
}
4.胜负判断
实现方法:每次点击后调用函数判断游戏是否结束。
效果展示:
五、一些思考与拓展
winXP自带的扫雷游戏的功能实际上是比较简单的,可加的拓展功能:实现用户的登录注册、记录用户的最高成绩、联机排行榜、撤回一步等。
六、参考博文
https://blog.youkuaiyun.com/livingsu/article/details/104774193