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

Author Topic: Problem with erasing elements from vector  (Read 4016 times)

0 Members and 1 Guest are viewing this topic.

AdrianHEY

  • Newbie
  • *
  • Posts: 14
    • View Profile
Problem with erasing elements from vector
« on: April 13, 2021, 10:48:04 am »
If I shoot a bullet and then another and the first bullet hits an enemy the program crashes.
If I shoot a bullet and hits an enemy but I dont shoot a 2nd one its fine.
If I shoot a bullet, it hits the enemy and then shoot a 2nd one its ok.
If I shoot any ammount of bullet and neither shooot the enemy its ok.
If I shoot a bullet that doesent it the enemy and another that hits while the first is active its ok.

The code for drawing the bullets:
void drawBullet(sf::RenderWindow& game, float playerRotation, float xPlayer, float yPlayer, sf::Vector2f vectorPosition) {
                float time2;
                time = clock.getElapsedTime();
                //std::cout << std::fixed << time.asSeconds() << std::endl;
                        if (sf::Mouse::isButtonPressed(sf::Mouse::Left) && time.asSeconds() > 0.5 && cateGloante > 0) {
                                cateGloante--;
                               
                                bullet.setRotation(playerRotation);
                                bullet.setPosition(vectorPosition);
                                rota = playerRotation;
                                vectorOfRotations.push_back(rota);
                                clock.restart();

                                vectorOfBullets.push_back(bullet);
                        }
                        for (int i = 0; i < vectorOfBullets.size(); i++) {
                                game.draw(vectorOfBullets[i]);
                                vectorOfBullets[i].move(cos(vectorOfRotations[i] * 3.14189 / 180) * 3, sin(vectorOfRotations[i] * 3.14189 / 180) * 3);
                        }
        }
The code in the main.cpp
for (int i = 0; i < enemy1.vectorOfEnemies.size(); i++) {
            for (int j = 0; j < bullet1.vectorOfBullets.size(); j++) {
                if (enemy1.vectorOfEnemies[i].getGlobalBounds().intersects(bullet1.vectorOfBullets[j].getGlobalBounds())) {

                    bullet1.vectorOfBullets.erase(bullet1.vectorOfBullets.begin() + j);
                    bullet1.vectorOfRotations.erase(bullet1.vectorOfRotations.begin() + j);
                    enemy1.vectorOfEnemies.erase(enemy1.vectorOfEnemies.begin() + i);
                   
                    bullet1.cateGloante++;
                }
            }
        }
And the drawEnemies code
void drawEnemies(sf::RenderWindow& game) {

                srand(time(0));
                if (randomized == false) {
                        enemy.setPosition(1 + (rand() % 1920), 1 + (rand() % 1080));
                        randomized = true;
                }
                enemy.setOrigin(25, 62.5);
               
                if(oSinguraData == true) vectorOfEnemies.push_back(enemy);
                oSinguraData = false;

                for (int i = 0; i < vectorOfEnemies.size(); i++) {
                        game.draw(vectorOfEnemies[i]);
                }
        }
I do not know what doesent work while the 2 bullets are active...

eXpl0it3r

  • SFML Team
  • Hero Member
  • *****
  • Posts: 10818
    • View Profile
    • development blog
    • Email
Re: Problem with erasing elements from vector
« Reply #1 on: April 13, 2021, 11:25:58 am »
Since you erase content from a vector while you're iterating it, which is often not necessarily recommended, you need to adjust the iterators and limits.

Best you run your code through a debugger and go through it step by step, while observing the content of the vectors, that way you should notice how one might get a past-the-end access to a vector or similar.
Official FAQ: https://www.sfml-dev.org/faq.php
Official Discord Server: https://discord.gg/nr4X7Fh
——————————————————————
Dev Blog: https://duerrenberger.dev/blog/

kojack

  • Sr. Member
  • ****
  • Posts: 313
  • C++/C# game dev teacher.
    • View Profile
Re: Problem with erasing elements from vector
« Reply #2 on: April 13, 2021, 04:43:51 pm »
There's two issues here.
The first is relatively harmless: If a bullet hits an enemy, the next bullet isn't tested for collision for one frame. The reason is when you erase one of the bullets, all the later bullets shuffle down one slot to fill in the gap. Then you advance to the next slot (j++) and test again. But because you shuffled down then moved forward before testing collision, the bullet after the erased one got skipped. Next update it will get checked, so it's collision is just a frame late. Usually when I use int for loops and erase things, I would do a j-- after the erase, so the loop steps back one, then hits the j++ to step forward again, meaning it ends up on the same position for a second time and sees the shuffled down bullet.

The crash is happening because you are testing an enemy after erasing it.
If you have 1 enemy and 2 bullets, the inner loop is testing that enemy twice (once for each bullet). But if the first bullet hits, the enemy is erased. But then the loop tries to test the same enemy (that is now gone) against the second bullet.
So after erasing an enemy, do a break;. This will stop the inside loop (which can't work since the enemy is gone, so you need to start again on the next enemy) and the outside enemy loop will now get to check if any enemies are left.
(Same thing will then happen as the first issue, the next enemy will be skipped for one frame if you don't move i back by 1 after erasing, but that won't cause a crash)

AdrianHEY

  • Newbie
  • *
  • Posts: 14
    • View Profile
Re: Problem with erasing elements from vector
« Reply #3 on: April 13, 2021, 07:22:00 pm »
It worked! Thanks so much, I've been strugleing with this for 3 days now:)

kojack

  • Sr. Member
  • ****
  • Posts: 313
  • C++/C# game dev teacher.
    • View Profile
Re: Problem with erasing elements from vector
« Reply #4 on: April 14, 2021, 02:50:03 am »
Cool! :)

Something else that can be done is giving each object a flag (I usually call it something like "dead") to say you want to remove it. So in your collision check you'd do enemy1.vectorOfEnemies.dead = true; (and same for the bullet), but no erasing.
Then after all the logic you finish off by looping over the enemies and bullets separately and erasing anything with dead==true.

In the situation you posted there's not really a benefit to that, but if you start using a physics engine (like box2d) it won't like you adding or removing objects in the middle of a collision function. So you need to flag them for later when box2d isn't doing its update.