扫雷小游戏详解

本文详细介绍了如何使用Qt编程语言实现一个简单的扫雷游戏,涉及鼠标事件处理、画面绘制、随机数生成和雷点显示等核心功能。代码示例清晰,适合初学者学习Qt编程中的事件驱动和图形界面设计。
摘要由CSDN通过智能技术生成

思路分析

        qt的学习中,可以试着写一写感兴趣的东西,比如小游戏。贪吃蛇、扫雷、俄罗斯方块等小游戏是很好的练习目标,本文讲讲扫雷的小游戏实现过程及思路。

        首先思考下扫雷用到了什么东西?鼠标点击、画面显示、音频等等,简单的做一个鼠标点击与画面显示的扫雷。鼠标点击与画面显示在qt中可以使用事件来完成,对应的是鼠标点击事件与图像绘制事件,即QMouseEvent和QPainter,这两个可以查询qt事件来学习。

        扫雷的逻辑大概可以简述为:鼠标左键点击方格,不是雷显示空白区域或者数字区域,点中雷即游戏失败。邮件插小旗子,再次点击右键取消小旗子。刚才的逻辑中涉及到随机数的生成,雷的生成使用随机数生成,根据难度生成不同数量的雷,本文以简单的10颗雷为例子。数字是根据一个方格周围的8个方格中雷的数量来显示数字大小,没有雷不显示。

        游戏胜利:旗子所插的位置跟雷所在位置完全一致,游戏成功!

现在开始代码讲解!

代码讲解

1、工程文件

没有用到需要添加功能块的类,直接新建一个工程文件即可

2、头文件

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QPainter>
#include <QVector>
#include <random>
#include <QRandomGenerator>
#include <QDebug>
#include <QMouseEvent>
#include <QMessageBox>

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

private:
    Ui::Widget *ui;

    int random_boom_i;
    int random_boom_j;
    QVector<QPoint> mines;//雷点
    int mouseclicked=0;
    bool flag = false;
    bool isflag = false;
    QVector<QPoint> flagPainter;//旗子显示点
    QVector<QPoint> isflagPainter;//取消旗子显示点
    bool iswin = false;

    //初始化函数
    void initmines(void);

protected:
    void mousePressEvent(QMouseEvent *event);
    void paintEvent(QPaintEvent *event);
    void boomLocation(void);//坤坤位置
    void promptNumber(void);//周围数字提示

};
#endif // WIDGET_H

头文件定义了一些变量与引用了些类,类的使用可以自行查阅帮助文件查找(要形成自己查阅帮助文件的好习惯),变量名字中式直译即可知道代表什么。

3、cpp文件

#include "widget.h"
#include "ui_widget.h"

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    boomLocation();//坤坤生成

}

Widget::~Widget()
{
    delete ui;
}

void Widget::initmines()
{
    mines.clear();
    mouseclicked=0;
    flag = false;
    isflag = false;
    flagPainter.clear();
    isflagPainter.clear();
    iswin = false;

    boomLocation();
    update();
}

void Widget::mousePressEvent(QMouseEvent *event)
{
    //点中坤坤即失败
    int mines_x = event->x();
    int mines_y = event->y();

    for (int i=0;i<9;i++)
    {
        for (int j=0;j<9;j++)
        {
            if((25+i*50+i<=mines_x)&&(75+i*50+i>=mines_x)&&(75+j*50+j<=mines_y)&&(125+j*50+j>=mines_y))
            {
                mines_x = 25+i*50+i;
                mines_y = 75+j*50+j;
            }
        }
    }

    QPoint mines_pos(mines_x, mines_y);

    //显示数字
    if(!mines.contains(mines_pos)&&(event->button() == Qt::LeftButton))
    {
        mouseclicked = 2;
        update();
    }

    //插旗显示旗子图标
    if((event->button() == Qt::RightButton)&&(!flagPainter.contains(QPoint(mines_x, mines_y)))&&(flagPainter.size() < 10))
    {
        flag = true;
        flagPainter.append(QPoint(mines_x, mines_y));
        isflagPainter.removeOne(QPoint(mines_x, mines_y));
        update();
    }
    //取消旗子显示
    else if((event->button() == Qt::RightButton)&&(flagPainter.contains(QPoint(mines_x, mines_y))))
    {
        flag = true;
        isflag = true;
        flagPainter.removeOne(QPoint(mines_x, mines_y));
        isflagPainter.append(QPoint(mines_x, mines_y));
        update();
    }


    //GAMEOVER
    if(mines.contains(mines_pos)&&(event->button() == Qt::LeftButton)&&(!flagPainter.contains(QPoint(mines_x, mines_y))))
    {
        mouseclicked = 1;
        update();
        QMessageBox::critical(this, "Gameover", "游戏结束,请重新开始!!!");
        initmines();//初始化
    }

    //win
    for (int i=0;i < mines.size();i++)
    {
        int wincount;
        for (int j=0;j < flagPainter.size();j++)
        {
            (mines[i] == flagPainter[j]) ? ++wincount : wincount;
        }
        if(wincount == mines.size())
        {
           iswin = true;
        }
    }
    if(iswin)
    {
        QMessageBox::information(this, "结算画面", "YOU WIN THE GAME!!!!");
        initmines();//初始化
    }
}

void Widget::paintEvent(QPaintEvent *event)
{
    Q_UNUSED(event)
    QPainter painter(this);
    //底图
//    if(mouseclicked == 0)
//    {
        for (int i=0;i<9;i++)
        {
            for (int j=0;j<9;j++)
            {
                QPixmap pix;
                pix.load(":/img/nofan.png");//底图小方块
                painter.drawPixmap(25+i*50+i,75+j*50+j,pix);
            }
        }
//    }

    //坤坤
    if(mouseclicked == 1)
    {
        QPixmap pixmap;
        pixmap.load(":/img/ikun.jpg");
        pixmap = pixmap.scaled(50, 50);
        for (int i=0;i<10;i++)
        {
            painter.drawPixmap(mines[i], pixmap);
        }
    }

    //数字
    if(mouseclicked == 2)
    {
        for (int i=0;i<9;i++)
        {
            for (int j=0;j<9;j++)
            {
                int num = 0;
                //考虑嵌套4个for消耗资源过多,使用if的方法写
                if(!mines.contains(QPoint(25+i*50+i,75+j*50+j)))
                {
                    mines.contains(QPoint(25+i*50+i,75+(j+1)*50+(j+1))) ? ++num : num;
                    mines.contains(QPoint(25+i*50+i,75+(j-1)*50+(j-1))) ? ++num : num;
                    mines.contains(QPoint(25+(i-1)*50+(i-1),75+j*50+j)) ? ++num : num;
                    mines.contains(QPoint(25+(i-1)*50+(i-1),75+(j+1)*50+(j+1))) ? ++num : num;
                    mines.contains(QPoint(25+(i-1)*50+(i-1),75+(j-1)*50+(j-1))) ? ++num : num;
                    mines.contains(QPoint(25+(i+1)*50+(i+1),75+j*50+j)) ? ++num : num;
                    mines.contains(QPoint(25+(i+1)*50+(i+1),75+(j+1)*50+(j+1))) ? ++num : num;
                    mines.contains(QPoint(25+(i+1)*50+(i+1),75+(j-1)*50+(j-1))) ? ++num : num;
                }
                QString path = ":/img/";//空
                if (num == 0)
                {
                    path = ":/img/";
                }
                else
                {
                    path.append(QString::number(num));
                    path.append(".png");
                }
                QPixmap pixmap1;
                pixmap1.load(path);
                painter.drawPixmap(25+i*50+i,75+j*50+j, pixmap1);
            }
        }
    }

    //小旗子
    if(flag)
    {
        QPixmap pixmap2;
        pixmap2.load(":/img/flag.png");
        qDebug() << flagPainter.size();
        for (int i=0;i<flagPainter.size();i++)
        {
            painter.drawPixmap(flagPainter[i], pixmap2);
        }
        if(isflag)
        {
            QPixmap pixmap3;
            pixmap3.load(":/img/nofan.png");
            for (int i=0;i<isflagPainter.size();i++)
            {
                painter.drawPixmap(isflagPainter[i], pixmap3);
            }
        }

        flag = false;
        isflag = false;
    }
}

void Widget::boomLocation()
{
    //生成坤坤的坐标,重复的重新生成,10个坤坤
    for (int i=0;i<10;i++)
    {
        random_boom_i = QRandomGenerator::global()->bounded(9);
        random_boom_j = QRandomGenerator::global()->bounded(9);
        //重复了重新选点
        while (mines.contains(QPoint(random_boom_i, random_boom_j)))
        {
            random_boom_i = QRandomGenerator::global()->bounded(9);
            random_boom_j = QRandomGenerator::global()->bounded(9);
        }
        mines.append(QPoint(25+random_boom_i*50+random_boom_i, 75+random_boom_j*50+random_boom_j));
    }
}




大体分为四个部分,初始化、鼠标事件、绘画事件、雷生成。其中雷、数字等也可以用QVector<QPoint>的二维数组来存放使用,更简洁一些。

4、资源文件

资源文件放一些小图片,可网上自行找一些扫雷用的工具小图片,也可以下载本文源码,自己找资源文件的小图片。

视频演示

扫雷

总结

本文代码写的比较乱,未优化,作者有点懒,能用就行,写的时候也是缺啥补啥,没有简洁的写,但是这样写应该大部分人能看懂,因为未做优化套层之类的。后续根据需求自己修改即可。主要是为读者提供下思路,功能也为增加太多,后续可根据需求自行添加音频、计时、联网排名等等功能。最后,帮助文档很重要,没事多看看没有坏处,谢谢。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值