当前位置: 首页 > news >正文

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_OBJECTpublic: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_NAMESPACEclass MainWindow : public QMainWindow
{Q_OBJECTpublic: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, [=](){//直接转到胜利界面bugui->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) {//无冷却bugthis->bwait = true;//无冷却bugQPixmap pmPlayer(":/Playerw.png");pmPlayer = pmPlayer.scaled(48, 48);lb->setPixmap(pmPlayer);if (this->bwait = ((cp - &strmaze[0][0]) / 20) && '*' != cp[-20]){//无冷却bugif (this->bmove){lb->move(0, -484);this->bmove = false;}this->cp -= 20;pa->setEasingCurve(QEasingCurve::Linear);0pa->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) {//无冷却bugthis->bwait = true;//无冷却bugQPixmap pmPlayer(":/Playera.png");pmPlayer = pmPlayer.scaled(48, 48);lb->setPixmap(pmPlayer);if (this->bwait = ((cp - &strmaze[0][0]) % 20) && '*' != cp[-1]){//无冷却bugif (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) {//无冷却bugthis->bwait = true;//无冷却bugQPixmap pmPlayer(":/Players.png");pmPlayer = pmPlayer.scaled(48, 48);lb->setPixmap(pmPlayer);if (this->bwait = 19 != ((cp - &strmaze[0][0]) / 20) && '*' != cp[20]){//无冷却bugif (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) {//无冷却bugthis->bwait = true;//无冷却bugQPixmap pmPlayer(":/Playerd.png");pmPlayer = pmPlayer.scaled(48, 48);lb->setPixmap(pmPlayer);if (this->bwait = 19 != ((cp - &strmaze[0][0]) % 20) && '*' != cp[1]){//无冷却bugif (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移动后的位置飘忽不定bugthis->setWindowIcon(QPixmap(":/icon.png"));//无iconBugthis->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为真
...(胜利的操作)

你可扩展

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

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

下一篇博客要说的东西

链表的介绍

相关文章:

Qt简单迷宫游戏

目录 你将学到你将准备你将改变你将设计你将编程开始界面游玩界面胜利界面其它bug修复 你可扩展下一篇博客要说的东西 你将学到 Qt中QKeySequence对象的基本创建Qt中QShortcut对象的基本应用Qt中QSoundEffect对象的基本应用 你将准备 在开始制作Qt简单迷宫游戏之前&#xff…...

Webrtc (1) - Windows 编译

最近项目上遇到webrtc wgc 的几个test case无法通过&#xff0c;与webrtc人员沟通后决定要自行修复一下(因为他们不想管…) 参考文档 https://webrtc.org/support/contributinghttps://chromium.googlesource.com/chromium/src//main/docs/#checking-out-and-building 以上两…...

深圳大学-智能网络与计算-实验一:RFID原理与读写操作

实验目的与要求 掌握超高频RFID标签的寻卡操作。掌握超高频RFID标签的读写操作。掌握超高频RFID标签多张卡读取时的防冲突机制。 方法&#xff0c;步骤 软硬件的连接与设置超高频RFID寻卡操作超高频RFID防冲突机制超高频RFID读写卡操作 实验过程及内容 一&#xff0e;软硬…...

文献引用指南ChatGPT提示词分享

文献引用指南 在学术写作中&#xff0c;准确引用是至关重要的环节。它不仅能够为您的研究提供坚实的学术基础&#xff0c;还能确保您尊重并认可他人的学术成果&#xff0c;从而有效避免抄袭的问题。而ChatGPT在这一方面同样能够为您提供有力的支持。借助ChatGPT&#xff0c;您…...

什么是计算机应用基础知识

计算机应用基础知识是指学习和掌握计算机的基本理论、硬件、软件、网络和应用技能的基础内容。它是为使用计算机进行日常工作、学习和解决实际问题打下的基础。计算机应用基础知识涉及多个领域,主要包括以下几个方面: 一、计算机硬件基础 计算机硬件组成:了解计算机的基本组…...

SpringBoot集成Flink-CDC,实现对数据库数据的监听

一、什么是 CDC &#xff1f; CDC 是Change Data Capture&#xff08;变更数据获取&#xff09;的简称。 核心思想是&#xff0c;监测并捕获数据库的变动&#xff08;包括数据或数据表的插入、 更新以及删除等&#xff09;&#xff0c;将这些变更按发生的顺序完整记录下来&…...

微信小程序云开发服务端存储API 从云存储空间删除文件

deleteFile 从云存储空间删除文件 const cloud require(wx-server-sdk)exports.main async (event, context) > {const fileIDs [xxx, xxx]const result await cloud.deleteFile({fileList: fileIDs,})return result.fileList }写的资式如有不对&#xff0c;请各位大神多…...

[操作系统] 深入进程地址空间

程序地址空间回顾 在C语言学习的时&#xff0c;对程序的函数、变量、代码等数据的存储有一个大致的轮廓。在语言层面上存储的地方叫做程序地址空间&#xff0c;不同类型的数据有着不同的存储地址。 下图为程序地址空间的存储分布和和特性&#xff1a; 使用以下代码来验证一下…...

gitlab处理空文件夹的提交或空文件夹提交失败

问题描述 厂家发给了我一个压缩包文件&#xff0c;压缩包解压之后本地编译没问题&#xff1b;推送到gitlab之后&#xff0c;再编译就报错了&#xff1b; 问题原因 经过分析之后发现&#xff0c;压缩包解压之后存在很多空文件夹&#xff1b;但是gitlab推送的时候&#xff0c;…...

C++ —— 智能指针 unique_ptr (上)

C —— 智能指针 unique_ptr &#xff08;上&#xff09; 普通指针的不足普通指针的释放智能指针智能指针 unique_ptr智能指针初始化错误用法get()方法返回裸指针智能指针不支持指针的运算&#xff08;、-、、- -&#xff09; 普通指针的不足 new和new [] 的内存需要用delete和…...

ruoyi-vue-pro集成magic-api(图文代码)

目录 前言1. 配置依赖2. 集成登录3. 成功展示前言 🤟 找工作,来万码优才:👉 #小程序://万码优才/r6rqmzDaXpYkJZF 原先写过简单的集成:了解 magic-api的基本用法 附实战代码 magic-api相关文档推荐阅读:接口鉴权 相关的Java代码推荐阅读: java框架 零基础从入门到精…...

【线上问题定位处理】及【性能优化】系列文章

目录 性能优化 性能优化 九大服务架构性能优化方式 如何进行GC调优 如何排查线上系统出现的Full GC MySQL - 性能优化 MySQL - 分库分表 大数据查询的处理方案 MySQL优化手段有哪些 服务CPU100%问题如何快速定位? 服务内存OOM问题如何快速定位? JVM调优6大步骤 线…...

如何解压rar格式文件?8种方法(Win/Mac/手机/网页端)

RAR 文件是一种常见的压缩文件格式&#xff0c;由尤金・罗谢尔&#xff08;Eugene Roshal&#xff09;开发&#xff0c;因其扩展名 “rar” 而得名。它通过特定算法将一个或多个文件、文件夹进行压缩&#xff0c;大幅减小存储空间&#xff0c;方便数据传输与备份。然而&#xf…...

GORM 支持的数据库解析

GORM 是一个流行的 Go 语言 ORM&#xff08;对象关系映射&#xff09;库&#xff0c;它简化了 Go 与数据库之间的交互。作为一款功能强大的库&#xff0c;GORM 支持多种主流关系型数据库&#xff0c;满足不同开发场景下的需求。本文将探讨 GORM 支持的主要数据库及其特点&#…...

分布式光纤应变监测是一种高精度、分布式的监测技术

一、土木工程领域 桥梁结构健康监测 主跨应变监测&#xff1a;在大跨度桥梁的主跨部分&#xff0c;如悬索桥的主缆、斜拉桥的斜拉索和主梁&#xff0c;分布式光纤应变传感器可以沿着这些关键结构部件进行铺设。通过实时监测应变情况&#xff0c;能够精确捕捉到车辆荷载、风荷…...

如何在oracle关闭情况下如何修改spfile的参数

如何在oracle关闭情况下如何修改spfile的参数 一、问题背景二、处理方案 一、问题背景 在ORACLE数据库启动状态下通过下列代码修改了最大连接数 alter system set processes2000 scopespfile sid*;关闭了数据库再重新启动提示如下报错&#xff1a; ORA-00838: Specified valu…...

[ Spring ] Spring Cloud Gateway 2025 Comprehensive Overview

文章目录 Spring Gateway ArchitectureProject Level DependencyService CenterService ProviderGateway ServiceLaunch All Service Spring Gateway Architecture Service Center : register and find service providerService Provider : programs that provide actual serv…...

软考信安27~Windows操作系统安全相关

1、Windows账户与组管理 1.1、用户账户查看 whoami #查看当前登录的用户名称 whoami /all #查看当前系统的用户名和组信息,以及SID whoami /user #查看当前用户的SID net user #查看系统中包含哪些用户 wmic useraccount get name,sid #查看…...

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整合Mybatis、junit纯注解

如何创建一个Spring项目 错误问题 不知道什么原因&#xff0c;大概是依赖版本不兼容、java版本不对的问题&#xff0c;折磨了好久就是搞不成。 主要原因看pom.xml配置 pom.xml配置 java版本 由于是跟着22年黑马视频做的&#xff0c;java版本换成了jdk-11&#xff0c;用21以…...

HAM-TTS大模型:基于token的零样本文字转语音分层声学建模

HAM-TTS大模型:基于token的零样本文字转语音分层声学建模 ​​ 吉利自研语音大模型HAM-TTS的全称是:Hierarchical Acoustic Modeling for Token-Based Zero-Shot Text-to-Speech,直译是基于token的零样本文字转语音分层声学建模,是星睿AI大模型体系下的重要一员。顾名思义…...

SpringBoot开发(三)SpringBoot介绍、项目创建、运行

1. SpringBoot 1.1. SpringBoot介绍 Spring Boot给世界程序员带来了春天&#xff0c;越来越多的企业选择使用spring boot来开发他们的软件&#xff0c;因此学习spring boot是科技发展的必然趋势。本门课程将从web最基础的知识点开始讲起&#xff0c;逐步带你攻破spring boot的…...

SQL Server 建立每日自动log备份的维护计划

SQLServer数据库可以使用维护计划完成数据库的自动备份&#xff0c;下面以在SQL Server 2012为例说明具体配置方法。 1.启动SQL Server Management Studio&#xff0c;在【对象资源管理器】窗格中选择数据库实例&#xff0c;然后依次选择【管理】→【维护计划】选项&#xff0…...

git Bash通过SSH key 登录github的详细步骤

1 问题 通过在windows 终端中的通过git登录github 不再是通过密码登录了&#xff0c;需要本地生成一个密钥&#xff0c;配置到gihub中才能使用 2 步骤 &#xff08;1&#xff09;首先配置用户名和邮箱 git config --global user.name "用户名"git config --global…...

软件越跑越慢的原因分析

如果是qt软件&#xff0c;可以用Qt Creator Profiler 作性能监控如果是通过web请求&#xff0c;可以用JMeter监控。 软件运行过程中逐渐变慢的现象&#xff0c;通常是因为系统资源&#xff08;如 CPU、内存、磁盘 I/O 等&#xff09;逐渐被消耗或软件中存在性能瓶颈。这个问题…...

C++AVL树(二)详解

文章目录 AVL树旋转单旋右单旋左单旋 双旋左右双旋右左双旋 平衡因子的更新左右双旋右左双旋 判断是不是AVL树时间复杂度分析全部的代码 AVL树 旋转 单旋 单旋是纯粹的一边高 单旋平衡因子是同号 右单旋 a,b,c自身不能发生旋转 并且也不能不向上继续更新&#xff08;不能停…...

【GoLang】利用validator包实现服务端参数校验时自定义错误信息

在C/S架构下&#xff0c;服务端在校验请求参数时&#xff0c;若出现参数错误&#xff0c;要响应给客户端一个错误消息&#xff0c;通常我们会统一响应“参数错误”。 但是&#xff0c;如果只是一味的提示参数错误&#xff0c;我并不知道具体是哪个参数错了呀&#xff01;能不能…...

AIP-128 声明友好接口

编号128原文链接AIP-128: Declarative-friendly interfaces状态批准创建日期2020-10-06更新日期2020-10-06 许多服务需要与常见的DevOps工具交互&#xff0c;特别是创建和管理可网络寻址资源&#xff08;如虚拟机、负载均衡器、数据库实例等&#xff09;的工具。这些工具采用“…...

【Jave全栈】Java与JavaScript比较

文章目录 前言一、Java1、 历史与背景2、语言特点3、应用场景4、生态系统 二、JavaScript1、历史与背景2、语言特点3、应用场景4、 生态系统 三、相同点四、不同点1、语言类型2、用途3、语法和结构4、性能5、生态系统6、开发模式 前言 Java和JavaScript是两种不同的编程语言&a…...

钉钉群机器人设置——python版本

钉钉群机器人设置——python版本 应用场景钉钉界面操作程序开发效果展示 应用场景 由于工作需要&#xff0c;很多项目执行程序后出现报错信息无法第一时间收到&#xff0c;因此实时预警对于监控程序还是有必要。&#xff08;仅个人观点&#xff09; 参考文档及博客&#xff1a…...

报错:{‘csrf_token‘: [‘The CSRF token is missing.‘]}

flask实现一个简单的注册界面报错 register.html <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>Title</title> </head> <body> <form action"" method"post&…...

STM32新建不同工程的方式

新建工程的方式 1. 安装开发工具 MDK5 / keil52. CMSIS 标准3. 新建工程3.1 寄存器版工程3.2 标准库版工程3.3 HAL/LL库版工程3.4 HAL库、LL库、标准库和寄存器对比3.5 库开发和寄存器的关系 4. STM32CubeMX工具的作用 1. 安装开发工具 MDK5 / keil5 MDK5 由两个部分组成&#…...

Java数据库操作指南:快速上手JDBC【学术会议-2025年数字化教育与信息技术(DEIT 2025】

大会官网&#xff1a;www.ic-deit.org 前言 在现代企业应用中&#xff0c;数据库是数据存储和管理的重要组成部分。Java作为一种广泛使用的编程语言&#xff0c;提供了多种方式与数据库进行交互。本文将介绍 JDBC&#xff08;Java Database Connectivity&#xff09;&#x…...

基于Springboot + vue实现的在线装修管理系统

“前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站&#xff1a;人工智能学习网站” &#x1f496;学习知识需费心&#xff0c; &#x1f4d5;整理归纳更费神。 &#x1f389;源码免费人人喜…...

计算机网络之应用层

本文章目录结构出自于《王道计算机考研 计算机网络_哔哩哔哩_bilibili》 05 应用层 在网上看到其他人做了相关笔记&#xff0c;就不再多余写了&#xff0c;直接参考着学习吧。 王道考研 计算机网络笔记 第六章&#xff1a;应用层_王道考研 应用层 笔记-CSDN博客 DNS&#x…...

SpringMVC框架

第1章 SpringMVC入门 1.1 SpringMVC简介 Spring MVC 全称:Spring Web MVC是 Spring 框架的一部分&#xff0c;专注于实现 Web 应用程序的模型-视图-控制器&#xff08;Model-View-Controller, MVC&#xff09;设计模式。它为构建灵活且松耦合的 Web 应用提供了强大的功能&…...

将 OneLake 数据索引到 Elasticsearch - 第 1 部分

作者&#xff1a;来自 Elastic Gustavo Llermaly 学习配置 OneLake&#xff0c;使用 Python 消费数据并在 Elasticsearch 中索引文档&#xff0c;然后运行语义搜索。 OneLake 是一款工具&#xff0c;可让你连接到不同的 Microsoft 数据源&#xff0c;例如 Power BI、Data Activ…...

flutter跨端UI框架简介

flutter跨端UI框架简介 简介 Flutter是由Google开发的开源应用开发框架&#xff0c;主要用于构建高性能、跨平台的移动、Web和桌面应用程序。Flutter使用Dart语言&#xff0c;提供了一套丰富的Widgets&#xff0c;使开发者能够快速创建美观的用户界面。其最大特点是热重载功能…...

Centos 修改历史读录( HISTSIZE)

history命令 -c #清空命令历史 -r #读历史文件附加到历史列表 -w #保存历史列表到指定的历史文件 命令历史相关环境变量 HISTSIZE #命令历史记录的条数 HISTFILE #指定历史文件&#xff0c;默认为~/.bash_history HISTFILESIZE #命令历史文件记录历史的条数 以上变量可以 exp…...

利用ML.NET精准提取人名

在当今信息爆炸的时代&#xff0c;文本处理任务层出不穷&#xff0c;其中人名提取作为基础且重要的工作&#xff0c;广泛应用于信息检索、社交网络分析、客户关系管理等领域。随着人工智能不断进步&#xff0c;ML.NET作为微软推出的开源机器学习框架&#xff0c;为开发者提供了…...

关于deepin上运行Qt开发的程序

国产化替代是将来各单位的主流趋势&#xff0c;探索自行开发应用程序在国产操作系统上正常运行是将来的主要工作之一。本文浅尝gui程序在统信社区版——deepin上遇到的小问题。 使用Qt在deepin上做了一个类似gif的帧动画弹窗&#xff0c;在编译运行时&#xff0c;程序可以正常…...

3.3 Go函数可变参数

可变参数&#xff08;variadic parameters&#xff09;是一种允许函数接受任意数量参数的机制。它在函数定义中使用 ...type 来声明参数类型&#xff0c;所有传递的参数会被收集为一个切片&#xff0c;函数内部可以像操作普通切片一样处理这些参数。 package mainimport "…...

Queries Acceleration -Tuning- Tuning Execution 学习笔记

1 Adjustment of RuntimeFilter Wait Time 1.1 Case: Too Short RuntimeFilter Wait Time 1.1.1 没有看懂,好像是等待时间过小也会导致性能下降 1.1.2 set runtime_filter_wait_time_ms = 3000; 2 Data Skew Handling 2.1 Case 1: Bucket Data Skew Leading to Suboptimal …...

VS C++ 配置OPENCV环境

VS C 配置OPENCV环境 1.下载opencv2.安装环境3.opencv环境4.VS配置opencv环境5.EXE执行文件路径的环境lib和dll需要根据是debug还是release环境来区分使用哪个 6.Windows环境 1.下载opencv 链接: link 2.安装环境 双击运行即可 3.opencv环境 include文件路径:opencv\build\…...

Midscene.js:重新定义UI自动化的新时代工具

前言 Midscene.js 是一个创新的、面向开发者的 UI 自动化解决方案&#xff0c;并通过人工智能技术简化自动化脚本的编写与维护。 它提供了三种核心方法——交互&#xff08;.ai, .aiAction&#xff09;、提取&#xff08;.aiQuery&#xff09;和断言&#xff08;.aiAssert&am…...

记录让cursor帮我给ruoyi-vue后台管理项目整合mybatis-plus

自己整合过程中会出现 work.web.exception.GlobalExceptionHandler :100 | 请求地址/admin/device/install/detail/1,发生未知异常. org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): com.fire.mapper.DeviceInstallMapper.selectById at o…...

uniapp中h5的微应用解决办法

考虑过用wujie&#xff0c;参考官网Vue组件封装 | 无界的教程&#xff0c;虽然没报错&#xff0c;但是子应用的vue节点根本没挂载上&#xff0c;不知道什么原因&#xff0c;如下图所示 后面采用iframe方式将子应用导入进来&#xff1a; 父应用&#xff1a; <template>&…...

再尝Semantic Kernel,planning特性很香

背景 书接上回《浅尝semantic kernel》&#xff1b; 上次只试了试SK框架的插件特性&#xff0c;这次试了下编排的特性&#xff0c;也就是把多个本地任务按顺序按逻辑执行&#xff0c;没想到效果也是出奇的好&#xff01; 关键步骤 架构层面的代码本篇不在赘述&#xff0c;这…...

华为OD机试真题---战场索敌

华为OD机试真题“战场索敌”是一道考察算法和数据结构应用能力的题目。以下是对该题目的详细解析&#xff1a; 一、题目描述 有一个大小是NM的战场地图&#xff0c;被墙壁’#‘分隔成大小不同的区域。上下左右四个方向相邻的空地’.‘属于同一个区域&#xff0c;只有空地上可…...

Case逢无意难休——深度解析JAVA中case穿透问题

Case逢无意难休——深度解析JAVA中case穿透问题~ 不作溢美之词&#xff0c;不作浮夸文章&#xff0c;此文与功名进取毫不相关也&#xff01;与大家共勉&#xff01;&#xff01; 更多文章&#xff1a;个人主页 系列文章&#xff1a;JAVA专栏 欢迎各位大佬来访哦~互三必回&#…...