Search code examples
c++texturessfml

Initialize texture once for derived classes in sfml


Recently, I've tried to exercise SFML with textures. However, I've encountered some problems. SFML website tells me that the most efficient way to use textures is to update sprites. Otherwise, our program may consume a lot of memory. I have one base class and two other classes derived from it. How may I initialize my texture variable so derived classes could use it with their sprites? I've tried initializing texture in Base class constructor and call it in derived classes, but then, I realized that my problem wasn't solved, because calling one constructor twice is a nonsense. This problem is so important for me, because I'm using the State Pattern which creates a lot of dynamically allocated objects.

#include <iostream>

class Base
{
protected:
    sf::Texture texture; //How to initialize this?
    sf::Sprite sprite;
public:
    Base();
};

class Derived1 : public Base
{
public:
    Derived1()
    {
        sprite.setTexture(texture);
    }
};

class Derived2 : public Base
{
public:
    Derived2()
    {
        sprite.setTexture(texture);
    }
};

Solution

  • The Flyweight Pattern

    SFML website tells me that the most efficient way to use textures is to update sprites. Otherwise, our program may consume a lot of memory.

    Yes, the relationship between sf::Sprite and sf::Texture corresponds to The Flyweight Pattern, a structural design pattern.

    An sf::Texture is a kind of heavyweight object whose data we want to share when possible to save memory. An sf::Sprite is a flyweight object, and multiple sf::Sprite objects can refer to the same sf::Texture object to share its data.

    The sf::Sprite only contains information that is specific to a particular use of the texture it refers to, e.g., the position of the texture, its rotation, size – this is the extrinsic state. An sf::Texture object contains data (i.e., the texture image itself) that can be shared by multiple sf::Sprite objects – this is the intrinsic state that is shared by all the sf::Sprite objects that refer to the same sf::Texture.

    In the SFML library you will also find this pattern with sf::Text/sf::Font and sf::Sound/sf::SoundBuffer.


    Not giving up Flyweight

    Keeping the pattern exposed above in mind, you may want to follow it in your design as well:

    class Base {
    public:
        Base(const sf::Texture& texture): sprite(texture) {}
    protected:
        sf::Sprite sprite;
    };
    
    class Derived: public Base {
    public:
        Derived(const sf::Texture& texture): Base(texture) {}
        // ...
    };
    

    The sprite data member is initialized with the sf::Texture passed by reference to the constructor, and multiple Base and Derived objects can share the same sf::Texture.

    As an example:

    sf::Texture myTexture;
    myTexture.loadFromFile("myImageFile.png");
    
    Base b1(myTexture), b2(myTexture);
    Derived d1(myTexture), d2(myTexture);
    

    b1, b2, d1 and d2 all share the same sf::Texture instance – myTexture:

    Flyweight applied

    Of course, none of these objects should outlive myTexture.