从0开始的QT项目:扫雷

从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

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值