Displaying a sprite

Introduction

Displaying sprites is maybe the most important part of a 2D API. Because, basically, everything is a sprite. Let's have a look at what they look like in SFML.

Loading an image

Usually, sprites are loaded from files on hard drive. To load an image from a file with SFML you have to use the sf::Image class and its LoadFromFile function :

sf::Image Image;
if (!Image.LoadFromFile("sprite.tga"))
{
    // Error...
}

You can as well load a file already loaded in memory with the LoadFromMemory function :

sf::Image Image;
if (!Image.LoadFromMemory(FilePtr, Size))
{
    // Error...
}

Images can also be created and filled with a color, or loaded from an array of pixels :

sf::Image Image1(200, 200, sf::Color(0, 255, 0));
sf::Image Image2(200, 200, PointerToPixelsInMemory);

// Or, if you want to do it after construction :

Image1.Create(200, 200, sf::Color(0, 255, 0));
Image2.LoadFromPixels(200, 200, PointerToPixelsInMemory);

Please note that when you load an image from memory, pixels must have a specific format, which is 32-bits RGBA.

You can also access image pixels for reading and writing :

sf::Color Color = Image.GetPixel(10, 20);
Image.SetPixel(20, 30, Color);

Be careful when manipulating images : they can be copied, but it's not a lightweight operation. So try to avoid it as possible (pass them by reference when you can).

Creating a sprite

Ok, we can now create / load / modify an image. But this is not a sprite. In SFML, images and sprites are split into two classes. You can see images as an array of pixels in memory, and sprites as a way to display these arrays of pixels in a window. Having image manipulation not in the sprite interface has two main advantages :

Let's go back to sprites. In SFML, a sprite is an instance of the sf::Sprite class. This class inherits from sf::Drawable, which is the base of every drawable thing (sprites, strings, post-effects, ...). sf::Drawable defines a set of functions for setting / getting the visual appearance of the drawable object. Here is an example with a sprite, but these functions are common to every drawable object, so remember them.

sf::Sprite Sprite;
Sprite.SetColor(sf::Color(0, 255, 255, 128));
Sprite.SetX(200.f);
Sprite.SetY(100.f);
Sprite.SetPosition(200.f, 100.f);
Sprite.SetRotation(30.f);
Sprite.SetCenter(0, 0);
Sprite.SetScaleX(2.f);
Sprite.SetScaleY(0.5f);
Sprite.SetScale(2.f, 0.5f);
Sprite.SetBlendMode(sf::Blend::Multiply);

Sprite.Move(10, 5);
Sprite.Rotate(90);
Sprite.Scale(1.5f, 1.5f);

Note that every function taking two floats as parameters, can also take a sf::Vector2f instance. The GetXxx functions corresponding to SetXxx function taking a vector, also returns a sf::Vector2f instance.

The center of the object, defined by the function SetCenter, is defined relative to the left-top corner of the object and represents its center of translation, rotation and scaling. You can see it as the origin of the object, which will remain unchanged when you apply geometric transformations to it.

Notice that the color has an alpha component of 128, meaning that our sprite will be a bit transparent. If you wonder why the sprite's position is expressed with floats whereas pixels are integers, this is to allow for more flexibility. When you move a sprite, this is not always of an integer amount of pixels, but rather a small value at each frame. This avoids you to have another variable just to store the sprite's position as floats. Another reason is that when using views, "scene" coordinates no more match screen pixels ; but let's keep this for next tutorial.

Once you've applied all these transformations to your sprite, it may become difficult to get the resulting rectangle, or generally to get any local point's final position. That's why sf::Sprite and all drawable classes define two conversion functions :

// Convert a local point to global coordinates
sf::Vector2f Global = Sprite.TransformToGlobal(sf::Vector2f(10, 25));

// Convert a global point to local coordinates
sf::Vector2f Local = Sprite.TransformToLocal(sf::Vector2f(425, 120));

On top of that, sf::Sprite provides more specific functions :

// Change the source image (texture) of the sprite
sf::Image Image;
...
Sprite.SetImage(Image);

// Get the sprite's dimensions
float Width  = Sprite.GetSize().x;
float Height = Sprite.GetSize().y;

// Resize the sprite
Sprite.Resize(60, 100);

// Flip the sprite on X and Y axis
Sprite.FlipX(true);
Sprite.FlipY(true);

// Get the color of a given pixel inside the sprite
sf::Color Pixel = Sprite.GetPixel(10, 5);

We can then easily add some interaction with our sprite :

// Get elapsed time
float ElapsedTime = App.GetFrameTime();

// Move the sprite
if (App.GetInput().IsKeyDown(sf::Key::Left))  Sprite.Move(-100 * ElapsedTime, 0);
if (App.GetInput().IsKeyDown(sf::Key::Right)) Sprite.Move( 100 * ElapsedTime, 0);
if (App.GetInput().IsKeyDown(sf::Key::Up))    Sprite.Move(0, -100 * ElapsedTime);
if (App.GetInput().IsKeyDown(sf::Key::Down))  Sprite.Move(0,  100 * ElapsedTime);

// Rotate the sprite
if (App.GetInput().IsKeyDown(sf::Key::Add))      Sprite.Rotate(-100 * ElapsedTime);
if (App.GetInput().IsKeyDown(sf::Key::Subtract)) Sprite.Rotate( 100 * ElapsedTime);

Drawing a sprite

Drawing a sprite in a render window is straight-forward :

App.Draw(Sprite);

No extra parameter is needed, because the sprite already knows all about its visual (position, color, ...).

If you want to draw only a sub-rectangle of the original image, you can change the SubRect parameter of the sprite :

Sprite.SetSubRect(sf::IntRect(10, 10, 20, 20));

Here we will only display a sub-rectangle of the image, going from pixel (10, 10) to pixel (20, 20).

sf::IntRect is just an utility class for manipulating rectangles. For rectangles with float coordinates, there is also the sf::FloatRect class (in fact they are both instances of the sf::Rect template class).

Images and sprites management

You have to be particularly careful when manipulating images. A sf::Image instance is a resource which is slow to load, heavy to copy and uses a lot of memory.

A lot of people, especially beginners, will just put an instance of sf::Image wherever they have an instance of sf::Sprite, because it may seem the simplest way to draw something. The fact is that it's generally a bad idea. The most obvious problem is when copying such objects (just putting them into an array generates copies) : the sprites will most likely appear white. The reason is that a sprite only points to an external image it doesn't own one, so when the image is copied the sprite has to be updated to point to the new copy of the image. This is quite easy to handle, you just have to define a copy constructor for such classes holding (or deriving from) a sprite and an image :

class MyPicture
{
public :

    // This is the copy constructor
    MyPicture(const MyPicture& Copy) : Image(Copy.Image), Sprite(Copy.Sprite)
    {
        // This is the trick : we setup the sprite
        // to use our image, instead of the one of Copy
        Sprite.SetImage(Image);
    }

    // ... a lot of useful functions ...

private :

    sf::Image  Image;
    sf::Sprite Sprite;
};

But this is a particular case, and generally you will most likely share an image among several sprites. In fact, there shouldn't be more than one instance of sf::Image per image file that you load (after all, why should we have the same array of pixels loaded several times in memory ?). That's why the concepts of sprites and images have been split in SFML : images are heavy and expensive, whereas sprites are lightweight, they are just a particular visual representation of an image.

There are a lot of good designs which take care of image management. For example, if you have a class which instances will always use the same image, you can simply do this :

class Missile
{
public :

    static bool Init(const std::string& ImageFile)
    {
        return Image.LoadFromFile(ImageFile);
    }

    Missile()
    {
        Sprite.SetImage(Image); // every sprite uses the same unique image
    }

private :

    static sf::Image Image; // shared by every instance

    sf::Sprite Sprite; // one per instance
};

In a more complex engine, images would be automatically handled by a manager. This is a more generic and easy way of handling resources. The idea is to make the manager store the images, associated to their filename (or whatever unique identifier), so that if the same image is requested several times, the same instance will actually always be returned by the manager.

sf::Sprite Sprite;

// GetImage will always return the same image for the same filename
Sprite.SetImage(ImageManager.GetImage("data/missile.png"));

Conclusion

Sprite is a basic concept in a 2D API, and as you've seen they are very easy to use in SFML. If you now want to know how to draw simple shapes with SFML, go to the next tutorial about shapes.