[Interest] Rendering with Qt Quick vs QGraphicsView

Curtis Mitch mitch.curtis at theqtcompany.com
Thu Aug 27 19:47:33 CEST 2015


I'm moving from QGraphicsScene/QGraphicsView to QQuickItem for rendering my game objects. One of the things this involves is setting up a scene and the view onto that scene, as this was previously managed by QGraphicsScene and QGraphicsView.

My naive attempt was to do the following:

void SceneView::centerOn(const QPointF &pos)
{
    if (mScene) {
        mScene->setX(-(pos.x() - width() / 2));
        mScene->setY(-(pos.y() - height() / 2));
    }
}

This works, but it produces this odd effect where some game objects "jump around" between neighbouring pixels while the camera comes to a halt while following the player:

http://i.imgur.com/AP1yIoO.gif

The player's movement is fine. The crate also does not seem to exhibit this problem (though its Box2D debug drawing marker does).

This is what it looks like when the player moves without the camera following it:

http://i.imgur.com/mA4JTC7.gif

This is what I'd expect from the other game objects when the camera is moving; no movement whatsoever relative to the scene.

The old, correct movement looked like this:

http://i.imgur.com/7yMcVpS.gif

The old code:

void MapView::centerOverFollowedItem()
{
    QRectF geometry(mFollowing->pos(), mFollowing->boundingRect().size());
    centerOn(geometry.center());
}

I thought I'd take a look at how QGraphicsView does it:

void QGraphicsView::centerOn(const QPointF &pos)
{
    Q_D(QGraphicsView);
    qreal width = viewport()->width();
    qreal height = viewport()->height();
    QPointF viewPoint = d->matrix.map(pos);
    QPointF oldCenterPoint = pos;

    if (!d->leftIndent) {
        if (isRightToLeft()) {
            qint64 horizontal = 0;
            horizontal += horizontalScrollBar()->minimum();
            horizontal += horizontalScrollBar()->maximum();
            horizontal -= int(viewPoint.x() - width / 2.0);
            horizontalScrollBar()->setValue(horizontal);
        } else {
            horizontalScrollBar()->setValue(int(viewPoint.x() - width / 2.0));
        }
    }
    if (!d->topIndent)
        verticalScrollBar()->setValue(int(viewPoint.y() - height / 2.0));
    d->lastCenterPoint = oldCenterPoint;
}

http://code.qt.io/cgit/qt/qtbase.git/tree/src/widgets/graphicsview/qgraphicsview.cpp#n1904

It casts the x and y position of its view to integers, so I tried doing that too:

void SceneView::centerOn(const QPointF &pos)
{
    if (mScene) {
        mScene->setX(int(-(pos.x() - width() / 2)));
        mScene->setY(int(-(pos.y() - height() / 2)));
    }
}

This fixes the movement of objects that shouldn't move, but produces a subtle yet irritating effect where the movement of the camera looks "blocky":

http://i.imgur.com/Z3Wc66h.gif

It's mostly noticeable when the player is coming to a stop.

Why does this happen with a Qt Quick scene, but not QGraphicsView, when they are both using integer positioning in this case?

(All objects are Images in the new code, and I set smooth to false on them because my assets will be pixel art)


More information about the Interest mailing list