1.主窗体基本设置
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
//1.游戏主场景配置
this->setFixedSize(320, 588);//设置大小
this->setWindowTitle("翻金币");
this->setWindowIcon(QIcon("://res//Coin0001.png"));
//设置退出
connect(ui->actionquit, &QAction::triggered, [=](){
this->close();
});
}
2.设置背景图片(需要重写paintEvent方法)
//2.设置背景图片
QPainter painter(this);// 指定绘图设备为该主窗口
QPixmap img;
img.load("://res//PlayLevelSceneBg.png");
painter.drawPixmap(0, 0, this->width(), this->height(), img);
3.设置标题(需要重写paintEvent方法)
//3.加载标题
img.load("://res//Title.png");
//缩放图片
painter.drawPixmap(10, 30, img.width()*0.5, img.height()*0.5, img);
4.创建开始按钮(需要自定义一个按钮类)
用QPixmap加载图片(load方法,返回值为bool类型,可用此判断是否加载成果)
下面这段代码是为了让图片以不规则图形显示
this->setStyleSheet("QPushButton{border:0px;}");
开始按钮创建完成后,需要对其定义事件,当对其进行点击操作后,应令图片向下向上来回弹跳一次。因此定义了两个动画 函数。
QT的动画操作需要利用QPropertyAnimation类
setDuration方法设置动画的间隔
setStartValue设置动画的起始位置
setEndValue设置动画的结束位置。
在zoom1中,结束位置为起始位置+20,意味着图片会向下走20个像素(QT中的(0,0)点在左上角),而zoom2应从(x,y+20)回到(x,y)处。
setEasingCurve设置弹跳曲线,具体用怎样的曲线可查阅帮助文档
这两个方法需要在主场景中定义事件来执行
void MyPushButton::zoom1()
{
QPropertyAnimation *animation = new QPropertyAnimation(this,"geometry");
animation->setDuration(200);//设置动画间隔
animation->setStartValue(QVariant(QRect(this->x(), this->y(), this->width(), this->height())));
animation->setEndValue(QVariant(QRect(this->x(), this->y()+20, this->width(), this->height())));
animation->setEasingCurve(QEasingCurve::OutBounce);//设置弹跳曲线
animation->start();//执行动画
}
void MyPushButton::zoom2()
{
QPropertyAnimation *animation = new QPropertyAnimation(this,"geometry");
animation->setDuration(200);//设置动画间隔
animation->setStartValue(QVariant(QRect(this->x(), this->y()+20, this->width(), this->height())));
animation->setEndValue(QVariant(QRect(this->x(), this->y(), this->width(), this->height())));
animation->setEasingCurve(QEasingCurve::OutBounce);//设置弹跳曲线
animation->start();//执行动画
}
由于不是所有的按钮都需要点击后执行按下的特效,因此该类的构造函数接收了两个图片(常态的图片路径和按下后的图片路径),若后者的路径为空则意味着不需要有按下特效。
若传入了两个路径,则需要在按下按钮后切换成另一个图片,在释放时切换回常态图片,以实现按下按钮的特效
自定义按钮整体代码如下:
#include "mypushbutton.h"
MyPushButton::MyPushButton(QString normal_path, QString press_path)
{
QPixmap img;
bool isok = img.load(normal_path);//加载图片
if(!isok){
qDebug()<<"加载失败";
}
this->setFixedSize(img.width(), img.height());
this->setIcon(QIcon(img));
this->setIconSize(QSize(this->width(), this->height()));
this->setStyleSheet("QPushButton{border:0px;}");
connect(this, &QPushButton::pressed, [=](){//按下时,若传入了按下的图片,则显示
if(press_path!=""){
QPixmap img;
bool isok = img.load(press_path);
if(!isok){
qDebug()<<"加载失败";
}
this->setIcon(QIcon(img));
}
});
connect(this, &QPushButton::released, [=](){//松开时应还原为normal图片
QPixmap img;
bool isok = img.load(normal_path);
if(!isok){
qDebug()<<"加载失败";
}
this->setFixedSize(img.width(), img.height());
this->setIcon(QIcon(img));
});
}
void MyPushButton::zoom1()
{
QPropertyAnimation *animation = new QPropertyAnimation(this,"geometry");
animation->setDuration(200);//设置动画间隔
animation->setStartValue(QVariant(QRect(this->x(), this->y(), this->width(), this->height())));
animation->setEndValue(QVariant(QRect(this->x(), this->y()+20, this->width(), this->height())));
animation->setEasingCurve(QEasingCurve::OutBounce);//设置弹跳曲线
animation->start();//执行动画
}
void MyPushButton::zoom2()
{
QPropertyAnimation *animation = new QPropertyAnimation(this,"geometry");
animation->setDuration(200);//设置动画间隔
animation->setStartValue(QVariant(QRect(this->x(), this->y()+20, this->width(), this->height())));
animation->setEndValue(QVariant(QRect(this->x(), this->y(), this->width(), this->height())));
animation->setEasingCurve(QEasingCurve::OutBounce);//设置弹跳曲线
animation->start();//执行动画
}
void MyPushButton::setPos(int x, int y)
{
this->move(QPoint(x, y));
}
在主场景中,需要自定义事件来执行动画,点击之后应该停留一段时间,然后跳转到关卡选择场景。
停留方法,QTimer::singleShot(延迟时间,延迟的对象,延迟结束后执行什么操作)
对于延迟后的操作可以用lambda表达式来定义。
此处延迟0.5秒后令当前窗体用hide()方法隐藏,令自定义的choosescene场景显示
connect(start, &QPushButton::clicked, [=](){
start->zoom1();
start->zoom2();
//点击之后,停留0.5秒后自身隐藏,scene显示
//QTimer::singleShot(延迟多久,谁延迟,时间结束后执行什么操作可用lambda表达式表示)
QTimer::singleShot(500, this, [=](){//即0.5秒后让this隐藏,另一个场景scene显示
this->hide();
choosescene->show();
});
});
[点击并拖拽以移动]
5.关卡选择场景设置
主要设置大小、窗体图标、窗体标题、菜单栏、返回按钮。
需要在菜单栏中添加退出事件。
在返回按钮中令其能够返回到主场景
#include "chooselevelscence.h"
ChooseLevelScence::ChooseLevelScence(QWidget *parent)
{
//设置大小
this->setFixedSize(320, 588);
//设置图标
this->setWindowIcon(QIcon("://res//Coin0001.png"));
//设置标题
this->setWindowTitle("选择关卡");
//设置菜单栏
QMenuBar * bar = new QMenuBar(this);
this->setMenuBar(bar);
QMenu * startMenu = bar->addMenu("开始");
QAction * quitAction = startMenu->addAction("退出");
connect(quitAction, &QAction::triggered, [=](){//点击退出后关闭
this->close();
});
//在该场景中添加返回按钮
//返回按钮不需要弹跳
MyPushButton * back = new MyPushButton("://res//BackButton.png","://res//BackButtonSelected.png");
back->setParent(this);
back->setPos(this->width()-back->width()-20, this->height()-back->height()-20);
connect(back, &QPushButton::clicked, [=](){
//点击返回按钮后返回主场景
this->hide();
});
}
ChooseLevelScence::~ChooseLevelScence()
{
}
void ChooseLevelScence::paintEvent(QPaintEvent *ev)
{
QPainter painter(this);
painter.drawPixmap(0, 0, this->width(), this->height(), QPixmap("://res//PlayLevelSceneBg.png"));
//显示标题,显示在中间
QPixmap *title = new QPixmap("://res//Title.png");
painter.drawPixmap(this->width()*0.5-title->width()*0.5, 30, title->width(), title->height(), *title);//绘制标题
}
6.主界面与选择关卡界面的切换
可通过发送接受自定义的信号来实现。
首先需要在chooselevelscence类中定义信号
signals:
void chooseBackbutton();
然后当其界面中的back按钮被点击后,延迟0.5秒后隐藏这个场景并发送这个信号。
connect(back, &QPushButton::pressed, [=](){//按下返回,切换场景
//点击返回按钮后返回主场景
QTimer::singleShot(500, this, [=](){//为了看到按下特效,需要在按下后0.5秒后隐藏
this->hide();
//发出出切换场景的信号
emit this->chooseBackbutton();
});
});
之后需要在mainwindows中接受信号并显示窗体
connect(choosescene, &ChooseLevelScence::chooseBackbutton, this, [=](){//当choosescene发出信号后,主窗体显示
choosescene->hide();
this->show();
});
7.创建选择关卡的按钮
在chooselevelscence类中用一层for循环来创建关卡按钮
i%4令有4列(0,1,2,3)
i/4令有20/4=5行
25、130是初试位置,70是按钮之间的间距
由于按钮类的setText显示文字不是很好,因此采用QLabel来显示关卡数,但如此就会对按钮造成遮挡,当需要监听按钮的点击事件时就无法获取信号。
因此需要设置鼠标穿透
对label调用setAttribute方法,传参:Qt::WA_TransparentForMouseEvents
//创建选择关卡的按钮
for(int i=0;i<20;i++){
MyPushButton *button = new MyPushButton("://res//LevelIcon.png");
button->setParent(this);
button->setPos(25+i%4*70, 130+i/4*70);//最终有4列,每列有20/4=5个按钮
QLabel *label = new QLabel(this);
label->setFixedSize(button->width(), button->height());
label->move(25+i%4*70, 130+i/4*70);
label->setText(QString::number(i+1));
label->setAlignment(Qt::AlignVCenter | Qt::AlignHCenter);//设置标签水平居中且垂直居中
//此时由于label将button遮掩,因此需要设置鼠标穿透
label->setAttribute(Qt::WA_TransparentForMouseEvents);
//需要监听每个按钮的点击事件
connect(button, &QPushButton::clicked, [=](){
qDebug()<<"您选择的是第"<<i+1<<"关";
});
}
8.创建关卡场景
点击关卡按钮后需要跳转到对应的关卡场景,定义类PlayScene
先对其进行基本设置:大小、窗体图标、标题,菜单栏的添加,退出设置
背景绘制
返回按钮添加(点击该按钮后应返回chooselevelscence场景,因此需要在该类中自定义信号,由chooselevelscence类接受信号),因此在chooselevelscence实现的选择关卡部分应为:
//创建选择关卡的按钮
for(int i=0;i<20;i++){
MyPushButton *button = new MyPushButton("://res//LevelIcon.png");
button->setParent(this);
button->setPos(25+i%4*70, 130+i/4*70);//最终有4列,每列有20/4=5个按钮
QLabel *label = new QLabel(this);
label->setFixedSize(button->width(), button->height());
label->move(25+i%4*70, 130+i/4*70);
label->setText(QString::number(i+1));
label->setAlignment(Qt::AlignVCenter | Qt::AlignHCenter);//设置标签水平居中且垂直居中
//此时由于label将button遮掩,因此需要设置鼠标穿透
label->setAttribute(Qt::WA_TransparentForMouseEvents);
//需要监听每个按钮的点击事件
connect(button, &QPushButton::clicked, [=](){
//进入到关卡场景中
PlayScene * playscene = new PlayScene(this, i+1);
this->hide();//将当前的隐藏掉
playscene->show();//显示关卡
//场景切换
connect(playscene, &PlayScene::chooseBack, [=](){//此时返回
this->show();
delete playscene;
});
});
}
在PlayScene的构造函数需要接收一个id属性来看是在第几个场景中
#include "playscene.h"
PlayScene::PlayScene(QWidget *parent, int id) : QMainWindow(parent), id(id)
{
//基本设置
this->setFixedSize(320, 588);//设置大小
this->setWindowIcon(QIcon("://res//Coin0001.png"));//设置窗体图标
this->setWindowTitle("游戏场景");
//添加开始菜单栏
QMenuBar * bar = new QMenuBar(this);
this->setMenuBar(bar);
QMenu * start = bar->addMenu("开始");
QAction * quit = start->addAction("退出");
connect(quit, &QAction::triggered, [=](){
this->close();
});
//添加返回按钮
MyPushButton * button = new MyPushButton("://res//BackButton.png", "://res//BackButtonSelected.png");
button->setParent(this);
button->setPos(this->width()-button->width()-20, this->height()-button->height()-20);
//点击返回之后需要返回到场景选择界面
connect(button, &QPushButton::clicked, [=](){
QTimer::singleShot(500, this, [=](){//延迟一下再发送
emit this->chooseBack();//发送信号,令场景选择界面接受
});
});
}
void PlayScene::paintEvent(QPaintEvent *event)
{
QPainter painter(this);//绘制当前关卡场景
//绘制背景
painter.drawPixmap(0, 0, this->width(), this->height(), QPixmap("://res//OtherSceneBg.png"));
//绘制标题
QPixmap pix;
pix.load("://res//Title.png");
painter.drawPixmap(10, 30, pix.width()*0.5, pix.height()*0.5, pix);
}
void PlayScene::setId(int id)
{
this->id = id;
}
int PlayScene::getId()
{
return this->id;
}
9.在游戏场景中显示关卡号
在PlayScene类中实现
使用label显示文字“Level 关卡号”
可调用setGeometry(x, y, width, height)来设置标签位置和大小
通过QFont来定义字体
setPointSize设置字体大小
QLabel通过setFont方法设置字体
//显示关卡号
QLabel * label = new QLabel(this);
QString str = QString("Level ") + QString::number(this->id);
label->setGeometry(20, this->height()-70, 150, 50);//设置标签大小
label->setText(str);
QFont font("华文新魏");
font.setPointSize(24);
label->setFont(font);//设置字体
10.金币类的封装
用按钮类来表示金币图片
传入的path是金币或银币图片的路径
#include "mycoin.h"
MyCoin::MyCoin(QWidget *parent) : QPushButton(parent)
{
}
MyCoin::MyCoin(QString path)
{
QPixmap pix;
bool isok = pix.load(path);
if(!isok){
qDebug()<<path<<" "<<"加载失败";
}
//此时说明加载成果
this->setFixedSize(pix.width(), pix.height());//设置按钮固定大小
this->setStyleSheet("QPushButton{border:0px;}");//设置风格样式
this->setIcon(QIcon(pix));
this->setIconSize(QSize(pix.width(), pix.height()));//设置图标的大小
}
11.设置每个关卡的默认显示
需要根据配置文件中定义好的关卡矩阵来对金币或银币进行显示
配置文件代码:
在该文件中可以通过mData[id][i][j]来访问关卡名为id的矩阵中第i行,第j列的元素值
值为1代表这个位置初始为金币
值为0代表这个位置初始为银币
一共20个关卡。
#ifndef DATACONFIG_H
#define DATACONFIG_H
#include <QObject>
#include <QMap>
#include <QVector>
class dataConfig : public QObject
{
Q_OBJECT
public:
explicit dataConfig(QObject *parent = 0);
public:
QMap<int, QVector< QVector<int> > >mData;
signals:
public slots:
};
#endif // DATACONFIG_H
#include "dataconfig.h"
#include <QDebug>
dataConfig::dataConfig(QObject *parent) : QObject(parent)
{
int array1[4][4] = {{1, 1, 1, 1},
{1, 1, 0, 1},
{1, 0, 0, 0},
{1, 1, 0, 1} } ;
QVector< QVector<int>> v;
for(int i = 0 ; i < 4;i++)
{
QVector<int>v1;
for(int j = 0 ; j < 4;j++)
{
v1.push_back(array1[i][j]);
}
v.push_back(v1);
}
mData.insert(1,v);
int array2[4][4] = { {1, 0, 1, 1},
{0, 0, 1, 1},
{1, 1, 0, 0},
{1, 1, 0, 1}} ;
v.clear();
for(int i = 0 ; i < 4;i++)
{
QVector<int>v1;
for(int j = 0 ; j < 4;j++)
{
v1.push_back(array2[i][j]);
}
v.push_back(v1);
}
mData.insert(2,v);
int array3[4][4] = { {0, 0, 0, 0},
{0, 1, 1, 0},
{0, 1, 1, 0},
{0, 0, 0, 0}} ;
v.clear();
for(int i = 0 ; i < 4;i++)
{
QVector<int>v1;
for(int j = 0 ; j < 4;j++)
{
v1.push_back(array3[i][j]);
}
v.push_back(v1);
}
mData.insert(3,v);
int array4[4][4] = { {0, 1, 1, 1},
{1, 0, 0, 1},
{1, 0, 1, 1},
{1, 1, 1, 1}} ;
v.clear();
for(int i = 0 ; i < 4;i++)
{
QVector<int>v1;
for(int j = 0 ; j < 4;j++)
{
v1.push_back(array4[i][j]);
}
v.push_back(v1);
}
mData.insert(4,v);
int array5[4][4] = { {1, 0, 0, 1},
{0, 0, 0, 0},
{0, 0, 0, 0},
{1, 0, 0, 1}} ;
v.clear();
for(int i = 0 ; i < 4;i++)
{
QVector<int>v1;
for(int j = 0 ; j < 4;j++)
{
v1.push_back(array5[i][j]);
}
v.push_back(v1);
}
mData.insert(5,v);
int array6[4][4] = { {1, 0, 0, 1},
{0, 1, 1, 0},
{0, 1, 1, 0},
{1, 0, 0, 1}} ;
v.clear();
for(int i = 0 ; i < 4;i++)
{
QVector<int>v1;
for(int j = 0 ; j < 4;j++)
{
v1.push_back(array6[i][j]);
}
v.push_back(v1);
}
mData.insert(6,v);
int array7[4][4] = { {0, 1, 1, 1},
{1, 0, 1, 1},
{1, 1, 0, 1},
{1, 1, 1, 0}} ;
v.clear();
for(int i = 0 ; i < 4;i++)
{
QVector<int>v1;
for(int j = 0 ; j < 4;j++)
{
v1.push_back(array7[i][j]);
}
v.push_back(v1);
}
mData.insert(7,v);
int array8[4][4] = { {0, 1, 0, 1},
{1, 0, 0, 0},
{0, 0, 0, 1},
{1, 0, 1, 0}} ;
v.clear();
for(int i = 0 ; i < 4;i++)
{
QVector<int>v1;
for(int j = 0 ; j < 4;j++)
{
v1.push_back(array8[i][j]);
}
v.push_back(v1);
}
mData.insert(8,v);
int array9[4][4] = { {1, 0, 1, 0},
{1, 0, 1, 0},
{0, 0, 1, 0},
{1, 0, 0, 1}} ;
v.clear();
for(int i = 0 ; i < 4;i++)
{
QVector<int>v1;
for(int j = 0 ; j < 4;j++)
{
v1.push_back(array9[i][j]);
}
v.push_back(v1);
}
mData.insert(9,v);
int array10[4][4] = { {1, 0, 1, 1},
{1, 1, 0, 0},
{0, 0, 1, 1},
{1, 1, 0, 1}} ;
v.clear();
for(int i = 0 ; i < 4;i++)
{
QVector<int>v1;
for(int j = 0 ; j < 4;j++)
{
v1.push_back(array10[i][j]);
}
v.push_back(v1);
}
mData.insert(10,v);
int array11[4][4] = { {0, 1, 1, 0},
{1, 0, 0, 1},
{1, 0, 0, 1},
{0, 1, 1, 0}} ;
v.clear();
for(int i = 0 ; i < 4;i++)
{
QVector<int>v1;
for(int j = 0 ; j < 4;j++)
{
v1.push_back(array11[i][j]);
}
v.push_back(v1);
}
mData.insert(11,v);
int array12[4][4] = { {0, 1, 1, 0},
{0, 0, 0, 0},
{1, 1, 1, 1},
{0, 0, 0, 0}} ;
v.clear();
for(int i = 0 ; i < 4;i++)
{
QVector<int>v1;
for(int j = 0 ; j < 4;j++)
{
v1.push_back(array12[i][j]);
}
v.push_back(v1);
}
mData.insert(12,v);
int array13[4][4] = { {0, 1, 1, 0},
{0, 0, 0, 0},
{0, 0, 0, 0},
{0, 1, 1, 0}} ;
v.clear();
for(int i = 0 ; i < 4;i++)
{
QVector<int>v1;
for(int j = 0 ; j < 4;j++)
{
v1.push_back(array13[i][j]);
}
v.push_back(v1);
}
mData.insert(13,v);
int array14[4][4] = { {1, 0, 1, 1},
{0, 1, 0, 1},
{1, 0, 1, 0},
{1, 1, 0, 1}} ;
v.clear();
for(int i = 0 ; i < 4;i++)
{
QVector<int>v1;
for(int j = 0 ; j < 4;j++)
{
v1.push_back(array14[i][j]);
}
v.push_back(v1);
}
mData.insert(14,v);
int array15[4][4] = { {0, 1, 0, 1},
{1, 0, 0, 0},
{1, 0, 0, 0},
{0, 1, 0, 1}} ;
v.clear();
for(int i = 0 ; i < 4;i++)
{
QVector<int>v1;
for(int j = 0 ; j < 4;j++)
{
v1.push_back(array15[i][j]);
}
v.push_back(v1);
}
mData.insert(15,v);
int array16[4][4] = { {0, 1, 1, 0},
{1, 1, 1, 1},
{1, 1, 1, 1},
{0, 1, 1, 0}} ;
v.clear();
for(int i = 0 ; i < 4;i++)
{
QVector<int>v1;
for(int j = 0 ; j < 4;j++)
{
v1.push_back(array16[i][j]);
}
v.push_back(v1);
}
mData.insert(16,v);
int array17[4][4] = { {0, 1, 1, 1},
{0, 1, 0, 0},
{0, 0, 1, 0},
{1, 1, 1, 0}} ;
v.clear();
for(int i = 0 ; i < 4;i++)
{
QVector<int>v1;
for(int j = 0 ; j < 4;j++)
{
v1.push_back(array17[i][j]);
}
v.push_back(v1);
}
mData.insert(17,v);
int array18[4][4] = { {0, 0, 0, 1},
{0, 0, 1, 0},
{0, 1, 0, 0},
{1, 0, 0, 0}} ;
v.clear();
for(int i = 0 ; i < 4;i++)
{
QVector<int>v1;
for(int j = 0 ; j < 4;j++)
{
v1.push_back(array18[i][j]);
}
v.push_back(v1);
}
mData.insert(18,v);
int array19[4][4] = { {0, 1, 0, 0},
{0, 1, 1, 0},
{0, 0, 1, 1},
{0, 0, 0, 0}} ;
v.clear();
for(int i = 0 ; i < 4;i++)
{
QVector<int>v1;
for(int j = 0 ; j < 4;j++)
{
v1.push_back(array19[i][j]);
}
v.push_back(v1);
}
mData.insert(19,v);
int array20[4][4] = { {0, 0, 0, 0},
{0, 0, 0, 0},
{0, 0, 0, 0},
{0, 0, 0, 0}} ;
v.clear();
for(int i = 0 ; i < 4;i++)
{
QVector<int>v1;
for(int j = 0 ; j < 4;j++)
{
v1.push_back(array20[i][j]);
}
v.push_back(v1);
}
mData.insert(20,v);
//测试数据
// for( QMap<int, QVector< QVector<int> > >::iterator it = mData.begin();it != mData.end();it++ )
// {
// for(QVector< QVector<int> >::iterator it2 = (*it).begin(); it2!= (*it).end();it2++)
// {
// for(QVector<int>::iterator it3 = (*it2).begin(); it3 != (*it2).end(); it3++ )
// {
// qDebug() << *it3 ;
// }
// }
// qDebug() << endl;
// }
}
关卡默认显示的代码:
在playscene类的构造函数中,通过getId方法获取场景的id,然后根据id到配置类中寻找对应的矩阵,根据矩阵各个元素值创建金币或银币,完成当前关卡场景初始化
//创建金币的背景图
//需要根据场景ID与配置文件对应的矩阵创建金币或银币
dataConfig fig;//创建配置类
QVector<QVector<int>> arr = fig.mData[this->getId()];//根据传入的id获取相应的矩阵
for(int i=0;i<arr.size();i++){
for(int j=0;j<arr[i].size();j++){
//用QLabel创建金币背景
QLabel *lab = new QLabel(this);
lab->setPixmap(QPixmap("://res//BoardNode(1).png"));
lab->setFixedSize(50, 50);
lab->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
lab->move(57+i*50, 200+j*50);
//创建金币
//如果矩阵当前位置的值为1创建金币,0创建银币
MyCoin *coin=nullptr;
if(arr[i][j]==1){
coin = new MyCoin("://res//Coin0001.png");
}
else{
coin = new MyCoin("://res//Coin0008.png");
}
coin->setParent(this);
coin->move(59+i*50, 204+j*50);
}
}
12.金币反转的特效
在金币类中定义函数changeFlag,当点击钱币时会调用该方法,判断当前金币的flag,将其flag反转,并开启一个定时器,每30秒发送一次信号
void MyCoin::changeFlag()
{
//正面翻反面
if(this->flag==1)
{
//开始翻正面的计时器
time1->start(30);
flag = 0;//改变flag
}
else{//反面翻正面
time2->start(30);
flag = 1;
}
}
发送的信号会被金币内置的timer1或2接受,这两个定时器分别是用于正面反转和反面反转的定时器,当正面反转时,之前的time1每隔30s发送信号,然后会开始读路径,当读完之后停止定时器并将min值重置。
这里的min=1,max=8,因为从正->反的动画图片一共8张,每次发送信号时会读取一次新的图片,当min=8时,已经反转结束。
最后还需要监听一下钱币的点击事件,当点击时,调用changeFlag方法启动对应定时器发送信号实现反转动画
connect(time1, &QTimer::timeout, [=](){
QPixmap img;
QString path = QString("://res//Coin000%1").arg(min++);
img.load(path);
this->setIcon(QIcon(img));
this->setFixedSize(img.width(), img.height());
this->setStyleSheet("QPushButton{border:0px;}");
this->setIconSize(QSize(img.width(), img.height()));
if(min > max){//正翻反面结束
min = 1;
time1->stop();//停止定时器
}
});
connect(time2, &QTimer::timeout, [=](){
QPixmap img;
QString path = QString("://res//Coin000%1").arg(max--);
img.load(path);
this->setIcon(QIcon(img));
this->setFixedSize(img.width(), img.height());
this->setStyleSheet("QPushButton{border:0px;}");
this->setIconSize(QSize(img.width(), img.height()));
if(max < min){//反面翻正面结束
max = 8;
time2->stop();//停止定时器
}
});
connect(this, &QPushButton::clicked, [=](){//当coin被点击时将其反转
this->changeFlag();
});
13.游戏胜利的判断
在PlayScene中定义isSucceed函数判断游戏是否胜利,点击某个金币后,调用该函数,遍历每个金币查看其flag,若全为true则胜利,否则为false。该函数在反转金币后调用。
bool PlayScene::isSucceed()
{
for(int i=0;i<gameArr.size();i++){
for(int j=0;j<gameArr[i].size();j++){
if(gameArr[i][j]==0){
this->isok=false;
return false;
}
}
}
this->isok=true;
return true;
}
connect(coin, &QPushButton::clicked, [=](){//当coin被点击时将其反转
coin->changeFlag();//调用后coin的flag已经改变了
//点击之后,还应将矩阵中的值修改
this->gameArr[i][j] = coin->getFlag();
//延时反转周围
QTimer::singleShot(300, this, [=](){
if(j>0){//反转上边
coin_arr[i][j-1]->changeFlag();
this->gameArr[i][j-1] = coin_arr[i][j-1]->getFlag();
}
if(j<gameArr.size()-1){//反转下边
coin_arr[i][j+1]->changeFlag();
this->gameArr[i][j+1] = coin_arr[i][j+1]->getFlag();
}
if(i>0){//反转左边
coin_arr[i-1][j]->changeFlag();
this->gameArr[i-1][j] = coin_arr[i-1][j]->getFlag();
}
if(i<gameArr[0].size()-1){//反转右边
coin_arr[i+1][j]->changeFlag();
this->gameArr[i+1][j] = coin_arr[i+1][j]->getFlag();
}
if(isSucceed()){//成功
cout<<"succeed"<<endl;
//胜利后设置金币为不可点击状态
QTimer::singleShot(1000, this, [=](){
this->setEnabled(false);
});
//应该掉落succeed字样
}
});
});
14.设置胜利图片
需要实现定义好胜利后的图片,将其置于窗口之外的地方。
//提前设置胜利图片
QLabel * succeed_pic = new QLabel(this);
QPixmap img;
img.load("://res//LevelCompletedDialogBg.png");
succeed_pic->setPixmap(img);
succeed_pic->setGeometry(this->width()/2 - img.width()/2, -img.height(), img.width(), img.height());
当胜利时,定义好执行动画,将其从窗口外的位置运行到目标位置处即可。
1秒后,应只让金币无法使用,因此需要遍历金币按钮,setEnabled为false
if(isSucceed()){//成功
cout<<"succeed"<<endl;
//胜利后设置金币为不可点击状态
QPropertyAnimation *animation = new QPropertyAnimation(succeed_pic,"geometry");
animation->setDuration(1000);//设置动画间隔
animation->setStartValue(QVariant(QRect(succeed_pic->x(), succeed_pic->y(), succeed_pic->width(), succeed_pic->height())));
animation->setEndValue(QVariant(QRect(succeed_pic->x(), succeed_pic->y()+200, succeed_pic->width(), succeed_pic->height())));
animation->setEasingCurve(QEasingCurve::OutBounce);//设置弹跳曲线
animation->start();//执行动画
QTimer::singleShot(1000, this, [=](){
for(int i=0;i<coin_arr.size();i++){
for(int j=0;j<coin_arr[i].size();j++){
coin_arr[i][j]->setEnabled(false);//设置所有金币不能使用
}
}//不能设置this为Enabled,因为back按钮应该是可以继续使用的,只有金币按钮无法使用
});
}
15.设置音效
#include<QSound>
该头文件默认没有,需要在.pro文件中的开头进行添加multimedia
QT += core gui multimedia
QSound创建时传入wav文件路径以及父亲,然后调用play方法开始执行音效
QSound *sound = new QSound("://res//LevelWinSound.wav");
sound->setParent(this);
// sound->setLoops(2);//设置循环次数,值为-1则无限循环
sound->play();//播放音效