0
\$\begingroup\$

I'm working on creating a 2d map prototype, and I've come across the rendering bit of it. I have a tilesheet with tiles, each tile is 30x30 pixels, and there's a 1px border to delimitate them.

In SFML the usual method of drawing a part of a tilesheet is declaring an IntRect with the rectangle coordinates then calling the setTextureRectangle() method to a sprite.

In a small game it would work, but I have well over 45 tiles and adding more every day, I can't declare 45 intRects for every material, the map is not optimized yet, it would get even worse if I would have to call the setTextureRect() method, aside from declaring 45 rectangleInts.

How could I simplify this task? All I need is a very simple and flexible solution for extracting a region of the tilesheet.

Basically I have a Tile class. I create multiple instances of tiles (vectors) and each tile has a position and a material. I parse a map file and as I parse it I set the materials of the map according to the parsed map file, and all I need to do is render.

Basically I need to do something like this:

switch(tile.getMaterial())
{
   case GRASS:
       material_sprite.setTextureRect(something);
       window.draw(material_sprite);
       break;

   case WATER:
       material_sprite.setTextureRect(something);
       window.draw(material_sprite);
       break;

  // handle more cases
}
\$\endgroup\$

1 Answer 1

1
\$\begingroup\$

I was looking at the documentation. Far as I can tell, a sprite has a pointer to its texture, which in this case would be the tilesheet. I assume you declare this once somewhere, not per render, in which case all is well. I assume you would use one sprite as it sees you are suggesting, so that leaves only the IntRect's. As far as I can tell, though, you only need one IntRect per tile TYPE. For example, say you have two tiles, GRASS and WATER. So you would need two IntRect dictating where in the tilesheet the tile's texture is. I assume you would also declare this beforehand and not every render.

When rendering, then - and in whatever render function/thread/etc have you, the tilesheet and IntRects need to be in scope - you can declare a sprite - or just use one potentially declared before like the above - and set its texture as the tilesheet. (so perhaps having the sprite declared beforehand would be good). Then, by case, set the correct texture rect and position (you could have the grass tile, but you also need to draw it where the tile is on the screen), and draw it.

Note that I have not tested this myself. As far as I can tell, you do not need 45 IntRects per tile type, and I don't think this is to bad optimization wise, although this approach takes a toll on memory.

I don't think you would need more than one IntRect per tile, as your tilesheet should never change at runtime.

Edit after comment:

Your right, that would be a problem. I don't think there is a way around it unless you don't move the other tiles and make them independant of each other (leaving empty space). Although, assuming your tilesheet is perfect, you might be able to hold the IntRects in an array, which is created whilest looping. (you could then have an Enum which dictates which index is which).

As per declaring IntRects... the setTextureRect argument needs one, thus you need at least one. But somewhere, perhaps as defined/constant/magic numbers, but that is not very efficient either. Perhaps, though, you could... automate the process. Say, your tilesheet is x tiles by y tiles. You make an enumerated value for each tile. You can set IntRects either beforehand or at render (probably not to fast the latter way). Since you know how many tiles wide and high it is (given the texture and tilesize you can calculate this at runtime so changes don't break it), you can go from left to right, and upon grabbing the last tile, drop down a row, and so on. For each one, you fill an IntRect,, hopefully in an array of IntRects, with the coordinates of that texture.

Now, you would need an enum, with the tile values, like WATER, GRASS etc. Put them in the order the algorithm would fetch them - say, if it looks like this:

WATER, GRASS, SAND, ROCK, ICE, SOMETHING

then the enum is like:

enum TileType
{
    WATER,
    GRASS,
    SAND,
    ROCK,
    ICE,
    SOMETHING,
    NUM_TILES
};

Note that last one is optional, but it allows you to know how many tiles you have (assuming you don't modify it and it goes sequentially starting from 0), which might be useful.

Then, to access the according texture for a tile, you could go, say, intRects[WATER], and then it would access the water tile. Assuming you change the tilesheet, making sure to leave no enclosed holes, then you can simply change the order of enums. This is also where NUM_TILES can come in handy, if you have an unfinished row for instance, so you can still collect it correctly. Assuming that works (this is speculation), I think this is decent from a maintenance. Worst part is adjusting the tilesheet, but once you have done that the worst is over, in theory.

\$\endgroup\$
2
  • \$\begingroup\$ The problem is, it must be flexible. What if I ever remove a certain tile, and every tiles get moved back? I'd have to edit all the intRects again. It's a very painful thing to declare all those intRects, I'm looking for a way which spares me from having to declare a ton of intRects \$\endgroup\$
    – Bugster
    Nov 10, 2012 at 14:43
  • \$\begingroup\$ I've actually been thinking of the solution you posted after my comment, it appears like it could work, I was thinking of defining a row cell size of, say, 10 tiles per row, then for example if the material > 10 change rows. Overall I'd be working with an intRect(material*30, row, material*60, row+30) or something of that sort. I don't think there's any workaround. Thanks. \$\endgroup\$
    – Bugster
    Nov 10, 2012 at 18:11

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .