Qt游戏制作-翻金币案例

1. 创建项目

点击新建项目,选择Qt窗口应用,下一步

设置项目名为CoinFlip,注意路径不要有中文,下一步

类名为MainScene(主场景),基类为QMainWindow,选择生成界面ui

至于其他设置和之前的创建项目一致,完成项目创建后如下:

编译项目,发现创建没有问题


2. 添加资源

添加资源和之前类似,先把res文件下复制到项目文件CoinFlip下,然后根据我们之前讲过的导入资源的方法导入资源即可。资源在下:

res.zip

添加后如图所示:


3. 项目基本配置

3.1. 配置主场景

下面我们首先配置项目的固定大小、图标和标题

    //配置国定大小
    setFixedSize(320,588);

    //设置图标
    setWindowIcon(QIcon(":/res/Coin0001.png"));

    //设置标题
    setWindowTitle("翻金币主场景");

3.2. 开始——退出菜单实现

我们先在ui中创建好开始菜单的退出选项,并且获取到它的名字actionquit

说明:这里的退出刚开始只能输入英文quit,然后在右下角的Text中将其设为中文的退出

然后我们用信号槽将其和关闭函数连接起来,实现点击退出结束程序的功能

    //退出按钮实现
    connect(ui->actionquit,&QAction::triggered,[=](){
       this->close();
    });

3.3. 绘制背景和背景标题

我们先在主场景的头文件中声明一个绘图函数

    //重写paintEvent事件  画背景图
    void paintEvent (QPaintEvent *);

然后再在主场景资源文件中重写绘图事件,绘制背景和背景的标题

//绘图事件  画背景图
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);//图标的缩放
    painter.drawPixmap(10,30,pix);

}

注意:

  • 当背景大小和窗口大小不符时,我们可以在drawPixmap()函数中增加两个参数,将背景拉伸为指定宽和高的图片
  • 当图标大小不合适时,我们可以通过调用scaled()函数,将其进行缩放然后重新赋值给自己进行绘制

可以看到初步配置后的项目窗口如下所示:


4. 开始按钮创建

4.1. 封装自定义按钮MyPushButton

因为Qt中并没有设置按下和弹起两种状态的控件,因此我们只有自定义一个按钮MyPushButton,并在Qt中创建好

创建好之后,我们要修改其继承的类,把QWidget改为QPushButton(修改三处)

mypushbutton.h中的:

mypushbutton.cpp中的

4.2. 构造丞数(默认显示图片,按下后显示的图片)

我们注释掉自带的构造函数,声明了一个具有初始默认值的构造函数,因为我们不希望按下后开始按钮切换图片,所以我们默认按下的图片为空。

    //explicit MyPushButton(QWidget *parent = nullptr);

    //构造函数 参数1 正常显示的图片路径   参数2 按下后显示的图片路径
    MyPushButton(QString normalImg,QString pressImg="" );

    //成员属性 保存用户传入的默认显示路径 以及按下后显示的图片路径
    QString normalImgPath;
    QString pressImgPath;

然后在构造函数中,我们先判断导入图片是否成功,然后再对开始的图标进行了设置

  • 设置国定大小——setFixedSize();
  • 设置不规则图片样式——setStyleSheet("QPushButton{border:0px;});
  • 设置图标——setIcon();
  • 设置图标大小——setIconSize();
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()));

}

4.3. 测试开始按钮

然后我们在主场景函数中实例化开始按钮,并通过move()函数,设置开始按钮的位置

    //开始按钮
    MyPushButton * startBtn = new MyPushButton(":/res/MenuSceneStartButton.png");
    startBtn->setParent(this);
    startBtn->move(this->width()*0.5 - startBtn->width()*0.5,this->height()*0.7);

完成后效果如下:

4.4. 开始制作特效

然后我们开始制作按下按钮的弹跳特效,首先我们在mypushbutton头文件中,声明两个特效函数

    //弹跳特效
    void zoom1();//向下跳
    void zoom2();//向上跳

然后我们再分别在cpp文件中实现两个特效函数

4.5. zoom1向下跳

弹跳动画流程:

  1. 创建动画对象——new QPropertyAnimation();
  2. 设置动画时间间隔(ms为单位)——setDuration();
  3. 设置起始位置——setStartValue();
  4. 设置结束位置——setEndValue();
  5. 设置弹跳曲线——setEasingCurve();
  6. 执行动画——start();
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();
}

4.6. zoom2向上跳

向下跳和向上跳类似,只是起始和结束位置不同

void MyPushButton::zoom2()
{
    //创建动画对象
    QPropertyAnimation * animation = new QPropertyAnimation(this,"geometry");
    //设置动画时间间隔
    animation->setDuration(100);

    //起始位置
    animation->setStartValue(QRect(this->x(),this->y()+10,this->width(),this->height()));
    //结束位置
    animation->setEndValue(QRect(this->x(),this->y(),this->width(),this->height()));

    //设置弹跳曲线
    animation->setEasingCurve(QEasingCurve::OutBounce);

    //执行动画
    animation->start();
}

4.7. 连接按钮和特效

在主场景中,我们通过信号和槽,实现了点击开始的特效

    connect(startBtn,&MyPushButton::clicked,[=](){
        qDebug()<<"点击了开始";
        //做弹起特效
        startBtn->zoom1();
        startBtn->zoom2();
    });

效果如下:

此处为语雀视频卡片,点击链接查看:开始按钮.mp4


5. 选择关卡场景配置

5.1. 创建选择关卡类

首先我们先创建一个ChooseLevelScene

然后在主场景中实例化选择关卡场景

    //实例化选择关卡场景
    chooseScene = new ChooseLevelScene;

5.2. 延时进入选择关卡场景

因为点击开始后,就需要切换场景,所以我们的代码还是写在之前的信号槽函数中。

  • 延时——QTimer::singleShot();
  • 关闭当前窗口——this->hide();
  • 显示选择关卡场景——chooseScene->show();
        //延时进入选择关卡场景中
        QTimer::singleShot(500,this,[=](){
            //自身隐藏
            this->hide();
            //显示选择关卡场景
            chooseScene->show();
        });

5.3. 配置选择关卡场景

此时我们切换后的场景是一个空的大小不符合要求的窗口,下面我们在ChooseLevelScene.cpp中配置这个窗口。

我们按照之前在主场景中设置的方式同样的在选择关卡中设置窗口大小、图标、标题、和菜单栏等。

ChooseLevelScene::ChooseLevelScene(QWidget *parent) : QMainWindow(parent)
{
    //配置选择关卡场景
    this->setFixedSize(320,588);

    //设置图标
    this->setWindowIcon(QPixmap(":/res/Coin0001.png"));

    //设置标题
    this->setWindowTitle("选择关卡场景");

    //创建菜单栏
    QMenuBar *bar = menuBar();
    setMenuBar(bar);

    //创建开始菜单
    QMenu * startMenu = bar->addMenu("开始");

    //创建退出 菜单项
    QAction * quitAction = startMenu->addAction("退出");

    //点击退出 实现退出游戏
    connect(quitAction,&QAction::triggered,[=](){
        this->close();
    });
}

5.4. 设置背景、标题图片

但此时我们的窗口下面仍然是空白的,下面我们就需要加载背景和标题

我们现在其头文件中重新声明一个绘制事件

    //重写绘图事件
    void paintEvent(QPaintEvent *);

然后我们在其资源文件中实现绘图事件

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()*0.5-pix.width()*0.5,30,pix);
}

5.5. 创建返回按钮

这样我们就得到了有背景和标题的窗口,下面我们继续制作返回按钮

我们使用之前的MyPushButton类创建一个返回按钮,设置好它在窗口中的位置后,我们先简单的让他dubug

    //返回按钮
    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()<<"点击了返回";

5.6. 实现返回按钮图片切换

我们先在mypushbutton.h中重写按钮的按下和释放事件

    //重写按钮 按下 和 释放事件
    void mousePressEvent (QMouseEvent *e);

    void mouseReleaseEvent (QMouseEvent *e);

然后在实现中判断是否有切换的图片。然后分别切换为按下图片和初始图片

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);
}

效果如下:

此处为语雀视频卡片,点击链接查看:返回按钮.mp4

6. 开始场景和选择关卡场景切换

我们要实现点击选择关卡的返回,返回到主场景界面

6.1. 自定义信号

我们需要选择关卡场景释放一个信号,然后由主场景来监听,是否需要返回到主场景。

首先我们先在chooselevelscene.h中自定义一个信号

signals:
    //写一个自定义信号,告诉主场景  点击了返回
    void chooseSceneBack();

然后我们在返回按键的信号和槽函数中,发送这个信号——emit();,同样的我们可以选择延时发送信号。

    //点击返回
    connect(backBtn,&MyPushButton::clicked,[=](){
        //告诉主场景 我返回了 主场景监听ChooseLevelScene的返回按钮
        //延时返回
        QTimer::singleShot(0,this,[=](){
            emit this->chooseSceneBack();
        });

6.2. 连接主场景和信号

因为我们只需要连接信号和槽一次,所以我们不把它写在开始的信号和槽函数中,而是写在外面的构造函数中

    //监听选择关卡的返回按钮的信号
    connect(chooseScene,&ChooseLevelScene::chooseSceneBack,this,[=](){
        chooseScene->hide();//把选择关卡的场景  关闭掉
        this->show();//重新显示主场景
    });

通过检测这个信号,我们就可以返回到主场景了,效果如下:

此处为语雀视频卡片,点击链接查看:返回主场景.mp4


7. 创建选择关卡中的按钮

7.1. 布置按钮到场景

我们通过一个for循环加上 % 和 / 实现了一个二维矩阵,让按钮分布到不同的位置了。

    //创建选择关卡的按钮
    for(int i=0;i<20;i++){
        MyPushButton * menuBtn = new MyPushButton(":/res/LevelIcon.png");
        menuBtn->setParent(this);
        menuBtn->move(i%4 * 70 + 25, i/4 *70 + 130);
    }

7.2. 显示关卡数

因为按钮是不规则的图形,如果我们直接在按钮上设置文本,那么会显示错误i

因此,我们直接创建Label,让其覆盖在按钮的上方,但是因为和按钮大小相同,所以默认在最左边,因此我们需要设置对齐方式,让其处于中心位置

        QLabel * label = new QLabel;
        label->setParent(this);
        label->setFixedSize(menuBtn->width(),menuBtn->height());
        label->setText(QString::number(i+1));
        label->move(i%4 * 70 + 25, i/4 *70 + 130);

        //设置 label上的文字对齐方式
        label->setAlignment(Qt::AlignCenter | Qt::AlignVCenter);

7.3. 监听每个按钮的点击事件

我们通过信号和槽函数,获取到选择的是哪一个关卡,但是因为label覆盖在按钮的上方,鼠标点击被label拦截了,因此我们需要设置,让鼠标具有穿透力,让其作用于按钮上。

        //监听每个按钮的点击事件
        connect(menuBtn,&MyPushButton::clicked,[=](){
            QString str = QString("您选择的是第 %1 关").arg(i+1);
            qDebug()<<str;
        });
        //设置让鼠标进行穿透
        label->setAttribute(Qt::WA_TransparentForMouseEvents);

效果如下:

此处为语雀视频卡片,点击链接查看:选择关卡按钮.mp4


8. 翻金币场景创建

8.1. 进入翻金币游戏场景

先创建一个翻金币场景类PlayScene,我们在选择场景中创建一个游戏场景并显示

            //进入到游戏场景
            this->hide();//将选关场景关闭
           play = new PlayScene(i+1);//创建游戏场景
           play->show();//显示游戏场景

但是我们的构造函数参数并非int型,所以我们还要自己重写一个构造函数

public:
    //explicit PlayScene(QWidget *parent = nullptr);

    PlayScene(int levelNum);

    int levelIndex;//内部成员属性  记录所选的关卡
PlayScene::PlayScene(int levelNum)
{
    QString str = QString("进入了第%1关").arg(levelNum);
    qDebug()<<str;
    this->levelIndex=levelNum;
}

8.2. 配置翻金币游戏场景

我们先配置图标和菜单栏等部分

    //初始化游戏场景
    //设置固定大小
    this->setFixedSize(320,588);
    //设置图标
    this->setWindowIcon(QPixmap(":/res/Coin0001.png"));
    //设置标题
    this->setWindowTitle("翻金币场景");

    //创建菜单栏
    QMenuBar *bar = menuBar();
    setMenuBar(bar);

    //创建开始菜单
    QMenu * startMenu = bar->addMenu("开始");

    //创建退出 菜单项
    QAction * quitAction = startMenu->addAction("退出");
    //点击退出 实现退出游戏
    connect(quitAction,&QAction::triggered,[=](){
        this->close();
    });

然后再重写一个绘图事件,用来绘制背景

    //重写painterEvent事件
    void paintEvent(QPaintEvent *);
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);
    painter.drawPixmap(10,30,pix.width(),pix.height(),pix);
}

8.3. 实现返回按钮

对于返回按钮,我们定义一个自定义信号

signals:
    void chooseSceneBack();

然后我们设置一个按钮,让其发出信号

    //返回按钮
    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,[=](){
        //告诉主场景 我返回了 主场景监听ChooseLevelScene的返回按钮
        //延时返回
        qDebug()<<"翻金币场景中点击了返回按钮";
        QTimer::singleShot(0,this,[=](){
            emit this->chooseSceneBack();
        });
    });

然后我们实现接受信号,返回上一场景

           connect(play,&PlayScene::chooseSceneBack,[=](){
               this->show();
               delete play;
               play = NULL;
           });

9. 实现显示关卡标签

我们创建一个标签,显示当前关卡的数目,并通过QFont改变标签的字体和字号

    //显示当前关卡数
    QLabel * label = new QLabel;
    label->setParent(this);
    QFont font;
    font.setFamily("华文新魏");
    font.setPointSize(20);
    QString str2 = QString("Level: %1").arg(this->levelIndex);
    //将字体设置到标签控件中
    label->setFont(font);
    label->setText(str2);
    label->setGeometry(30,this->height()-50,120,50);

效果如下:


10. 创建金币类

10.1. 绘制金币背景图案

我们先把金币的背景图案在PlayScene中绘制出来

    //显示金币背景图案
    for(int i=0;i<4;i++){
        for(int j=0;j<4;j++){
            //绘制背景图片
            QPixmap pix = QPixmap(":/res/BoardNode(1).png");
            QLabel *label = new QLabel;
            label->setGeometry(0,0,pix.width(),pix.height());
            label->setPixmap(pix);
            label->setParent(this);
            label->move(57+i*50,200+j*50);
        }
    }

10.2. 创建自定义金币按钮类

我们先创建继承于QWidget的MyCoin类,将其修改为为继承于QPushButton类

10.3. 在构造函数中加载金币图片

我们在金币按钮类中自定义一个构造函数

    //参数代表 传入的金币路径 还是银币路径
    MyCoin(QString btnImg);

然后在构造函数中加载金币的图片并设置金币图片的大小

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()));
}

10.4. 在游戏场景中创建所有金币

我们在创建金币背景中的循环中也创建金币

            //创建金币
            MyCoin * coin = new MyCoin(":/res/Coin0001.png");
            coin->setParent(this);
            coin->move(59+i*50,204+j*50);

效果如下:


11. 每个关卡的默认显示

11.1. 添加文件

先将两个文件放入项目文件夹,然后选择添加现有文件,添加到项目中

dataconfig.cpp

dataconfig.h

11.2. 初始化每个关卡的二维数组

我们在游戏场景中初始化每个关卡的初始二维数组

    //初始化每个关卡的二维数组
    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];
        }
    }

11.3. 显示默认金币效果

然后我们根据二维数组的值确定金币的颜色,进行显示

            //创建金币
            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(59+i*50,204+j*50);

效果如下:


12. 金币翻转效果

12.1. 给金币添加属性

我们先给金币添加x坐标、y坐标和正反面标志flag三个属性

    //金币的属性
    int posX;//x坐标位置
    int posY;//y坐标位置
    bool flag;//正反标示

12.2. 声明翻转函数

我们声明一个用于翻转金币状态的函数,并创建两个定时器,设置最小值和最大值用于制作动画

    //改变标志的方法
    void changeFlag();
    QTimer * timer1;//正面翻反面的定时器
    QTimer * timer2;//反面翻正面的定时器
    int min=1;
    int max=8;

12.3. 实现翻转函数

我们先初始化两个定时器对象

    //初始化定时器对象
    timer1 = new QTimer(this);
    timer2 = new QTimer(this);

然后我们分别检验金币的状态,并选择触发timer1或timer2

//改变正反面标志的方法
void MyCoin::changeFlag()
{
    //如果是正面  翻成反面
    if(this->flag){
        //开始正面翻反面的定时器
        timer1->start(30);
        this->flag=false;
    }
    //反面翻正面
    else{
        timer2->start(30);
        this->flag=true;
    }
}

然后我们用两个槽函数分别接收timer1和timer2的信号,让图片逐个被加载

    //监听正面翻反面的信号,并且翻转金币
    connect(timer1,&QTimer::timeout,[=](){
        QPixmap pix;
        QString str = QString(":/res/Coin000%1.png").arg(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%8.png").arg(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()));

        //判断 如果翻完了,将min设置为1
        if(this->max<this->min){
            this->max = 8;
            timer2->stop();
        }
    });

12.4. 实现点击金币翻转

实现了翻转效果后,我们要把点击和翻转用信号和槽函数连接起来,实现点击金币翻转

            //点击金币进行翻转
            connect(coin,&MyCoin::clicked,[=](){
                coin->changeFlag();
            });

效果如下:

此处为语雀视频卡片,点击链接查看:金币翻转.mp4


13. 翻转周围金币

因为无法获取到每个金币的坐标,所以我们单独把它们保存到一个二维数组中

    MyCoin * coinBtn[4][4];

同时我们再翻转金币的信号和槽函数中,同时延时翻转该金币的周围金币

                //翻转周围硬币
                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] == 0 ? 1: 0;
                    }
                    //周围左侧硬币翻转条件
                    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] == 0 ? 1: 0;
                    }
                    //周围上侧的硬币翻转条件
                    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] == 0 ? 1: 0;
                    }
                    //周围下侧的硬币翻转条件
                    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] == 0 ? 1: 0;
                    }
                });

效果如下:

此处为语雀视频卡片,点击链接查看:翻转周围金币.mp4


14. 判断游戏胜利

14.1. 添加isWin的标志

首先我们在mycoin.hplayscene.h中声明并初始化胜利标志

    //是否胜利的标志
    bool isWin = false;
    //是否胜利的标志
    bool isWin = false;

14.2. 判断是否胜利

我们需要在每一次翻转金币后判断是否胜利,因此我们将其写在延迟翻转中的匿名函数

                    //判断是否胜利
                    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){
                        //胜利了
                        qDebug()<<"游戏胜利";
                    }

14.3. 胜利后屏蔽算法点击

在胜利后,我们把所有金币标志设置为true

                    if(this->isWin == true){
                        //胜利了
                        qDebug()<<"游戏胜利";
                        //将所有按钮的胜利标志改为true,如果再次点击按钮,直接return,不做响应
                        for(int i=0;i<4;i++){
                            for(int j=0;j<4;j++){
                                coinBtn[i][j]->isWin=true;
                            }
                        }
                    }

当胜利标志为true后,点击金币直接return不做响应

void MyCoin::mousePressEvent(QMouseEvent *e)
{
    if(this->isAnimation==true || this->isWin){
        return;
    }
    else{
        QPushButton::mousePressEvent(e);
    }
}

效果如下:

此处为语雀视频卡片,点击链接查看:胜利效果.mp4


15. 胜利图片效果

15.1. 将胜利图片放到游戏场景上方外

因为胜利图片在胜利之前是看不见的,因此我们把它写在外面

    //胜利图片显示
    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());

15.2. 将胜利图片移到中央

因为我们在游戏胜利后才把图片移到中央,因此我们把代码写在判断游戏胜利的条件语句中

                        //将胜利的图片移动下来
                        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()+144,winLabel->width(),winLabel->height()));
                        //设置缓和曲线
                        animation->setEasingCurve(QEasingCurve::OutBounce);
                        //执行动画
                        animation->start();

效果如下:

此处为语雀视频卡片,点击链接查看:胜利图片.mp4


16. 添加音效

16.1. 添加多媒体模块

添加音效需要使用#include <QSound>头文件,但是其属于多媒体模块,我们需要在.pro文件中加入这个模块

QT       += core gui multimedia

16.2. 添加主场景音效

我们先在主场景中准备好开始按钮的音效

    //准备开始按钮的音效
    QSound * startSound = new QSound(":/res/TapButtonSound.wav",this);

然后我们在开始按钮的信号和槽函数中,加入音效,这样点击了按钮后就会播放音效

        //播放开始音效资源
//        startSound->setLoops(-1);//设置循环,如果是-1,代表无限循环
        startSound->play();

16.3. 添加选择场景音效

类比于开始按钮,我们添加选择场景中的音效

    //选择关卡按钮音效
    QSound * chooseSound = new QSound(":/res/TapButtonSound.wav",this);
    //返回按钮音效
    QSound * backSound = new QSound(":/res/BackButtonSound.wav",this);

分别在返回按钮和选择按钮信号和槽函数中使用音效

        //返回按钮音效
        backSound->play();
            //播放选择关卡音效
            chooseSound->play();

16.4. 添加游戏场景中的音效

    //添加音效资源
    //返回按钮音效
    QSound * backSound = new QSound("/res/BackButtonSound.wav",this);
    //翻金币音效
    QSound * flipSound = new QSound(":/res/ConFlipSound.wav",this);
    //胜利按钮音效
    QSound * winSound = new QSound(":/res/LevelWinSound.wav",this);
        //返回按钮音效
        backSound->play();
                //点击金币音效
                flipSound->play();
                        //游戏胜利音效
                        winSound->play();

效果如下:

此处为语雀视频卡片,点击链接查看:游戏音效.mp4


17. 项目优化

17.1. 统一主界面和选择界面位置

我们把进入选择场景的界面位置设置为当前界面位置

            //设置chooseScene场景的位置
            chooseScene->setGeometry(this->geometry());

当点击返回时,我们又把主界面位置设为选择界面位置

        this->setGeometry(chooseScene->geometry());

17.2. 统一选择界面和游戏界面位置

我们把游戏场景位置设为当前位置,它应该在创建场景之后、显示场景之前设置

           //设置游戏场景的初始位置
           play->setGeometry(this->geometry());

当点击返回时,我们把选择场景位置设置为游戏场景位置

               this->setGeometry(play->geometry());

效果如下:

此处为语雀视频卡片,点击链接查看:场景位置优化.mp4

18. 项目文件

CoinFlip.zip


  • 18
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值