zoukankan      html  css  js  c++  java
  • Colliding Mice

    QT的GVF(Graphics View framework)框架提供QGraphicsScene类用于和大量从QGraphicsItem类派生的定制2D图元的管理和交互,和一个支持缩放和旋转的QGraphicsView窗口使这些图元可视化。

    Mouse Class 定义

    mouse类继承自QGraphicsItem,用于绘制老鼠。QGraphicsItem是所有在GVF中的图元的基类。

    绘制定制图元时,必须实现QGraphicsItem的两个纯虚函数:boundingRect()函数返回图元绘制区域的估计值,paint()函数实现绘制工作。

    mouse.h

     1 #ifndef MOUSE_H
     2 #define MOUSE_H
     3 
     4 #include <QGraphicsItem>
     5 #include <QTime>
     6 #include <QTimer>
     7 class Mouse : public QGraphicsItem
     8 {
     9 public:
    10     Mouse();
    11 
    12     QRectF boundingRect() const;
    13     QPainterPath shape() const;
    14     void paint(QPainter *painter, const QStyleOptionGraphicsItem *option,QWidget *widget);
    15 
    16 protected:
    17     void advance(int step);
    18 
    19 private:
    20     qreal angle;
    21     qreal speed;
    22     qreal mouseEyeDirection;
    23     QColor color;
    24 };
    25 
    26 #endif

    mouse.cpp

      1 #include "mouse.h"
      2 
      3 #include <QGraphicsScene>
      4 #include <QPainter>
      5 #include <QStyleOption>
      6 
      7 #include <math.h>
      8 
      9 static const double Pi = 3.14159265358979323846264338327950288419717;
     10 static double TwoPi = 2.0 * Pi;
     11 
     12 static qreal normalizeAngle(qreal angle)
     13 {
     14     while (angle < 0)
     15         angle += TwoPi;
     16     while (angle > TwoPi)
     17         angle -= TwoPi;
     18     return angle;
     19 }
     20 
     21 Mouse::Mouse() : angle(0), speed(0), mouseEyeDirection(0), color(qrand() % 256, qrand() % 256, qrand() % 256)
     22 {
     23     setRotation(qrand() % (360 * 16));//顺时针随机旋转图元角度
     24 }
     25 //boundingRect()函数返回一个矩形范围作为图元的外边界,图元所有的绘制必须在这个矩形范围内完成。
     26 QRectF Mouse::boundingRect() const
     27 {
     28     qreal adjust = 0.5;
     29     return QRectF(-18 - adjust, -22 - adjust, 36 + adjust, 60 + adjust);
     30 }
     31 //QGraphicsView调用paint()函数来绘制图元,该函数采用本地坐标系统(图元坐标系统)
     32 //我们使用QGraphicsScene::collidingItems() 函数来检测是否发生碰撞。当发生碰撞时,老鼠的耳朵将被填充为红色,否则为深黄色。
     33 //QGraphicsItem::shape()函数采用本地坐标系统可以返回一个精确的图形用于碰撞检测,命中测试和QGraphicsScene :: items()函数。
     34 QPainterPath Mouse::shape() const
     35 {
     36     QPainterPath path;
     37     path.addRect(-10, -20, 20, 40);//当碰撞发生在小鼠的身体时即认为发生碰撞
     38     return path;
     39 }
     40 
     41 void Mouse::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *)
     42 {
     43     // Body
     44     painter->setBrush(color);
     45     painter->drawEllipse(-10, -20, 20, 40);
     46 
     47     // Eyes
     48     painter->setBrush(Qt::white);
     49     painter->drawEllipse(-10, -17, 8, 8);
     50     painter->drawEllipse(2, -17, 8, 8);
     51 
     52     // Nose
     53     painter->setBrush(Qt::black);
     54     painter->drawEllipse(QRectF(-2, -22, 4, 4));
     55 
     56     // Pupils
     57     painter->drawEllipse(QRectF(-8.0 + mouseEyeDirection, -17, 4, 4));
     58     painter->drawEllipse(QRectF(4.0 + mouseEyeDirection, -17, 4, 4));
     59 
     60     // Ears
     61     //collidingItems()碰撞检测函数
     62     painter->setBrush(scene()->collidingItems(this).isEmpty() ? Qt::darkYellow : Qt::red);
     63     painter->drawEllipse(-17, -12, 16, 16);
     64     painter->drawEllipse(1, -12, 16, 16);
     65 
     66     // Tail
     67     QPainterPath path(QPointF(0, 20));
     68     path.cubicTo(-5, 22, -5, 22, 0, 25);
     69     path.cubicTo(5, 27, 5, 32, 0, 30);
     70     path.cubicTo(-5, 32, -5, 42, 0, 35);
     71     painter->setBrush(Qt::NoBrush);
     72     painter->drawPath(path);
     73 }
     74 //所以当step为0时,我们不做任何操作(advance()函数会被QGraphicsScene :: advance()调用两次,第一次step==0表示图元将要开始移动,当step == 1表示开始移动)。
     75 //注意,QGraphicsItem提供的mapFromScene()函数可以把场景坐标映射成系统坐标
     76 void Mouse::advance(int step)
     77 {
     78     if (!step)
     79         return;
     80     // Don't move too far away
     81     QLineF lineToCenter(QPointF(0, 0), mapFromScene(0, 0));//图元坐标原点和场景坐标原点之间的直线
     82     if (lineToCenter.length() > 150) //当距离大于150像素时,调整方向
     83     {
     84         qreal angleToCenter = ::acos(lineToCenter.dx() / lineToCenter.length());
     85         if (lineToCenter.dy() < 0)
     86             angleToCenter = TwoPi - angleToCenter;
     87         angleToCenter = normalizeAngle((Pi - angleToCenter) + Pi / 2);
     88 
     89         if (angleToCenter < Pi && angleToCenter > Pi / 4) 
     90         {
     91             // Rotate left
     92             angle += (angle < -Pi / 2) ? 0.25 : -0.25;
     93         }
     94         else if (angleToCenter >= Pi && angleToCenter < (Pi + Pi / 2 + Pi / 4))
     95         {
     96             // Rotate right
     97             angle += (angle < Pi / 2) ? 0.25 : -0.25;
     98         }
     99     }
    100     else if (::sin(angle) < 0) 
    101     {
    102         angle += 0.25;
    103     }
    104     else if (::sin(angle) > 0) 
    105     {
    106         angle -= 0.25;
    107     }
    108 
    109     // Try not to crash with any other mice
    110     QList<QGraphicsItem *> dangerMice = scene()->items (QPolygonF() << mapToScene(0, 0) << mapToScene(-30, -50) << mapToScene(30, -50));
    111     foreach(QGraphicsItem *item, dangerMice)//检测场景中的图元
    112     {
    113         if (item == this)//如果只有自己则跳过,否则调整方向
    114             continue;
    115 
    116         QLineF lineToMouse(QPointF(0, 0), mapFromItem(item, 0, 0));
    117         qreal angleToMouse = ::acos(lineToMouse.dx() / lineToMouse.length());
    118         if (lineToMouse.dy() < 0)
    119             angleToMouse = TwoPi - angleToMouse;
    120         angleToMouse = normalizeAngle((Pi - angleToMouse) + Pi / 2);
    121 
    122         if (angleToMouse >= 0 && angleToMouse < Pi / 2) 
    123         {
    124             // Rotate right
    125             angle += 0.5;
    126         }
    127         else if (angleToMouse <= TwoPi && angleToMouse >(TwoPi - Pi / 2))
    128         {
    129             // Rotate left
    130             angle -= 0.5;
    131         }
    132     }
    133 
    134     // Add some random movement
    135     if (dangerMice.size() > 1 && (qrand() % 10) == 0) 
    136     {
    137         if (qrand() % 1)
    138             angle += (qrand() % 100) / 500.0;
    139         else
    140             angle -= (qrand() % 100) / 500.0;
    141     }
    142 
    143     speed += (-50 + qrand() % 100) / 100.0;
    144 
    145     qreal dx = ::sin(angle) * 10;
    146     mouseEyeDirection = (qAbs(dx / 5) < 1) ? 0 : dx / 5;
    147 
    148     setRotation(rotation() + dx);
    149     setPos(mapToParent(0, -(3 + sin(speed) * 3)));
    150 }

    mainwindow.h

     1 #pragma once
     2 
     3 #include <QtWidgets/QMainWindow>
     4 #include "ui_MainWindow.h"
     5 #include <QGraphicsScene>
     6 #include <QGraphicsView>
     7 #include <QHBoxLayout>
     8 #include "mouse.h"
     9 class MainWindow : public QMainWindow
    10 {
    11     Q_OBJECT
    12 
    13 public:
    14     MainWindow(QWidget *parent = Q_NULLPTR);
    15 public:
    16     void ConstructScene();
    17 private:
    18     QTimer timer;
    19     static const int MouseCount = 8;
    20     QGraphicsScene * scene;
    21     QGraphicsView *view;
    22     Ui::MainWindowClass ui;
    23 };

    mainwindow.cpp

     1 #include "MainWindow.h"
     2 
     3 MainWindow::MainWindow(QWidget *parent)
     4     : QMainWindow(parent)
     5 {
     6     ui.setupUi(this);
     7     scene = new QGraphicsScene(this);//创建场景
     8     view = new QGraphicsView(scene);//创建视图
     9     ui.verticalLayout->addWidget(view);//将场景加入主界面
    10     ConstructScene();//构建动画
    11 }
    12 
    13 
    14 void MainWindow::ConstructScene()
    15 {
    16     qsrand(QTime(0, 0, 0).secsTo(QTime::currentTime()));//随机数种子
    17     scene->setSceneRect(-300, -300, 600, 600);
    18     scene->setItemIndexMethod(QGraphicsScene::NoIndex);
    19     for (int i = 0; i < MouseCount; ++i) 
    20     {
    21         Mouse *mouse = new Mouse;
    22         mouse->setPos(::sin((i * 6.28) / MouseCount) * 200,::cos((i * 6.28) / MouseCount) * 200);
    23         scene->addItem(mouse);
    24     }
    25 
    26     view->setRenderHint(QPainter::Antialiasing);
    27     //view.setBackgroundBrush(QPixmap(":/images/cheese.jpg"));
    28     view->setCacheMode(QGraphicsView::CacheBackground);
    29     view->setViewportUpdateMode(QGraphicsView::BoundingRectViewportUpdate);
    30     view->setDragMode(QGraphicsView::ScrollHandDrag);
    31     //view->resize(400, 300);
    32     //view->show();
    33 
    34 
    35     QObject::connect(&timer, SIGNAL(timeout()), scene, SLOT(advance()));
    36     timer.start(1000 / 33);//每秒30帧
    37 }

    运行效果

     

    参考:QT5参考文档

  • 相关阅读:
    php7垃圾回收机制
    PHP-FPM
    go main.main之前的准备
    ElementUI MessageBox 弹框
    ElementUI table 样式修改
    ElementUI Dropdown 下拉菜单
    vue+django实现下载文件
    ElementUI 点击展开/隐藏
    vue+django实现websocket连接
    ElementUI 上传文件以及限制
  • 原文地址:https://www.cnblogs.com/liuxianglei/p/9727000.html
Copyright © 2011-2022 走看看