Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. What is a good way to display a tilemap
QtWS25 Last Chance

What is a good way to display a tilemap

Scheduled Pinned Locked Moved Solved General and Desktop
9 Posts 2 Posters 1.2k Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • Q Offline
    Q Offline
    Q74r3wq-
    wrote on last edited by
    #1

    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)
    Ultima 4 spritesheet

    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?

    jeremy_kJ 1 Reply Last reply
    0
    • Q Q74r3wq-

      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)
      Ultima 4 spritesheet

      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?

      jeremy_kJ Offline
      jeremy_kJ Offline
      jeremy_k
      wrote on last edited by
      #2

      @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.

      Asking a question about code? http://eel.is/iso-c++/testcase/

      Q 1 Reply Last reply
      3
      • jeremy_kJ jeremy_k

        @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.

        Q Offline
        Q Offline
        Q74r3wq-
        wrote on last edited by Q74r3wq-
        #3

        @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 my editorView (which is a subclass of QGraphicsView) so that I don't need to rely on a QGraphicsScene? However the doc seems to indicate that QGraphicsView visualizes the contents of a QGraphicsScene 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_kJ 1 Reply Last reply
        0
        • Q Q74r3wq-

          @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 my editorView (which is a subclass of QGraphicsView) so that I don't need to rely on a QGraphicsScene? However the doc seems to indicate that QGraphicsView visualizes the contents of a QGraphicsScene 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_kJ Offline
          jeremy_kJ Offline
          jeremy_k
          wrote on last edited by jeremy_k
          #4

          @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 my editorView (which is a subclass of QGraphicsView) so that I don't need to rely on a QGraphicsScene? However the doc seems to indicate that QGraphicsView visualizes the contents of a QGraphicsScene 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);
          }
          

          Asking a question about code? http://eel.is/iso-c++/testcase/

          Q 1 Reply Last reply
          2
          • jeremy_kJ jeremy_k

            @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 my editorView (which is a subclass of QGraphicsView) so that I don't need to rely on a QGraphicsScene? However the doc seems to indicate that QGraphicsView visualizes the contents of a QGraphicsScene 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);
            }
            
            Q Offline
            Q Offline
            Q74r3wq-
            wrote on last edited by
            #5

            @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 the paint() 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 of QPainter. It doesn't own a QPainter but instead receive one from outside and draw things using methods like drawTilePlayer(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_kJ 1 Reply Last reply
            0
            • Q Q74r3wq-

              @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 the paint() 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 of QPainter. It doesn't own a QPainter but instead receive one from outside and draw things using methods like drawTilePlayer(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_kJ Offline
              jeremy_kJ Offline
              jeremy_k
              wrote on last edited by
              #6

              @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 the paint() 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 of QPainter. It doesn't own a QPainter but instead receive one from outside and draw things using methods like drawTilePlayer(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 from width, but the rest of the question isn't clear to me.

              Asking a question about code? http://eel.is/iso-c++/testcase/

              Q 1 Reply Last reply
              0
              • jeremy_kJ jeremy_k

                @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 the paint() 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 of QPainter. It doesn't own a QPainter but instead receive one from outside and draw things using methods like drawTilePlayer(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 from width, but the rest of the question isn't clear to me.

                Q Offline
                Q Offline
                Q74r3wq-
                wrote on last edited by
                #7

                @jeremy_k

                Which second one? I'm guessing weight was autocorrected from width, 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);
                }
                
                jeremy_kJ 1 Reply Last reply
                0
                • Q Q74r3wq-

                  @jeremy_k

                  Which second one? I'm guessing weight was autocorrected from width, 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);
                  }
                  
                  jeremy_kJ Offline
                  jeremy_kJ Offline
                  jeremy_k
                  wrote on last edited by jeremy_k
                  #8

                  @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.

                  Asking a question about code? http://eel.is/iso-c++/testcase/

                  Q 1 Reply Last reply
                  1
                  • jeremy_kJ jeremy_k

                    @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.

                    Q Offline
                    Q Offline
                    Q74r3wq-
                    wrote on last edited by Q74r3wq-
                    #9

                    @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 a TileRenderer and the coordinate data. TileGraphicsItem passes a Qpainter to TileRenderer and the later calls drawPixmap. In that way, all QPixmap are contained in TileRenderer only so each tile doesn't need to duplicate (a part of) the spritesheet Qpixmap. So far I think this is the ideal setup.

                    1 Reply Last reply
                    1

                    • Login

                    • Login or register to search.
                    • First post
                      Last post
                    0
                    • Categories
                    • Recent
                    • Tags
                    • Popular
                    • Users
                    • Groups
                    • Search
                    • Get Qt Extensions
                    • Unsolved