Welcome, Guest. Please login or register. Did you miss your activation email?

Author Topic: sf::Rect questions  (Read 2931 times)

0 Members and 1 Guest are viewing this topic.

N_K

  • Jr. Member
  • **
  • Posts: 64
    • View Profile
sf::Rect questions
« on: August 30, 2012, 02:30:06 pm »
Hello all. Sorry for spamming the forum with beginner questions lately, this one will be the last for a while, I promise.

I'm experimenting with a tile engine. I have two different methods to set a specific tile, one uses arrays, one uses vectors.

First, here's the one with arrays:
void Tileset::setTile(int tileNum)
{
        tileRect = new sf::IntRect[numTiles];

        int counter = 0;

        for(int texture_h = 0; texture_h < numTilesY; texture_h++)
        {
                for(int texture_w = 0; texture_w < numTilesX; texture_w++)
                {
                        if(counter < numTiles)
                        {
                                tileRect[counter].left = texture_w * tileWidth;
                                tileRect[counter].top = texture_h * tileHeight;
                                tileRect[counter].width = tileWidth;
                                tileRect[counter].height = tileHeight;

                                counter++;
                        }
                }
        }

        this->setTextureRect(tileRect[tileNum]);

        delete tileRect;
}

In the header, tileRect is defined like this:
sf::IntRect *tileRect;


Now, here is the one that uses vectors:
void Tileset::setTile(int tileNum)
{
        for(int h = 0; h < numTilesY; h++)
        {
                for(int w = 0; w < numTilesX; w++)
                {
                        rect.left = w * tileWidth;
                        rect.top = h * tileHeight;
                        rect.width = tileWidth;
                        rect.height = tileHeight;

                        subRect.push_back(rect);
                }
        }

        this->setTextureRect(subRect[tileNum]);

        subRect.clear();
}

In the header, rect and subRect are defined like this:
std::vector <sf::Rect<int>> subRect;
sf::Rect <int> rect;

Both of these are working fine, and this is what I don't understand. In both methods, I have to destroy the temporary rectangles (tileRect and subRect) after they have been assigned to the texture, othervise they will produce memory leaks (the vector-based one is quite dangerous, it keeps allocating more and more memory until the app is closed).

But how can SFML display the area defined by the rectangle, if the rectangle doesn't even exist any more? Once a set of coordinates are assigned to the setTextureRect function, it will store them until they have been overwritten by another set, or the app is closed? Is this code the way it supposed to be done, anyway, or just plain broken and dangerous?

Thanks for your help in advance.

FRex

  • Hero Member
  • *****
  • Posts: 1848
  • Back to C++ gamedev with SFML in May 2023
    • View Profile
    • Email
Re: sf::Rect questions
« Reply #1 on: August 30, 2012, 02:46:18 pm »
What are you doing and trying to achieve? Is tileset derieved from sprite? If it is, sprite copies the rect for itself and updates it's 4 vertices everytime rect changes. Why do you fill and destroy all tiles every call to setTile instead of making your vector local variable or filling it only once?
« Last Edit: August 30, 2012, 02:48:30 pm by FRex »
Back to C++ gamedev with SFML in May 2023

N_K

  • Jr. Member
  • **
  • Posts: 64
    • View Profile
Re: sf::Rect questions
« Reply #2 on: August 30, 2012, 04:49:35 pm »
I'm not really "trying to achieve" anything, as it already does what it supposed to do. Yes, the tileset is derived from sf::Sprite.

Anyway, what it does: it loads a tileset, then divides it into tiles of the size of tileWidth and tileHeight. The setTile function selects the n-th tile to draw (tileNum), and clips it out for drawing by adjusting the texture rect of the tileset.

And I don't understand your last question at all (mind that I'm fairly new to C++), so could you please post some (pseudo)code to make it clear?
« Last Edit: August 30, 2012, 04:56:33 pm by N_K »

FRex

  • Hero Member
  • *****
  • Posts: 1848
  • Back to C++ gamedev with SFML in May 2023
    • View Profile
    • Email
Re: sf::Rect questions
« Reply #3 on: August 30, 2012, 05:41:05 pm »
Fill once for future setTile calls of it:
void Tileset::setTile(int tileNum)
{
                if(subRect.size()==0)
                {
        for(int h = 0; h < numTilesY; h++)
        {
                for(int w = 0; w < numTilesX; w++)
                {
                        rect.left = w * tileWidth;
                        rect.top = h * tileHeight;
                        rect.width = tileWidth;
                        rect.height = tileHeight;
                        subRect.push_back(rect);
                }
        }
                }
        this->setTextureRect(subRect[tileNum]);
}
Using local variables:
void Tileset::setTile(int tileNum)
{
        std::vector <sf::Rect<int>> subRect;
        for(int h = 0; h < numTilesY; h++)
        {
                for(int w = 0; w < numTilesX; w++)
                {
                                                sf::Rect <int> rect;
                        rect.left = w * tileWidth;
                        rect.top = h * tileHeight;
                        rect.width = tileWidth;
                        rect.height = tileHeight;
                        subRect.push_back(rect);
                }
        }
        this->setTextureRect(subRect[tileNum]);
}//all memory used by subRect gets released by its dtor
There is like bilion problems here: using sprites(classes derieved from it = even worse) for tiles is bad, you should use vertexarray instead. Why make entire array of all possible rects and not compute the tile rect directly like that(there might be error here, I wrote it very hastily):
void Tileset::setTile(int tileNum)
{
        int x=tileNum%numTilesX;
        int y=tileNum/numTilesX;
        sf::Rect<int> rect;
        rect.left=x*tileWidth;
        rect.top=y*tileHeight;
        rect.width=tileWidth;
        rect.height=tileHeight;
        this->setTextureRect(rect);
}
 
Back to C++ gamedev with SFML in May 2023

N_K

  • Jr. Member
  • **
  • Posts: 64
    • View Profile
Re: sf::Rect questions
« Reply #4 on: August 30, 2012, 05:52:42 pm »
Hm, you're right, and thanks for the code. Unfortunately, I've never used vertex arrays (well, I did when I was learning OpenGL, but I don't know how they work in SFML yet).

The last code snippet you've posted looks neat, but some parts are not clear. For example, if numTilesX is 8 (like on my test tileset), and tileNum is 1, then x will equal to 1, and y will equal to 0.125. Then, assuming the tiles are 32x32, rect.left will be 32, but rect.top will be 4, when it should be 0. How is this system supposed to work?

FRex

  • Hero Member
  • *****
  • Posts: 1848
  • Back to C++ gamedev with SFML in May 2023
    • View Profile
    • Email
Re: sf::Rect questions
« Reply #5 on: August 30, 2012, 05:58:50 pm »
Dividing int by int returns trunced int, and even if it didn't then saving a float to int would trunc it(with a warning from some compilers about implicit trunc).
Quote
but I don't know how they work in SFML yet
They hold vertices, and the primitive type that these vertices are supposed to take, that's all.
Back to C++ gamedev with SFML in May 2023

N_K

  • Jr. Member
  • **
  • Posts: 64
    • View Profile
Re: sf::Rect questions
« Reply #6 on: August 30, 2012, 10:26:31 pm »
Okay, so basically, anything that is below 1 will be truncated and rounded down to zero? I think I get it. Since, for example, for the last tile of the first row (number 7), y = 7 / 8 = 0.875, which will be truncated to 0.8, then rounded down (but why down?) to 0. For tile number 8 (the first tile of the second row), y = 8 / 8 = 1, and then the top of the rectangle will be at 1 * 32 = 32, which is the second row.

Now I could be wrong, but in this case, this method is not suitable for larger tile sets, beyond a certain number of tiles, it will become unstable/inaccurate because of the finite precision error. Anybody knows how many tiles can it safely handle per tile sheet?

Anyway, now I understand why all professional tile-based games utilize fixed size tile sets...



FRex

  • Hero Member
  • *****
  • Posts: 1848
  • Back to C++ gamedev with SFML in May 2023
    • View Profile
    • Email
Re: sf::Rect questions
« Reply #7 on: August 30, 2012, 10:31:33 pm »
There are no floats involved in int=int/int, whatever is after decimal point gets cut off, there is no step in between with floats.
Back to C++ gamedev with SFML in May 2023

N_K

  • Jr. Member
  • **
  • Posts: 64
    • View Profile
Re: sf::Rect questions
« Reply #8 on: August 30, 2012, 11:03:10 pm »
Obviously, int can't hold floating point. I just fail sometimes...  :-X

Anyway, thank you for your help, for being patient, and for the code snippets and suggestions!