c++sfml

Updating Transformable Child Objects in a Class Derived from sf::Transformable


I am new to the sf::Transformable class and recently started experimenting with it. I'm encountering an issue where a sf::Transformable object is placed inside a class that itself is derived from sf::Transformable. When transformation methods are called on the parent class, it only updates the transformation of the parent object, not the child objects.

While drawing, SFML automatically manages the transformations and positions the child objects correctly. This is usually fine, but in cases where we need the actual transformed positions of the child objects (for example, in a board game where each board place is a sf::Transformable), this automatic management isn't sufficient.

Here’s a simple example demonstrating the issue:

#include <SFML/Graphics.hpp>
#include <iostream>

std::ostream& operator << (std::ostream& os, const sf::Vector2f& vect) {
    os << "(" << vect.x << ", " << vect.y << ")";
    return os;
}

class TestClass : public sf::Transformable, public sf::Drawable {
public:
    TestClass() {
        shape.setSize(sf::Vector2f(100, 50));
        shape.setPosition(0, 0);
        shape.setFillColor(sf::Color::Red);
    }

    void displayInfo() const {
        std::cout << "\nShape Position : " << shape.getPosition() << std::endl; 
        std::cout << "Shape Rotation : " << shape.getRotation() << std::endl;
        std::cout << "Shape Origin : " << shape.getOrigin() << std::endl;
        std::cout << "Position of Object : " << getPosition() << std::endl;
        std::cout << "Rotation of Object : " << getRotation() << std::endl;
        std::cout << "Origin of Object : " << getOrigin() << std::endl;            
    }

protected:
    virtual void draw(sf::RenderTarget& target, sf::RenderStates states) const override {
        states.transform *= getTransform();
        target.draw(shape, states);
    }  

private:
    sf::RectangleShape shape;
};

int main() {
    sf::RenderWindow window(sf::VideoMode(800, 600), "Transformable Test");
    TestClass test;
    sf::Clock clock;
    double delta = 0;
    const double multiplier = 60;
    const float movement = 10.f;
    const float rotation = 2.f;

    while (window.isOpen()) {
        delta = clock.restart().asSeconds();
        sf::Event event;

        while (window.pollEvent(event)) {
            if (event.type == sf::Event::Closed)
                window.close();
            else if (event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::I)
                test.displayInfo();
        }

        if (sf::Keyboard::isKeyPressed(sf::Keyboard::A))
            test.move(-movement * delta * multiplier, 0);
        else if (sf::Keyboard::isKeyPressed(sf::Keyboard::D))
            test.move(movement * delta * multiplier, 0);

        if (sf::Keyboard::isKeyPressed(sf::Keyboard::W))
            test.move(0, -movement * delta * multiplier);
        else if (sf::Keyboard::isKeyPressed(sf::Keyboard::S))
            test.move(0, movement * delta * multiplier);

        if (sf::Keyboard::isKeyPressed(sf::Keyboard::Q))
            test.rotate(-rotation * delta * multiplier);
        else if (sf::Keyboard::isKeyPressed(sf::Keyboard::E))
            test.rotate(rotation * delta * multiplier);

        window.clear();
        window.draw(test);
        window.display();
    }

    return 0;
}

Problem : The shape inside TestClass does not get its transformation updated when TestClass is transformed. This results in discrepancies when accessing shape's position, rotation, or other transformations directly.

Question : How can I correctly update the transformations of sf::Transformable objects (like shape) within a parent class (like TestClass) so that their transformed positions, rotations, and scales are accurate when accessed?

I’ve tried different approaches, but none have worked so far. Any guidance on how to achieve this would be greatly appreciated.


Solution

  • When you create a positional/transformable hierarchy of objects, what you're effectively doing is changing the coordinate system of the children to match the parent's coordinate system. This means that for example the point (0, 0) is not at the global zero point, but at the origin of the parent. The same applies to all other points and translations. That's why one refers to it as being relative to the parent.

    Now if you want to get the global position/transformation of a child, you will have to actively transform that child from the parents coordinate system to the global coordinate system. This can be done by calling transformPoint(vec) on the getTransform() of the parent, with the relative position of the child.

        void displayInfo() const {
            std::cout << "\nShape Global Position : " << getTransform().transformPoint(shape.getPosition()) << std::endl; 
            std::cout << "Shape Global Origin : " << getTransform().transformPoint(shape.getOrigin()) << std::endl;
    
            // ...            
        }
    

    As mentioned on the SFML forum, SFML doesn't provide a decomposition of the different components (translation, rotation and scale), but it can mathematically be extracted.