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

Author Topic: Overriding sf::Drawable::draw  (Read 18321 times)

0 Members and 1 Guest are viewing this topic.

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32498
    • View Profile
    • SFML's website
    • Email
Re: Overriding sf::Drawable::draw
« Reply #15 on: October 24, 2015, 06:36:49 pm »
You can't. The only way would be to use another function
Boards.setNextElementToDraw(0);
window.draw(Boards);
... but that's extremely ugly.

You know, you're not forced to inherit from sf::Drawable and use this draw function. You can just avoid sf::Drawable and create the draw function that suits your needs.

Boards.draw(window, 0);
Laurent Gomila - SFML developer

Skorpion

  • Newbie
  • *
  • Posts: 28
    • View Profile
Re: Overriding sf::Drawable::draw
« Reply #16 on: October 24, 2015, 08:49:26 pm »
Well, that's what I did. I mean I made a function that tells my object of class Board which element to pick. And yes, I see how ugly it is :)

The second solution you suggested seems good and profesional. Although I'm not sure if I'm able to write it, I'll try it.

Which of them is better (quicker to execute)?

Thank you for reply :)

EDIT:

I've changed my code a bit again so it handles displaying some of my sprites but there are still some that are appear as white shapes. Can I ask once again to tell me, where my mistake is? I tried everything, nothig worked :(

My code now:
Board.h
class Board : public Drawable, Transformable
{
public:
    bool set_up(int nrOfBoard, int level);  // that method sets all sprites and loads
                                    // from files coordinates of everything
    int what_board;
private:
    Texture texture;    // texture of board
    Sprite background;  // sprite of board
   
    struct oneBoard : public Drawable, public Transformable
    {
        vector<Sprite> adds;    
        vector<Sprite> things;
        virtual void draw(RenderTarget &target, RenderStates states) const
                {
                        for (int i = 0; i < adds.size(); i++)
                                target.draw(adds[i], states);
                        for (int i = 0; i < things.size(); i++)
                                target.draw(things[i], states);
                }
    };
    vector<oneBoard> map_of_all_boards;

    virtual void draw(RenderTarget &target, RenderStates states) const;
};
 
Board.cpp
bool Board::set_up(int nrOfBoard, int level)
{
        // some stuff, also determining the number_of_boards

        for (int i = 0; i < number_of_boards; i++)
        {
                Board board;
                oneBoard one;

                //again some stuff, determining amount_of_adds

                for (int i = 0; i < amount_of_adds; i++)
                {
                        Texture tex;
                        Sprite spr;
                        string directory;
                        Vector2f vec;

                        plik >> directory;
                        plik >> vec.x;
                        plik >> vec.y;

                        tex.loadFromFile(directory);
                        spr.setTexture(tex);
                        spr.setPosition(vec);
                        one.adds.push_back(spr);
                }
               
                // and again stuff, detrmining amount_of_things

                for (int i = 0; i < amount_of_things; i++)
                {
                        Texture tex;
                        Sprite spr;
                        string directory;
                        Vector2f vec;

                        plik >> directory;
                        plik >> vec.x;
                        plik >> vec.y;

                        tex.loadFromFile(directory);
                        spr.setTexture(tex);
                        spr.setPosition(vec);
                        one.things.push_back(spr);
                }
               
                // WEPCHNIECIE CALEGO POKOJU DO WEKTORA POKOI
                map_of_all_boards.push_back(one);

                plik.close();
        }
}

void Board::which_board(int brd)
{
        what_board = brd;
}

void Board::draw(sf::RenderTarget& target, sf::RenderStates states) const
{
        states.transform *= getTransform();
        states.texture = NULL;
        target.draw(background);
        target.draw(map_of_all_boards[what_board]);
}
 
I tought that the way I load all that sprites can have an impact on my problem, so I decided to show you more code this time.

So my problem is: my code loads and displays proprely backgroung and loads properly sprites into adds and things. No doubt it does. But on the screen I see my background and white shapes of sprites from vectors adds and things. Any solution? Can you tell me, if part of my code is incorrect, how to fix it?

Sorry for my english :)
« Last Edit: October 25, 2015, 04:35:28 pm by Skorpion »

Nexus

  • SFML Team
  • Hero Member
  • *****
  • Posts: 6287
  • Thor Developer
    • View Profile
    • Bromeon
Re: Overriding sf::Drawable::draw
« Reply #17 on: October 25, 2015, 04:45:49 pm »
for (int i = 0; i < amount_of_things; i++)
        {
            Texture tex;
            Sprite spr;
            string directory;
            Vector2f vec;

            plik >> directory;
            plik >> vec.x;
            plik >> vec.y;

            tex.loadFromFile(directory);
            spr.setTexture(tex);
            spr.setPosition(vec);
            one.things.push_back(spr);
        }
The texture is a local variable. You should really have a deeper look at C++, it's crucial to understand scopes, copy semantics and object ownership.

Furthermore, please avoid using namespace, it's a terrible habit that will lead to problems sooner or later.
Zloxx II: action platformer
Thor Library: particle systems, animations, dot products, ...
SFML Game Development:

Skorpion

  • Newbie
  • *
  • Posts: 28
    • View Profile
Re: Overriding sf::Drawable::draw
« Reply #18 on: October 26, 2015, 06:22:52 pm »
(...) The texture is a local variable.(...)

I didn't know that it is needed. I tought that when I once push a texture to sprite using:
Texture tex;
Sprite spr;
spr.setTexture(tex);
 
the texture becomes a part of sprite and that sprite is a thing that is ready to be moved and displayed.

So if I make a structure of a texture and a sprite and then make a vector that structure's type my graphic should be displayed properly?

EDIT:


I tried it like I said. It didn't work.

Do you know what the problem is? I don't know how to do something, I ask you, but you don't give me precision answer. You keep telling me, that something is fundamental, something I should know etc. but I don't know it. If I knew I would do it properly. I don't know and that's why I ask you for help. Could anyone just write how can I make that textures visible for my sprites? I really need your help, when you write a code and comment it I could probably learn something but by keep telling me that I should know something, I search for it again and again and I can't find any answer to my questions. Is here anyone, who could help me, write that code and explain why did it in another way that I did?

Special "thank you in advance" to the person who will.

Sorry for my english.
« Last Edit: October 26, 2015, 06:59:38 pm by Skorpion »

Nexus

  • SFML Team
  • Hero Member
  • *****
  • Posts: 6287
  • Thor Developer
    • View Profile
    • Bromeon
Re: Overriding sf::Drawable::draw
« Reply #19 on: October 26, 2015, 07:32:19 pm »
I tought that when I once push a texture to sprite using:
Texture tex;
Sprite spr;
spr.setTexture(tex);
 
the texture becomes a part of sprite and that sprite is a thing that is ready to be moved and displayed.
Why do you assume that? This behavior is nowhere documented and very uncommon in C++. Even the opposite is documented, namely that the texture object must live as long as it's used.

So if I make a structure of a texture and a sprite
Don't do that. The whole reason why sf::Texture and sf::Sprite are two separate classes is to allow you to store them separately, and have multiple sprites that reference the same texture. This is precisely outlined in the tutorial, please read it carefully.

You should store textures in their own container (preferably not std::vector, because it invalidates pointers). There are also ready-to-use solutions like thor::ResourceHolder.
« Last Edit: October 26, 2015, 07:44:52 pm by Nexus »
Zloxx II: action platformer
Thor Library: particle systems, animations, dot products, ...
SFML Game Development:

Skorpion

  • Newbie
  • *
  • Posts: 28
    • View Profile
Re: Overriding sf::Drawable::draw
« Reply #20 on: October 26, 2015, 10:15:16 pm »
Why do you assume that?

Seemed logical to me. It's easier to move or change one object than many. If it doesn't work like that thank you for informating me. Now I know, now I know how to handle such situations in the future.


So if I make a structure of a texture and a sprite
Don't do that. The whole reason why sf::Texture and sf::Sprite are two separate classes is to allow you to store them separately, and have multiple sprites that reference the same texture. This is precisely outlined in the tutorial, please read it carefully.

Well, yes, that's something I didn't think about. That's good point of viev when you use one big texture, like a big tileset and you make multiple sprites from that. But my situation is one texture -> one sprite. This is why I want to store them together. Then I'll have the situation when pairs are in one place.


You should store textures in their own container (preferably not std::vector, because it invalidates pointers). There are also ready-to-use solutions like thor::ResourceHolder.

Should I use tables? I don't have time to learn a new library. I also tought about std::map. Would it work? Is it I definitly can't store those textures and sprites just like I want to or it is only the most painful way?

Thank you for fast replay and sorry for my english.

Hapax

  • Hero Member
  • *****
  • Posts: 3379
  • My number of posts is shown in hexadecimal.
    • View Profile
    • Links
Re: Overriding sf::Drawable::draw
« Reply #21 on: October 26, 2015, 10:22:48 pm »
I tought that when I once push a texture to sprite[...]the texture becomes a part of sprite and that sprite is a thing that is ready to be moved and displayed.
Why do you assume that?
Seemed logical to me.
This is explain in the initial tutorials.

It's easier to move or change one object than many.
This isn't true. Sprites are lightweight and can be copied, created and destroyed in real-time whereas textures are heavyweight and slow to copy, move, create or destroy.
Decoupling them gives a number of advantages. One simple advantage is the ability to use the same texture for multiple sprites.
Selba Ward -SFML drawables
Cheese Map -Drawable Layered Tile Map
Kairos -Timing Library
Grambol
 *Hapaxia Links*

Skorpion

  • Newbie
  • *
  • Posts: 28
    • View Profile
Re: Overriding sf::Drawable::draw
« Reply #22 on: October 26, 2015, 11:30:28 pm »
This is explain in the initial tutorials.

I red it 'till the end of "Ok, can I have my Sprite now" and gave up as I didn't found what I was looking for :/ That changes a lot. But now I (unexpectedly) changed my code once again. And now it loads textures directly to the structures. Then, when texture is in the right place and won't be moved from there I set textures to sprites. So I set textures that are in structure in vector in vector to the sprites that are in structure in vector in vector. And still dosen't work :(

Hapax

  • Hero Member
  • *****
  • Posts: 3379
  • My number of posts is shown in hexadecimal.
    • View Profile
    • Links
Re: Overriding sf::Drawable::draw
« Reply #23 on: October 27, 2015, 03:08:40 pm »
This is explain in the initial tutorials.
I red it 'till the end of "Ok, can I have my Sprite now" and gave up as I didn't found what I was looking for
You gave up when you got to "The White Square Problem" when because you didn't find the information on the problem that you are having with a white square?

But now I (unexpectedly) changed my code once again. And now it loads textures directly to the structures. Then, when texture is in the right place and won't be moved from there I set textures to sprites. So I set textures that are in structure in vector in vector to the sprites that are in structure in vector in vector. And still dosen't work :(
I don't understand what your code does from this description and what the result of it is.

Try storing your textures globally (not global scope but rather last the lifetime of the application) and pass reference/pointer to them to each class, function, method that uses them.
A simple local-sprite-using-long-term-texture could look something like this:
#include <SFML/Graphics.hpp>
int main()
{
    sf::RenderWindow window; // set up window how you need it
    sf::Texture texture;
    // load and set up texture here
    while (window.isOpen())
    {
        // event loop here

        sf::Sprite localTempSprite;
        localTempSprite.setTexture(texture);

        window.clear();
        window.draw(localTempSprite);
        window.display();
    }
    return EXIT_SUCCESS;
}
Selba Ward -SFML drawables
Cheese Map -Drawable Layered Tile Map
Kairos -Timing Library
Grambol
 *Hapaxia Links*

Skorpion

  • Newbie
  • *
  • Posts: 28
    • View Profile
Re: Overriding sf::Drawable::draw
« Reply #24 on: October 27, 2015, 05:10:56 pm »
This is explain in the initial tutorials.
I read it 'till the end of "Ok, can I have my Sprite now" and gave up as I didn't found what I was looking for
You gave up when you got to "The White Square Problem" when because you didn't find the information on the problem that you are having with a white square?

I read the "Ok, can I have my sprite now?"and then gave up, the "The White Square Problem" is under it and I didn't saw it. Other words I read everything 'till end of "Ok, can I have my sprite now?" and closed the tab.

I don't understand how exactly the sprites work. As these objects have a pointer to the texture then why when I store my textures in std::vector, then call sprite.setTexture(textures_in_vector[4]); it doesn't work properly? Is it I can't have a pointer to something that is stored in a vector and the class Sprite has a pointer so the value the pointer is indicating to is unreachable?

Sorry for my english.

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32498
    • View Profile
    • SFML's website
    • Email
Re: Overriding sf::Drawable::draw
« Reply #25 on: October 27, 2015, 06:31:50 pm »
When you call sprite.setTexture, the sprite stores the address of (pointer to) the texture object. So if you copy or move the texture around, the address stored in the sprite becomes invalid and all you get is a white square instead of the texture data.

The problem is that objects can easily be copied/moved in C++, even if you don't want them to. When you use a vector, for example, you insert copies into it, not original objects. And when the vector needs to grow, it goes elsewhere in memory and moves all its elements. All these operations invalidate the sprite's textures that were already stored.

So there are usually three solutions to this problem:

1. Use a texture manager, so that texture are managed and stored elsewhere and remain at the same location in memory, regardless of how you use them.

2. If you don't need to share textures (ie. one texture, one sprite) then the class that encapsulates them needs to handle copy/move operations properly. When you copy an instance of a class that holds a sprite and its texture, both are copied, and the sprite copy needs to use the texture copy, not the original texture (as its the case by default). So you need to write the code that does it, in the copy/move constructor and copy/move assignment operators. If you let the compiler generate the copy/move code for you, it will be wrong, the sprites will end up pointing to texture objects that don't exist anymore.

3. Make sure that no copy or move ever happens, by reserving space in your vector or using containers that don't invalidate their elements (std::list). I don't like this approach, I don't consider it a clean solution (or a solution at all) to this design problem.
Laurent Gomila - SFML developer

Jesper Juhl

  • Hero Member
  • *****
  • Posts: 1405
    • View Profile
    • Email
Re: Overriding sf::Drawable::draw
« Reply #26 on: October 27, 2015, 07:33:41 pm »
Basically, textures need lifetimes that are longer than the sprites that use them.
You need to know what operations on containers (etc) invalidates pointers, references and iterators pointing to textures.
This is essential C++ stuff you need to know for any object, not just textures.

Skorpion

  • Newbie
  • *
  • Posts: 28
    • View Profile
Re: Overriding sf::Drawable::draw
« Reply #27 on: October 27, 2015, 07:44:10 pm »
When you call sprite.setTexture, the sprite stores the address of (pointer to) the texture object. So if you copy or move the texture around, the address stored in the sprite becomes invalid and all you get is a white square instead of the texture data.

At fitst I would like to thank you for briliant post with many useful informations for me. Now I see how much do I have to learn still to use C++ fluenltly. Thank you again. Still, as I'm qiuet noob in programing I have a few questions to you but I feel I'm beginning to understand what happens when my code is executed. That seems and for sure is really important.

(...)
So there are usually three solutions to this problem:

1. Use a texture manager, so that texture are managed and stored elsewhere and remain at the same location in memory, regardless of how you use them.

Is that something that is built-in in SFML or should it be a class wrote by myself? If it relays on me are there some other ways better than std::vector, std::list (which you metnioned in point 3 as not the best idea) and reallocing tables?

2. If you don't need to share textures (ie. one texture, one sprite) then the class that encapsulates them needs to handle copy/move operations properly. When you copy an instance of a class that holds a sprite and its texture, both are copied, and the sprite copy needs to use the texture copy, not the original texture (as its the case by default). So you need to write the code that does it, in the copy/move constructor and copy/move assignment operators. If you let the compiler generate the copy/move code for you, it will be wrong, the sprites will end up pointing to texture objects that don't exist anymore.

As my code looks now like this:
class CLASS
{
    struct 1
    {
        Sprite spr;
        Texture tex;
    }
    struct 2
    {
        Sprite spr;
        Texture tex;
    }
    struct 3
    {
        vector<1> one;
        vector<2> two;
    }
    vector<3> map;
}
 
Would it work, when I'm never touching those vectors after reading textures from file, if I matched sprites to the textures after reading last one thing at all into vector? I mean at first I read textures, other stuff, create some other variables and push everything to the vectors. The vectors are moved in memory a few times during that reading and creating variables. But then, do they finaly stand still at one place, that I could call spr.setTexture(tex); when nothing else would be pushed to the vectors or is it still possible, that dusring executing my programm vector would be moved (not intentionaly by me)?

3. Make sure that no copy or move ever happens, by reserving space in your vector or using containers that don't invalidate their elements (std::list). I don't like this approach, I don't consider it a clean solution (or a solution at all) to this design problem.

Well, that was what I liked most when I read all 3 your propositions :( But if you say it is bad...

Thank you for fast replay and I'm sorry for my english.

Nexus

  • SFML Team
  • Hero Member
  • *****
  • Posts: 6287
  • Thor Developer
    • View Profile
    • Bromeon
Re: Overriding sf::Drawable::draw
« Reply #28 on: October 27, 2015, 09:33:59 pm »
1. Use a texture manager, so that texture are managed and stored elsewhere and remain at the same location in memory, regardless of how you use them.
Is that something that is built-in in SFML or should it be a class wrote by myself?
SFML doesn't have it, but Thor does, as I mentioned earlier. You said you wouldn't want to use another library, but when you're considering yourself a beginner in these things, it may not be the best idea to write such a class from scratch ;)

If it relays on me are there some other ways better than std::vector, std::list (which you metnioned in point 3 as not the best idea) and reallocing tables?
You could use an associative container like std::map, and refer to the texture using some identifier (the key in the map). That's also a part of what thor::ResourceHolder does, it just simplifies many things on top of that.

But then, do they finaly stand still at one place, that I could call spr.setTexture(tex); when nothing else would be pushed to the vectors or is it still possible, that dusring executing my programm vector would be moved moved (not intentionaly by me)?
If you don't touch the vectors themselves, it's only possible if the object of the surrounding class (which contains the vector) is moved/copied.

And don't apologize for your English in every post, it's not that bad 8)
Zloxx II: action platformer
Thor Library: particle systems, animations, dot products, ...
SFML Game Development:

Skorpion

  • Newbie
  • *
  • Posts: 28
    • View Profile
Re: Overriding sf::Drawable::draw
« Reply #29 on: October 27, 2015, 10:49:55 pm »
1. Use a texture manager, so that texture are managed and stored elsewhere and remain at the same location in memory, regardless of how you use them.
Is that something that is built-in in SFML or should it be a class wrote by myself?
SFML doesn't have it, but Thor does, as I mentioned earlier. You said you wouldn't want to use another library, but when you're considering yourself a beginner in these things, it may not be the best idea to write such a class from scratch ;)

If it relays on me are there some other ways better than std::vector, std::list (which you metnioned in point 3 as not the best idea) and reallocing tables?
You could use an associative container like std::map, and refer to the texture using some identifier (the key in the map). That's also a part of what thor::ResourceHolder does, it just simplifies many things on top of that.

The problem is more complex, I don't write it alone ;) But I'll try to convince our engraver to make one tileset for level, that would be more efective and simplify many things :) (Now I'm so smart, when you told me all that things :) )

But then, do they finaly stand still at one place, that I could call spr.setTexture(tex); when nothing else would be pushed to the vectors or is it still possible, that dusring executing my programm vector would be moved moved (not intentionaly by me)?
If you don't touch the vectors themselves, it's only possible if the object of the surrounding class (which contains the vector) is moved/copied.

Strange. I did it like I said, first made everything up, then called spr.setTexture(); and from then on never touched any vector or whole object. Still didn't worked.

And don't apologize for your English in every post, it's not that bad 8)

"it's not that bad" = still not perfect = still ther's something to apologize for, because it makes reading harder and I'm not an easy interlocutor; I write long posts and ask stupid questions ;)