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参考文档

猜你喜欢

转载自www.cnblogs.com/liuxianglei/p/9727000.html