从0开始的QT项目:扫雷
文章目录
前言
本文所示项目使用QT开发,已经学过很久了,突然想起来想要整理一下,就做了一个“扫雷”的小游戏。由于开发时间比较短(只用了一天不到),做的也就比较粗糙。另外打包时还出了点问题,打包出来的exe文件总是不显示图片,就导致exe启动的游戏无法进行(在bin文件夹下是有的,但单独拿出来就没了),尝试了网上的许多方法,包括改绝对路径,加dll文件等,均未果。最后发现把jpg文件改成png文件就好了(有jpg的dll用jpg还是有问题)。
项目文件概览
效果图
主要功能及其实现
生成随机地图
主要包括生成随机地图并以数组的形式记录,和以贴图的形式在前端展示两个部分。
设定几个参数变量
int boom[10][10];//记录该点是否炸弹或数字
int flag[10][10];//记录该点有没有被亮出
int air[10][10];//保证dfs遍历不会循环
int sign[10][10];//插旗子
int cover[10][10];//保证炸弹不重复
SubWeight w1;//失败界面
successweight w2;//成功界面
int see;//是否为棋盘可见不可动状态
int mine_num;//地雷数
int map_long;//地图长
int map_wide;//地图宽
初始化
这里要注意不能让地雷重合起来,可以开一个数组记录该位置是否已有地雷存在。
void MainWindow::init(int number,int m_long,int m_wide)
{
w1.close();
w2.close();
mine_num=number;
map_long=m_long;
map_wide=m_wide;
see=0;
memset(boom,0,sizeof(boom));
memset(air,0,sizeof(air));
memset(sign,0,sizeof(sign));
srand(time(NULL));
memset(cover,0,sizeof(cover));
int x[10];
int y[10];
int num=0;
while(num<mine_num)
{
int xx=rand()%m_long;
int yy=rand()%m_wide;
if(cover[xx][yy]==0)
{
x[num]=xx;
y[num]=yy;
cover[xx][yy]=1;
num++;
}
}
for(int i=0;i<mine_num;i++)
{
boom[x[i]][y[i]]=-1;
}
for(int i=0;i<mine_num;i++)
{
if(x[i]+1<m_long&&boom[x[i]+1][y[i]]!=-1)
{
boom[x[i]+1][y[i]]+=1;
}
if(x[i]+1<m_long&&y[i]+1<m_wide&&boom[x[i]+1][y[i]+1]!=-1)
{
boom[x[i]+1][y[i]+1]+=1;
}
if(x[i]+1<m_long&&y[i]-1>=0&&boom[x[i]+1][y[i]-1]!=-1)
{
boom[x[i]+1][y[i]-1]+=1;
}
if(x[i]-1>=0&&boom[x[i]-1][y[i]]!=-1)
{
boom[x[i]-1][y[i]]+=1;
}
if(x[i]-1>=0&&y[i]+1<m_wide&&boom[x[i]-1][y[i]+1]!=-1)
{
boom[x[i]-1][y[i]+1]+=1;
}
if(x[i]-1>=0&&y[i]-1>=0&&boom[x[i]-1][y[i]-1]!=-1)
{
boom[x[i]-1][y[i]-1]+=1;
}
if(y[i]+1<m_wide&&boom[x[i]][y[i]+1]!=-1)
boom[x[i]][y[i]+1]+=1;
if(y[i]-1>=0&&boom[x[i]][y[i]-1]!=-1)
boom[x[i]][y[i]-1]+=1;
}
memset(flag,0,sizeof(flag));
}
扫雷棋盘上的各种图片
这里是网上直接找的素材。
设置图片大小
QPainter painter(this);
QPixmap pix;
pix.load("://image/base.jpg");
pix=pix.scaled(650,50);//设置图片大小
画图函数
这个paintEvent是官方的画图函数,画图操作只能写在这个函数里面(写在别的地方是没有用的)。在这里我直接使用了painter.drawPixmap()函数,就不用把那个长图手动截取成一个一个的了。贴图函数不会自动重新执行,需要update()函数的调用,在扫雷中,鼠标点击格子后就需要重新编辑画布。
void MainWindow::paintEvent(QPaintEvent *event){
QPainter painter(this);
QPixmap pix;
pix.load("://image/base.jpg");
pix=pix.scaled(650,50);//设置图片大小
if(see!=0)
{
for(int i=0;i<map_long;i++)
{
for(int j=0;j<map_wide;j++)
{
if(boom[i][j]!=-1){
painter.drawPixmap(i*50,j*50,pix,boom[i][j]*50,0,50,50);//新图x,新图y,原图,原图x,原图y,原图宽,原图高
}
else
{
if(flag[i][j]==1)
{
painter.drawPixmap(i*50,j*50,pix,600,0,50,50);
}
else
{
painter.drawPixmap(i*50,j*50,pix,450,0,50,50);
}
}
}
}
}
else
{
for(int i=0;i<map_long;i++)
{
for(int j=0;j<map_wide;j++)
{
if(sign[i][j]==1)
{
painter.drawPixmap(i*50,j*50,pix,550,0,50,50);
}
else if(flag[i][j]==0){
painter.drawPixmap(i*50,j*50,pix,500,0,50,50);
}
else{
if(boom[i][j]!=-1){
painter.drawPixmap(i*50,j*50,pix,boom[i][j]*50,0,50,50);
}
else{
painter.drawPixmap(i*50,j*50,pix,600,0,50,50);
}
}
}
}
}
}
鼠标事件
主要部分——确定画布是否需要重绘,以及重绘时参数的改变
左键:如点击区域的空格为空,则dfs(dfs见下文)搜索附近格子,将周围的空格子状态均设为可视;
如点击区域的空格为数字,则将其状态设为可视;
如点击区域的空格为地雷,则游戏结束,所有格子可视。
右键:标记小旗子或取消标记。
注:w1,w2为另外的两个子界面,分别为游戏输赢的提示框。
void MainWindow::mousePressEvent(QMouseEvent *event){
int px=event->x();
int py=event->y();
int xnum=px/50+judgeIt(px)-1;
int ynum=py/50+judgeIt(py)-1;
if(see!=0)
{
return;
}
if(event->button() == Qt::LeftButton){
if(xnum<map_long&&ynum<map_wide)
{
flag[xnum][ynum]=1;
sign[xnum][ynum]=0;
dfs(xnum,ynum);
update();
if(boom[xnum][ynum]==-1)
{
see=1;
update();
w1.show();
}
if(judgeWin())
{
see=2;
update();
w2.show();
}
}
}
if(event->button() == Qt::RightButton){
if(xnum<map_long&&ynum<map_wide)
{
if(flag[xnum][ynum]==0)
{
if(sign[xnum][ynum]==0)
sign[xnum][ynum]=1;
else
sign[xnum][ynum]=0;
update();
}
}
}
}
int MainWindow::judgeIt(int num){
if(num%50==0)
return 0;
else
return 1;
}
dfs搜索空格子
很暴力,没什么说的。
void MainWindow::dfs(int x, int y){
if(boom[x][y]!=0)
return;
if(air[x][y]==1)
return;
flag[x][y]=1;
air[x][y]=1;
if(x+1<10&&y+1<10)
dfs(x+1,y+1);
if(x+1<10)
dfs(x+1,y);
if(x+1<10&&y-1>=0)
dfs(x+1,y-1);
if(y+1<10)
dfs(x,y+1);
if(y-1>=0)
dfs(x,y-1);
if(x-1>=0&&y+1<10)
dfs(x-1,y+1);
if(x-1>=0)
dfs(x-1,y);
if(x-1>=0&&y-1>=0)
dfs(x-1,y-1);
}
胜利条件判断
bool MainWindow::judgeWin(){
int num=0;//剩余空格子数量
for(int i=0;i<map_long;i++)
{
for(int j=0;j<map_wide;j++)
{
if(flag[i][j]==0)
num++;
}
}
if(num==mine_num&&see==0)
{
return 1;
}
return 0;
}
其他功能
包括重新开始游戏,重设地雷数量,重设地图大小,均为按钮的点击事件。
重新开始游戏
void MainWindow::on_pushButton_clicked()
{
see=0;
init(mine_num,map_long,map_wide);
update();
}
重设地雷数量
void MainWindow::on_pushButton_2_clicked()
{
QString qstr=ui->lineEdit->text();
bool ok;
int number=qstr.toInt(&ok,10);//ok表示转换是否成功,10表示十进制
if(ok==1&&number>0&&number<=10)
{
if(map_long*map_wide>mine_num)
{
mine_num=number;
on_pushButton_clicked();
}
else
{
QMessageBox::about(NULL, "提示", "错误,雷区数应大于地雷数");
ui->lineEdit->setText("10");
mine_num=10;
}
}
else
{
QMessageBox::about(NULL, "提示", "错误,请输入 <font color='red'>1~10以内的正整数</font>");
ui->lineEdit->setText("10");
mine_num=10;
}
}
重设地图大小
void MainWindow::on_pushButton_3_clicked()
{
QString qstr1=ui->lineEdit_2->text();
QString qstr2=ui->lineEdit_4->text();
bool ok1;
bool ok2;
int number1=qstr1.toInt(&ok1,10);
int number2=qstr2.toInt(&ok2,10);
if(ok1==1&&ok2==1&&number1>0&&number1<=10&&number2>0&&number2<=10)
{
if(number1*number2>mine_num)
{
map_long=number1;
map_wide=number2;
on_pushButton_clicked();
}
else
{
QMessageBox::about(NULL, "提示", "错误,雷区数应大于地雷数");
ui->lineEdit_2->setText("10");
ui->lineEdit_4->setText("10");
map_long=10;
map_wide=10;
}
}
else
{
QMessageBox::about(NULL, "提示", "错误,请输入 <font color='red'>1~10以内的正整数</font>");
ui->lineEdit_2->setText("10");
ui->lineEdit_4->setText("10");
map_long=10;
map_wide=10;
}
}
其他说明
在关闭主窗口的同时关闭子窗口
在子窗口的cpp中声明:
setAttribute(Qt::WA_QuitOnClose,false);
设置窗口名称,图标,窗口大小
setWindowTitle("扫雷");
setWindowIcon(QIcon("://image/logo.jpg"));
this->setFixedSize(800,500); //设置窗体固定大小
打包后的可运行文件
链接: https://pan.baidu.com/s/1Hpyuyn_cUGuKuIG-GK027Q
提取码: huxt