Box2D C++ tutorials - Removing bodies safely

Last edited: July 14 2013

Chinese version -> 中文

Removing bodies safely


Unless you are making a very basic game, you will probably want to remove some bodies from the scene at some point. This could be for example if the player kills an enemy, the bullets they fire need to be cleaned up, or two objects collide and one breaks, etc. The actual code to remove a body is dead simple - you just say world->DestroyBody(b2Body*), and if you are removing the body outside the timestep that's all there is to it. The problem comes when you want to remove a body as a result of something that happened within a timestep, usually a collision callback. If we remind ourselves of the timing of collision callbacks, we see that we cannot remove a body inside a collision callback, because the world is right in the middle of performing a physics step, and removing the data it is working with is a bad idea. Removing bodies safely A very similar situation is encountered if you use threads or timers to remove bodies, for example if you want a body to be removed a few seconds after some event in the game. If the timer fires while the world is stepping, this can also cause problems.

The solution to this is quite simple. When you get a collision callback which means a body should be removed, just add the body to a list of bodies scheduled for removal, and remove them all together after the time step has finished.

Example


As an example, let's set up a demonstration where bodies will be removed from the scene at runtime depending on the collisions between them. We will bring back the friendly smiley faces from the collision callbacks topic to help us with this. In fact, we can pick up right where we left off at the end of that topic. To recap, we had a situation where one ball was red, and when it bounced against another ball the red state would swap between them as if they were playing tag and the red ball was 'it': Removing bodies safely This time, lets imagine that the red ball has caught a deadly virus and is about to die, but not until it has infected one other ball first. As soon as the virus has been passed to another ball by a collision, the first ball with the virus will be removed from the world.

There are only a few small changes we need to make to accomplish this. We'll need a list to store the bodies to remove from the world after the timestep, and the collision callback should be changed to put the already infected ball into that list. To actually delete a ball properly we should add a destructor to the Ball class and inside it, we will remove the physics body from the Box2D world.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
  //at global scope
  std::set<Ball*> ballsScheduledForRemoval;
  
  //revised handleContact function
  void handleContact( Ball* b1, Ball* b2 ) {
    if ( ! outbreak )
      return;
    if ( b1->m_imIt ) {
        ballsScheduledForRemoval.push_back(b1);
        b2->m_imIt = true;
    }
    else if ( b2->m_imIt ) {
        ballsScheduledForRemoval.push_back(b2);
        b1->m_imIt = true;
    }
  }
  
  //implement Ball class destructor
  Ball::~Ball()
  {
    m_body->GetWorld()->DestroyBody( m_body );
  }
The 'outbreak' variable is optional, I just added this as a global variable initially set to false so that I can use the Keyboard function to start the infection spreading when I choose - otherwise the balls are too bunched up in the beginning and most of them get infected and die in the blink of an eye which is no fun. You could also just space them out more to start with I guess, it's up to you.

Now in the Step() function, after calling Test::Step() and before rendering the scene, we'll need to process the entities that got scheduled for removal.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
  //process list for deletion
  std::set<Ball*>::iterator it = ballsScheduledForRemoval.begin();
  std::set<Ball*>::iterator end = ballsScheduledForRemoval.end();
  for (; it!=end; ++it) {
      Ball* dyingBall = *it;
  
      //delete ball... physics body is destroyed here
      delete dyingBall;
  
      //... and remove it from main list of balls
      std::vector<Ball*>::iterator it = std::find(balls.begin(), balls.end(), dyingBall);
      if ( it != balls.end() )
          balls.erase( it );
  }
  
  //clear this list for next time
  ballsScheduledForRemoval.clear();
Now you should see just one red ball in the scene, infecting another ball and dying when it collides with it.

Dealing with the removal bodies as a result of a thread or timing routine is very similar, just gather all the bodies to be deleted in a list, and deal with them when you can be sure that the world is not stepping. One simple method for implementing a 'timed removal', that is for example if you want to wait a certain time before deleting something, is just to calculate how many time steps that would be for your game loop (eg. 2 seconds is 120 time steps at 60fps), set that value in the entity and then decrement it every frame until it gets to zero, then remove it.

By the way, although this topic has focused on removing bodies since that is the most common case to cause problems, the creation of new bodies, and the creation and removal of fixtures must be dealt with in the same way.