Welcome, Guest. Please login or register. Did you miss your activation email?

Author Topic: Nested transforms for sf::sprite and mouse event collision  (Read 12438 times)

0 Members and 1 Guest are viewing this topic.

20lbpizza

  • Newbie
  • *
  • Posts: 4
    • View Profile
    • Email
Nested transforms for sf::sprite and mouse event collision
« on: August 23, 2021, 09:17:13 pm »
Context: In following the "SFML game development book" (Moreira, Hansson, Haller)  chapter 4 and 6 cover input handling and game menu's respectively. The menu's in the example code are all keyboard based while I'd like to apply mouse based input for a project I am currently working on. A button class is used to pair a sf::Sprite and sf::Text and ultimately inherits from sf::transformable so you only need to make one call of setPosition on the wrapper class in order to move/align both the text and button sprite.

Problem: I am unsure on the proper way of checking collision with the mouse for the buttons. My current work around based on previous forum post is as follow:

In MenuState.cpp - replace the calls of setPosition of the wrapper class with a function that directly passes the coordinates to the sprite and text object.
        // A subset of the constructor of the MenuState
        auto playButton = std::make_shared<GUI::Button>(context);
        float offsetX = playButton->getSize().width / 2 ;
        playButton->setPos(winX/2 - offsetX, winY/3); // Replace a call of setPosition here ***
        playButton->setText("Play");
        playButton->setCallback([this] ()
        {
                requestStackPop();
                requestStackPush(States::Game);
        });
 

In Button.cpp a method is added to directly apply the position to the sprite and text.
// Add a function to pass the position to the underlying objects (sprite and text) instead of the button itself
void Button::setPos(float x, float y)
{
    this->mSprite.setPosition(x,y);
    this->mText.setPosition(x + this->getSize().width /2 ,y + this->getSize().height /2);
}
 

The reason this work around was applied was because in a method which handles the Mouse event, I could not check collision with the sprite since the Button class was moved but the globalPosition of the sprite and text was still set to 0,0 (moving mouse to top left corner would select last piece of menu). For the draw menu the transforms are passed down so this is not an issue when drawing. The final menu has a container for the buttons that are iterated over and the position of the sprite it checked using .contains against the mouse event coordinates.

Final question: What would be the best way to pass a wrapper objects transform to an underlying sprite to check if the sprite (or wrapper object) contain the position of the mouse click?

Hope this question is not too verbose or vague this is my first post and welcome to feedback.
« Last Edit: August 23, 2021, 09:22:46 pm by 20lbpizza »

20lbpizza

  • Newbie
  • *
  • Posts: 4
    • View Profile
    • Email
Re: Nested transforms for sf::sprite and mouse event collision
« Reply #1 on: August 30, 2021, 04:42:57 am »
I eventually figured this out after a few days away from this part of the project.

In the original problem we didn't apply the transform like we did by with draw:
mSprite.getGlobalBounds().contains(x,y); // only works if we change the sprites position directly
// defeats the point of using a wrapper class
 


It would be somewhat awkward to pass "sf::RenderStates" as a extra parameter to a simple contains() function so we didn't want to necessarily copy how draw works but keep the same premise. First make a bounding box with the sprites global bounds like we originally expected. Then simply apply the transform of the wrapper class to the bounding box and check for this modified box.

bool Button::contains(int x, int y)
{
    sf::FloatRect boundingBox = mSprite.getGlobalBounds();
    boundingBox = getTransform().transformRect(boundingBox);
    return boundingBox.contains(x,y);
}
 

The end result is I can now use the default setPosition function from sf::Transform on this Button class and it accurately handles collision and drawing without direct manipulation of the sprite the Button contains.

To my understanding the tutorial doesn't really help for this particular situation so it may be useful to add?

eXpl0it3r

  • SFML Team
  • Hero Member
  • *****
  • Posts: 11030
    • View Profile
    • development blog
    • Email
Re: Nested transforms for sf::sprite and mouse event collision
« Reply #2 on: August 30, 2021, 08:57:12 am »
Would have to double check what the book does exactly, but the general idea when working with composites and transforms, is that your parent class is deriving from sf::Transformable and then all your checks should be made against/with the transform in mind. So yes, your solution is exactly what one should do.

Personally, I'd argue that your button class shouldn't really have a contains function and much rather suggest to provide a function to retrieve the bounding box of your button class and let the user/caller of your button decide how they want to check collision.
That also then removes the reimplementation of the contains function, as the button global bounds is already correctly transformed.
Official FAQ: https://www.sfml-dev.org/faq.php
Official Discord Server: https://discord.gg/nr4X7Fh
——————————————————————
Dev Blog: https://duerrenberger.dev/blog/