Qt_翻金币02

金币类

金币这个对象,和金币翻转这个特效,其实是可以看作是按钮和按钮的特效吧,就和前面的返回按钮一样,但这里需要重新定义金币类,因为前面封装的MyPushButton已经不合适了,很多功能在金币这里都用不到,而金币需要的功能又没有



#ifndef MYCOIN_H
#define MYCOIN_H

#include <QPushButton>

class MyCoin : public QPushButton
{
    Q_OBJECT
public:
    // 参数功能 显示默认是金币还是银币
    MyCoin(QString CoinImg);

signals:

};

#endif // MYCOIN_H


#include "mycoin.h"
#include<QPixmap>
#include<QMessageBox>
//MyCoin::MyCoin(QWidget *parent) : QPushButton(parent)
//{

//}
MyCoin::MyCoin(QString CoinImg){

    QPixmap pix;
    bool ret = pix.load(CoinImg);
    if(!ret){
        QString str = QString("图片加载失败 %1").arg(CoinImg);
        QMessageBox::critical(this,"加载失败",str);
        return ;
    }
    this->setFixedSize(pix.width(),pix.height());
    this->setStyleSheet("QPushButton{border:0px}");

    this->setIcon(pix);
    this->setIconSize(QSize(pix.width(),pix.height()));
}



封装好后,就可以在第三个界面playScene.cpp中使用了

    // 设置金币的背景阴影
    for(int i=0;i<4;++i){
        for(int j=0;j<4;++j){
			
			//  ......
			//  ......
			
            // 创建金币
            MyCoin * coin = new MyCoin(":/res/Coin0001.png");
            coin->setParent(this);
            coin->move(59+i*50,204+j*50);
        }
    }



丰富游戏中的金币设定

这里导入了两个文件,dataconfig.hdataconfig.cpp,其实就是尤其不同关卡的游戏设计,哪个位置应该放银币哪个位置应该放金币,不可能随便这放银哪放金的吧,万一是死局不尴尬了?
这里是用矩阵的形式记录,共20个矩阵,然后还得记录对应矩阵的关卡号,所以QMap<int, QVector< QVector<int> > >mData;



在playscene.h中,已经有一个记录关卡号的成员变量levelIndex了,所以这里只需要维护一个记录游戏数据的4x4的二维数组int gameArray[4][4];


接着初始化

    // 初始化游戏中的二维数组
    dataConfig data;
    for(int i=0;i<4;++i){
        for(int j=0;j<4;++j){
            this->gameArray[i][j] = data.mData[this->levelIndex][i][j];
        }
    }



然后初始化关卡的金币银币,通过int gameArray[4][4];判断该放金币还是银币

    // 设置金币的背景阴影
    for(int i=0;i<4;++i){
        for(int j=0;j<4;++j){

            QString str;
            if(this->gameArray[i][j]==1){
                str = QString(":/res/Coin0001.png");
            }else{
                str = QString(":/res/Coin0008.png");
            }

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

        }
    }



摆放好后,就弄翻转特效了吧?
但你得仔细想想,翻转特效的功能又需要哪些数据来辅助实现?

  • 金币翻转为银币,总得还能再翻转回来吧,这不就得记录硬币当前状态
  • 再记录下是哪个硬币,也就是坐标
  • 实现翻转是用8张图,这里用一个计时器来辅助实现

所以mycoin.h

	public:
    // 参数功能 显示默认是金币还是银币
    MyCoin(QString CoinImg);

    // 记录每个硬币所在坐标 
    int posX;
    int posY;
    // 记录硬币的状态
    bool flag;

    // 翻转特效
    void changeFlag();

    // 正面翻反面 定时器
    QTimer *timer1;
    // 反面翻正面 定时器
    QTimer *timer2;
    // coin图片1-8
    int min=1;
    int max=8;



初始化坐标和状态,在playscene.cpp创建硬币时进行

            QString str;
            if(this->gameArray[i][j]==1){
                str = QString(":/res/Coin0001.png");
            }else{
                str = QString(":/res/Coin0008.png");
            }

            // 创建金币
            MyCoin * coin = new MyCoin(str);
            coin->posX = i;
            coin->posY = j;
            coin->flag = this->gameArray[i][j]; // 记录金币正反
            coin->setParent(this);
            coin->move(59+i*50,204+j*50);
			
			// 翻转后还需要修改二维数组
            connect(coin,&MyCoin::clicked,[=](){
               coin->changeFlag();
               gameArray[i][j] = gameArray[i][j] == 0 ? 1 : 0;
            });



翻转特效

金币翻转

mycoin.cpp

// 翻金币特效
void MyCoin::changeFlag(){

    if(this->flag){

        timer1->start(30) ;
        this->flag = false;
    }
    else
    {
        timer2->start(30);
        this->flag = true;
    }

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


       // 如果显示到 最后一张图   停止定时器
       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()));


       // 如果显示到 最后一张图   停止定时器
       if(this->min>this->max){
           this->max=8;
           timer2->stop();
       }
    });

看着两个connect,完成了不止一个事件啊,这还能完成循环事件?
这不是借助了定时器,信号和槽才完成了8个事件嘛




优化特效

如果快速点击两次某一个硬币,其实可以看到,第一次的翻转1-8并没有完全做完,就执行8-1了

这里就优化一下
mycoin.h

    // 判断是否正在执行动画
    bool isAnimation;
    // 鼠标按下事件重写
    void mousePressEvent(QMouseEvent* e);

mycoin.cpp


void MyCoin::mousePressEvent(QMouseEvent* e){
    if(this->isAnimation){
        // 正在做动画 不做鼠标事件处理
        return ;
    }
    else{
        // 交给父类 做默认处理
        QPushButton::mousePressEvent(e);
    }
}

isAnimation什么时候为true什么时候为false

没在做动画:

  1. 游戏初始化时没有做动画吧,所以初始值为false
  2. 动画做完还得把标志设为false吧,也就是定时器停止后

正在做动画:

  1. 翻金币特效进行中,就是在做动画吧,也就是void MyCoin::changeFlag()

    // 初始化动画执行标志
    isAnimation = false;

	// 定时器
    if(this->min>this->max){
        this->min=1;
        timer1->stop();
        this->isAnimation=false;
    }


	// 翻金币特效
void MyCoin::changeFlag(){
    if(this->flag){

        timer1->start(30) ;
        this->flag = false;

    }
    else
    {
        timer2->start(30);
        this->flag = true;
    }
    	isAnimation = true;
}




翻动周围的硬币,分别在playscene.hplayscene.cpp中进行修改代码


MyCoin * coinBtn[4][4];

            // 将coin放进到维护的金币二维数组中
            coinBtn[i][j] = coin;

            connect(coin,&MyCoin::clicked,[=](){
               coin->changeFlag();
               gameArray[i][j] = gameArray[i][j] == 0 ? 1 : 0;


               // 延时翻转周围的
               QTimer::singleShot(200,this,[=](){
                   // 继续反动周围的硬币
                   // 检测 翻右侧硬币
                   if(coin->posX+1<=3){
                       coinBtn[coin->posX+1][coin->posY]->changeFlag();
                       gameArray[coin->posX+1][coin->posY] = gameArray[coin->posX+1][coin->posY] == 0?1:0;
                   }
                   // 检测左侧
                   if(coin->posX-1>=0){
                       coinBtn[coin->posX-1][coin->posY]->changeFlag();
                       gameArray[coin->posX-1][coin->posY] = gameArray[coin->posX-1][coin->posY] == 0?1:0;
                   }
                   // 检测下侧
                   if(coin->posY + 1<=3){
                       coinBtn[coin->posX][coin->posY+1]->changeFlag();
                       gameArray[coin->posX][coin->posY+1] = gameArray[coin->posX][coin->posY+1] == 0?1:0;
                   }
                   // 检测上侧
                   if(coin->posY - 1>=0){
                       coinBtn[coin->posX][coin->posY-1]->changeFlag();
                       gameArray[coin->posX][coin->posY-1] = gameArray[coin->posX][coin->posY-1] == 0?1:0;
                   }
               });

            });




是否胜利

胜利后禁止点击金币

 //mycoin.h
 bool isWin = false; //是否胜利

//playscene.h
    bool isWin; //是否胜利

//mycoin.cpp
void MyCoin::mousePressEvent(QMouseEvent *e)
{
    if(this->isAnimation || this->isWin)
    {
        qDebug() << "无法点击";
        return;
    }
    else
    {
        QPushButton::mousePressEvent(e);

    }
}

//playscene.cpp
//判断是否胜利
                    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() << "游戏胜利";
                        //将所有按钮的胜利标志改为true
                        //如果再次点击按钮,直接return,不做响应
                        for(int i = 0 ; i < 4 ; i++)
                        {
                            for(int j = 0 ; j < 4 ; j++)
                            {
                                coinBtn[i][j]->isWin = true;

                            }
                        }
                    }



胜利图片

//playscene.cpp
	// 创建胜利后弹出的图片
    QLabel * winLabel = new QLabel(this);
    QPixmap pix ;
    pix.load(":/res/LevelCompletedDialogBg.png");
    winLabel->setGeometry(QRect(0,0,pix.width(),pix.height()));
    winLabel->setPixmap(pix);
    winLabel->move((this->width()-pix.width()) * 0.5 ,   - pix.height() );


// 胜利检测
   if(this->isWin){
       for(int i=0;i<4;i++){
            for(int j=0;j<4;j++){
                 coinBtn[i][j]->isWin = true;
			}
      }

                       // 将胜利的图片 显示出来               第一个参数表示动作的使用者
     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()+114,winLabel->width(),winLabel->height()));
     animation->setEasingCurve(QEasingCurve::OutBounce);
     animation->start();

                   }





Bug

其实在点完最后一个就能胜利时,再快速点击其他的硬币,就会出现胜利的图片已经弹出来了并且硬币点击都禁用了,但第二次点击的硬币仍然翻转了

// 点击后先禁用所有按钮
                for(int i=0;i<4;i++)
                {
                    for(int j=0;j<4;j++)
                    {
                       this->coinBtn[i][j]->isWin = true; 
                    }
                }




//等翻完金币后  将所有按钮解禁
               for(int i=0;i<4;i++){
                   for(int j=0;j<4;j++){
                       this->coinBtn[i][j]->isWin = false;
                   }
               }




优化代码

界面切换时,其实会有一个不舒服的地方,就是在第一界面时,将游戏窗口移动一下,然后点击开始按钮跳到第二界面,但第二界面会移动会刚刚的位置,同样的第二界面进入第三界面一样有这个问题

        chooseScene->hide();
        // 返回时不会使窗口位置变化
        this->setGeometry(chooseScene->geometry());
        this->show();



		// 隐藏第一个界面
        this->hide();
        // 选择关卡的位置 !!!
        chooseScene->setGeometry(this->geometry());
        // 进入第二个界面
        chooseScene->show();




        //进入到具体的游戏场景
        playScene = new PlayScene(i+1);
        //将游戏场景的位置 同步为 上一场景的位置
        playScene->setGeometry(this->geometry());
        playScene->show();



解决方案就是在每一次show之前先将位置设置setGeometry为上一窗口的位置



打包发布

怎么将这个游戏发给别人呢?
直接给.exe是不行的
在Qt中,先切换成release模式再运行一次
在这里插入图片描述
运行完就在本地项目文件中找到release文件夹下的CoinFlip.exe,将此文件移至桌面的一个空文件夹中,在该空文件夹中,按住shift键+鼠标右键,会出现在此处打开 PowerShell窗口
打开后输入命令windeployqt CoinFlip.exe,回车即可
然后这个文件夹就会生成许多库文件,是连接CoinFlip.exe用的,这样就可以不需要在什么编译环境下就能运行游戏了

可以将文件夹压缩发送给别人,也可以上网下载一个软件HM NIS Edit将文件打包成安装包

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值