X and y offset on tiled images
-
I run into a problem when making a game, I want to do a parallax background but when I set an image with the Tile property I couldn't move the image while maintaining the Image object on the same position.
So I hacked the source code of the qml image object to have two new properties: xTileOffset and yTileOffset.
512kb video showing red balls being animated moving to the side:
http://real.purpleorangegames.com/wp-content/uploads/2014/05/TestingTileOffset2.mp4
Vimeo version of the video:
https://vimeo.com/96127913
(It is still being processed at the time I published this post)And the code is quite simple:
@ Image
{
anchors.fill: parent
source: "Images/Background/AnimCircle/10.png"
fillMode: Image.Tile
SequentialAnimation on xTileOffset
{
running: true
loops: Animation.Infinite
NumberAnimation { to: 101; duration: 500 }
}
}@The image covers the parent, the entire window.
It has 101x101 pixels, it's a red ball on a transparent background.
So when I animate the xTileOffset it looks like I'm moving the Image object to the side but the Image actually remains at the same place.I modified the following files in QtDeclarative/src/quick/items/ :
qquickimage_p_p.h
qquickimage_p.h
qquickimage.cppAdding to qquickimage_p_p.h the variables on the class QQuickImagePrivate:
@qreal xTileOffset;
qreal yTileOffset;@Then I added to qquickimage_p.h the following to the class QQuickImage:
@ Q_PROPERTY(qreal xTileOffset READ xTileOffset WRITE setXTileOffset NOTIFY xTileOffsetChanged)
Q_PROPERTY(qreal yTileOffset READ yTileOffset WRITE setYTileOffset NOTIFY yTileOffsetChanged)public:
qreal xTileOffset() const;
qreal yTileOffset() const;
void setXTileOffset(qreal value);
void setYTileOffset(qreal value);Q_SIGNALS:
void xTileOffsetChanged();
void yTileOffsetChanged();
@
And finally I added to qquickimage.cpp:
@qreal QQuickImage::xTileOffset() const
{
Q_D(const QQuickImage);
return d->xTileOffset;
}qreal QQuickImage::yTileOffset() const
{
Q_D(const QQuickImage);
return d->yTileOffset;
}void QQuickImage::setXTileOffset(qreal value)
{
Q_D(QQuickImage);
if (d->xTileOffset == value)
return;d->xTileOffset = value; update(); updatePaintedGeometry(); emit xTileOffsetChanged();
}
void QQuickImage::setYTileOffset(qreal value)
{
Q_D(QQuickImage);
if (d->yTileOffset == value)
return;d->yTileOffset = value; update(); updatePaintedGeometry(); emit yTileOffsetChanged();
}@
And the most important part, inside the function
@QSGNode *QQuickImage::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)@
you can see the variables that I created (xTileOffset and yTileOffset):@ case Tile:
targetRect = QRectF(0, 0, width(), height());
sourceRect = QRectF(-xOffset+xTileOffset(), -yOffset+yTileOffset(), width(), height());
hWrap = QSGTexture::Repeat;
vWrap = QSGTexture::Repeat;
break;case TileHorizontally: targetRect = QRectF(0, 0, width(), height()); sourceRect = QRectF(-xOffset+xTileOffset(), 0, width(), d->pix.height()); hWrap = QSGTexture::Repeat; break; case TileVertically: targetRect = QRectF(0, 0, width(), height()); sourceRect = QRectF(0, -yOffset+yTileOffset(), d->pix.width(), height()); vWrap = QSGTexture::Repeat; break;@
And that's it.
Should I try to submit it back to Qt?
-
Sorry, I did a few tests and could not achieve the same result with the option you mentioned, can you provide a minimal example of it?
The requirements are that a image must remain the same size, at the same place and be Tiled. The real image is just one red ball.
EDIT:
I tested some more and came up with this:
http://purpleorangegames.com/wp-content/uploads/2014/05/TestingTileOffset3.mp4
@ Image
{
width: parent.width
height: parent.height
anchors.centerIn: parent
source: "Images/Background/AnimCircle/10.png"
fillMode: Image.Tile
SequentialAnimation on anchors.horizontalCenterOffset
{
running: true
loops: Animation.Infinite
NumberAnimation { to: -101; duration: 500 }
}
}@
I can't set anchors.fill, instead I need to manually set width and height, the only thing that didn't come out correctly is that at the left side you can see the ball popping up from nowhere instead of coming from the side.So I increased the size of the Image to two times the original image (101 pixels):
http://purpleorangegames.com/wp-content/uploads/2014/05/TestingTileOffset3.mp4
@ Image
{
width: parent.width+202
height: parent.height
anchors.centerIn: parent
source: "Images/Background/AnimCircle/10.png"
fillMode: Image.Tile
SequentialAnimation on anchors.horizontalCenterOffset
{
running: true
loops: Animation.Infinite
NumberAnimation { to: -101; duration: 500 }
}
}@My goal is to use small images (little stars) and one huge image (a galaxy probably 4000x4000 pixels) and make a parallax background of it.
I will test this solution with a huge image to see it works as expected.
Thanks for the tip! -
I'm sorry, the purpose of Image is display an static image. However it has some properties to fill a large area like tiled.
I've no experience with that, but if you need to scroll elements on the screen, perhaps a Canvas could help to you.