用Qt图形视图框架开发拼图游戏
2016-10-14 07:10
411 查看
用Qt的图形视图框架(Graphics View Framework)做了一个拼图游戏DEMO,演示了:
QGraphicsView、QGraphicsScene、QGraphicsItem的基本用法
drag && drop
自定义QGraphicsItem
先来看看效果吧:
现在,来看下代码了。
如上图所示,项目名称为qPuzzle,三个源文件,main.cpp是入口,imageitem.h和imageitem.cpp实现了:
PuzzleImageItem,就是界面左上侧那两个可以拖动的碎片,支持拖动
PuzzlePart,用于接受拖放的item
PuzzlePartManager,管理可拖放的PuzzleImageItem,拖放到位后从QGraphicsScene中移除PuzzleImageItem
项目还有几个图片资源,model.png是带有拼图区域的房子图片,mode_1.png和model_2.png是房子上扣出来的小图片。
main()方法创建了QGraphicsScene实例,构造各种item并添加到场景中,将QGraphicsView与QGraphicsScene关联起来,代码很直接,不多说了。
PuzzleImageItem代表完整图片的一部分,它从QGraphicsObject继承而来,持有一个图片、宽、高以及图片的id(m_partId)。其中m_partId是碎片索引,在拖放到位后,PuzzlePartManager通过它来将界面左上角的碎片从视图中移除。
重写了boundingRect和paint方法,这是自定义QGraphicsItem时通常都需要做的。
重写了mousePressEvent,在它里面将鼠标形状修改为抓紧的小手:
重写了mouseMoveEvent,在它里面组装QDrag和QMimeData:
注意我在这里用QMimeData传递m_partId给接受拖放的PuzzlePartItem,这样可以区分碎片该放到哪个目标区域。在PuzzlePartItem的dragEnterEvent方法中有用到,代码如下:
这里可以留意一下使用QMimeData传递非典型类型数据的做法。
重写了mouseReleaseEvent,在它里面重新设置鼠标形状为打开的小手:
这个类也保留了一个id(m_partId),可以用来决定接受哪个PuzzleImageItem。前面贴出来的dragEnterEvent方法的代码里就用到了。dragEnterEvent方法在PuzzleImageItem被拖到PuzzlePartItem所在区域时触发。
当释放鼠标时,会触发dropEvent,代码如下:
我们在这里接收QMimeData里的图片,触发重新绘制,还调用PuzzlePartManager的removeItem将源item(PuzzleImageItem)从视图中移除。
重写dragLeaveEvent是为了在鼠标拖着源object离开自己时重绘。
好啦,这个demo基本就这样了。
需要项目源码的,可以关注我的订阅号“程序视界”,回复“qPuzzle”获取下载地址。
QGraphicsView、QGraphicsScene、QGraphicsItem的基本用法
drag && drop
自定义QGraphicsItem
先来看看效果吧:
现在,来看下代码了。
项目说明
如上图所示,项目名称为qPuzzle,三个源文件,main.cpp是入口,imageitem.h和imageitem.cpp实现了:
PuzzleImageItem,就是界面左上侧那两个可以拖动的碎片,支持拖动
PuzzlePart,用于接受拖放的item
PuzzlePartManager,管理可拖放的PuzzleImageItem,拖放到位后从QGraphicsScene中移除PuzzleImageItem
项目还有几个图片资源,model.png是带有拼图区域的房子图片,mode_1.png和model_2.png是房子上扣出来的小图片。
源码说明
分开来说吧,main()、PuzzleImageItem和PuzzlePartItem。入口函数main
先看main.cpp吧:class GraphicsView : public QGraphicsView { public: GraphicsView(QGraphicsScene *scene) : QGraphicsView(scene) { } protected: virtual void resizeEvent(QResizeEvent *) Q_DECL_OVERRIDE { } }; int main(int argc, char *argv[]) { QApplication a(argc, argv); QGraphicsScene scene(0, 0, 480, 360); PuzzleImageItem *image1 = new PuzzleImageItem(":/model_1.png", 60, 60, 1); image1->setPos(4, 4); scene.addItem(image1); PuzzleImageItem *image2 = new PuzzleImageItem(":/model_2.png", 60, 60, 2); image2->setPos(4, 70); scene.addItem(image2); QGraphicsPixmapItem *model = new QGraphicsPixmapItem(QPixmap(":/model.png")); scene.addItem(model); model->setPos(100, 100); PuzzlePartManager mgr(&scene); mgr.addSourceItems(1, image1); mgr.addSourceItems(2, image2); PuzzlePart *part1 = new PuzzlePart(&mgr, 60, 60, 1); part1->setPos(261, 149); part1->setZValue(2); scene.addItem(part1); PuzzlePart *part2 = new PuzzlePart(&mgr, 60, 60, 2); part2->setPos(231, 199); part2->setZValue(2); scene.addItem(part2); GraphicsView view(&scene); view.setRenderHint(QPainter::Antialiasing); view.setViewportUpdateMode(QGraphicsView::BoundingRectViewportUpdate); view.setBackgroundBrush(QColor(230, 200, 167)); view.setWindowTitle("House Puzzle"); view.show(); return a.exec(); }
main()方法创建了QGraphicsScene实例,构造各种item并添加到场景中,将QGraphicsView与QGraphicsScene关联起来,代码很直接,不多说了。
PuzzleImageItem
再来看看支持拖动的PuzzleImageItem的实现:class PuzzleImageItem : public QGraphicsObject { public: PuzzleImageItem(const QString & imagePath, int w, int h, int partId); QRectF boundingRect() const Q_DECL_OVERRIDE; void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) Q_DECL_OVERRIDE; protected: void mousePressEvent(QGraphicsSceneMouseEvent *event) Q_DECL_OVERRIDE; void mouseMoveEvent(QGraphicsSceneMouseEvent *event) Q_DECL_OVERRIDE; void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) Q_DECL_OVERRIDE; protected: QImage m_image; int m_width; int m_height; int m_partId; };
PuzzleImageItem代表完整图片的一部分,它从QGraphicsObject继承而来,持有一个图片、宽、高以及图片的id(m_partId)。其中m_partId是碎片索引,在拖放到位后,PuzzlePartManager通过它来将界面左上角的碎片从视图中移除。
重写了boundingRect和paint方法,这是自定义QGraphicsItem时通常都需要做的。
重写了mousePressEvent,在它里面将鼠标形状修改为抓紧的小手:
void PuzzleImageItem::mousePressEvent(QGraphicsSceneMouseEvent *event) { setCursor(Qt::ClosedHandCursor); }
重写了mouseMoveEvent,在它里面组装QDrag和QMimeData:
void PuzzleImageItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event) { if (QLineF(event->screenPos(), event->buttonDownScreenPos(Qt::LeftButton)) .length() < QApplication::startDragDistance()) { return; } QDrag *drag = new QDrag((QObject*)event->widget()); QMimeData *mime = new QMimeData; mime->setImageData(m_image); mime->setData(QString(QMetaType::typeName(QMetaType::Int)), QString("%1").arg(m_partId).toLatin1()); drag->setMimeData(mime); drag->setPixmap(QPixmap::fromImage(m_image)); drag->setHotSpot(QPoint(15, 30)); drag->exec(); setCursor(Qt::OpenHandCursor); }
注意我在这里用QMimeData传递m_partId给接受拖放的PuzzlePartItem,这样可以区分碎片该放到哪个目标区域。在PuzzlePartItem的dragEnterEvent方法中有用到,代码如下:
void PuzzlePart::dragEnterEvent(QGraphicsSceneDragDropEvent *event) { const QMimeData *mime = event->mimeData(); int partId = mime->data(QMetaType::typeName(QMetaType::Int)).toInt(); if(mime->hasImage() && partId == m_partId) { event->setAccepted(true); m_dragOver = true; update(); } else { event->setAccepted(false); } }
这里可以留意一下使用QMimeData传递非典型类型数据的做法。
重写了mouseReleaseEvent,在它里面重新设置鼠标形状为打开的小手:
void PuzzleImageItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) { setCursor(Qt::OpenHandCursor); }
PuzzlePartItem
PuzzlePartItem定义了一个区域,用于接受拖放。它同样从QGraphicsObject继承,声明如下:class PuzzlePart : public QGraphicsObject { public: PuzzlePart(PuzzlePartManager *mgr, int w, int h, int partId, QGraphicsItem *parent = 0); QRectF boundingRect() const Q_DECL_OVERRIDE; void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0) Q_DECL_OVERRIDE; protected: void dragEnterEvent(QGraphicsSceneDragDropEvent *event) Q_DECL_OVERRIDE; void dragLeaveEvent(QGraphicsSceneDragDropEvent *event) Q_DECL_OVERRIDE; void dropEvent(QGraphicsSceneDragDropEvent *event) Q_DECL_OVERRIDE; PuzzlePartManager *m_sourcePartManager; int m_width; int m_height; int m_partId; QImage m_image; bool m_dragOver; };
这个类也保留了一个id(m_partId),可以用来决定接受哪个PuzzleImageItem。前面贴出来的dragEnterEvent方法的代码里就用到了。dragEnterEvent方法在PuzzleImageItem被拖到PuzzlePartItem所在区域时触发。
当释放鼠标时,会触发dropEvent,代码如下:
void PuzzlePart::dropEvent(QGraphicsSceneDragDropEvent *event) { m_dragOver = false; const QMimeData *mime = event->mimeData(); int partId = mime->data(QMetaType::typeName(QMetaType::Int)).toInt(); if(mime->hasImage() && partId == m_partId) { m_image = qvariant_cast<QImage>(event->mimeData()->imageData()); m_sourcePartManager->removeItem(partId); } update(); }
我们在这里接收QMimeData里的图片,触发重新绘制,还调用PuzzlePartManager的removeItem将源item(PuzzleImageItem)从视图中移除。
重写dragLeaveEvent是为了在鼠标拖着源object离开自己时重绘。
好啦,这个demo基本就这样了。
需要项目源码的,可以关注我的订阅号“程序视界”,回复“qPuzzle”获取下载地址。
相关文章推荐
- 用Qt图形视图框架开发拼图游戏
- QT图形视图框架(待完成)
- QT图形视图框架(The QGraphics View Framework)(转)(六)
- QT图形视图框架
- Qt之图形视图框架
- QT 图形视图框架
- Qt Graphics View Framework 图形视图框架
- QT图形视图框架(The Graphics View Framework)
- Qt图形视图框架
- 用qt的图形视图框架实现的五子棋
- Qt之图形视图框架
- QT图形视图框架(The Graphics View Framework)
- 转载:QT图形视图框架(The Graphics View Framework)
- Qt 框架的图形性能高(OpenGL上的系统效率高),网络性能低,开发效率高,Quick是可以走硬件加速——Qt中分为好几套图形系统,差不多代表了2D描画的发展史。最经典的软描画系统
- (转)Qt图形视图框架(一)
- QT图形视图框架
- 转载:QT图形视图框架(The Graphics View Framework)
- [Qt教程] 第20篇 2D绘图(十)图形视图框架(下)
- QT 图形视图框架之角色移动
- Qt Graphics View Framework 图形视图框架