SFML community forums

Help => Graphics => Topic started by: Jan666 on July 21, 2022, 09:35:28 am

Title: Erase only one rectangleShape
Post by: Jan666 on July 21, 2022, 09:35:28 am
Hi,
I have a player and when he intersects with the door, the door should erase, but the player still erase all doors, how can i fix it that he only erase the door he intersects, here is my code:
RectangleShape door;
std::vector<RectangleShape> doors(3u);

....

 (auto& door: doors){
 FloatRect playerBounds = player.getGlobalBounds();
 FloatRect doorBounds =  door.getGlobalBounds();
       
          for(int i = 0; i < doors.size(); i++){
       
         if(doorBounds.intersects(playerBounds)){
               
                doors.erase(doors.begin(), doors.end());
               
         }
 }     
       
 window.draw(doors[1]);
 window.draw(doors[2]);
       
       
 }

 
Title: Re: Erase only one rectangleShape
Post by: eXpl0it3r on July 21, 2022, 11:22:16 am
doors.erase(doors.begin(), doors.end()); this deletes all the doors, because you say it should erase everything in the range from the beginning of the vector the the end of the vector.
If you just want to erase one specific entry you can just pass that iterator into the erase method.

But keep in mind, you can't remove the element while you're iterating over the container with range for loop, otherwise you end up modifying the container while iterating it.
You'd instead need to iterate over it with iterators and update the iterate accordingly when erasing the specific element.
Title: Re: Erase only one rectangleShape
Post by: fallahn on July 21, 2022, 11:30:57 am
As exploiter says, erasing mid-iteration is not a good idea. Instead consider std::remove_if (https://en.cppreference.com/w/cpp/algorithm/remove).

doors.erase(std::remove_if(doors.begin(), doors.end(), Predicate(const Door&)), doors.end());
 

Predicate is a functor, std::function, function pointer or lambda that returns true on a condition when an element should be removed. Each element in your vector is automatically passed to it as a parameter. In your case you can use a lambda to decide if the door collides

[playerBounds](const Door& door)
{
    return playerBounds.intersects(door.getGlobalBounds());
}
 
Title: Re: Erase only one rectangleShape
Post by: Jan666 on July 22, 2022, 12:00:47 pm
The remove thing dont work i try this, but still booth doors gone,

RectangleShape door;

std::vector<RectangleShape> doors(3);

std::vector<RectangleShape>::iterator it;

...

while (window.isOpen())
        {
               
                sf::Event event;
                while (window.pollEvent(event));

...

for (int j = 0; j < doors.size(); j++){
       
       
       
        doors[j].setFillColor(Color::Cyan);
       
        doors[1].setSize(Vector2f(100, 100));
        doors[1].setPosition(Vector2f(650, 1000));
       
        doors[2].setSize(Vector2f(100, 100));
     doors[2].setPosition(Vector2f(650, 700));
       
       
 }
 
 for (auto& door: doors){

 FloatRect playerBounds = player.getGlobalBounds();
 
 FloatRect doorBounds = door.getGlobalBounds();
 
 for ( auto it = doors.begin(); it != doors.end();)
  {
               
  if(doorBounds. intersects(playerBounds)) {
       
 
         it = doors.erase(it);
         
          }
          else
          {
          ++it;
          }
         
           
       
 window.draw(doors[1]);
 window.draw(doors[2]);
       
       
 }
 
 }

 
Title: Re: Erase only one rectangleShape
Post by: Arcade on July 22, 2022, 08:19:24 pm
for (auto& door: doors)
{
   FloatRect playerBounds = player.getGlobalBounds();
   FloatRect doorBounds = door.getGlobalBounds();
 
   for ( auto it = doors.begin(); it != doors.end();)
   {            
      if(doorBounds.intersects(playerBounds)) {
 

You are looping through every door (outer for loop), and for each of those doors you are looping through every door again (inner for loop). If the current door from the outer loop intersects the players bounds then you are deleting every door in the inner loop. You likely don't need these nested for loops.

One thing that might be helpful is to look up how to use a debugger. A debugger would allow you to step through one line of code at a time while your program is running to get a better understanding of what's happening. You can usually spot this type of problem pretty quickly with a debugger.
Title: Re: Erase only one rectangleShape
Post by: fallahn on July 23, 2022, 04:28:37 pm
std::remove_if works perfectly for removing all instances which intersect. Here's a CME:

template <typename T>
std::ostream& operator << (std::ostream& out, sf::Rectangle<T> r)
{
    out << "[ " << r.left << ", " << r.bottom << ", " << r.width << ", " << r.height << " ]";
    return out;
}

int main()
{
    sf::FloatRect playerBounds = { 0.f, 0.f, 10.f, 10.f };
    std::vector<sf::FloatRect> doors =
    {
        sf::FloatRect(-2.f, 0.f, 10.f, 10.f),
        sf::FloatRect(-20.f, 0.f, 10.f, 10.f),
        sf::FloatRect(2.f, 0.f, 10.f, 10.f),
        sf::FloatRect(0.f, 20.f, 10.f, 10.f),
    };

    std::cout << "Doors before: " << std::endl;
    for (auto door : doors)
    {
        std::cout << door << std::endl;
    }

    doors.erase(std::remove_if(doors.begin(), doors.end(),
        [playerBounds](const sf::FloatRect& door)
        {
            return playerBounds.intersects(door);        
        }), doors.end());

    std::cout << "Doors after: " << std::endl;
    for (auto door : doors)
    {
        std::cout << door << std::endl;
    }

    return 0;
}

 

And the output:
Doors before:
[ -2, 0, 10, 10 ]
[ -20, 0, 10, 10 ]
[ 2, 0, 10, 10 ]
[ 0, 20, 10, 10 ]
Doors after:
[ -20, 0, 10, 10 ]
[ 0, 20, 10, 10 ]
 
Title: Re: Erase only one rectangleShape
Post by: Jan666 on July 23, 2022, 10:08:40 pm
I dont understand all parts of your code, but is it right, that you put multiple FloatRects in a Vector for the doors instead of shapes?
Title: Re: Erase only one rectangleShape
Post by: fallahn on July 23, 2022, 11:12:04 pm
That's right, for the example I put the FloatRects in the vector directly just to simplify - in your case you would have a vector of Door (or whichever class you like that contains a FloatRect / bounds). The point is that you can use remove_if() to search your entire vector for you without manually looping, and without having to worry about deleting in the middle of a loop.