学习视频链接
05 开始按钮创建_哔哩哔哩_bilibili05 开始按钮创建是最新QT从入门到实战完整版|传智教育的第46集视频,该合集共计63集,视频收藏或关注UP主,及时了解更多相关视频内容。https://www.bilibili.com/video/BV1g4411H78N?p=46&spm_id_from=333.1007.top_right_bar_window_history.content.click
项目资料,提取码 6666
百度网盘 请输入提取码
目录
一、主场景配置
1.1 设置窗口大小、图标、标题
1.2 开始菜单栏
1.3 槽函数
1.4 绘图事件
二、开始按钮的创建
2.1 新建类
2.2 构造函数
2.3 加入到主场景中
2.4 点击事件
2.5 平缓移动
三、新场景
3.1 创建新场景
3.2 进入新的场景
3.3 新场景设置
3.4 绘图事件
3.5 返回按钮
3.6 选择关卡按钮
四、翻金币的场景
4.1 创建类
4.2 选择关卡场景中添加关卡
4.3 游戏场景中的配置
4.4 游戏场景返回到选择关卡
4.5 显示当前关卡数
4.6 显示金币背景图案
五、翻转金币
5.1 金币类
5.2 游戏场景中包含金币
5.3 初始化金币银币数据
5.4 金币翻转特效实现
5.5 翻转周围的金币
5.7 周围延时翻转
六、胜利处理
6.1 胜利判断并禁用按钮
6.2 胜利图片实现
6.3 优化一些内容
6.5 设置音效
七、优化和发布
7.1 在每个场景中都保持在一个位置
7.2 打包发布
/* 配置主场景 */
// 设置固定大小
setFixedSize(320, 500);
// 设置图标
setWindowIcon(QIcon(":/res/Coin0001.png"));
// 设置标题
setWindowTitle("翻金币主场景");
图片缩放
void MainScene::paintEvent(QPaintEvent *)
{
QPainter painter(this);
QPixmap pix;
// 背景
pix.load(":/res/PlayLevelSceneBg.png");
painter.drawPixmap(0, 0, this->width(), this->height(), pix); // 图片自动拉伸适应屏幕
// 图标
pix.load(":/res/Title.png");
pix = pix.scaled(pix.width() * 0.5, pix.height() * 0.5); // 宽高变成原来的0.5
painter.drawPixmap(10, 30, pix);
}
#ifndef MYPUSHBUTTON_H
#define MYPUSHBUTTON_H
#include
#include
class MyPushButton : public QPushButton
{
Q_OBJECT
public:
// 构造函数 参数1:正常显示的图片路径 参数2:按下后显示的图片路径
MyPushButton(QString normalImg, QString pressImg = "");
// 成员属性 保存用户传入的默认显示路径以及按下后显示的图片路径
QString normalImgPath;
QString pressImgPath;
signals:
};
#endif // MYPUSHBUTTON_H
MyPushButton::MyPushButton(QString normalImg, QString pressImg)
{
this->normalImgPath = normalImg;
this->pressImgPath = pressImg;
QPixmap pix;
bool ret = pix.load(normalImg);
if(!ret)
{
qDebug() << "图片加载失败";
return;
}
// 设置图片固定大小
this->setFixedSize(pix.width(), pix.height());
// 设置不规则图片样式
this->setStyleSheet("QPushButton{border:0px;}");
// 设置图标
this->setIcon(pix);
// 设置图标大小
this->setIconSize(QSize(pix.width(), pix.height()));
}
如果不设置不规则样式就会有这种情况
void MyPushButton::zoom1()
{
// 创建动态对象
QPropertyAnimation *animation = new QPropertyAnimation(this, "geometry");
// 设置动画时间间隔
animation->setDuration(200);
// 起始位置
animation->setStartValue(QRect(this->x(), this->y(), this->width(), this->height()));
// 结束位置
animation->setEndValue(QRect(this->x(), this->y() + 10, this->width(), this->height()));
// 设置弹跳曲线
animation->setEasingCurve(QEasingCurve::OutBounce);
// 执行动画
animation->start();
}
右边就有了一个抖动的效果
添加新的场景类 ChooseLevelScene,把这个场景类放到主场景中
构造函数中加入申请内存的代码,点击后隐藏自身并且显示选择关卡场景
直接跳转太僵硬了,所以加入延时和优化的效果
加入头文件 #include
添加头文件 #include
#include "chooselevelscene.h"
ChooseLevelScene::ChooseLevelScene(QWidget *parent) : QMainWindow(parent)
{
/* 选择关卡场景 */
// 设置固定大小
this->setFixedSize(320, 500);
// 设置图标
this->setWindowIcon(QPixmap(":/res/Coin0001.png"));
// 设置标题
this->setWindowTitle("选择关卡场景");
// 创建菜单栏
QMenuBar *menubar= menuBar();
setMenuBar(menubar);
// 创建开始菜单
QMenu *menuStart = menubar->addMenu("开始");
// 创建退出菜单项
QAction *actionQuit = menuStart->addAction("退出");
// 点击退出实现退出游戏
connect(actionQuit, &QAction::triggered, [=](){
this->close();
});
}
void ChooseLevelScene::paintEvent(QPaintEvent *)
{
QPainter painter(this);
QPixmap pix;
// 背景
pix.load(":/res/OtherSceneBg.png");
painter.drawPixmap(0, 0, this->width(), this->height(), pix); // 图片自动拉伸适应屏幕
// 标题
pix.load(":/res/Title.png");
painter.drawPixmap((this->width() - pix.width()) * 0.5, 30, pix.width(), pix.height(), pix);
}
使用我们封装好的按钮,我们包含一下按钮的头文件 #include "mypushbutton.h",
// 返回按钮
MyPushButton *backBtn = new MyPushButton(":/res/BackButton.png", ":/res/BackButtonSelected.png");
backBtn->setParent(this);
backBtn->move(this->width() - backBtn->width(), this->height() - backBtn->height());
// 点击返回
connect(backBtn, &MyPushButton::clicked, [=](){
qDebug() << "点击了返回按钮";
});
重写按钮按下和释放事件
void MyPushButton::mousePressEvent(QMouseEvent *e)
{
if(this->pressImgPath != "") // 传入的按下图片不为空 说明需要有按下状态,切换图片
{
QPixmap pix;
bool ret = pix.load(this->pressImgPath);
if(!ret)
{
qDebug() << "图片加载失败";
return;
}
// 设置图片固定大小
this->setFixedSize(pix.width(), pix.height());
// 设置不规则图片样式
this->setStyleSheet("QPushButton{border:0px;}");
// 设置图标
this->setIcon(pix);
// 设置图标大小
this->setIconSize(QSize(pix.width(), pix.height()));
}
// 让父类执行其他内容
return QPushButton::mousePressEvent(e);
}
void MyPushButton::mouseReleaseEvent(QMouseEvent *e)
{
if(this->pressImgPath != "") // 传入的按下图片不为空 说明需要有按下状态,切换图片
{
QPixmap pix;
bool ret = pix.load(this->normalImgPath);
if(!ret)
{
qDebug() << "图片加载失败";
return;
}
// 设置图片固定大小
this->setFixedSize(pix.width(), pix.height());
// 设置不规则图片样式
this->setStyleSheet("QPushButton{border:0px;}");
// 设置图标
this->setIcon(pix);
// 设置图标大小
this->setIconSize(QSize(pix.width(), pix.height()));
}
// 让父类执行其他内容
return QPushButton::mouseReleaseEvent(e);
}
现在按钮点击和释放就有不同的样子了
写一个自定义信号,点击按钮后发出信号,主场景接受
// 点击返回
connect(backBtn, &MyPushButton::clicked, [=](){
qDebug() << "点击了返回按钮";
// 告诉主场景 我返回了,主场景监听chooseLevelScene的返回按钮
emit this->chooseSceneBack();
});
// 监听选择关卡的返回按钮的信号
connect(chooseScene, &ChooseLevelScene::chooseSceneBack, this, [=]() {
chooseScene->hide(); // 将选择关卡场景隐藏掉
this->show(); // 重新显示主场景
});
添加头文件 #include
在构造函数里面添加这些代码
// 创建选择关卡的按钮
for(int i = 0; i < 20; i++) {
MyPushButton *menuBtn = new MyPushButton(":/res/LevelIcon.png");
menuBtn->setParent(this);
menuBtn->move(25 + i % 4 * 70, 130 + i / 4 * 70);
// 监听每隔按钮的点击事件
connect(menuBtn, &MyPushButton::clicked, [=](){
QString str = QString("您选择的是第%1关").arg(i + 1);
qDebug() << str;
});
QLabel *label = new QLabel;
label->setParent(this);
label->setFixedSize(menuBtn->width(), menuBtn->height());
label->setText(QString::number(i + 1));
label->move(25 + i % 4 * 70, 130 + i / 4 * 70);
// 设置label上的文字对齐方式 水平居中和垂直居中
label->setAlignment(Qt::AlignCenter | Qt::AlignVCenter);
// 设置让鼠标进行穿透51号属性
label->setAttribute(Qt::WA_TransparentForMouseEvents);
}
重新写一个构造函数
添加头文件 #include
#include "playscene.h"
PlayScene::PlayScene(int levelNum)
{
this->levelIndex = levelNum;
// 初始化游戏场景
// 设置固定大小
this->setFixedSize (320,588) ;
//设置图标
this->setWindowIcon(QPixmap(":/res/Coin0001.png"));
//设置标题
this->setWindowTitle("翻金币场景");
// 创建菜单栏
QMenuBar *menubar= menuBar();
setMenuBar(menubar);
// 创建开始菜单
QMenu *menuStart = menubar->addMenu("开始");
// 创建退出菜单项
QAction *actionQuit = menuStart->addAction("退出");
// 点击退出实现退出游戏
connect(actionQuit, &QAction::triggered, [=](){
this->close();
});
}
重写绘图事件,添加头文件 #include
void PlayScene::paintEvent(QPaintEvent *)
{
QPainter painter(this);
QPixmap pix;
pix.load(":/res/PlayLevelSceneBg.png");
painter.drawPixmap(0, 0, this->width(), this->height(), pix); // 图片自动拉伸适应屏幕
// 加载标题
pix.load(":/res/Title.png");
pix = pix.scaled(pix.width() * 0.5, pix.height() * 0.5); // 宽高变成原来的0.5
painter.drawPixmap(10, 30, pix.width(), pix.height(), pix);
}
先添加自定义按钮的头文件 #include "mypushbutton.h"
添加信号:
点击后发送信号
在游戏选择场景中监听发送的信号,如果返回到这个场景就删除创建的游戏
// 创建选择关卡的按钮
for(int i = 0; i < 20; i++) {
MyPushButton *menuBtn = new MyPushButton(":/res/LevelIcon.png");
menuBtn->setParent(this);
menuBtn->move(25 + i % 4 * 70, 130 + i / 4 * 70);
// 监听每隔按钮的点击事件
connect(menuBtn, &MyPushButton::clicked, [=](){
QString str = QString("您选择的是第%1关").arg(i + 1);
qDebug() << str;
// 进入到游戏场景
this->hide(); // 将选关场景隐藏掉
play = new PlayScene(i + 1); // 创建游戏场景
play->show(); // 显示游戏场景
connect(play, &PlayScene::chooseSceneBack, [=](){
this->show();
delete play;
play = nullptr;
});
});
QLabel *label = new QLabel;
label->setParent(this);
label->setFixedSize(menuBtn->width(), menuBtn->height());
label->setText(QString::number(i + 1));
label->move(25 + i % 4 * 70, 130 + i / 4 * 70);
// 设置label上的文字对齐方式 水平居中和垂直居中
label->setAlignment(Qt::AlignCenter | Qt::AlignVCenter);
// 设置让鼠标进行穿透51号属性
label->setAttribute(Qt::WA_TransparentForMouseEvents);
}
添加头文件 #include
// 显示当前关卡数
QLabel *label = new QLabel;
label->setParent(this);
QFont font;
font.setFamily("黑体");
font.setPointSize(20);
QString str1 = QString("Level:%1").arg(this->levelIndex);
qDebug() << str1;
//将字体设置到标签控件中
label->setFont(font);
label->setText(str1);
label->setGeometry(30, this->height() - 50, 140, 50);
// 显示金币背景图案
for(int i = 0; i < 4; i++) {
for(int j = 0; j < 4; j++)
{
// 绘制背景图片
QLabel* label = new QLabel;
label->setGeometry(0, 0, 50, 50);
label->setPixmap(QPixmap(":/res/BoardNode.png"));
label->setParent(this);
label->move(57 + i * 50, 200 + j * 50);
}
}
#include "mycoin.h"
MyCoin::MyCoin(QString btnImg)
{
QPixmap pix;
bool ret = pix.load(btnImg);
if(!ret)
{
QString str = QString("图片 %1 加载失败").arg(btnImg);
qDebug() << str;
return;
}
// 设置图片固定大小
this->setFixedSize(pix.width(), pix.height());
// 设置不规则图片样式
this->setStyleSheet("QPushButton{border:0px;}");
// 设置图标
this->setIcon(pix);
// 设置图标大小
this->setIconSize(QSize(pix.width(), pix.height()));
}
把配置文件弄进来
右键添加现有文件
包含头文件,然后新加一个二维数组
创建游戏界面的时候初始化二维数组
初始化金币
dataConfig config;
// 初始化每个关卡的二维数组
for(int i = 0; i < 4; i++)
{
for(int j = 0; j < 4; j++)
{
this->gameArray[i][j] = config.mData[this->levelIndex][i][j];
}
}
// 显示金币背景图案
for(int i = 0; i < 4; i++) {
for(int j = 0; j < 4; j++)
{
// 绘制背景图片
QLabel* label = new QLabel;
label->setGeometry(0, 0, 50, 50);
label->setPixmap(QPixmap(":/res/BoardNode.png"));
label->setParent(this);
label->move(57 + i * 50, 157 + j * 50);
// 创建金币
QString str;
if(this->gameArray[i][j] == 1)
{
// 显示金币
str = ":/res/Coin0001.png";
}
else
{
str = ":/res/Coin0008.png";
}
MyCoin *coin = new MyCoin(str);
coin->setParent(this);
coin->move(60 + i * 50, 160 + j * 50);
}
}
添加金币属性
金币来回翻转的效果(类似于 gif 一帧一帧播放)
#include "mycoin.h"
MyCoin::MyCoin(QString btnImg)
{
QPixmap pix;
bool ret = pix.load(btnImg);
if(!ret)
{
QString str = QString("图片 %1 加载失败").arg(btnImg);
qDebug() << str;
return;
}
// 设置图片固定大小
this->setFixedSize(pix.width(), pix.height());
// 设置不规则图片样式
this->setStyleSheet("QPushButton{border:0px;}");
// 设置图标
this->setIcon(pix);
// 设置图标大小
this->setIconSize(QSize(pix.width(), pix.height()));
// 初始化定时器对象
timer1 = new QTimer(this);
timer2 = new QTimer(this);
// 监听金币翻银币的信号,并且翻转金币
connect(timer1, &QTimer::timeout, [=](){
QPixmap pix;
QString str = QString(":/res/Coin000%1.png").arg(this->min++);
pix.load(str);
this->setFixedSize(pix.width(), pix.height());
this->setStyleSheet("QPushButton{border:0px;}");
this->setIcon(pix);
this->setIconSize(QSize(pix.width(), pix.height()));
// 判断 如果翻完了,将min重置为1
if(this->min > this->max)
{
this->min = 1;
timer1->stop();
}
});
// 监听银币金银币的信号,并且翻转银币
connect(timer2, &QTimer::timeout, [=](){
QPixmap pix;
QString str = QString(":/res/Coin000%1.png").arg(this->max--);
pix.load(str);
this->setFixedSize(pix.width(), pix.height());
this->setStyleSheet("QPushButton{border:0px;}");
this->setIcon(pix);
this->setIconSize(QSize(pix.width(), pix.height()));
// 判断 如果翻完了,将max重置为8
if(this->max < this->min)
{
this->max = 8;
timer2->stop();
}
});
}
// 改变金银币标志的方法
void MyCoin::changeFlag()
{
// 如果是正面 翻成反面
if(this->flag)
{
// 开始正面翻方面的定时器
timer1->start(30);
this->flag = false;
}
else // 银币翻金币
{
timer2->start(30);
this->flag = true;
}
}
中间有一些bug,例如动画没有播放完就点了,我们可以限制一下
将创建好的金币放入数组用于维护,并作翻转
// 显示金币背景图案
for(int i = 0; i < 4; i++) {
for(int j = 0; j < 4; j++)
{
// 绘制背景图片
QLabel* label = new QLabel;
label->setGeometry(0, 0, 50, 50);
label->setPixmap(QPixmap(":/res/BoardNode.png"));
label->setParent(this);
label->move(57 + i * 50, 157 + j * 50);
// 创建金币
QString str;
if(this->gameArray[i][j] == 1)
{
// 显示金币
str = ":/res/Coin0001.png";
}
else
{
str = ":/res/Coin0008.png";
}
MyCoin *coin = new MyCoin(str);
coin->setParent(this);
coin->move(60 + i * 50, 160 + j * 50);
// 给金币属性赋值
coin->posX = i;
coin->posY = j;
coin->flag = this->gameArray[i][j]; // 1金币 0银币
// 将金币放入到二维数组 以便后期的维护
coinBtn[i][j] = coin;
// 点击金、银币进行翻转
connect(coin, &MyCoin::clicked, [=](){
coin->changeFlag();
this->gameArray[i][j] = !this->gameArray[i][j];
// 翻转周围硬币
if(coin->posX + 1 <= 3) // 金币的右侧金币翻转条件
{
coinBtn[coin->posX + 1][coin->posY]->changeFlag();
this->gameArray[coin->posX + 1][coin->posY] = !this->gameArray[coin->posX + 1][coin->posY];
}
if(coin->posX - 1 >= 0) // 金币的左侧金币翻转条件
{
coinBtn[coin->posX - 1][coin->posY]->changeFlag();
this->gameArray[coin->posX - 1][coin->posY] = !this->gameArray[coin->posX - 1][coin->posY];
}
if(coin->posY + 1 <= 3) // 金币的上侧金币翻转条件
{
coinBtn[coin->posX][coin->posY + 1]->changeFlag();
this->gameArray[coin->posX][coin->posY + 1] = !this->gameArray[coin->posX][coin->posY + 1];
}
if(coin->posY - 1 >= 0) // 金币的下侧金币翻转条件
{
coinBtn[coin->posX][coin->posY - 1]->changeFlag();
this->gameArray[coin->posX][coin->posY - 1] = !this->gameArray[coin->posX][coin->posY - 1];
}
});
}
}
如果胜利了,所有的银币按钮的标志位都改成游戏胜利
// 翻转周围硬币
// 周围延时翻转
QTimer::singleShot(300, this, [=](){
if(coin->posX + 1 <= 3) // 金币的右侧金币翻转条件
{
coinBtn[coin->posX + 1][coin->posY]->changeFlag();
this->gameArray[coin->posX + 1][coin->posY] = !this->gameArray[coin->posX + 1][coin->posY];
}
if(coin->posX - 1 >= 0) // 金币的左侧金币翻转条件
{
coinBtn[coin->posX - 1][coin->posY]->changeFlag();
this->gameArray[coin->posX - 1][coin->posY] = !this->gameArray[coin->posX - 1][coin->posY];
}
if(coin->posY + 1 <= 3) // 金币的上侧金币翻转条件
{
coinBtn[coin->posX][coin->posY + 1]->changeFlag();
this->gameArray[coin->posX][coin->posY + 1] = !this->gameArray[coin->posX][coin->posY + 1];
}
if(coin->posY - 1 >= 0) // 金币的下侧金币翻转条件
{
coinBtn[coin->posX][coin->posY - 1]->changeFlag();
this->gameArray[coin->posX][coin->posY - 1] = !this->gameArray[coin->posX][coin->posY - 1];
}
// 判断是否胜利
this->isWin = true;
for(int i = 0; i < 4;i ++)
{
for(int j = 0; j < 4; j++)
{
if(coinBtn[i][j]->flag == false) { // 只要有一个是反面,那就算失败
this->isWin = false;
break;
}
}
}
if(this->isWin == true)
{
for(int i = 0; i < 4;i ++)
{
for(int j = 0; j < 4; j++)
{
coinBtn[i][j]->isWin = true;
}
}
}
});
已经胜利就屏蔽点击,下图已经是点击不了了
已经创建了图片,但是在屏幕的上方,我们在后面写一个函数,用于这个胜利的图片掉下来的效果i哦
// 胜利图像绘制
QLabel *winLabel = new QLabel;
QPixmap tmpPix;
tmpPix.load(":/res/LevelCompletedDialogBg.png");
winLabel->setGeometry(0, 0, tmpPix.width(), tmpPix.height());
winLabel->setPixmap(tmpPix);
winLabel->setParent(this);
winLabel->move((this->width() - tmpPix.width()) * 0.5, -tmpPix.height());
加入头文件 #include
// 将胜利的图片从上往下移动
QPropertyAnimation *animation = new QPropertyAnimation(winLabel, "geometry");
// 设置时间间隔
animation->setDuration(1000);
// 设置开始位置
animation->setStartValue(QRect(winLabel->x(), winLabel->y(), winLabel->width(), winLabel->height()));
// 设置结束位置
animation->setEndValue(QRect(winLabel->x(), winLabel->y() + 134, winLabel->width(), winLabel->height()));
// 设置曲线
animation->setEasingCurve(QEasingCurve::OutBounce);
// 执行动画
animation->start();
1、通关后不能再点击,这是因为硬币 isWin 含有初始值为 true,我们可以在构造函数的时候设置硬币的游戏是否胜利为 false
2、手速快出问题了,因为只禁用了一个按钮
解决方案是先把所有按钮禁用了
等完成动画后再开启所有按钮
增加多媒体模块
包含头文件
后面的和前面类似处理,
选择关卡音效
游戏关卡音效
音乐可以循环播放, Qsound 对象里面有 setLoops(10) 这个方法,设置为循环 10 次,也可以 setLoops(-1) 无限循环
因为每次切换场景,如果移动了窗口,新的窗口所在的位置和原来的窗口可能不同
后面可以用其他的打包软件(就是变成压缩包,别人下载后解压用的)