Box2d destroy all objects in a set - c++

I'm using cocos2d and box2d and I have up to 5 b2bodies that need to be destroyed at the same time. They are all added to a set std::set<b2Body*>row1RedArray; and added by row1RedArray.insert(spriteBody);, and i've deleted all the items in the array through iteration, but my program just crashes when I touch the screen after they are removed. Am I destroying the b2Bodies correctly?
//if that array is empty, then remove all objects from this array (row4)
if ((row4BlueArray.count == 0) && (row4.count >> 0) && (row4Removed == NO)) {
std::set<b2Body *>::iterator pos04;
for(pos04 = row4RedArray.begin(); pos04 != row4RedArray.end(); ++pos04) {
b2Body *rowBody = *pos04;
if (rowBody->GetUserData() != NULL)
{
for (CCSprite* sprite4 in row4) {
[self removeChild:sprite4 cleanup:YES];
}
//Adding the b2Body to the toDelete Set and then removing it from the set of b2Bodies
toDestroy.insert(rowBody);
row4RedArray.erase(rowBody);
row4Removed = YES;
}
}
}
std::set<b2Body *>::iterator pos2;
for(pos2 = toDestroy.begin(); pos2 != toDestroy.end(); ++pos2) {
b2Body *body = *pos2;
if (body->GetUserData() != NULL)
{
//Then removing the b2Body completely (this is all at the end of the tick method)
_world->DestroyBody(body);
}
}

Solution of Captain Obvlious in comments is obvious, but is not correct. Bodies should be destroyed by world->DestroyBody(). Its needed simple iterate through bodies, and destroy each by calling this method (and never call delete for b2Body, b2Fixture or b2Joint). There no way to destroy them all at once.

You should destroy the bodies via world->DestroyBody(), do not dispose of them in any other way. There is no way to remove them at the same time, but the removal of the body has to be done outside of the box2d world step. This means that if you iterate through your list and destroy the bodies you want next time the box2d world will be updated it will look like the bodies have been disposed of at the same time.
There are few issues on the C++ side which might cause undefined behaviour. One of them is removing from an container when iterating over it. Once you used erase on any container the iterators to that container become invalid. This is more less the code I would propose:
std::vector<b2Body *> toDestroy;
if ((row4BlueArray.count == 0) && (row4.count >> 0) && (row4Removed == NO))
{
for(std::set<b2Body *>::iterator pos04 = row4RedArray.begin(); pos04 != row4RedArray.end(); ++pos04)
{
b2Body *rowBody = *pos04;
if (rowBody->GetUserData() != NULL)
{
toDestroy.push_back(rowBody);
row4Removed = YES;
}
}
for (CCSprite* sprite4 in row4)
{
[self removeChild:sprite4 cleanup:YES];
}
}
for( std::set<b2Body *>::iterator pos2 = toDestroy.begin(); pos2 != toDestroy.end(); ++pos2)
{
row4RedArray.erase( (*body) );
_world->DestroyBody( (*body) );
}
//I have put the toDestroy vector in as a local variable so this is not needed, but if you
//are having it as a member variable etc. you need to clear it before you are going to use
//it in next loop, otherwise it will try to delete the same elements a second time.
toDestroy.clear();
I'm not sure why you used a std::set for storing the b2Body pointers. Sets are generally slower then any unordered container, such us vectors. I have also removed the if (rowBody->GetUserData() != NULL) from the second for loop as you do that check when you add the objects to the toDestroy vector, it can be assumed that the objects passed the criteria for removal.
You also remove sprites from the scene when iterating over the row4 container ( I assume from the code it is a container of some sort ), but you never clear it. It might wise to do so after you deleted all of the elements in that container, which is happening here from what I can see. You also attempt to remove those sprites multiple times, with the "for (CCSprite* sprite4 in row4)" inside the for loop iterating the bodies, so if more then one body has passed the criteria to be removed you iterate through the row4 again to remove the sprites.
I'm not sure why you are removing the bodies based on "if (rowBody->GetUserData() != NULL) criteria", but that might be something that you require in your game, which isn't apparent from the provided code.

Related

Segmentation fault during erasing from vector

I'm developing an Asteroid game clone but I'm facing a problem during erasing elements from vector of asteroids. So generally when I hit the asteroid it should split into 3 parts. So I create 3 new asteroids and erase the old one an then it crashes.
void Level::missleAsteroidCollision(){
std::cout<<this->asteroidVector.size()<<std::endl;
for(auto ptr = this->missleVector.begin();ptr!=this->missleVector.end();++ptr){
sf::FloatRect missleBounds = (*ptr)->shape.getGlobalBounds();
for(auto ptrTwo = this->asteroidVector.begin(); ptrTwo!= this->asteroidVector.end();++ptrTwo){
if(missleBounds.intersects((*ptrTwo)->shape.getBounds()) && (*ptrTwo)->isFinalForm == false){
for(int i = 0; i < 3; ++i){
this->createAsteroid((*ptrTwo)->origin,true);
}
delete *ptrTwo;
this->asteroidVector.erase(ptrTwo);
}
else if(missleBounds.intersects((*ptrTwo)->shape.getBounds()) && (*ptrTwo)->isFinalForm == true){
delete *ptrTwo;
this->asteroidVector.erase(ptrTwo);
}
}
}
}
First of all, when you use the .erease function, the iterator is changed so you need to update it, in your case, ptr = this->asteroidVector.erase(ptrTwo); the iterator will now point to the next element after deletion so keep that in mind (either you decrease the pointer by one or you only increase the ptr (ptr++) if you did not use the .erase function.
Secondly, I believe this->createAsteroid((*ptrTwo)->origin,true); creates new items, this will also invalidate the iterator, so one fix could be, creating the new asteroids after checking and deleting the old one. Maybe store the new asteroids in a vector created before the for loop, adding the new meteorites there and after the for loop add the vector to your current vector of asteroids.
Inserting an item into a vector (whether or not at the end of the vector) can invalidate all iterators to that vector. I suspect your createAsteroid does so.

Program crashes when attempting to erase the last std::vector element

I'm iterating through the vector std::vector<Bullet*> bullets, and I'm looking for collisions with an enemy. It works great in every case, except for the case when: the last fired bullet (there has to be more than one) collides with the enemy.
Code-
for(std::vector<Bullet*>::iterator it = bullets.begin(); it != bullets.end(); ++it)
{
if ((*it)->getSprite()->getGlobalBounds().intersects(enemy->getSprite()->getGlobalBounds()))
{
delete *it;
bullets.erase(it);
enemy->destroy();
if (bullets.size() == 0)
break;
}
}
I commented particular elements in the for loop, and found out that the bullet.erase(it) call crashes the program.
When that crash happens, I receive a return code: 134 (0x86). What's the issue for that code?
(*it)->getSprite() returns a pointer to a sprite from Bullet class.
What about using remove_if and erase combo:
auto is_hit = [&enemy](Bullet *bullet)
{
if (bullet->getSprite()->getGlobalBounds().intersects(enemy->getSprite()->getGlobalBounds()))
{
delete bullet;
enemy->destroy();
return true;
}
return false;
};
bullets.erase(std::remove_if(bullets.begin(), bullets.end(), is_hit), bullets.end());
For your consideration:
The following code snippet shows how I clean a vector from its tail (the complementary action to adding element to the tail with push_back())
while(!gBoard.empty())
{
Cell_t* cell = gBoard.back(); // fetch last element (a ptr)
gBoard.pop_back(); // remove last element
delete cell; // remove cell from heap - raw pointer
}
Perhaps you could do this style of clean and use multiple vectors ... it still might be faster than alternatives.
In your problem, each bullet appears to have at least two destinations ... hit or miss.
while ( ! Bullets.empty() ) // spin through bullet list
{
Bullet* aBullet = Bullets.back(); // fetch copy of last element
Bullets.pop_back(); // remove last element
if (*aBullet)-> getSprite()->getGlobalBounds().
intersects(enemy->getSprite()->getGlobalBounds()))
{
// HIT!
Hit.push_back(aBullet); // capture the element to Hit bucket
enemy->destroy(); // tbd - a decision? or always final?
// no delete
if (bullets.size() == 0) // no more to compute, redundant to while
break;
}
else
{
// MISS
Missed.push_back(aBullet); // capture element to Missed bucket
}
} // while
assert(bullets.empty()); // bullets have been consumed
// clean up spent bullets that intersected
while (! Hit.empty() )
{
Bullet* aBullet = Hit.back(); // copy last element from Hit
Hit.pop_back(); // remove last element from Hit
delete aBullet; // tbr - delete the dynamic memory
}
// clean up spent bullets that missed
// move the bullet from Missed vec back into Bullets vec
// for tbd - furthur evaluation ... did the bullet hit any other obj
// the following also happens to 'undo' the seq reversal
while (! Missed.empty() )
{
Bullets.push_back (Missed.back()); // copy last element from Missed
Missed.pop_back(); // remove last element from Missed
// tbd - also delete the missed bullet?
// or do you check for these bullets to collide with other objects
}
// possibly a copy can do this last loop, but this is simple and
// undoes the reversal.
and found out that the bullet.erase(it) call [for that last element]
crashes the program
In some sense, you are probably performing the erase prematurely.
Consider the following:
It is possible that the test parameters of range, target, and weapon-type might combine to achieve, for example, a 10% hit ratio. Thus, in a collection of 1000 shots, (1000 == bullets.size()), there would be (~) 100 bullets that have hit the target.
Your code finds each of the elements, and creates 100 'holes' in the vector by using bullets.erase(). Because the vector data is maintained contiguous, the erase method also moves other elements to fill the holes created by erase. (The details of how might differ between implementations.)
Generally, 100 erases causes 100 shuffles of less than (at-most) 1000 elements each time ... this one-at-a-time-approach would probably be a comparatively 'slow' process.
As an alternative to the current design, instead of find-and-erase, defer the erase until your code has identified and marked all 'intersects'.
you can find the intersects (hits) in the same way, but 'mark' them, don't erase them yet. Options include adding a bool to the Bullet class, or maintaining a matching bool vector to hold this flag for each bullet.
Using two indexes,
-- i1 initialized to 0 (first (left most) vector element) and
-- i2 initialized to (bullets.size() - 1) [last (right most) vector element]
-- Spin increment i1 to find the first hit,
-- Spin decrement i2 to find the last miss,
-- then std::swap (bullets[i1], bullets[i2])
Repeat until i1 >= i2
Now that all the hits are contiguous AND at the tail of the vector, perform a single erase of the 100 hits
This should eliminate any shuffling.
Also, there should be no use of an erased element ... because the erase occurs at the end of the process.

Vector iterators incompatible... but why?

I receive the message "Vector iterators incompatible". I tried to wrap my head around it, but nothing. I did it before. Same code, just not used in a class that receives "cWORLD* World". What am I doing wrong?
Thank you!
else if (Click[2] == true)
{
//go through objects and check collision
for (vector<cOBJECT*>::iterator it = World->ReturnWorldObjects().begin(); it != World->ReturnWorldObjects().end();)
{
//Check for collision and delete object
if (PointInRect(MouseX + offX, MouseY + offY, (*it)->getrect()) == true)
{
// delete object, delete slot, pick up next slot
delete *it;
it = World->ReturnWorldObjects().erase(it);
}
else
{ // no action, move to next
++it;
}
}//for
}//else if (Click[2] == true)
Looks like ReturnWorldObjects returns copy of vector, not reference. In this case, you are trying to compare iterators of different objects, that is not checked by standard, but can be checked by checked iterators (in this case, I think it's MSVC checked iterators).
Like #ForEveR already mentioned, you possibly return a copy of a vector in the function ReturnWorldObjects(). Without seeing the declaration of this method I can only assume it's something like vector<cOBJECT*> ReturnWorldObject();
You can come around this with 2 Solutions, I think:
1. Return a reference to the vector in your World Class
const vector<cOBJECT*>& ReturnWorldObjects()
{
return m_vecWorldObjects; // Your vector here
}
2. Get one copy of that function and use that in your code
...
vector<cOBJECT*> worldObjects = World->ReturnWorldObjects();
for (vector<cOBJECT*>::iterator it = worldObjects.begin(); it != worldObjects.end(); it++)
{
...
}
...

Iterators in event manager for game becoming invalidated upon unregister

I am writing an EventManager class for a game I am making for school. It utilizes a double queue system for events and holds an unordered_map consisting of events types and vectors of ID's registered to respond to the particular events. The problem I am having is that certain events (like when you click the play button for the main menu)may cause an actor to unregister events. While this is intentional it causes problems with the iterators which is not intentional. In the example where it switches from the main menu to the game it destroys all the actors(buttons and such) that are part of the main menu. These, in turn, unregister themselves with the event manager which deletes their id from the corresponding vector stored in the unordered_map and invalidates the iterator throwing an exception at the beginning of the loop. As such no event can cause an actor to unregister anything. This is undesirable because some trigger objects in the game may be one time triggers at which point they need to unregister themselves from receiving more events. Any help or ideas would be greatly appreciated.
void EventManager::Flush()
{
Event* current;
while (m_eventList[m_current].size() > 0)
{
current = m_eventList[m_current].front();
m_eventList[m_current].pop_front();
unordered_map<string, vector<IDTYPE>>::iterator it = m_registeredEvents.find(current->GetType());
if (it == m_registeredEvents.end())
continue;
vector<IDTYPE>* toProcessAct = &(it->second);
vector<IDTYPE>::iterator actIt = toProcessAct->begin();
while (actIt != toProcessAct->end()) //this becomes invalid
{
Actor* temp = ACTORS->GetActor(*actIt);
if (temp == NULL)
actIt = toProcessAct->erase(actIt);
else
{
actIt++;
temp->Process(current); //Because this may unregister events
}
}
delete current; current = 0;
}
Swap();
}
void EventManager::UnregisterEvent(string Event, IDTYPE actor)
{
unordered_map<string, vector<IDTYPE>>::iterator it = m_registeredEvents.find(Event);
//Found
if (it != m_registeredEvents.end())
{
//Find if actor is already registered
vector<IDTYPE>::iterator actIt = it->second.begin();
while (actIt != it->second.end())
{
if (actor == *actIt)
{
it->second.erase(actIt);
return;
}
actIt++;
}
}
}
This is a good way to shoot yourself in the foot
Event* current; // not the issue here but you should always initialize your variables.
while (m_eventList[m_current].size() > 0) {
current = m_eventList[m_current].front(); // current points the the first element
m_eventList[m_current].pop_front(); // now the first element gets destroyed
unordered_map<string, vector<IDTYPE>>::iterator it = m_registeredEvents.find(current->GetType()); // its pure coincident if this works.
pop_front() destroys the front element making the pointer invalid and you enter undefined behaviour. Make a copy of the record instead, then you also don't have problems with destroying it.
Further all iterators to elements after the a delete are invalid, so if your code saves iterators as a state you will get hurt, just presume all deletes makes all iterators to that or related containers invalid to be safe.
If you are running a multi-threaded program you have the further problem that other threads could change the content, use at least a mutex to protect it.
OK, now for what you actually asked for:
while (actIt != toProcessAct->end()) { // end() should update but actIt doesn't.
Actor* temp = ACTORS->GetActor(*actIt);
if (temp == NULL)
actIt = toProcessAct->erase(actIt);
else {
actIt = temp->Process(actIt, current); // make Process return the new valid It
}
}
delete current; // would fail as there is a continue higher up!!!
current = 0; // if using C++11 use nullptr instead else NULL
Make Process return the new valid Iterator or toProcessAct->end() if no more are valid after actIt.
One solution can be to iterate over a copy of event handlers vector, that is instead of
vector<IDTYPE>* toProcessAct = &(it->second);
write
vector<IDTYPE> toProcessAct = it->second;
(and make corresponding changes for the code to compile).
Also probably it will be faster if you make a copy of the whole m_registeredEvents in the start of Flush(), so you won't have to copy vectors several times.
Another solution would be to make unregistration deferred, that is UnregisterEvent() instead of executing instantly would enqueue the unregistration request into separate queue and execute it later, for example at the end of Flush().

Vector Collision

I am quite green regarding vectors, and this is my first time actually using them for collision checking. This is for my project, and I am stumped on how to implement the collision. The current Collision check and response codes I have seem to be ... bad design.
This is my code:
for(auto it = ArrayofEntities.begin(); it != ArrayofEntities.end(); it++)
{
CEntity * go = (*it);
for(auto i = ArrayofEntities.begin(); i != ArrayofEntities.end();)
{
//Collision for entities. Collision Event returns the iterator after an element is erased.
CEntity * other = (*i);
if (go != other)
{
if (!theCollision.CheckCollision(go, other, false, false, false, false)) //Checks if it has collided go with other
{
i = go->CollisionEvent(*other, ArrayofEntities); //Run collision code, setting i to the iterator which is returned.
//break;
}
else
{
i++;
}
}
else
{
i++;
}
}
}
CEntity is the base class for all the entities.
My CheckCollision just returns a true or false on the collision, and my collision event runs the collision and returns an iterator (because I might have to destroy things in the vector).
My collision event is below
vector<CEntity*>::iterator bullet::CollisionEvent(CEntity &other, vector<CEntity*> & theArray)
{
case ZOMBIE:
{
other.hp -= power * 0.01;//Effect
int Counter, index, bulletindex;
auto it = theArray.begin();
//Find the bullet and the other in the array.
for (it = theArray.begin(), Counter = 0; it != theArray.end();it++, Counter++)
{
CEntity *go = NULL;
go = (*it);
if (go == &other)
{
index = Counter;
}
if(go->ID == BULLET && go->GetX() == GetX() && go->GetY() == GetY())
{
bulletindex = Counter;
}
}
this->~bullet();//Delete the bullet
theArray.erase(theArray.begin() + bulletindex);
if(other.hp <= 0)
{
other.~CEntity();
it = theArray.erase(theArray.begin() + index); //delete from array.
return it;
}
it = theArray.begin() + index;
return it;
}
}
I have basically done this like how I would do an array. Just check it against itself. The error it gives is "Vector Iterator not Incrementable", on the first for loop after the collision event has been run.
So my question: 1) What am I doing wrong?
2) Is my thinking to do this like checking arrays wrong?
This is my school project, so I have full control of the codes.
I would prefer to have a quick fix over a complete rewriting of all the collision codes, but if it really comes down to it, I will rewrite my codes.
If you look at the implementation of std::remove_if, you'll see that they've solved the issue of iterator invalidation in another way. instead of erasing elements, they move them to the end of the array.
This may be the easiest solution for you as well. Keep an iterator which points after the last "live" entirty. It starts out at .end but as bullets hit things, you swap the entities to the back of your range and decrement that last-live iterator.
Then, when you're done looping over your array, you clean up with a single call to .erase.
And yes, you should use either std::unique_ptr<CEntity> or std::shared_ptr<CEntity> in the collection. In that way, .erase won't just erase the pointer but also the object pointed to.