欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

QT项目实战——行车记录仪之开场动画设计

程序员文章站 2022-04-19 18:38:36
...

总体介绍

我们通过创建一个场景和视图,向场景中添加图元,利用定时器让图元开始移动,同时检测碰撞,若发生碰撞后,则关闭定时器,同时关闭视图。

一、自定义图元创建

定义一个图元MyItem,继承与 public QObject和public QGraphicsItem,分别重写advance、boundingRect、paint函数,继承QObject是为了能够使用槽函数。然后因为某些图元是不需要移动的,所以我们定义一个变量去控制int move。
下面是实例代码

class MyItem: public QObject, public QGraphicsItem
{
    Q_OBJECT  //为了能够使用信号和槽所以需要写这个
public:
    MyItem(QString path, int num);
    //运动
    void advance(int);
    //返回图元所在区域
    QRectF boundingRect() const;
    //绘制图元
    void  paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = Q_NULLPTR);

    //判断是否需要关闭定时器
    int type;   //0--关闭, 1--开启
signals:
    void mysignal();

private:
    QPixmap img;   //这个不能用指针,不然绘图的时候用不了
    int move;   //判断是否需要移动  0--不移动, 1--移动
};

图元移动
我们通过在advance函数里面判断move的值来判断图元是否需要移动,我这边设置0:不移动;1:横向移动;2:纵向移动。然后我们去调用setPos去设置移动单位

//负号控制方向
this->setPos(this->mapToScene(-1, 0));   //每次横向(左)移动二个单位
this->setPos(this->mapToScene(0, 0.5));   //每次纵向(下方)移动二个单位

碰撞检测
使用全局函数collidingItems来做碰撞检测,如果在场景和图元中有发生碰撞的话那么这个函数就会讲发生碰撞的图元通过这个函数返回一个QList的链表,自然如果这个链表中有数据那么就等于有图元发生了碰撞,所以我们做个if判断
if(collidingItems().count()>0 && !flag) //碰撞
那为什么后面还有一个flag呢?是因为图元发生了碰撞后就不能在移动了,所以这边有个静态标志位,发生碰撞后就不在进入到这个条件内,也就是图元不在移动。

二、视图的创建

自定义一个视图类为View继承QGraphicsView,定义一个场景(QGraphicsScene)gamescene

class View: public QGraphicsView
{
    Q_OBJECT  //为了能够使用信号和槽所以需要写这个
public:
    View();
public slots:
    void StopTime();

protected:
    QGraphicsScene *gamescene;  //场景
    MyItem *item1, *item2, *item3, *itemname, *itemlogo;  //图元
    QTimer *clock1;  //定时器
};

然后添加图元到场景中去,下面是示例

this->item2 = new MyItem(":/resource/img/people.png", 2);  //创建一个图元
this->gamescene->addItem(this->item2);  //添加图元
this->item2->setPos(300, 300);  //设置图元起始位置

开启定时器移动图元
图元需要移动,那么我们就要去调用advance函数才能移动我们的图元。如何调用呢?答案是通过我们的定时器,每几毫秒移动(改变位置)一次就好了,那么我们肉眼就会看起来有移动的效果。

//设置定时器
this->clock1 = new QTimer;
this->clock1->start(10);   //设置定时时间为10ms

连接信号和槽,使图元移动起来

//连接槽函数
connect(this->clock1, SIGNAL(timeout()), this->gamescene, SLOT(advance()));

那么这里可能会有一个疑问,为什么连接的是场景的槽函数而不是图元的槽函数,答案显然是:你的图元已经添加到场景中去了,而这个场景中的advance函数可以直接取调用每个图元的advance函数,比起你去一个个连接图元的advance函数简单了很多。如此你的图元就可以动起来了。

三、碰撞检测后定时器停止

这里的实现有两种方法:1.是通过在创建一个定时器去关闭图元移动的定时器。2. 是通过信号和槽的方式去关闭定时器。方案一这里不讲,讲的是方案二。
方案一:简单一点,说下思路吧,你只要开一个比那个定时器比移动图元的快,然后去读取碰撞图元的一个标志位,查看是否发生碰撞就好了(也就是需要在图元里面定义一个变量,发生碰撞那么这个标志位就改变)。
方案二:我们通过图元发生碰撞后产生信号,然后发送给我们的视图,我们视图执行对应的槽函数,然后关闭定时器
在图元的advance函数中发送信号:emit mysignal(); //发送信号
在视图中接受信号:

//信号与槽的连接---注意这边连接的图元要是能发生碰撞的图元
connect(this->item1, SIGNAL(mysignal()), this, SLOT(StopTime()));    //自定义信号

//StopTime()函数中写
//此时定时器停止
this->clock1->stop();
//添加一个碰撞图元
this->gamescene->addItem(this->item3);  //显示碰撞效果图元
//隐藏之前的图元
this->item1->hide();
this->item2->hide();
QMessageBox::information(this, "提示:", "程序启动成功!", QMessageBox::Yes);

到此开场动画结束!
效果:
QT项目实战——行车记录仪之开场动画设计
QT项目实战——行车记录仪之开场动画设计