Search code examples
c++pointersdrawingsfmldereference

SFML drawing elements of vector of pointers


here is the structure of my program:

class Game
{
public:
    unsigned int progressFlags;

    sf::RenderWindow appWindow;
    Screen* currentScreen;

    Game(const unsigned int& width, const unsigned int& height, 
         const unsigned int& bitsPerPixel, const std::string& windowName);
   ~Game();

    void drawScreen();
};

Game contains a Screen which is defined as:

class Screen
{
public:
    std::vector<sf::Drawable*> sceneElements;
    sf::Sprite backgroundSprite;
    Screen();
    virtual ~Screen();
protected:
    sf::Texture backgroundTexture;  
};

Elements of std::vector sceneElements are collected from screens like this:

sceneElements.push_back(&backgroundSprite);

At last, I am trying to draw all my graphic objects using a simple method:

void Game::drawScreen()
{
    for (unsigned int i = 0; i < currentScreen->sceneElements.size(); i++)
        appWindow.draw(*currentScreen->sceneElements(i));
}

But appWindow.draw() fails. I think it is something about dereferencing pointers (some basic c++ knowledge I cant just remember), but I don't know how to fix it. I would appreciate any help.


Solution

  • I'd just derive your custom classes from sf::Drawable (and potentially sf::Transformable). This way drawing hierarchically is very easy and basically done by SFML. The following snippets are written from memory; there might be typos. :)

    You'll just have to subclass your Screen class:

    class Screen : public sf::Drawable
    {
    public:
        std::vector<sf::Drawable*> sceneElements;
        sf::Sprite backgroundSprite;
        Screen();
        virtual ~Screen();
        virtual void draw(sf::RenderTarget &rt, sf::RenderStates states) const;
    protected:
        sf::Texture backgroundTexture;  
    };
    

    Inside draw() you'd just iterate over all your elements (similar to the way you do already):

    void draw(sf::RenderTarget &rt, sf::RenderStates states) const
    {
        for (const auto &e : sceneElements)
            rt.draw(*e, states);
    }
    

    Here we're dereferencing once to get from a pointer back to the object, which is then passed by reference (where inheritance does the rest).

    Game::drawScreen() becomes more simple as well:

    void Game::drawScreen()
    {
        appWindow.draw(*currentScreen);
    }
    

    Once again, derefrencing once to go from the pointer to the actual object (passed by reference).

    Also note that your original design violates encapuslation, by Game having to know how to work with stuff that's inside your Scene. So if you change things inside Scene, you'll have to adjust Game accordingly. Using inheritance, you're avoiding this issue.