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

Author Topic: Erase only one rectangleShape  (Read 2700 times)

0 Members and 1 Guest are viewing this topic.

Jan666

  • Jr. Member
  • **
  • Posts: 64
    • View Profile
Erase only one rectangleShape
« 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]);
       
       
 }

 

eXpl0it3r

  • SFML Team
  • Hero Member
  • *****
  • Posts: 11032
    • View Profile
    • development blog
    • Email
Re: Erase only one rectangleShape
« Reply #1 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.
Official FAQ: https://www.sfml-dev.org/faq.php
Official Discord Server: https://discord.gg/nr4X7Fh
——————————————————————
Dev Blog: https://duerrenberger.dev/blog/

fallahn

  • Hero Member
  • *****
  • Posts: 507
  • Buns.
    • View Profile
    • Trederia
Re: Erase only one rectangleShape
« Reply #2 on: July 21, 2022, 11:30:57 am »
As exploiter says, erasing mid-iteration is not a good idea. Instead consider std::remove_if.

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());
}
 

Jan666

  • Jr. Member
  • **
  • Posts: 64
    • View Profile
Re: Erase only one rectangleShape
« Reply #3 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]);
       
       
 }
 
 }

 

Arcade

  • Full Member
  • ***
  • Posts: 230
    • View Profile
Re: Erase only one rectangleShape
« Reply #4 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.

fallahn

  • Hero Member
  • *****
  • Posts: 507
  • Buns.
    • View Profile
    • Trederia
Re: Erase only one rectangleShape
« Reply #5 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 ]
 

Jan666

  • Jr. Member
  • **
  • Posts: 64
    • View Profile
Re: Erase only one rectangleShape
« Reply #6 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?

fallahn

  • Hero Member
  • *****
  • Posts: 507
  • Buns.
    • View Profile
    • Trederia
Re: Erase only one rectangleShape
« Reply #7 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.

 

anything