我试图从图块集渲染单个图块。例如,我想在下面的tileset中显示灰色磁贴:
在实际使用案例中,这些将是例如游戏中的水,草等瓷砖。渲染这些图块有一些要求:
据我所知,内置的Qt Quick类型都没有满足这些要求(渲染图像的一部分未经过平滑处理)。我已尝试QQuickPaintedItem
QPainter
render hints(例如SmoothPixmapTransform
设置为false
)但没有成功;图像模糊"模糊"放大时。 AnimatedSprite
支持渲染图像的各个部分,但没有API来禁用平滑。
我的想法是使用场景图API实现自定义QQuickItem
。
main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQuickItem>
#include <QQuickWindow>
#include <QSGImageNode>
static QImage image;
static const int tileSize = 32;
static const int tilesetSize = 8;
class Tile : public QQuickItem
{
Q_OBJECT
Q_PROPERTY(int index READ index WRITE setIndex NOTIFY indexChanged)
public:
Tile() :
mIndex(-1) {
setWidth(tileSize);
setHeight(tileSize);
setFlag(QQuickItem::ItemHasContents);
}
QSGNode *updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
{
if (!oldNode) {
oldNode = window()->createImageNode();
}
if (mIndex == -1)
return oldNode;
if (image.isNull()) {
image = QImage("C:/tileset.png");
if (image.isNull())
return oldNode;
}
QSGTexture *texture = window()->createTextureFromImage(image);
qDebug() << "textureSize:" << texture->textureSize();
if (!texture)
return oldNode;
QSGImageNode *imageNode = static_cast<QSGImageNode*>(oldNode);
// imageNode->setOwnsTexture(true);
imageNode->setTexture(texture);
qDebug() << "source rect:" << (mIndex % tileSize) * tileSize << (mIndex / tileSize) * tileSize << tileSize << tileSize;
imageNode->setSourceRect((mIndex % tileSize) * tileSize, (mIndex / tileSize) * tileSize, tileSize, tileSize);
return oldNode;
}
int index() const {
return mIndex;
}
void setIndex(int index) {
if (index == mIndex)
return;
mIndex = index;
emit indexChanged();
}
signals:
void indexChanged();
private:
int mIndex;
};
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
qmlRegisterType<Tile>("App", 1, 0, "Tile");
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
return app.exec();
}
#include "main.moc"
main.qml
:
import QtQuick 2.9
import QtQuick.Controls 2.2
import App 1.0
ApplicationWindow {
id: window
width: 800
height: 800
visible: true
Slider {
id: slider
from: 1
to: 10
}
Tile {
scale: slider.value
index: 1
anchors.centerIn: parent
Rectangle {
anchors.fill: parent
color: "transparent"
border.color: "darkorange"
}
}
}
此应用程序的输出看起来很好,但矩形内没有任何内容呈现:
textureSize: QSize(256, 256)
source rect: 32 0 32 32
从minimal docs判断,我的实现(就我如何创建节点而言)似乎没问题。我哪里错了?
答案 0 :(得分:2)
晚会结束一年,但我遇到了和你一样的问题。为了试图继承QQuickItem
并且遇到过这个主题的其他人,在updatePaintNode
的文档中有一个小小的金块:
该函数是
QQuickItem::update()
的结果, 如果用户在项目上设置了QQuickItem::ItemHasContents
标志。
当我设置那个标志时,所有渲染的东西。
我认为自己是一个注重细节的人......
编辑:
在OP指出他们已经设置了ItemHasContents
标志之后,我再次查看了代码并看到OP在节点上设置了sourceRect
,OP没有&#39; t设置节点的rect
,这确实是OP遇到的问题。
答案 1 :(得分:1)
最后我和朋友一起使用QQuickImageProvider
:
<强> tileimageprovider.h
强>:
#ifndef TILEIMAGEPROVIDER_H
#define TILEIMAGEPROVIDER_H
#include <QQuickImageProvider>
#include <QHash>
#include <QString>
#include <QImage>
class TileImageProvider : public QQuickImageProvider
{
public:
TileImageProvider();
QImage requestImage(const QString &id, QSize *size, const QSize &requestedSize) override;
private:
QHash<QString, QImage> mTiles;
};
#endif // TILEIMAGEPROVIDER_H
<强> tileimageprovider.cpp
强>:
#include "tileimageprovider.h"
#include <QImage>
#include <QDebug>
TileImageProvider::TileImageProvider() :
QQuickImageProvider(QQmlImageProviderBase::Image)
{
QImage tilesetImage(":/sprites/tiles/tileset.png");
if (tilesetImage.isNull()) {
qWarning() << "Failed to load tileset image";
return;
}
int index = 0;
for (int row = 0; row < 8; ++row) {
for (int col = 0; col < 8; ++col) {
int sourceX = col * 32;
int sourceY = row * 32;
QImage subTile = tilesetImage.copy(sourceX, sourceY, 32, 32);
if (tilesetImage.isNull()) {
qWarning() << "Tile image at" << sourceX << sourceY << "is null";
return;
}
mTiles.insert(QString::number(index++), subTile);
}
}
}
QImage TileImageProvider::requestImage(const QString &id, QSize *size, const QSize &)
{
Q_ASSERT(mTiles.find(id) != mTiles.end());
*size = QSize(32, 32);
return mTiles.value(id);
}
然后我从以下Component
创建切片实例:
Image {
width: 32
height: 32
smooth: false
asynchronous: true
source: "image://tile/" + index
property int index
}
还有Tiled的tile rendering code,它使用场景图节点,可能效率更高。
答案 2 :(得分:0)
看起来您可以更轻松,更高效地使用它。
您可以使用简单的QQuickItem
,而不是实现自定义ShaderEffect
。您可以传递偏移并控制采样。
此外,您只需要一个黑色和白色加号,并且可以通过将其作为参数传递给着色器来使颜色动态化。
最后,自己做地图册可能是多余的,因为场景图无论如何都可能在地图集上放置小纹理。当然,没有必要编写单行C ++,同时仍然可以获得高效灵活的解决方案。