Qt简单迷宫游戏

目录

  • 你将学到
  • 你将准备
  • 你将改变
  • 你将设计
  • 你将编程
    • 开始界面
    • 游玩界面
    • 胜利界面
    • 其它bug修复
  • 你可扩展
  • 下一篇博客要说的东西

你将学到

  • QtQKeySequence对象的基本创建
  • QtQShortcut对象的基本应用
  • QtQSoundEffect对象的基本应用

你将准备

在开始制作Qt简单迷宫游戏之前,我们先要准备这一些东西。


  1. 这样,一个关于Qt简单迷宫游戏的项目就正式构建完成了。

你将改变

之后,我们我将要改变一些东西在这个游戏的项目里。


  1. 在你做改好这一些东西之后,你的Qt简单迷宫游戏就可以发出音效并使用图片了。

你将设计

为了完成这个迷宫游戏,我们也要设计一下UI,才能让用户看着舒服。


  1. 之后,你的迷宫游戏所有界面的UI的基本框架也就构建完成了。

你将编程

开始界面

在编程之前,如果没有一个做这个迷宫游戏的方法或策略的话,那么对于一个普通人来说,小型的项目大多数可能还是很快就可以能想到怎么做的,而大型的项目呢,则根本做不了,简直无从下手就像打怪一样,弱的怪,你轻轻松松就能打败;而强的怪呢,即使你有实力,但你如果没有策略的话,就很难打败他了,甚至你会因此而失败!因此,我们在做这个迷宫游戏,乃至其他的东西的时候,最好要制定一个方法或策略来做这个东西。看到这,你或许就可以想一下这游戏到底要怎么做了,当然,你也可以看下下面的建议方法。

转到开始界面,点击开始按钮开始游戏
开始玩迷宫游戏,玩家走到终点后就自动跳转到胜利界面
跳转到胜利界面之后播放胜利的声音,播好之后就关闭窗口

在上面,你可以知道,首先要实现开始界面,就要为开始界面里的一些UI实现一些用于开始的功能,那么要怎样开始呢?很简单,只需要把这个迷宫游戏的UI里的stackwidget的当前索引设为1就行了,当然,为了使游戏的开始界面更美观,我们可以把资源里的图片设置于当标题图片用的QLabel标签label和用于当开始按钮的QToolButtonIcon,并使菜单栏中刚才唯一设置过的名为kaishi1动作的名字设为开始,就好了,现在的MainWindow的构造方法代码及流程图如下。

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    QPixmap pm(":/start.png");
    QPixmap pma(":/title.png");
    ui->label->setPixmap(pma);
    ui->toolButton->setStyleSheet("QToolButton{border:0px;}");
    ui->toolButton->setIconSize(QSize(200,200));
    ui->toolButton->setIcon(pm);
    connect(ui->toolButton, &QToolButton::clicked, [=](){
        ui->stackedWidget->setCurrentIndex(1);
    });
    connect(ui->actionkaishi1, &QAction::triggered, [=](){
        ui->stackedWidget->setCurrentIndex(1);
    });
    ui->actionkaishi1->setText("开始");
}
开始,设置ui
定义QPixmap图片pm为资源里的图片start.png
定义QPixmap图片pma为资源里的图片title.png
设用来当标题图片用的Label标签label的图片为QPixmap图片pma
设用来当开始按钮的ToolButton的边框宽度为0像素
设用来当开始按钮的ToolButton的Icon的大小为200x200
设用来当开始按钮的ToolButton的Icon为QPixmap图片pm
设用来当开始按钮的ToolButton的点击后信号所触发的槽函数为“让stackedwidget转到第2页(会改,原因请见下面)”
设用来当开始按钮的ToolButton的点击后信号所触发的槽函数为“让stackedwidget转到第2页(会改,原因请见下面)”
设菜单栏中的开始动作的名设为“开始”
结束开始界面的实现

在开始界面实现好之后,我们可以来测试一下这个开始界面有没有bug。测试好之后,如果你发现了stackwidget当前跳转的页不对,那么请转到刚才的设计界面,右键选中stackWidget,并点击改变页顺序,在这里,你可以修正这个stackwidget当前跳转的页不对”的bug,具体实现步骤如下。


  1. 修正好这个bug之后,我们这个Qt简单迷宫游戏也就完成了 1 3 \frac{1}{3} 31了,接下来,我们就正式实现Qt简单迷宫游戏开始之后的游玩界面了,准备好了吗?

游玩界面

在实现这个游戏界面之前,我们需要一个迷宫,才能实现这个迷宫游戏的游玩界面,那么,迷宫哪里去找呢?很简单,要么上网去查迷宫,然后加以改进,要么自己造个迷宫,我这里由于迷宫比较难找,所以,就自己造了个迷宫。

造好后,我们就要根据这个迷宫及迷宫的大小定义一个有20行20列的二维字符数组strmaze,当然,你也可以按这样的方式定义一个独属于你自己的strmaze。之后,我们就要根据建议方法中的“走”字,来制定一套玩家行走的逻辑了,如果玩家不走的话,那游戏就无聊了,别人肯定跟你一样也不想玩,那么,普通的迷宫大家都应该了解了吧,就是有墙不能走,走到出口就胜利,并且能走四个方向,因此,即使没了解过迷宫的读者也不用担心,因为我们在这里抽象成了一个逻辑,能更好的为你知道迷宫该怎样走(迷宫的边界也可以算作墙)。

那么,知道这个迷宫游戏中玩家走的逻辑之后,我们就先要显示出这个迷宫了,只有这样,玩家才能更好的玩上这个迷宫(我不推荐玩家盲走,因为玩家会觉得游戏太难,但随你便),那要怎样显示出这个迷宫呢?很简单,我们只需要先按刚才造好的迷宫的字符一一按照对应的图片及格式添加进窗口里面,然后再添加玩家朝着某个方向的图片(我这里选玩家朝右的图片)在窗口的左上角,就好了,那么,如果我们要创建这个标签并给标签设置图片的话,就需要一个临时的标签变量来调整这个标签,而之后,由于我们还要操作这个用了玩家图片的标签,所以,我们还需要一个临时的标签变量来存储这个标签。由此,你大概可以想到这个代码应该是怎样的了。但不管你想到的代码是怎么样,有bug的,没bug的,很短的……反正如果没有封装过这些代码的话,后面开发的时候重复部分越来越臭长,让人看得越来越红温。所以,我们还要通过一个成员方法来完成开始游戏的操作。

//一般在mainwindow.h里面的Mainwindow类里
class MainWindow : public QMainWindow
{
    Q_OBJECT

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

    void startgame();
private:
    Ui::MainWindow *ui;
};
//一般要在mainwindow.cpp里面添加的方法
void MainWindow::startgame(){//函数名不能用“start”,会有二义性
    ui->stackedWidget->setCurrentIndex(1);
}

同时,我们也可以改变一下MainWindow构造方法中的某些信号所执行的槽函数。

connect(ui->toolButton, &QToolButton::clicked, [&](){
    this->startgame(); 
});
connect(ui->actionkaishi1, &QAction::triggered, [&](){
    this->startgame();  
});

之后,就可以往startgame函数里面添加你刚才所想的代码了,注意要引上QLabel类文件,下面的流程图能更好地理解下面代码的意思。

ui->stackedWidget->setCurrentIndex(1);
char strmaze[20][20] = {
    'P',' ',' ','*',' ',' ','*',' ','*',' ',' ',' ',' ','*',' ',' ',' ',' ',' ','*',
    ' ',' ',' ',' ',' ','*',' ','*',' ',' ',' ',' ','*',' ',' ',' ',' ',' ',' ','*',
    ' ',' ',' ','*',' ','*',' ',' ',' ',' ',' ','*',' ',' ',' ','*',' ',' ','*','*',
    ' ','*','*',' ',' ',' ','*','*',' ','*',' ',' ',' ',' ','*',' ',' ',' ',' ',' ',
    ' ',' ','*',' ',' ',' ','*','*',' ','*',' ','*',' ','*',' ',' ',' ','*',' ',' ',
    ' ',' ','*',' ','*',' ',' ','*',' ',' ','*',' ','*',' ',' ',' ',' ','*',' ','*',
    ' ','*','*',' ',' ','*',' ','*',' ',' ',' ',' ',' ','*',' ',' ','*',' ',' ',' ',
    ' ',' ','*','*',' ',' ','*','*','*','*','*',' ',' ',' ','*',' ',' ',' ','*',' ',
    '*',' ','*',' ','*',' ',' ',' ',' ',' ',' ','*','*',' ',' ','*','*','*',' ',' ',
    ' ',' ','*',' ',' ','*',' ','*','*','*',' ',' ',' ',' ','*',' ',' ',' ',' ',' ',
    ' ','*','*',' ',' ',' ',' ','*',' ',' ','*','*','*',' ','*',' ',' ','*','*','*',
    ' ',' ','*',' ',' ','*',' ','*',' ','*',' ',' ','*',' ','*',' ',' ',' ','*',' ',
    '*',' ','*',' ','*',' ','*','*','*',' ',' ','*',' ',' ','*',' ',' ',' ','*',' ',
    ' ','*','*',' ',' ',' ',' ',' ',' ',' ',' ',' ','*',' ',' ','*','*',' ','*',' ',
    ' ','*',' ','*','*','*',' ',' ','*',' ',' ','*',' ',' ',' ','*',' ',' ',' ',' ',
    ' ',' ',' ',' ',' ',' ',' ',' ',' ','*',' ',' ','*',' ',' ','*',' ','*',' ',' ',
    ' ',' ','*',' ','*',' ','*','*','*',' ',' ','*',' ',' ',' ','*',' ','*','*',' ',
    ' ',' ','*','*','*',' ',' ','*',' ',' ',' ',' ','*',' ',' ','*',' ','*','*','*',
    ' ',' ','*',' ','*',' ','*','*','*',' ',' ','*',' ',' ',' ','*',' ',' ','*',' ',
    ' ',' ',' ',' ',' ',' ',' ',' ',' ','*',' ',' ','*',' ',' ','*','*',' ',' ','G',
};
QLabel* lb = new QLabel(ui->page_2);
QPixmap pm(48, 48);
for (int i = 0; i <= 400; i++){
    lb = new QLabel(ui->page_2);
    if (400 == i){
        lb->move(0, 0);
        pm.load(":/Playerd.png");
    }
    else {
        lb->move(i % 20 * 48, i / 20 * 48);
        switch(strmaze[i / 20][i % 20]){
        case '*':
            pm.load(":/wall.png");
            break;
        case 'G':
            pm.load(":/Goal.png");
            break;
        default:
            pm.fill(Qt::white);
            break;
        }
    }
    pm = pm.scaled(48, 48);
    lb->setPixmap(pm);
    lb->show();
}
break
break
break
开始
让stackedwidget转到第2页
定义有20行20列的二维字符数组strmaze为上面代码中关于迷宫的这一个二维字符数组
定义QLabel标签对象lb为new出来的在stackedwidget第2页的QLabel标签
定义QPixmap图片pm,并设它的大小为48x48
定义整型i为0
i <= 400?
设标签lb为new出来的在stackedwidget第2页的QLabel标签
400 == i?
把标签lb移到x坐标为0,y坐标为0的位置上
让图片pm加载资源里的玩家向右的图片
设图片pm为大小为48x48的图片pm
设Qlabel标签lb的图片为图片pm
显示标签lb
i自增1
结束
把标签lb移到x坐标为整型i模上20再乘48的结果,y坐标为整型i除以20再乘48的结果的位置上
'*' == strmaze[i / 20][i % 20]?
让图片pm加载资源里的墙的图片
'G' == strmaze[i / 20][i % 20]?
让图片pm加载资源里的终点的图片
让图片pm填充白色

在做好显示迷宫的代码后,我们就要对这个玩家标签lb进行移动操作了。移动,肯定是要有个操作方式来执行的,比如大家最广为人知的键盘,又比如PushButtonToolButton……而这次,我们要用键盘来实现移动的功能。首先,我们要想一种既得心应手又畅通无阻而且可以接收键盘按键按下的信号的东西,KeyPressEvent事件可行吗?不可行,可能会使程序卡死。那——除了KeyPressEvent事件,没有什么可以接收键盘按键按下的信号啊。欸——其实有一个东西既能得心应手,又畅通无阻,而且可以接收键盘按键按下的信号,那就是:QShortcut快捷键!
那么,我们既然知道了QShortcut快捷键这一个东西,就应该要知道QShortcut快捷键的用途了,QShortcut快捷键,顾名思义,主要优点就是很快捷,而“键”字,就说明他跟键盘有关系,能触发有关键盘的信号,那,QShortcut要怎样创建呢?其实只要先导入QShortcut类文件,然后Qt的人性系统就让我们知道了它的其中一种构造方式:之后,我们可以知道,在这个重载的构造方法中,首先需要一个QKeySequence对象,因此,我们就在QShortcut构造方法的第一个参数中填入一个匿名的QKeySequence对象来,那这个QKeySequence对象是用来干什么的呢?其实翻译一下KeySequence这个单词,就可以知道它是用来存键的序列的,“序列”这个词,第一次看有点不熟悉,但只要加以查找一下它的意思,可能还有点陌生,回过头来,只要联想一下快捷键的功能,就可以知道它是用来存一个键或是几个键的组合的,是每个快捷键都必须拥有的核心,因此,根据刚才想到的,我们把你想要的要按下的键想成字符串,就可以定义一个QKeySequence对象了。现在,第一个参数QKeySequence("W")就出来了。然后,第二个参数需要一个parent对象,根据前面的理解,只需要万能的this指针就好。最后,还需要为这个快捷键有一个移动的方法,就需要便携的匿名槽函数来助力了。三个参数都有了,那QShortcut对象也就可以创建完成了:

new QShortcut(QKeySequence("W"), this, [=](){
    //移动操作
});

接下来,我们还要另外创建3个QShortcut对象用来使玩家朝其他的方向移动,不过,这里可以用数组来存这四个已经创建好的QShortcut对象和另外创建3个QShortcut对象:

//在mainwindow.cpp的startgame方法里
QShortcut* movesc[4]={
    new QShortcut(QKeySequence("W"), this, [=](){
        //上移操作
    }),
    new QShortcut(QKeySequence("A"), this, [=](){
        //左移操作
    }),
    new QShortcut(QKeySequence("S"), this, [=](){
        //下移操作
    }),
    new QShortcut(QKeySequence("D"), this, [=](){
        //右移操作
    })
};

创建好后,我们就得往这一些匿名槽函数添加点实现进去了,首先完成右移操作。要想使玩家右移,可以直接移动,也可以平滑移动,当然,为了追求用户的体验,比较合适的是平滑移动,平滑移动的话,就需要一个动画来让玩家进行平滑移动了,要想平滑移动,就用QPropertyAnimation动画对象来进行移动很合适。首先,要先导入QPropertyAnimation类文件,然后,在定义4个QShotrcut对象之前,new一个QPropertyAnimation对象,接着设置动画曲线,开始坐标,结束坐标,之后start方法一写,基本的平滑移动的方式就大功告成了,接着,如果需要改变玩家标签lb的朝向,那setPixmap一下就行了,在这之前,要设置的图片可以按你的想法来设置,只要让用户觉得没问题就行。在这两步操作之后,你右移操作的代码应该就是这样子了:

new QShortcut(QKeySequence("D"), this, [=](){
    pm.load(":/Playerd.png");
    pm = pm.scaled(48, 48);
    lb->setPixmap(pm);
    pa->setEasingCurve(QEasingCurve::Linear);
    pa->setStartValue(QRect(lb->x(), lb->y(), this->width(), this->height()));
    pa->setEndValue(QRect(lb->x() + 48, lb->y(), this->width(), this->height()));
    pa->start();
})

但,这有个问题,就是QPixmap图片pmPlayer变成const常量了,从而导致pmPlayer把图片缩成大小为48x48的图片时出现了问题,并且pmPlayer也无法加载图片了。“这还不简单?”你想,“我让QPixmap图片pmPlayer在这个槽函数里设为引用传递,就行了。这不轻而易举吗!”你想的的确是好,但又想得太好了,按你这样做的话,最终的结果应该就是这样子——
由此可知,这个槽函数还是有bug,那么,我们如何让槽函数里的QPixmap图片pmPlayer既不为const常量,又不让它为空图片呢,不难,如果单从QPixmap图片pmPlayer来想的话,那这个问题就困难多了,而直接在槽函数里另外创建一个QPixmap图片,远比刚才简单多了,虽然会占内存空间,但好在他简单,所以我们直接就用这个办法就行了:

new QShortcut(QKeySequence("D"), this, [=](){
    QPixmap pmPlayer(":/Playerd.png");
    pmPlayer = pmPlayer.scaled(48, 48);
    lb->setPixmap(pmPlayer);
    pa->setEasingCurve(QEasingCurve::Linear);
    pa->setStartValue(QRect(lb->x(), lb->y(), this->width(), this->height()));
    pa->setEndValue(QRect(lb->x() + 48, lb->y(), this->width(), this->height()));
    pa->start();
})

然后,我们测试之后,会看到玩家在向右移动的时候往下偏移了非常多格:所以,就要让玩家标签lb在第一次移动前再往上偏移几百像素,就好了,想知道具体偏移了多少就来测试一下吧,这是我的修正偏移的方法:

if (this->bmove){//有this指针,就有成员函数在mainwindow.h里面
    lb->move(0, -276);
    this->bmove = false;
}

之后,这个玩家标签lb就能正常的移动了,而接下来,我们只要往里面实现玩家移动的逻辑和如何判断玩家胜利的逻辑,整个游玩界面也就到此结束了。从移动的逻辑来,移动的逻辑,是整个迷宫游戏中最重要的逻辑,具体的逻辑可见上面的“游玩界面”标题旁的图。实现起来也比较简单,只要一个判断条件,一个指针,就行,但是,还有一步,也是最重要的一步,就是——把二维字符数组strmaze变成静态的二维字符数组,因为在这些逻辑之后,start成员方法就结束了,使没有静态化的strmaze就被销毁了,没有了这个strmaze,也就无法正常的运行了,之后,就可以正式地开始写右移的逻辑了。

new QShortcut(QKeySequence("D"), this, [=](){
    QPixmap pmPlayer(":/Playerd.png");//玩家移动时的朝向
    pmPlayer = pmPlayer.scaled(48, 48);
    lb->setPixmap(pmPlayer);
    if (19 != ((cp - &strmaze[0][0]) % 20) && '*' != cp[1/*检测右边有没有墙*/]){
        if (this->bmove){
            lb->move(0, -276);
            this->bmove = false;
        }
        this->cp++;
        pa->setEasingCurve(QEasingCurve::Linear);
        pa->setStartValue(QRect(lb->x(), lb->y(), this->width(), this->height()));
        pa->setEndValue(QRect(lb->x() + 48, lb->y(), this->width(), this->height()));
        pa->start();
    }
})

同理,其它方向的移动操作也可以写起来了。(让玩家指针cp下移,只需要让它右移二维字符数组strmaze一行的元素个数就行了。上移操作同理,检测上面或下面墙的操作也同理)

new QShortcut(QKeySequence("W"), this, [=](){
    QPixmap pmPlayer(":/Playerw.png");//玩家移动时的朝向
    pmPlayer = pmPlayer.scaled(48, 48);
    lb->setPixmap(pmPlayer);
    if (((cp - &strmaze[0][0]) / 20) && '*' != cp[-20/*检测上边有没有墙*/]){
        if (this->bmove){
            lb->move(0, -276);
            this->bmove = false;
        }
        this->cp -= 20;
        pa->setEasingCurve(QEasingCurve::Linear);
        pa->setStartValue(QRect(lb->x(), lb->y(), this->width(), this->height()));
        pa->setEndValue(QRect(lb->x(), lb->y() - 48, this->width(), this->height()));
        pa->start();
    }
}),
new QShortcut(QKeySequence("A"), this, [=](){
    QPixmap pmPlayer(":/Players.png");//玩家移动时的朝向
    pmPlayer = pmPlayer.scaled(48, 48);
    lb->setPixmap(pmPlayer);
    if (((cp - &strmaze[0][0]) % 20) && '*' != cp[-1/*检测左边有没有墙*/]){
        if (this->bmove){
            lb->move(0, -276);
            this->bmove = false;
        }
        this->cp--;
        pa->setEasingCurve(QEasingCurve::Linear);
        pa->setStartValue(QRect(lb->x(), lb->y(), this->width(), this->height()));
        pa->setEndValue(QRect(lb->x() - 48, lb->y(), this->width(), this->height()));
        pa->start();
    }
}),
new QShortcut(QKeySequence("S"), this, [=](){
    QPixmap pmPlayer(":/Players.png");//玩家移动时的朝向
    pmPlayer = pmPlayer.scaled(48, 48);
    lb->setPixmap(pmPlayer);
    if (19 != ((cp - &strmaze[0][0]) / 20) && '*' != cp[20/*检测下边有没有墙*/]){
        if (this->bmove){
            lb->move(0, -276);
            this->bmove = false;
        }
        this->cp += 20;
        pa->setEasingCurve(QEasingCurve::Linear);
        pa->setStartValue(QRect(lb->x(), lb->y(), this->width(), this->height()));
        pa->setEndValue(QRect(lb->x(), lb->y() + 48, this->width(), this->height()));
        pa->start();
    }
}),

之后,我们就测试一下,看到玩家标签lb果然能正常的移动起来了。

接下来,我们就来完成最后的判断玩家胜利的逻辑了。这个逻辑呢,想起来简单,做起来也不难,只需要在每次移动后检测玩家指针cp有没有到达终点G的位置,就行了。

//在mainwindow.cpp里的startgame方法里,一般如果mainwindow.h没有这个Iswin信号,就请在mainwindow.h中定义这个信号
connect(this, &MainWindow::Iswin, [=](){
    if ('G' == *this->cp){
        ui->stackedWidget->setCurrentIndex(2);
    }
});

而这,就是现在这个游戏的主要逻辑。

connect(this, &MainWindow::Iswin, [=](){
    if ('G' == *this->cp){
        ui->stackedWidget->setCurrentIndex(2);
    }
});
new QShortcut(QKeySequence("W"), this, [=](){
    QPixmap pmPlayer(":/Playerw.png");
    pmPlayer = pmPlayer.scaled(48, 48);
    lb->setPixmap(pmPlayer);
    if (((cp - &strmaze[0][0]) / 20) && '*' != cp[-20]){
        if (this->bmove){
            lb->move(0, -276);
            this->bmove = false;
        }
        this->cp -= 20;
        pa->setEasingCurve(QEasingCurve::Linear);
        pa->setStartValue(QRect(lb->x(), lb->y(), this->width(), this->height()));
        pa->setEndValue(QRect(lb->x(), lb->y() - 48, this->width(), this->height()));
        pa->start();
    }
    emit Iswin();
}),
new QShortcut(QKeySequence("A"), this, [=](){
    QPixmap pmPlayer(":/Playera.png");
    pmPlayer = pmPlayer.scaled(48, 48);
    lb->setPixmap(pmPlayer);
    if (((cp - &strmaze[0][0]) % 20) && '*' != cp[-1]){
        if (this->bmove){
            lb->move(0, -276);
            this->bmove = false;
        }
        this->cp--;
        pa->setEasingCurve(QEasingCurve::Linear);
        pa->setStartValue(QRect(lb->x(), lb->y(), this->width(), this->height()));
        pa->setEndValue(QRect(lb->x() - 48, lb->y(), this->width(), this->height()));
        pa->start();
    }
    emit Iswin();
}),
new QShortcut(QKeySequence("S"), this, [=](){
    QPixmap pmPlayer(":/Players.png");
    pmPlayer = pmPlayer.scaled(48, 48);
    lb->setPixmap(pmPlayer);
    if (19 != ((cp - &strmaze[0][0]) / 20) && '*' != cp[20]){
        if (this->bmove){
            lb->move(0, -276);
            this->bmove = false;
        }
        this->cp += 20;
        pa->setEasingCurve(QEasingCurve::Linear);
        pa->setStartValue(QRect(lb->x(), lb->y(), this->width(), this->height()));
        pa->setEndValue(QRect(lb->x(), lb->y() + 48, this->width(), this->height()));
        pa->start();
    }
    emit Iswin();
}),
new QShortcut(QKeySequence("D"), this, [=](){
    QPixmap pmPlayer(":/Playerd.png");
    pmPlayer = pmPlayer.scaled(48, 48);
    lb->setPixmap(pmPlayer);
    if (19 != ((cp - &strmaze[0][0]) % 20) && '*' != cp[1]){
        if (this->bmove){
            lb->move(0, -276);
            this->bmove = false;
        }
        this->cp++;
        pa->setEasingCurve(QEasingCurve::Linear);
        pa->setStartValue(QRect(lb->x(), lb->y(), this->width(), this->height()));
        pa->setEndValue(QRect(lb->x() + 48, lb->y(), this->width(), this->height()));
        pa->start();
    }
    emit Iswin();
})
右移操作(按下“D”键的操作)
下移操作(按下“S”键的操作)
左移操作(按下“A”键的操作)
上移操作(按下“W”键的操作)
判断胜利(触发Iswin信号所执行的槽函数)
触发Iswin信号,并结束
开始动画pa
设动画pa的终点为窗口中x坐标为标签lb的x坐标加48的结果,y坐标为标签lb的y坐标的地方
设动画pa的起点为窗口中x坐标为标签lb的x坐标,y坐标为标签lb的y坐标的地方
设QPropertyAnimation动画pa的动画曲线为直线
把Mainwindow的成员字符指针cp向右移动1位
设Mainwindow的成员布尔值bmove为假
把标签lb移到x坐标为0,y坐标为-276的地方(y坐标要改,原因请见下面)
this->bmove?
19 != ((cp - &strmaze[0][0]) % 20) && '*' != cp[1]?
设标签lb的图片为图片pmPlayer
设图片pmPlayer为大小为48x48的图片pmPlayer
定义QPixmap图片pmPlayer,并让图片pmPlayer加载资源里玩家向上的图片
开始
触发Iswin信号,并结束
开始动画pa
设动画pa的终点为窗口中x坐标为标签lb的x坐标,y坐标为标签lb的y坐标加48的结果的地方
设动画pa的起点为窗口中x坐标为标签lb的x坐标,y坐标为标签lb的y坐标的地方
设QPropertyAnimation动画pa的动画曲线为直线
把Mainwindow的成员字符指针cp向右移动20位
设Mainwindow的成员布尔值bmove为假
把标签lb移到x坐标为0,y坐标为-276的地方(y坐标要改,原因请见下面)
this->bmove?
19 != ((cp - &strmaze[0][0]) / 20) && '*' != cp[20]?
设标签lb的图片为图片pmPlayer
设图片pmPlayer为大小为48x48的图片pmPlayer
定义QPixmap图片pmPlayer,并让图片pmPlayer加载资源里玩家向下的图片
开始
触发Iswin信号,并结束
开始动画pa
设动画pa的终点为窗口中x坐标为标签lb的x坐标减48的结果,y坐标为标签lb的y坐标的地方
设动画pa的起点为窗口中x坐标为标签lb的x坐标,y坐标为标签lb的y坐标的地方
设QPropertyAnimation动画pa的动画曲线为直线
把Mainwindow的成员字符指针cp向左移动1位
设Mainwindow的成员布尔值bmove为假
把标签lb移到x坐标为0,y坐标为-276的地方(y坐标要改,原因请见下面)
this->bmove?
((cp - &strmaze[0][0]) % 20) && '*' != cp[-1]?
设标签lb的图片为图片pmPlayer
设图片pmPlayer为大小为48x48的图片pmPlayer
定义QPixmap图片pmPlayer,并让图片pmPlayer加载资源里玩家向左的图片
开始
触发Iswin信号,并结束
开始动画pa
设动画pa的终点为窗口中x坐标为标签lb的x坐标的结果,y坐标为标签lb的y坐标减48的结果的地方
设动画pa的起点为窗口中x坐标为标签lb的x坐标,y坐标为标签lb的y坐标的地方
设QPropertyAnimation动画pa的动画曲线为直线
把Mainwindow的成员字符指针cp向左移动20位
设Mainwindow的成员布尔值bmove为假
把标签lb移到x坐标为0,y坐标为-276的地方(y坐标要改,原因请见下面)
this->bmove?
((cp - &strmaze[0][0]) / 20) && '*' != cp[-20]?
设标签lb的图片为图片pmPlayer
设图片pmPlayer为大小为48x48的图片pmPlayer
定义QPixmap图片pmPlayer,并让图片pmPlayer加载资源里玩家向上的图片
开始
结束
让stackedwidget转到第3页
'G' == *this->cp?
开始

在你debug之后,如果你试着将玩家走到终点(虽然游戏窗口全屏时玩家还是在往下偏移),那么你就转到stackedwidget的第3页了,可以看到一大片的空白,是因为我们还没有实现这个胜利界面,现在,就要实现这个游戏的最后一个界面——胜利界面了。

胜利界面

胜利界面的话,就只要达成以下三点要求就好了。

  1. stackedwidget第3页的背景颜色为白色
  2. 设表示胜利的标签的图片为资源中表示胜利的图片(要大点,太小就不好看)
  3. 在刚开始转到胜利界面之后,要播放表示胜利的音乐,播放完后就关闭游戏的窗口

首先,我们完成第一个要求。要完成第一个要求,先需要在mainwindow.h中导入QPainter类文件,并声明出paintEvent方法,然后,在mainwindow.cpp里定义paintEvent方法之后,只需要在这个paintEvent方法里new一个QPainter画家对象painter,并让这个painter画家对象在这一页中填充一下白色用来当这一页的背景的颜色,最后只需要让这些操作只执行一次就好了,方法随意(这里定义成员布尔型变量bwin可以为之后的bug修复做准备)。

//在mainwindow.h里
#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QLabel>
#include <QPainter>

QT_BEGIN_NAMESPACE
namespace Ui {
class MainWindow;
}
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

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

    void startgame();

    void paintEvent(QPaintEvent*);
signals:
    void Iswin();

private:
    Ui::MainWindow *ui;

    bool bmove;

    bool bwin;

    char* cp;
};
#endif // MAINWINDOW_H
//在mainwindow.cpp里
void MainWindow::paintEvent(QPaintEvent*){
	if (2 == ui->stackedWidget->indexOf(ui->stackedWidget->currentWidget())){
        QPainter* painter = new QPainter(this);
        painter->fillRect(this->rect(), Qt::white);
        painter->end();
    }
}

然后,我们要让表示胜利的标签的图片为资源中表示胜利的图片,很简单,只要在Mainwindow的构造方法中setPixmap一下就行了,如果图片小,那张图片就可以通过QPixmap图片类中的关于scaled的一些方法来放大。

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    QPixmap pm(":/start.png");
    QPixmap pma(":/title.png");
    ui->label->setPixmap(pma);
    ui->toolButton->setStyleSheet("QToolButton{border:0px;}");
    ui->toolButton->setIconSize(QSize(200,200));
    ui->toolButton->setIcon(pm);
    connect(ui->toolButton, &QToolButton::clicked, [&](){
        this->startgame();
    });
    connect(ui->actionkaishi1, &QAction::triggered, [&](){
        this->startgame();
    });
    ui->actionkaishi1->setText("开始");
    ui->label_2->setPixmap(QPixmap(":/win(1).png"));//这里的win(1).png为在该游戏程序外部由win.png放大之后的图片
    this->bwin = false;
}

最后,要完成第3个要求,我们就要找到转到胜利界面的语句,找到之后,就导入一下类文件QSoundEffect,我们就在这条语句的后面new一个QSoundEffect对象来,那这个对象要怎样new呢?光看构造方式和QSoundEffect的意思也不知道啊,没事,不知道的话,就用最简单的无参构造方式试着new一下就好了,然后,就用刚new出来的QSoundEffect对象借助QUrl类去设一下播放的音乐,最后,给它去定义一个链接,如果它播放好了,就把窗口关掉,游戏就结束了。

connect(this, &MainWindow::Iswin, [=](){
    if ('G' == *this->cp){
        ui->stackedWidget->setCurrentIndex(2);
        QSoundEffect* sound = new QSoundEffect;
        sound->setSource(QUrl::fromLocalFile("//资源中的win.wav胜利音效的路径,自己去改!"));//<<<<<<请见左
        sound->play();
        connect(sound, &QSoundEffect::playingChanged, [=](){
            this->close();
        });
    }
});

至此,这个迷宫游戏基本也就完成了。接下来还有其它的bug要修,我们要尽力一下了。更新的地方代码和流程图这时都可以去看一下。

//触发Iswin信号之后的槽函数里
connect(this, &MainWindow::Iswin, [=](){
    if ('G' == *this->cp){
        ui->stackedWidget->setCurrentIndex(2);
        QSoundEffect* sound = new QSoundEffect;
        sound->setSource(QUrl::fromLocalFile("//资源中的win.wav胜利音效的路径,自己去改!"));//<<<<<<请见左
        sound->play();
        connect(sound, &QSoundEffect::playingChanged, [=](){
            this->close();
        });
    }
});
//Mainwindow的构造函数里
MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    QPixmap pm(":/start.png");
    QPixmap pma(":/title.png");
    ui->label->setPixmap(pma);
    ui->toolButton->setStyleSheet("QToolButton{border:0px;}");
    ui->toolButton->setIconSize(QSize(200,200));
    ui->toolButton->setIcon(pm);
    connect(ui->toolButton, &QToolButton::clicked, [&](){
        this->startgame();
    });
    connect(ui->actionkaishi1, &QAction::triggered, [&](){
        this->startgame();
    });
    ui->actionkaishi1->setText("开始");
    ui->label_2->setPixmap(QPixmap(":/win(1).png"));
    this->bwin = false;
}
//Mainwindow的paintEvent方法里
void MainWindow::paintEvent(QPaintEvent*){
	if (2 == ui->stackedWidget->indexOf(ui->stackedWidget->currentWidget())){
        QPainter* painter = new QPainter(this);
        painter->fillRect(this->rect(), Qt::white);
        painter->end();
    }
}

Mainwindow的paintEvent方法里
结束
开始
2 == ui->stackedWidget->indexOf(ui->stackedWidget->currentWidget())?
定义一个QPainter对象painter为new出来的QPainter对象
让画家对象painter把整个窗口填充成白色
让画家对象painter停止绘画
Mainwindow的构造函数里
结束
开始
...(开始界面的实现)
设用来表示胜利的标签label_2的图片为资源里名为win(1).png的表示胜利的图片
设Mainwindow的成员布尔型变量bwin为假
触发Iswin信号之后的槽函数里
结束
开始
'G' == *this->cp?
让stackedwidget转到第2页
定义QSoundEffect对象sound为new出来的QSoundEffect对象
设音乐sound为资源中名为win.wav的胜利音效
播放音乐sound
将音乐sound的结束信号所触发的槽函数设为“关掉窗口”

其它bug修复

首先,在“胜利界面”的标题旁,或者如果你刚才测试了一下游戏,那么你就会知道在游戏窗口变为全屏之后,这个玩家标签lb就又往下偏移了很多格,修复它的话,只需要把玩家标签lb往下偏移的像素数改一下就行了,因此还要再调整一下偏移的像素数。

//在movesc里的各个这样的语句里
if (this->bmove){
    lb->move(0, -484);
    this->bmove = false;
}

并且,游戏胜利时,重复的播放了胜利的音效,这不能,所以,就让Mainwindow的成员布尔型变量bwin在游戏胜利之后设为真就行了,并让触发Iswin信号之后的胜利的操作设成一次性的就行。

connect(this, &MainWindow::Iswin, [=](){
    if (!this->bwin && 'G' == *this->cp){
        ui->stackedWidget->setCurrentIndex(2);
        this->bwin = true;
        QSoundEffect* sound = new QSoundEffect;
        sound->setSource(QUrl::fromLocalFile("//资源中的win.wav胜利音效的路径,自己去改!"));//<<<
        sound->play();
        connect(sound, &QSoundEffect::playingChanged, [=](){
            this->close();
        });
    }
});

然后,由于游戏开始时窗口根本无法完整显示迷宫,并且在每次调整窗口的大小后,玩家标签lb移动后的位置飘忽不定,所以就要固定一下窗口的大小,防止出现这一些问题,那大小要怎么定呢,只需要记住长至少为迷宫的长+迷宫左边离布局的像素数+迷宫右边离布局右边的像素数,宽至少为迷宫的宽+迷宫左边离布局的像素数+迷宫右边离布局右边的像素数,就行了。(窗口的长和宽越短越好,但如果有bug可能就除外,具体窗口的长与宽请自己调出来)

this->setFixedSize(1017, 1017);//示例

接着,我们需要为游戏窗口设置一下icon和标题,直接照着粘贴就行。

this->setWindowIcon(QPixmap(":/icon.png"));
this->setWindowTitle("迷宫");

再接着,如果你在测试这个游戏的时候把玩家移动终点了,那游戏就会直接胜利,不会执行玩家移动到终点的动画了,因此,就要导入一下QTimer类文件,用singleShot在玩家胜利之后等待一下吧。

connect(this, &MainWindow::Iswin, [=](){
    if (!this->bwin && 'G' == *this->cp){
    	this->bwin = true;
    	QTimer::singleShot(300, [=](){
        	ui->stackedWidget->setCurrentIndex(2);
        	QSoundEffect* sound = new QSoundEffect;
        	sound->setSource(QUrl::fromLocalFile("//资源中的win.wav胜利音效的路径,自己去改!"));
        	sound->play();
        	connect(sound, &QSoundEffect::playingChanged, [=](){
            	this->close();
        	});
        });
	}
});

最后,就要给移动操作设一下冷却的时机了。要给移动操作设置冷却的时机,你就要知道,一般在玩家移动时的那段时间里就是最好的要冷却的时机,所以如果玩家开始移动了,那么就给所有的玩家移动的操作都不执行了;而如果玩家结束移动了,那么就给所有的玩家移动的操作都执行了,就是那么简单。

new QShortcut(QKeySequence("W"), this, [=](){
    if (!this->bwait) {
        this->bwait = true;
        QPixmap pmPlayer(":/Playerw.png");
        pmPlayer = pmPlayer.scaled(48, 48);
        lb->setPixmap(pmPlayer);
        if (this->bwait = ((cp - &strmaze[0][0]) / 20) && '*' != cp[-20]){
            if (this->bmove){
                lb->move(0, -484);
                this->bmove = false;
            }
            this->cp -= 20;
            pa->setEasingCurve(QEasingCurve::Linear);
            pa->setStartValue(QRect(lb->x(), lb->y(), this->width(), this->height()));
            pa->setEndValue(QRect(lb->x(), lb->y() - 48, this->width(), this->height()));
            pa->start();
        }
        emit Iswin();
    }
}),
new QShortcut(QKeySequence("A"), this, [=](){
    if (!this->bwait) {
        this->bwait = true;
        QPixmap pmPlayer(":/Playera.png");
        pmPlayer = pmPlayer.scaled(48, 48);
        lb->setPixmap(pmPlayer);
        if (this->bwait = ((cp - &strmaze[0][0]) % 20) && '*' != cp[-1]){
            if (this->bmove){
                lb->move(0, -484);
                this->bmove = false;
            }
            this->cp--;
            pa->setEasingCurve(QEasingCurve::Linear);
            pa->setStartValue(QRect(lb->x(), lb->y(), this->width(), this->height()));
            pa->setEndValue(QRect(lb->x() - 48, lb->y(), this->width(), this->height()));
            pa->start();
        }
        emit Iswin();
    }
}),
new QShortcut(QKeySequence("S"), this, [=](){
    if (!this->bwait) {
        this->bwait = true;
        QPixmap pmPlayer(":/Players.png");
        pmPlayer = pmPlayer.scaled(48, 48);
        lb->setPixmap(pmPlayer);
        if (this->bwait = 19 != ((cp - &strmaze[0][0]) / 20) && '*' != cp[20]){
            if (this->bmove){
                lb->move(0, -484);
                this->bmove = false;
            }
            this->cp += 20;
            pa->setEasingCurve(QEasingCurve::Linear);
            pa->setStartValue(QRect(lb->x(), lb->y(), this->width(), this->height()));
            pa->setEndValue(QRect(lb->x(), lb->y() + 48, this->width(), this->height()));
            pa->start();
        }
        emit Iswin();
    }
}),
new QShortcut(QKeySequence("D"), this, [=](){
    if (!this->bwait) {
        this->bwait = true;
        QPixmap pmPlayer(":/Playerd.png");
        pmPlayer = pmPlayer.scaled(48, 48);
        lb->setPixmap(pmPlayer);
        if (this->bwait = 19 != ((cp - &strmaze[0][0]) % 20) && '*' != cp[1]){
            if (this->bmove){
                lb->move(0, -484);
                this->bmove = false;
            }
            this->cp++;
            pa->setEasingCurve(QEasingCurve::Linear);
            pa->setStartValue(QRect(lb->x(), lb->y(), this->width(), this->height()));
            pa->setEndValue(QRect(lb->x() + 48, lb->y(), this->width(), this->height()));
            pa->start();
        }
        emit Iswin();
    }
})

好了,我们的Qt简单迷宫游戏也就正式完成了,让我们一起看一下我们做的Qt简单迷宫游戏,回顾bug修复的部分,在博客的最后再见吧。

Qt简单迷宫游戏

connect(this, &MainWindow::Iswin, [=](){
    if (!this->bwin && 'G' == *this->cp){
        this->bwin = true;
        QTimer::singleShot(300, [=](){//直接转到胜利界面bug
            ui->stackedWidget->setCurrentIndex(2);
            QSoundEffect* sound = new QSoundEffect;
            sound->setSource(QUrl::fromLocalFile("//资源中的win.wav胜利音效的路径,自己去改!"));
            sound->play();
            connect(sound, &QSoundEffect::playingChanged, [=](){
            this->close();
        });
    }
}
 new QShortcut(QKeySequence("W"), this, [=](){
     if (!this->bwait) {//无冷却bug
         this->bwait = true;//无冷却bug
         QPixmap pmPlayer(":/Playerw.png");
         pmPlayer = pmPlayer.scaled(48, 48);
         lb->setPixmap(pmPlayer);
         if (this->bwait = ((cp - &strmaze[0][0]) / 20) && '*' != cp[-20]){//无冷却bug
             if (this->bmove){
                 lb->move(0, -484);
                 this->bmove = false;
             }
             this->cp -= 20;
             pa->setEasingCurve(QEasingCurve::Linear);0
             pa->setStartValue(QRect(lb->x(), lb->y(), this->width(), this->height()));
             pa->setEndValue(QRect(lb->x(), lb->y() - 48, this->width(), this->height()));
             pa->start();
         }
         emit Iswin();
     }
 }),
new QShortcut(QKeySequence("A"), this, [=](){
    if (!this->bwait) {//无冷却bug
        this->bwait = true;//无冷却bug
        QPixmap pmPlayer(":/Playera.png");
        pmPlayer = pmPlayer.scaled(48, 48);
        lb->setPixmap(pmPlayer);
        if (this->bwait = ((cp - &strmaze[0][0]) % 20) && '*' != cp[-1]){//无冷却bug
            if (this->bmove){
                lb->move(0, -484);
                this->bmove = false;
            }
            this->cp--;
            pa->setEasingCurve(QEasingCurve::Linear);
            pa->setStartValue(QRect(lb->x(), lb->y(), this->width(), this->height()));
            pa->setEndValue(QRect(lb->x() - 48, lb->y(), this->width(), this->height()));
            pa->start();
        }
        emit Iswin();
    }
}),
new QShortcut(QKeySequence("S"), this, [=](){
    if (!this->bwait) {//无冷却bug
        this->bwait = true;//无冷却bug
        QPixmap pmPlayer(":/Players.png");
        pmPlayer = pmPlayer.scaled(48, 48);
        lb->setPixmap(pmPlayer);
        if (this->bwait = 19 != ((cp - &strmaze[0][0]) / 20) && '*' != cp[20]){//无冷却bug
            if (this->bmove){
                lb->move(0, -484);
                this->bmove = false;
            }
            this->cp += 20;
            pa->setEasingCurve(QEasingCurve::Linear);
            pa->setStartValue(QRect(lb->x(), lb->y(), this->width(), this->height()));
            pa->setEndValue(QRect(lb->x(), lb->y() + 48, this->width(), this->height()));
            pa->start();
        }
        emit Iswin();
    }
}),
new QShortcut(QKeySequence("D"), this, [=](){
    if (!this->bwait) {//无冷却bug
        this->bwait = true;//无冷却bug
        QPixmap pmPlayer(":/Playerd.png");
        pmPlayer = pmPlayer.scaled(48, 48);
        lb->setPixmap(pmPlayer);
        if (this->bwait = 19 != ((cp - &strmaze[0][0]) % 20) && '*' != cp[1]){//无冷却bug
            if (this->bmove){
                lb->move(0, -484);
                this->bmove = false;
            }
            this->cp++;
            pa->setEasingCurve(QEasingCurve::Linear);
            pa->setStartValue(QRect(lb->x(), lb->y(), this->width(), this->height()));
            pa->setEndValue(QRect(lb->x() + 48, lb->y(), this->width(), this->height()));
            pa->start();
        }
        emit Iswin();
    }
})
MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    QPixmap pm(":/start.png");
    QPixmap pma(":/title.png");
    ui->label->setPixmap(pma);
    ui->toolButton->setStyleSheet("QToolButton{border:0px;}");
    ui->toolButton->setIconSize(QSize(200,200));
    ui->toolButton->setIcon(pm);
    connect(ui->toolButton, &QToolButton::clicked, [&](){
        this->startgame();
    });
    connect(ui->actionkaishi1, &QAction::triggered, [&](){
        this->startgame();
    });
    ui->actionkaishi1->setText("开始");
    ui->label_2->setPixmap(QPixmap(":/win(1).png"));
    this->bwin = false;
    this->setFixedSize(1017, 1017);//无法完整显示迷宫,并且在每次调整窗口的大小后,玩家标签lb移动后的位置飘忽不定bug
    this->setWindowIcon(QPixmap(":/icon.png"));//无iconBug
    this->setWindowTitle("迷宫");//标题默认bug
}
Mainwindow的构造函数里
结束
开始
...(开始界面的实现及成员变量的初始化)
设游戏窗口的固定大小为1017x1017
设游戏窗口的icon为资源里名为icon.png的图片
设游戏窗口的标题为“迷宫”
玩家右移的操作里
结束
开始
!this->bwait?
设Mainwindow的成员布尔型变量bwait为真
...(给玩家标签lb的图片设置成资源里玩家向右的图片)
this->bwait = 19 != ((cp - &strmaze[0][0]) % 20) && '*' != cp[1]
...(右移操作)
触发Iswin信号
玩家下移的操作里
结束
开始
!this->bwait?
设Mainwindow的成员布尔型变量bwait为真
...(给玩家标签lb的图片设置成资源里玩家向下的图片)
this->bwait = 19 != ((cp - &strmaze[0][0]) / 20) && '*' != cp[20]
...(下移操作)
触发Iswin信号
玩家左移的操作里
结束
开始
!this->bwait?
设Mainwindow的成员布尔型变量bwait为真
...(给玩家标签lb的图片设置成资源里玩家向左的图片)
this->bwait = ((cp - &strmaze[0][0]) % 20) && '*' != cp[-1]
...(左移操作)
触发Iswin信号
玩家上移的操作里
结束
开始
!this->bwait?
设Mainwindow的成员布尔型变量bwait为真
...(给玩家标签lb的图片设置成资源里玩家向上的图片)
this->bwait = ((cp - &strmaze[0][0]) / 20) && '*' != cp[-20]
...(上移操作)
触发Iswin信号
触发Iswin信号之后的槽函数里
等待0.3秒
结束
开始
!this->bwin && 'G' == *this->cp
设Mainwindow里的成员布尔型变量bwin为真
...(胜利的操作)

你可扩展

如果你觉得这个游戏玩起来之后感到不好玩,那么就可以给你的游戏扩展这一些东西。

  • 随机生成迷宫
  • 限时闯迷宫
  • 多人迷宫竞赛
  • ……

下一篇博客要说的东西

链表的介绍

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/959139.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

SSM电子商城系统

&#x1f345;点赞收藏关注 → 添加文档最下方联系方式咨询本源代码、数据库&#x1f345; 本人在Java毕业设计领域有多年的经验&#xff0c;陆续会更新更多优质的Java实战项目希望你能有所收获&#xff0c;少走一些弯路。&#x1f345;关注我不迷路&#x1f345; 项目视频 电…

springboot3 集成 knife4j(接口文档)

提示&#xff1a;文章是集成 knife4j&#xff0c;而非 swagger2 或者 swagger3&#xff0c;效果如图 文章目录 前言一、添加依赖二、如何集成1.配置文件2.注解部分1.Tag2.Operation3.Parameter4.Schema 3.使用 总结 前言 提示&#xff1a;&#xff1a;大家在开发阶段&#xff…

亚博microros小车-原生ubuntu支持系列:7-脸部检测

背景知识 官网介绍&#xff1a; Face Mesh - mediapipe mpFaceMesh.FaceMesh() 类的参数有&#xff1a;self.staticMode, self.maxFaces, self.minDetectionCon, self.minTrackCon staticMode:是否将每帧图像作为静态图像处理。如果为 True&#xff0c;每帧都会进行人脸检测…

写作利器:如何用 PicGo + GitHub 图床提高创作效率

你好呀&#xff0c;欢迎来到 Dong雨 的技术小栈 &#x1f331; 在这里&#xff0c;我们一同探索代码的奥秘&#xff0c;感受技术的魅力 ✨。 &#x1f449; 我的小世界&#xff1a;Dong雨 &#x1f4cc; 分享我的学习旅程 &#x1f6e0;️ 提供贴心的实用工具 &#x1f4a1; 记…

thingsboard 动态报警

前言 考虑将报警上下限写入设备属性&#xff0c;设备遥测数据与设备属性实时做报警逻辑。这样做的好处在于&#xff0c;可以动态修改设备属性&#xff0c;进而修改设备报警触发上下限。 1、修改设备属性 基于mq &#xff0c;向设备写入属性。 topic v1/devices/me/attribut…

三、双链表

链表的种类有很多&#xff0c;单链表是不带头不循环单向链表&#xff0c;但双链表是带头循环双向链表&#xff0c;并且双链表还有一个哨兵位&#xff0c;哨兵位不是头节点 typedef int LTDataType;typedef struct ListNode{struct ListNode* next; //指针保存下⼀个结点的地址s…

(算法竞赛)使用广度优先搜索(BFS)解决迷宫最短路径问题

在这个充满奇思妙想的世界里&#xff0c;每一次探索都像是打开了一扇通往新世界的大门。今天&#xff0c;我们将踏上一段特别的旅程&#xff0c;去揭开那些隐藏在代码、算法、数学谜题或生活智慧背后的秘密。&#x1f389;&#x1f60a; 所以&#xff0c;系好安全带&#xff0…

支持大功率输出高速频闪的图像处理用光源控制器

机器视觉系统中的光源控制器在确保图像质量、提高系统稳定性、降低能耗以及方便系统扩展和升级等方面发挥着重要作用。它可提供稳定光源&#xff0c;调节参数&#xff0c;另外具有操作便捷性。 下面我们来看Gardasoft的光源控制器&#xff0c;Gardasoft拥有作为图像处理用LED光…

鸿蒙模块概念和应用启动相关类(HAP、HAR、HSP、AbilityStage、UIAbility、WindowStage、window)

目录 鸿蒙模块概念 HAP entry feature har shared 使用场景 HAP、HAR、HSP介绍 HAP、HAR、HSP开发 应用的启动 AbilityStage UIAbility WindowStage Window 拉起应用到显示到前台流程 鸿蒙模块概念 HAP hap包是手机安装的最小单元&#xff0c;1个app包含一个或…

为什么IDEA提示不推荐@Autowired❓️如果使用@Resource呢❓️

前言 在使用 Spring 框架时&#xff0c;依赖注入&#xff08;DI&#xff09;是一个非常重要的概念。通过注解&#xff0c;我们可以方便地将类的实例注入到其他类中&#xff0c;提升开发效率。Autowired又是被大家最为熟知的方式&#xff0c;但很多开发者在使用 IntelliJ IDEA …

【Uniapp-Vue3】uni-icons的安装和使用

一、uni-icon的安装 进入到如下页面中&#xff0c;点击“点击下载&安装”。 uni-icons 图标 | uni-app官网 点击“下载插件并导入HBuilder”&#xff0c;如果没有登录就登陆一下 网页中会打开Hbuilder&#xff0c;进入Hbuilder以后&#xff0c;选择需要使用该插件的项目进…

论文笔记(六十三)Understanding Diffusion Models: A Unified Perspective(三)

Understanding Diffusion Models: A Unified Perspective&#xff08;三&#xff09; 文章概括 文章概括 引用&#xff1a; article{luo2022understanding,title{Understanding diffusion models: A unified perspective},author{Luo, Calvin},journal{arXiv preprint arXiv:…

群晖docker获取私有化镜像http: server gave HTTP response to HTTPS client].

群晖docker获取私有化镜像提示http: server gave HTTP response to HTTPS clien 问题描述 层级时间用户事件Information2023/07/08 12:47:45cxlogeAdd image from xx.xx.31.240:1923/go-gitea/gitea:1.19.3Error2023/07/08 12:47:48cxlogeFailed to pull image [Get "http…

Charles 4.6.7 浏览器网络调试指南:HTTPS抓包(三)

概述 在现代互联网应用中&#xff0c;网络请求和响应是服务交互的核心。对于开发者和测试人员来说&#xff0c;能够准确捕获并分析这些请求&#xff0c;是保证系统稳定性和性能的关键。Charles作为一个强大的网络调试工具&#xff0c;不仅可以捕获普通的HTTP请求&#xff0c;还…

从spec到iso的koji使用

了解一下Linux发行版流程&#xff1a;:从spec到iso的koji使用 for Fedora 41。 Fedora 41有24235个包&#xff0c;我们选择 minimal 的几十个源码包&#xff0c;百多个rpm包构建。 配3台服务器 40C64G 44C64G 80C128G&#xff0c;有点大材小用&#xff0c;一台就够了 &#xf…

系统思考—复杂问题的根源分析

在企业中&#xff0c;许多问题看似简单&#xff0c;背后却潜藏着复杂的因果关系。传统的思维方式往往只能看到表面&#xff0c;而无法深入挖掘问题的真正根源。我们常常通过“表面解决”来应对眼前的症状&#xff0c;但这往往只是治标不治本。 比如&#xff0c;销量下降时&…

低代码开发:效率革命与市场机遇

一、引言 IT技术推动了全球信息化的浪潮&#xff0c;然而软件开发效率的提升却未能像摩尔定律那样迅速&#xff0c;逐渐成为发展的瓶颈。近年来&#xff0c;低代码领域发展迅猛&#xff0c;不仅诞生了估值超10亿美元的独角兽OutSystems&#xff0c;还吸引了AWS、Google、Micro…

leetcode——相交链表(java)

给你两个单链表的头节点 headA 和 headB &#xff0c;请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点&#xff0c;返回 null 。 图示两个链表在节点 c1 开始相交&#xff1a; 题目数据 保证 整个链式结构中不存在环。 注意&#xff0c;函数返回结果后&…

浅谈APP之历史股票通过echarts绘图

浅谈APP之历史股票通过echarts绘图 需求描述 今天我们需要做一个简单的历史股票收盘价格通过echarts进行绘图&#xff0c;效果如下&#xff1a; 业务实现 代码框架 代码框架如下&#xff1a; . 依赖包下载 我们通过网站下载自己需要的涉及的图标&#xff0c;勾选之后进…

【音视频处理】FFmpeg for Windows 安装教程

FFmpeg 是一个强大的多媒体处理工具&#xff0c;可以处理音视频的各种任务&#xff0c;包括格式转换、裁剪、合并等操作&#xff0c;市面上你可以看到的几乎所有的音视频的处理工具内部都离不开FFmpeg的身影。 本文将详细介绍如何在 Windows 系统上安装 FFmpeg。 1. 下载 FFmp…