Qt简单迷宫游戏

news2025/1/27 8:45:11

目录

  • 你将学到
  • 你将准备
  • 你将改变
  • 你将设计
  • 你将编程
    • 开始界面
    • 游玩界面
    • 胜利界面
    • 其它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为真
...(胜利的操作)

你可扩展

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

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

下一篇博客要说的东西

链表的介绍

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Ansys Thermal Desktop 概述

介绍 Thermal Desktop 是一种用于热分析和流体分析的通用工具。它可用于组件或系统级分析。 来源&#xff1a;CRTech 历史 Thermal Desktop 由 C&R Technologies (CR Tech) 开发。它采用了 SINDA/FLUINT 求解器。SINDA/FLUINT 最初由 CR Tech 的创始人为 NASA 的约翰逊航…

WPF基础 | WPF 基础概念全解析:布局、控件与事件

WPF基础 | WPF 基础概念全解析&#xff1a;布局、控件与事件 一、前言二、WPF 布局系统2.1 布局的重要性与基本原理2.2 常见布局面板2.3 布局的测量与排列过程 三、WPF 控件3.1 控件概述与分类3.2 常见控件的属性、方法与事件3.3 自定义控件 四、WPF 事件4.1 路由事件概述4.2 事…

西门子【Library of General Functions (LGF) for SIMATIC S7-1200 / S7-1500】

文章目录 概要整体架构流程技术名词解释技术细节小结 概要 通用函数库 (LGF) 扩展了 TIA Portal 中用于 PLC 编程的 STEP 7 指令&#xff08;数学函数、时间、计数器 等&#xff09;。该库可以不受限制地使用&#xff0c;并包含 FIFO 、搜索功能、矩阵计算、 astro 计…

Android实战经验篇-AndroidScrcpyClient投屏一

系列文章转如下链接&#xff1a; Android Display Graphics系列文章-汇总 Android实战经验篇-系列文章汇总 本文主要包括部分&#xff1a; 一、方案说明 1.1 适用场景 1.2 方案框架 二、功能演示 2.1 环境准备 2.2 演示 一、方案说明 1.1 适用场景 优秀的开源的scrc…

从 Spark 到 StarRocks:实现58同城湖仓一体架构的高效转型

作者&#xff1a;王世发&#xff0c;吴艳兴等&#xff0c;58同城数据架构部 导读&#xff1a; 本文介绍了58同城在其数据探查平台中引入StarRocks的实践&#xff0c;旨在提升实时查询性能。在面对传统Spark和Hive架构的性能瓶颈时&#xff0c;58同城选择StarRocks作为加速引擎&…

wangEditor富文本编辑器,Laravel上传图片配置和使用

文章目录 前言步骤1. 构造好前端模版2. 搭建后端存储3. 调试 前言 由于最近写项目需要使用富文本编辑器&#xff0c;使用的是VUE3.0版本所以很多不兼容&#xff0c;实际测试以后推荐使用wangEditor 步骤 构造好前端模版搭建后端存储调试 1. 构造好前端模版 安装模版 模版安…

【MySQL】我在广州学Mysql 系列——MySQL用户管理详解

ℹ️大家好&#xff0c;我是练小杰&#xff0c;本博客是春节前最后一篇了&#xff0c;在此感谢大佬们今年的支持&#xff01;&#xff01;&#x1f64f;&#x1f64f; 接下来将学习MYSQL用户管理的相关概念以及命令~~ 回顾&#xff1a;&#x1f449;【MYSQL触发器的使用】 数据…

2025年数学建模美赛 A题分析(3)楼梯使用方向偏好模型

2025年数学建模美赛 A题分析&#xff08;1&#xff09;Testing Time: The Constant Wear On Stairs 2025年数学建模美赛 A题分析&#xff08;2&#xff09;楼梯磨损分析模型 2025年数学建模美赛 A题分析&#xff08;3&#xff09;楼梯使用方向偏好模型 2025年数学建模美赛 A题分…

Spring Security(maven项目) 3.0.2.7版本

通过实践而发现真理&#xff0c;又通过实践而证实真理和发展真理。从感性认识而能动地发展到理性认识&#xff0c;又从理性认识而能动地指导革命实践&#xff0c;改造主观世界和客观世界。实践、认识、再实践、再认识&#xff0c;这种形式&#xff0c;循环往复以至无穷&#xf…

【二叉树】4. 判断一颗二叉树是否是平衡二叉树。5. 对称二叉树。6. 二叉树的构建及遍历 7. 二叉树的分层遍历 。

判断一颗二叉树是否是平衡二叉树。OJ链接 可以在求树高度的过程中判断树是否平衡 对称二叉树。OJ链接 二叉树的构建及遍历。OJ链接 注意&#xff1a;public static int i最好把static去掉 否则当有多个测试用例时 i无法重新为0二叉树的分层遍历 。OJ链接 但此题要求返回List…

Java如何实现反转义

Java如何实现反转义 前提 最近做的一个需求&#xff0c;是热搜词增加换一批的功能。功能做完自测后&#xff0c;交给了测试伙伴&#xff0c;但是测试第二天后就提了一个bug&#xff0c;出现了未知词 levis。第一眼看着像公司售卖的一个品牌-李维斯。然后再扒前人写的代码&…

“大模型横扫千军”背后的大数据挖掘--浅谈MapReduce

文章目录 O 背景知识1 数据挖掘2 邦费罗尼原则3 TF.IDF4 哈希函数5 分布式文件系统 一、MapReduce基本介绍1. Map 任务2. 按键分组3. Reduce 任务4. 节点失效处理5.小测验&#xff1a;在一个大型语料库上有100个map任务和若干reduce任务&#xff1a; 二、基于MapReduce的基本运…

蓝桥杯3519 填充 | 分类讨论

题目传送门 很简单&#xff0c;遍历一次字符串&#xff0c;将‘?’作为0或1处理&#xff0c;发现00和11统计次数即可。 s str(input()) cnt 0 arr [00, 11, 0?, ?0, 1?, ?1, ??] i0 while i < len(s)-1:if s[i:(i2)] in arr:i 2cnt 1else:i 1 print(cnt)END✨

嵌入式知识点总结 ARM体系与架构 专题提升(一)-硬件基础

嵌入式知识点总结 ARM体系与架构 专题提升(一)-硬件基础 目录 1.NAND FLASH 和NOR FLASH异同 ? 2.CPU,MPU,MCU,SOC,SOPC联系与差别? 3.什么是交叉编译&#xff1f; 4.为什么要交叉编译&#xff1f; 5.描述一下嵌入式基于ROM的运行方式和基于RAM的运行方式有什么区别? 1…

EchoMimicV2的部署使用

最近有一个录课的需要&#xff0c;我不想浪费人力&#xff0c;只想用技术解决。需求很简单&#xff0c;就是用别人现成的录课视频中的形象和声线&#xff0c;再结合我提供的讲稿去生成一个新的录课视频。我觉得应该有现成的技术了&#xff0c;我想要免费大批量生产。最近看到这…

迅为RK3568开发板篇OpenHarmony实操HDF驱动控制LED-添加内核编译

编译内核时将该 HDF 驱动编译到镜像中&#xff0c;接下来编写驱动编译脚本 Makefile&#xff0c;代码如下所示&#xff1a; 加入编译体系&#xff0c;填加模块目录到 drivers/hdf_core/adapter/khdf/linux/Makefile 文件 更多内容可以关注&#xff1a;迅为RK3568开发板篇OpenHa…

期权帮|在股指期货中超过持仓限额怎么办?

锦鲤三三每日分享期权知识&#xff0c;帮助期权新手及时有效地掌握即市趋势与新资讯&#xff01; 在股指期货中超过持仓限额怎么办&#xff1f; 一、立即平仓或减仓&#xff1a; &#xff08;1&#xff09;最直接且有效的方法是立即平仓或减仓&#xff0c;以降低持仓量至限额…

Linux 高级路由与流量控制-用 tc qdisc 管理 Linux 网络带宽

大家读完记得觉得有帮助记得关注和点赞&#xff01;&#xff01;&#xff01; 此分享内容比较专业&#xff0c;很多与硬件和通讯规则及队列&#xff0c;比较底层需要有技术功底人员深入解读。 Linux 的带宽管理能力 足以媲美许多高端、专用的带宽管理系统。 1 队列&#xff0…

openstack单机安装

openstack单机安装 网卡配置安装依赖开启虚拟环境修改配置文件 部署openstack部署openstack客户端访问可视化界面Horizon补充 本篇主要讲述Ubuntu2204单机安装openstackstable/2024.2。其他版本的Linux系统或者openstack版本&#xff0c;请参考openstack官网。 网卡配置 需要配…

计算机视觉-卷积

卷积-图像去噪 一、图像 二进制 灰度 彩色 1.1二进制图像 0 1 一个点可以用一个bit&#xff08;0/1&#xff09;来表示 1.2灰度图像 0-255 一个点可以用一个byte来表示 1.3彩色图像 RGB 表达一个彩色图像先说它的分辨率p/w&#xff08;宽&#xff09;和q/h&#xff08;高…