What is a good way to display a tilemap
-
Hi experts,
I know that the question is vague, allow me to clarify it here:
I have a spritesheet that looks like this one: (each tile is 16x16)
Now that I'd like to show a tilemap with each tile containing just the screen position and the spritesheet position:
struct tile { int screenX; int screenY; // position on screen int cropX; int cropY; // position on spritesheet to crop }
Here is how I render the tilemap: (pseudo code)
for (tile t : tilemap) { // spriteSheet is a QPixmap item containing the whole spritesheet QPixmap temp = p.copy(QRect(t.cropX, t.cropY)); // <- crop QGraphicsPixmapItem* temp = new QGraphicsPixmapItem(temp); // editorScene is the QGraphicsScene* and editorView is the QGraphicsView* temp->setPos(QPoint(t.screenX, t.screenY)); editorScene->addItem(temp); }
I'm wondering if this is the standard way to display a tilemap?
-
Hi experts,
I know that the question is vague, allow me to clarify it here:
I have a spritesheet that looks like this one: (each tile is 16x16)
Now that I'd like to show a tilemap with each tile containing just the screen position and the spritesheet position:
struct tile { int screenX; int screenY; // position on screen int cropX; int cropY; // position on spritesheet to crop }
Here is how I render the tilemap: (pseudo code)
for (tile t : tilemap) { // spriteSheet is a QPixmap item containing the whole spritesheet QPixmap temp = p.copy(QRect(t.cropX, t.cropY)); // <- crop QGraphicsPixmapItem* temp = new QGraphicsPixmapItem(temp); // editorScene is the QGraphicsScene* and editorView is the QGraphicsView* temp->setPos(QPoint(t.screenX, t.screenY)); editorScene->addItem(temp); }
I'm wondering if this is the standard way to display a tilemap?
@Q74r3wq said in What is a good way to display a tilemap:
for (tile t : tilemap) { // spriteSheet is a QPixmap item containing the whole spritesheet QPixmap temp = p.copy(QRect(t.cropX, t.cropY)); // <- crop QGraphicsPixmapItem* temp = new QGraphicsPixmapItem(temp); // editorScene is the QGraphicsScene* and editorView is the QGraphicsView* temp->setPos(QPoint(t.screenX, t.screenY)); editorScene->addItem(temp); }
I'm wondering if this is the standard way to display a tilemap?
It seems like an inefficient way to use the sprite sheet. Each loop is making a new deep copy of the sprite in question, even if the same sprite was previously used.
Deriving from QGraphicsItem and overriding QGraphicsItem::paint() to call one of the QPainter::drawPixmap() overloads with source and destination coordinates would allow copying directly from the sprite sheet into the scene.
Alternatively, the sprites could be split into QPixmap instances prior to iterating through the tile map.
-
@Q74r3wq said in What is a good way to display a tilemap:
for (tile t : tilemap) { // spriteSheet is a QPixmap item containing the whole spritesheet QPixmap temp = p.copy(QRect(t.cropX, t.cropY)); // <- crop QGraphicsPixmapItem* temp = new QGraphicsPixmapItem(temp); // editorScene is the QGraphicsScene* and editorView is the QGraphicsView* temp->setPos(QPoint(t.screenX, t.screenY)); editorScene->addItem(temp); }
I'm wondering if this is the standard way to display a tilemap?
It seems like an inefficient way to use the sprite sheet. Each loop is making a new deep copy of the sprite in question, even if the same sprite was previously used.
Deriving from QGraphicsItem and overriding QGraphicsItem::paint() to call one of the QPainter::drawPixmap() overloads with source and destination coordinates would allow copying directly from the sprite sheet into the scene.
Alternatively, the sprites could be split into QPixmap instances prior to iterating through the tile map.
@jeremy_k said in What is a good way to display a tilemap:
@Q74r3wq said in What is a good way to display a tilemap:
for (tile t : tilemap) { // spriteSheet is a QPixmap item containing the whole spritesheet QPixmap temp = p.copy(QRect(t.cropX, t.cropY)); // <- crop QGraphicsPixmapItem* temp = new QGraphicsPixmapItem(temp); // editorScene is the QGraphicsScene* and editorView is the QGraphicsView*
Thanks! Exactly the kind of answer I'd like to see.
I checked the doc and am still a bit confused about the two approaches.Deriving from QGraphicsItem and overriding QGraphicsItem::paint() to call one of the QPainter::drawPixmap() overloads with source and destination coordinates would allow copying directly from the sprite sheet into the scene.
Are you suggesting that I can actually directly call
QPainter
to draw something on myeditorView
(which is a subclass ofQGraphicsView
) so that I don't need to rely on aQGraphicsScene
? However the doc seems to indicate thatQGraphicsView
visualizes the contents of aQGraphicsScene
in a scrollable viewport so maybe I misunderstood.Alternatively, the sprites could be split into QPixmap instances prior to iterating through the tile map.
From my understanding, say that the sprite sheet contains 256 tiles, I should break it into 256 QPixmap instances? Is it related to the method described in this post? If it is then I'm actually using this method.
https://stackoverflow.com/questions/17181235/draw-32x32-tiled-images-in-qt
Thanks in advance~
-
@jeremy_k said in What is a good way to display a tilemap:
@Q74r3wq said in What is a good way to display a tilemap:
for (tile t : tilemap) { // spriteSheet is a QPixmap item containing the whole spritesheet QPixmap temp = p.copy(QRect(t.cropX, t.cropY)); // <- crop QGraphicsPixmapItem* temp = new QGraphicsPixmapItem(temp); // editorScene is the QGraphicsScene* and editorView is the QGraphicsView*
Thanks! Exactly the kind of answer I'd like to see.
I checked the doc and am still a bit confused about the two approaches.Deriving from QGraphicsItem and overriding QGraphicsItem::paint() to call one of the QPainter::drawPixmap() overloads with source and destination coordinates would allow copying directly from the sprite sheet into the scene.
Are you suggesting that I can actually directly call
QPainter
to draw something on myeditorView
(which is a subclass ofQGraphicsView
) so that I don't need to rely on aQGraphicsScene
? However the doc seems to indicate thatQGraphicsView
visualizes the contents of aQGraphicsScene
in a scrollable viewport so maybe I misunderstood.Alternatively, the sprites could be split into QPixmap instances prior to iterating through the tile map.
From my understanding, say that the sprite sheet contains 256 tiles, I should break it into 256 QPixmap instances? Is it related to the method described in this post? If it is then I'm actually using this method.
https://stackoverflow.com/questions/17181235/draw-32x32-tiled-images-in-qt
Thanks in advance~
@Q74r3wq said in What is a good way to display a tilemap:
@jeremy_k said in What is a good way to display a tilemap:
Deriving from QGraphicsItem and overriding QGraphicsItem::paint() to call one of the QPainter::drawPixmap() overloads with source and destination coordinates would allow copying directly from the sprite sheet into the scene.
Are you suggesting that I can actually directly call
QPainter
to draw something on myeditorView
(which is a subclass ofQGraphicsView
) so that I don't need to rely on aQGraphicsScene
? However the doc seems to indicate thatQGraphicsView
visualizes the contents of aQGraphicsScene
in a scrollable viewport so maybe I misunderstood.There's a misunderstanding. You need both a scene (the arrangement of objects), and one or more views (windows looking at regions of the scene).
The application code implements a class derived from QGraphicsItem and overrides QGraphicsItem::paint(). Qt calls QGraphicsItem::paint() with an appropriate QPainter when the item in the scene will be displayed via a view instance.
Alternatively, the sprites could be split into QPixmap instances prior to iterating through the tile map.
From my understanding, say that the sprite sheet contains 256 tiles, I should break it into 256 QPixmap instances? Is it related to the method described in this post? If it is then I'm actually using this method.
https://stackoverflow.com/questions/17181235/draw-32x32-tiled-images-in-qt
The single answer in that thread is proposing the same inefficiency that I'm trying to highlight.
In this version, 100 deep copies of the sprite are made, plus the copy in the sprite sheet, and potentially additional copies when the sprite is displayed.
QPixmap sprites("sprites.png"); for (int index = 0; index < 100; index++) { QPixmap sprite = sprites.copy(0, 0, width, height); auto *item = new QGraphicsPixmapItem(sprite); scene.addItem(item); }
In this version, only one deep copy of the sprite is made, plus the sprite sheet and display copies. QPixmap implicit sharing allows each QPixmapItem to use a shallow copy as long as the pixmap is unmodified.
QPixmap sprites("sprites.png"); QPixmap sprite = sprites.copy(0, 0, width, height); for (int index = 0; index < 100; index++) { auto *item = new QGraphicsPixmapItem(sprite); scene.addItem(item); }
-
@Q74r3wq said in What is a good way to display a tilemap:
@jeremy_k said in What is a good way to display a tilemap:
Deriving from QGraphicsItem and overriding QGraphicsItem::paint() to call one of the QPainter::drawPixmap() overloads with source and destination coordinates would allow copying directly from the sprite sheet into the scene.
Are you suggesting that I can actually directly call
QPainter
to draw something on myeditorView
(which is a subclass ofQGraphicsView
) so that I don't need to rely on aQGraphicsScene
? However the doc seems to indicate thatQGraphicsView
visualizes the contents of aQGraphicsScene
in a scrollable viewport so maybe I misunderstood.There's a misunderstanding. You need both a scene (the arrangement of objects), and one or more views (windows looking at regions of the scene).
The application code implements a class derived from QGraphicsItem and overrides QGraphicsItem::paint(). Qt calls QGraphicsItem::paint() with an appropriate QPainter when the item in the scene will be displayed via a view instance.
Alternatively, the sprites could be split into QPixmap instances prior to iterating through the tile map.
From my understanding, say that the sprite sheet contains 256 tiles, I should break it into 256 QPixmap instances? Is it related to the method described in this post? If it is then I'm actually using this method.
https://stackoverflow.com/questions/17181235/draw-32x32-tiled-images-in-qt
The single answer in that thread is proposing the same inefficiency that I'm trying to highlight.
In this version, 100 deep copies of the sprite are made, plus the copy in the sprite sheet, and potentially additional copies when the sprite is displayed.
QPixmap sprites("sprites.png"); for (int index = 0; index < 100; index++) { QPixmap sprite = sprites.copy(0, 0, width, height); auto *item = new QGraphicsPixmapItem(sprite); scene.addItem(item); }
In this version, only one deep copy of the sprite is made, plus the sprite sheet and display copies. QPixmap implicit sharing allows each QPixmapItem to use a shallow copy as long as the pixmap is unmodified.
QPixmap sprites("sprites.png"); QPixmap sprite = sprites.copy(0, 0, width, height); for (int index = 0; index < 100; index++) { auto *item = new QGraphicsPixmapItem(sprite); scene.addItem(item); }
@jeremy_k Thanks for the detailed reply!
OK I looked into the source code of Tiled 0.5 (some 2010 build) and found something close to what you describe. Basically for the tile class (he call it
TileLayerItem
as a scene contains multiple layers and each layer contain the tiles) he implemented thepaint()
function:void TileLayerItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *) { mRenderer->drawTileLayer(painter, mLayer, option->exposedRect); }
He has a sophsticated system in which
mRenderer
is sort of a manipulator ofQPainter
. It doesn't own aQPainter
but instead receive one from outside and draw things using methods likedrawTilePlayer(QPainter* p, const TileLayer* layer, etc...)
.I'm not sure about the second one though. Each tile needs to have a different coordinate (but same weight and height), how do I put it outside of the loop? Maybe I missed something, will come back to it again tomorrow morning :)
-
@jeremy_k Thanks for the detailed reply!
OK I looked into the source code of Tiled 0.5 (some 2010 build) and found something close to what you describe. Basically for the tile class (he call it
TileLayerItem
as a scene contains multiple layers and each layer contain the tiles) he implemented thepaint()
function:void TileLayerItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *) { mRenderer->drawTileLayer(painter, mLayer, option->exposedRect); }
He has a sophsticated system in which
mRenderer
is sort of a manipulator ofQPainter
. It doesn't own aQPainter
but instead receive one from outside and draw things using methods likedrawTilePlayer(QPainter* p, const TileLayer* layer, etc...)
.I'm not sure about the second one though. Each tile needs to have a different coordinate (but same weight and height), how do I put it outside of the loop? Maybe I missed something, will come back to it again tomorrow morning :)
@Q74r3wq said in What is a good way to display a tilemap:
@jeremy_k Thanks for the detailed reply!
OK I looked into the source code of Tiled 0.5 (some 2010 build) and found something close to what you describe. Basically for the tile class (he call it
TileLayerItem
as a scene contains multiple layers and each layer contain the tiles) he implemented thepaint()
function:void TileLayerItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *) { mRenderer->drawTileLayer(painter, mLayer, option->exposedRect); }
He has a sophsticated system in which
mRenderer
is sort of a manipulator ofQPainter
. It doesn't own aQPainter
but instead receive one from outside and draw things using methods likedrawTilePlayer(QPainter* p, const TileLayer* layer, etc...)
.That sounds reasonable. The QPainter controls what gets painted (eg the widget in a window or a PDF), and how (eg rotated 90 degrees). Wrapping its use in a convenience class might make code cleaner for some use cases.
I'm not sure about the second one though. Each tile needs to have a different coordinate (but same weight and height), how do I put it outside of the loop? Maybe I missed something, will come back to it again tomorrow morning :)
Which second one? I'm guessing
weight
was autocorrected fromwidth
, but the rest of the question isn't clear to me. -
@Q74r3wq said in What is a good way to display a tilemap:
@jeremy_k Thanks for the detailed reply!
OK I looked into the source code of Tiled 0.5 (some 2010 build) and found something close to what you describe. Basically for the tile class (he call it
TileLayerItem
as a scene contains multiple layers and each layer contain the tiles) he implemented thepaint()
function:void TileLayerItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *) { mRenderer->drawTileLayer(painter, mLayer, option->exposedRect); }
He has a sophsticated system in which
mRenderer
is sort of a manipulator ofQPainter
. It doesn't own aQPainter
but instead receive one from outside and draw things using methods likedrawTilePlayer(QPainter* p, const TileLayer* layer, etc...)
.That sounds reasonable. The QPainter controls what gets painted (eg the widget in a window or a PDF), and how (eg rotated 90 degrees). Wrapping its use in a convenience class might make code cleaner for some use cases.
I'm not sure about the second one though. Each tile needs to have a different coordinate (but same weight and height), how do I put it outside of the loop? Maybe I missed something, will come back to it again tomorrow morning :)
Which second one? I'm guessing
weight
was autocorrected fromwidth
, but the rest of the question isn't clear to me.Which second one? I'm guessing
weight
was autocorrected fromwidth
, but the rest of the question isn't clear to me.Hi, sorry for the confusion, the second one is (Quoted from your previous post):
In this version, 100 deep copies of the sprite are made, plus the copy in the sprite sheet, and potentially additional copies when the sprite is displayed.
QPixmap sprites("sprites.png"); for (int index = 0; index < 100; index++) { QPixmap sprite = sprites.copy(0, 0, width, height); auto *item = new QGraphicsPixmapItem(sprite); scene.addItem(item); }
In this version, only one deep copy of the sprite is made, plus the sprite sheet and display copies. QPixmap implicit sharing allows each QPixmapItem to use a shallow copy as long as the pixmap is unmodified.
QPixmap sprites("sprites.png"); QPixmap sprite = sprites.copy(0, 0, width, height); for (int index = 0; index < 100; index++) { auto *item = new QGraphicsPixmapItem(sprite); scene.addItem(item); }
My question is, since each
sprite
"crops" a different place, I think it should be placed inside of the loop? My situation is a bit different from his, my code would be:QPixmap sprites("sprites.png"); for (int index = 0; index < 100; index++) { // Get Crop rect info for each tile QPixmap tile = sprites.copy(cropRect); auto *item = new QGraphicsPixmapItem(sprite); scene.addItem(item); }
-
Which second one? I'm guessing
weight
was autocorrected fromwidth
, but the rest of the question isn't clear to me.Hi, sorry for the confusion, the second one is (Quoted from your previous post):
In this version, 100 deep copies of the sprite are made, plus the copy in the sprite sheet, and potentially additional copies when the sprite is displayed.
QPixmap sprites("sprites.png"); for (int index = 0; index < 100; index++) { QPixmap sprite = sprites.copy(0, 0, width, height); auto *item = new QGraphicsPixmapItem(sprite); scene.addItem(item); }
In this version, only one deep copy of the sprite is made, plus the sprite sheet and display copies. QPixmap implicit sharing allows each QPixmapItem to use a shallow copy as long as the pixmap is unmodified.
QPixmap sprites("sprites.png"); QPixmap sprite = sprites.copy(0, 0, width, height); for (int index = 0; index < 100; index++) { auto *item = new QGraphicsPixmapItem(sprite); scene.addItem(item); }
My question is, since each
sprite
"crops" a different place, I think it should be placed inside of the loop? My situation is a bit different from his, my code would be:QPixmap sprites("sprites.png"); for (int index = 0; index < 100; index++) { // Get Crop rect info for each tile QPixmap tile = sprites.copy(cropRect); auto *item = new QGraphicsPixmapItem(sprite); scene.addItem(item); }
@Q74r3wq said in What is a good way to display a tilemap:
My question is, since each
sprite
"crops" a different place, I think it should be placed inside of the loop?Is this referring to the cropping that occurs when the sprite is cut out of the sprite sheet, the cropping that may occur when part of the sprite is outside of the view, cropping from one item overlapping another, or something else? It might help to post a few examples of what a cropped sprite might look like.
My situation is a bit different from his, my code would be:
QPixmap sprites("sprites.png"); for (int index = 0; index < 100; index++) { // Get Crop rect info for each tile QPixmap tile = sprites.copy(cropRect); auto *item = new QGraphicsPixmapItem(sprite); scene.addItem(item); }
This will work. It will use more memory and compute time than necessary if several
tile
instances are identical. -
@Q74r3wq said in What is a good way to display a tilemap:
My question is, since each
sprite
"crops" a different place, I think it should be placed inside of the loop?Is this referring to the cropping that occurs when the sprite is cut out of the sprite sheet, the cropping that may occur when part of the sprite is outside of the view, cropping from one item overlapping another, or something else? It might help to post a few examples of what a cropped sprite might look like.
My situation is a bit different from his, my code would be:
QPixmap sprites("sprites.png"); for (int index = 0; index < 100; index++) { // Get Crop rect info for each tile QPixmap tile = sprites.copy(cropRect); auto *item = new QGraphicsPixmapItem(sprite); scene.addItem(item); }
This will work. It will use more memory and compute time than necessary if several
tile
instances are identical.@jeremy_k Thanks for the help! I think I figured out most of the stuffs, including both of your methods. The crop is the one that only takes part of the big spritesheet, e.g. my spritesheet has 16x16 = 256 tiles, with each the size of 16px16p, so yeah you are quite right that if I have a lot of same tiles (which is true), this methods will waste some memory as each tile contains its own
QPixmap
.At the moment I'm using the first method. Each
TileGraphicsItem
contains a pointer to aTileRenderer
and the coordinate data.TileGraphicsItem
passes aQpainter
toTileRenderer
and the later callsdrawPixmap
. In that way, allQPixmap
are contained inTileRenderer
only so each tile doesn't need to duplicate (a part of) the spritesheetQpixmap
. So far I think this is the ideal setup.