天天看点

基于QT的翻金币小游戏项目总结记录(二)

前言

  最近为了学习C++和qt5,跟着教程写了一个翻金币的小游戏,源码和资源文件的链接在这里:翻金币小游戏源码和资源文件的下载链接。里面有自己写的超多代码注释,只要2个C币,去秒。

  作为第一个QT项目,还是有必要做些总结和一些细节方面的记录的。

  承上一篇,这篇继续记录在项目中学到的知识。

事件(EVENT)

  在qt中事件机制实现了两个功能:1.实现与用户操作的交互(如读取鼠标、键盘的输入信息)。2.实现窗口内的特定功能(绘图事件等)。可以从不同的应用场景出发来学习事件的使用方式。

绘图事件

  绘图事件在程序中最基本的功能是绘制静态图像(绘制动态图像后面会讲),比如在主界面中我们需要给主界面添加一个背景,并且在背景中再叠加一个小图片当点缀,就应当通过主界面类中的绘图事件来实现:

void MainScene::paintEvent(QPaintEvent *event){//该事件用于设置窗口中的静态图像
    //画背景
    QPainter painter(this);
    QPixmap pix;
    pix.load(":/res/MenuSceneBg.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);
}

           

   在绘图事件中需要先实例化一个

QPainter

类的对象,将其加入对象树中,绘图事件中所有的图形都是由该对象绘制的。一般我们使用

Qpixmap

类存储对象,该类可支持常见的.png和.jpg图像。通过

QPixmap

类的load函数加载资源路径,并使用

drawPixmap

函数对图像的左上角坐标进行定位,并设置宽度和长度即可。

  这里可以注意到,

drawPixmap

函数是通过传参定义图像坐标的,如果该坐标设置为一个变量,而不是常量,是不是就可以实现绘制动态的图像了呢?

  实际上,如果没有其他操作的话,绘图事件函数只会在窗口初始化的时候调用一次,单纯将坐标设置为变量是不能实现动态图像绘制的。

  这里挖个坑,在第二个项目飞机大战中再讲。

鼠标点击事件

  鼠标点击事件是一大类事件的典型(鼠标相关事件、键盘相关事件等),因此在这里总结记录一下。

  贴出一段学习代码:

void MyPushbutton::mousePressEvent(QMouseEvent *e){//鼠标按下的event handler
    //额外判断,显然当第二张图片有传参时,使用该种效果,这样不会影响第一种效果
    if(this->pressImgPath != ""){
        QPixmap pix;
        bool ret = pix.load(this->pressImgPath);//判断图片加载是否成功
        if(!ret){//加载不成功的时候就终止程序
            qDebug()<<"picture loading failed";
            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);
}
           

  首先需要注意该鼠标点击事件是属于

MyPushbutton

类的,这里说明了两件事:

  1.只有当鼠标移动到该类对象上再点击,才会触发该函数,实现函数内相关功能。

  2.当鼠标在其他区域点击时,不应当触发该函数,而是实现其他区域设定的功能。

  ok,那我们可以分开看看这两件事怎么实现。

  第一件事在声明与实现这个函数的时候就默认实现了,是不需要去真正考虑的,只需要好好考虑鼠标点击后实现什么功能即可。

  第二件事就需要用到QT内事件处理的机制了,当我们在一个事件处理函数中实现功能后,在最后一定要将该事件抛给父类的同样的事件处理函数,这样就能保住,当其他鼠标按下事件出现的时候,与我无关,让更高层次的父类看看怎么去处理,简称甩锅机制 。

  实际上,如果不在函数中增加这句操作语句,在其他区域点击鼠标都会失去作用。

动画效果

  在QT中动画效果只能依靠代码来实现。

  掏出一段按钮向下跳动的代码:

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

  动画效果是依靠

QPropertyAnimation

类实现的,每一种动画效果都对应一个

QPropertyAnimation

类的对象。

  在新建动画效果对象的时候,需要传参设定1.该动画对象绑定的类,2.具体形式的动画,这里我们使用位置变换的动画特效。

  随后设定动画的起始位置与结束位置,在qt中坐标原点在窗口左上角,向右向下分别坐标相加,因此结束位置的坐标应该比起始坐标的y轴坐标数值更大。

  随后设定动画的具体动作方式,设定为弹跳模式,就像橡皮球砸在地上要反弹一样。

  最后写一下启动函数就好了。

  不过这也衍生出了另一个问题:动画执行需要时间,而程序内的操作都是立即执行的,是不是该给动画“留点空”?

  事实也确实如此,凡是添加了动画效果的组件在触发的时候都应该增加延时函数。

//因为特效动画需要时间,因此需要人为增加延时操作
        QTimer::singleShot(400,this,[=](){//延时400ms后,触发后续的slot函数
            chooseScene->setGeometry(this->geometry());//设定新窗口的坐标在屏幕上与原先窗口坐标相同
            this->hide();//将当前界面隐藏
            chooseScene->show();//切换到关卡选择界面
        });
           

  以上代码中可以看到,触发函数中使用

QTimer::singleShot

函数做了400ms的延时,专门为动画效果留了时间。

继续阅读