evanlws evanlws - 1 month ago 6
C++ Question

Box2d destroy all objects in a set

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

Answer

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.

Comments