SFML2.6 graphics module–position, rotation, scaling: entity transformation

Transform SFML entities

All SFML classes (sprites, text, shapes) use the same interface for transformation: sf::Transformable. This base class provides a simple API to move, rotate and scale your entities. It does not provide maximum flexibility, but defines an interface that is easy to understand and use, covering 99% of all use cases – for the remaining 1%, see the last chapter.

sf::Transformable (and all derived classes) defines four properties: position, rotation, scale, and origin. They all have their own getters and setters. These transform components are independent of each other: if you want to change the orientation of an entity, you only need to set its rotation property, and you don’t have to care about the current position and scale.

Location

Position is where the entity is in the 2D world. I don’t think it needs any more explanation.

// 'entity' can be a sf::Sprite, a sf::Text, a sf::Shape or any other transformable class

// set the absolute position of the entity
entity.setPosition(10.f, 50.f);

// move the entity relatively to its current position
entity.move(5.f, 5.f);

// retrieve the absolute position of the entity
sf::Vector2f position = entity.getPosition(); // = (15, 55)


By default, entities are positioned relative to their upper left corner. We’ll see later how to change this using the Origin property.

Rotate

Rotation is the orientation of an entity in the two-dimensional world. It is defined in degrees, in clockwise order (because in SFML, the Y-axis points downward).

// 'entity' can be a sf::Sprite, a sf::Text, a sf::Shape or any other transformable class

// set the absolute rotation of the entity
entity.setRotation(45.f);

// rotate the entity relatively to its current orientation
entity.rotate(10.f);

// retrieve the absolute rotation of the entity
float rotation = entity.getRotation(); // = 55


Note that when you call getRotation SFML always returns the angle in the range [0, 360).

Like position, rotation is performed around the top left corner by default, but can be changed by setting the origin.

Zoom

The scale factor allows the size of the entity to be adjusted. The default scale is 1. Setting it to a value less than 1 will make the entity smaller, and a value greater than 1 will make the entity larger. It is also possible to use negative scale values so that you can mirror entities.

// 'entity' can be a sf::Sprite, a sf::Text, a sf::Shape or any other transformable class

// set the absolute scale of the entity
entity.setScale(4.f, 1.6f);

// scale the entity relatively to its current scale
entity.scale(0.5f, 0.5f);

// retrieve the absolute scale of the entity
sf::Vector2f scale = entity.getScale(); // = (2, 0.8)

Origin

The origin is the center point of the three other transformations. The entity’s position is that of its origin, its rotation is about the origin, and scale is also applied relative to the origin. By default, it is the upper left corner of the entity (point (0,0)), but you can set it to the center of the entity or any other corner of the entity.

To simplify things, there is only one origin for all three transform components. This means that you cannot position an entity relative to its upper left corner while rotating it around its center. If you need to do this, check out the next section.

// 'entity' can be a sf::Sprite, a sf::Text, a sf::Shape or any other transformable class

// set the origin of the entity
entity.setOrigin(10.f, 20.f);

// retrieve the origin of the entity
sf::Vector2f origin = entity.getOrigin(); // = (10, 20)

Note that changing the origin also changes where the entity is drawn on the screen, even if its position property does not change. If you don’t understand why, read this tutorial again!

Convert your own class

sf::Transformable is not only applicable to SFML classes, it can also be a base class or member of your own class.

class MyGraphicalEntity : public sf::Transformable
{<!-- -->
    // ...
};

MyGraphicalEntity entity;
entity.setPosition(10.f, 30.f);
entity.setRotation(110.f);
entity.setScale(0.5f, 0.2f);

If you need to get the final transformation of an entity (usually when drawing it), you can call the getTransform function. This function returns an sf::Transform object. How to use it to convert SFML entities is explained below.

If you don’t need or want to use the full set of functions provided by the sf::Transformable interface, you can use it as a member and provide your own functions on it. It is not an abstract class, so it can be instantiated rather than just used as a base class.

Custom transformation

The sf::Transformable class is easy to use, but it also has certain limitations. Some users may require more flexible functionality. They may need to specify the final transform as a custom combination of individual transforms. For these users, a lower-level class is available: sf::Transform. It is nothing more than a 3×3 matrix, so it can represent any transformation in 2D space.

There are many ways to construct sf::Transform:

  • Predefined functions using the most common transformations (translation, rotation, scaling)
  • By combining two transformations
  • By directly specifying its 9 elements

Here are some examples:

// the identity transform (does nothing)
sf::Transform t1 = sf::Transform::Identity;

// a rotation transform
sf::Transform t2;
t2.rotate(45.f);

// a custom matrix
sf::Transform t3(2.f, 0.f, 20.f,
                 0.f, 1.f, 50.f,
                 0.f, 0.f, 1.f);

// a combined transform
sf::Transform t4 = t1 * t2 * t3;

You can also apply several predefined transforms to the same transform. They will be combined in order. Note that transforming an object by combining multiple transforms is equivalent to applying each operation in reverse order. The last operation (here scale) is applied first and will be affected by the operations above the code (e.g. the second operation might be translate(-10.f, 50.f)).

sf::Transform t;
t.translate(10.f, 100.f);
t.rotate(90.f);
t.translate(-10.f, 50.f);
t.scale(0.5f, 0.75f);

Back to the point: How to apply custom transforms to graphics entities? Simple: just pass it to the draw function.

window.draw(entity, transform);

…is actually shorthand for the following code:

sf::RenderStates states;
states.transform = transform;
window.draw(entity, states);

If your entity is a sf::Transformable (e.g. sprite, text, shape) and it contains its own internal transform, then the two transforms will be merged into the final transform.

Bounding box

For entities that have been transformed and drawn, you may want to perform some calculations, such as checking whether a collision occurred between them.

SFML entities can provide their bounding boxes. The bounding box is a minimal rectangle that contains all points of the entity and has its edges aligned with the X and Y axes.

Bounding boxes are very useful when implementing collision detection: you can check very quickly how a point or another axis-aligned rectangle relates to it, and its area is close enough to that of a real entity to provide a good approximation.

// get the bounding box of the entity
sf::FloatRect boundingBox = entity.getGlobalBounds();

// check collision with a point
sf::Vector2f point = ...;
if (boundingBox.contains(point))
{<!-- -->
    // collision!
}

// check collision with another box (like the bounding box of another entity)
sf::FloatRect otherBox = ...;
if (boundingBox.intersects(otherBox))
{<!-- -->
    // collision!
}

The function is named getGlobalBounds because it returns the bounding box of the entity in the global coordinate system, that is, after all transformations (position, rotation, scale) have been applied.

There is another function, getLocalBounds, which returns the bounding box of the entity in its local coordinate system (before applying the transformation). For example, you can use this function to get the initial size of an entity or perform more specific calculations.

Object hierarchy (scene graph)

Using the custom transforms introduced earlier, it becomes very simple to implement an object hierarchy (scene graph) where child objects are transformed relative to their parent objects. When drawing them, you just pass the combined transform from the parent object to the child object, all the way to the final drawable entity (sprite, text, shape, vertex array, or your own drawable entity).

// the abstract base class
class Node
{<!-- -->
public:

    // ... functions to transform the node

    // ... functions to manage the node's children

    void draw(sf::RenderTarget & amp; target, const sf::Transform & amp; parentTransform) const
    {<!-- -->
        // combine the parent transform with the node's one
        sf::Transform combinedTransform = parentTransform * m_transform;

        // let the node draw itself
        onDraw(target, combinedTransform);

        // draw its children
        for (std::size_t i = 0; i < m_children.size(); + + i)
            m_children[i]->draw(target, combinedTransform);
    }

private:

    virtual void onDraw(sf::RenderTarget & amp; target, const sf::Transform & amp; transform) const = 0;

    sf::Transform m_transform;
    std::vector<Node*> m_children;
};

// a simple derived class: a node that draws a sprite
class SpriteNode : public Node
{<!-- -->
public:

    // .. functions to define the sprite

private:

    virtual void onDraw(sf::RenderTarget & amp; target, const sf::Transform & amp; transform) const
    {<!-- -->
        target.draw(m_sprite, transform);
    }

    sf::Sprite m_sprite;
};