Box2D C++ tutorials - Explosions

Last edited: July 14 2013


Halt! You should have a good understanding of
the basic tutorials before venturing further.


Explosions


This topic will look at a few ways to do explosions in Box2D. This makes use of a variety of techniques already covered in other topics, so here we'll focus on broader design aspects and the pros and cons of each method rather than look at the actual coding in detail. By now your brain is probably so full of Box2D info that you have to wear earplugs to keep it from falling out anyway.

Simulating an explosion comes down to finding out what bodies are in the vicinity of the blast and applying an impulse to push them away from the blast location. In this topic we will look at three methods of varying complexity:
  • Proximity - find bodies in range
  • Raycast - find bodies in range and line-of-sight
  • Particles - spray a bunch of small bodies outward from the blast location
Actually, in the last of these methods we don't need to find what bodies are in range because the phyics engine does the work for us. Let's take a look at each of these in more detail. We will use this test scene to expose the strengths and weaknesses of each method: Explosions

Applying a blast impulse


For this topic I did not get too technical with any formulas for how pressure (and therefore impulse) changes relative to the distance from the blast point, but we know that the area of a circle increases relative to the square of the radius. An explosion causes a finite amount of gas to expand until the pressure with the surrounding air is equalized, which will be when it takes up the same volume (area for 2d). So the pressure should decrease inversely to the square of the radius... okay maybe I did get a little technical.

Anyway, we could put this in a function like:
1
2
3
4
5
6
7
8
9
10
  void applyBlastImpulse(b2Body* body, b2Vec2 blastCenter, b2Vec2 applyPoint, float blastPower) {
      b2Vec2 blastDir = applyPoint - blastCenter;
      float distance = blastDir.Normalize();
      //ignore bodies exactly at the blast point - blast direction is undefined
      if ( distance == 0 )
          return;
      float invDistance = 1 / distance;
      float impulseMag = blastPower * invDistance * invDistance;
      body->ApplyLinearImpulse( impulseMag * blastDir, applyPoint );
  }
There are some other considerations to take into account, such as limiting the impulse to a maximum value etc, but these have been left out here for clarity. Check the source code below for details.


Proximity method


The simplest approach is to find all bodies within a certain distance of the blast location. To define this a little more, we want bodies with their center of mass within range. We can use an area query to efficiently find all bodies with a fixture in the area around the blast point, and then do a simple distance check to see if their center of mass is close enough.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
  //find all bodies with fixtures in blast radius AABB
  MyQueryCallback queryCallback; //see "World querying topic"
  b2AABB aabb;
  aabb.lowerBound = center - b2Vec2( blastRadius, blastRadius );
  aabb.upperBound = center + b2Vec2( blastRadius, blastRadius );
  m_world->QueryAABB( &queryCallback, aabb );
  
  //check which of these bodies have their center of mass within the blast radius
  for (int i = 0; i < queryCallback.foundBodies.size(); i++) {
      b2Body* body = queryCallback.foundBodies[i];
      b2Vec2 bodyCom = body->GetWorldCenter();
      
      //ignore bodies outside the blast range
      if ( (bodyCom - center).Length() >= m_blastRadius )
          continue;
          
      applyBlastImpulse(body, center, bodyCom, blastPower );
  }
Now let's look at the results this gives us. Here is a screenshot showing which bodies in the test scene will be given an impulse by this method: Explosions This is a pretty good start, but there are a few problems. The most obvious is that the blast can travel through the platforms, the ground, and other obstructing objects. Another problem is that the large body on the far left is in the blast range but it is not affected because its center of mass is not close enough. How about this: Explosions The objects on each side of the explosion have the same mass, and they also present the same surface area to the blast, but those on the right will collect four times as much impulse. One more: Explosions These two frames are from just before and just after the blast impulse is applied. Because the impulse is applied at the center of mass of the bodies, no torque is caused which looks odd. Still, a simple implementation like this would be fine for many situations.


Raycast method


We can improve on this substantially by using raycasts to find which bodies to interact with instead of a simple distance check. Check out the topic on ray casting and the related section in the world querying topic for implementation details.
1
2
3
4
5
6
7
8
9
10
11
  for (int i = 0; i < numRays; i++) {
      float angle = (i / (float)numRays) * 360 * DEGTORAD;
      b2Vec2 rayDir( sinf(angle), cosf(angle) );
      b2Vec2 rayEnd = center + blastRadius * rayDir;
  
      //check what this ray hits
      RayCastClosestCallback callback;//basic callback to record body and hit point
      m_world->RayCast(&callback, center, rayEnd);
      if ( callback.m_body ) 
          applyBlastImpulse(callback.body, center, callback.point, (m_blastPower / (float)numRays));
  }
Notice we divide the blast magnitude by the number of rays - this is just to make it easier to adjust the number of rays and without changing the overall blast effect. Let's see how this goes in the test scene with 32 rays: Explosions Good, no magical blasting through solid walls anymore, so the boxes above the platforms are correctly not affected. The second case is also much improved because the surface area of each side is taken into account: Explosions Finally, because the impulses are being applied at non-symmetrical points where rays intersect the fixtures, they tend to cause some torque to spin the bodies in a more interesting way. Explosions The number of rays can be adjusted to allow the blast to reach between smaller gaps, and to balance CPU usage with the quality of result.


Particle method


The last method we'll try in this topic is somewhat different to the first two. Instead of checking what bodies are around the blast point, we'll just create a bunch of small bodies to simulate quickly expanding air particles, and let them fly. This is a closer simulation of what actually happens in an explosion so we can expect it to look more realistic.

Not only does this method gives great results, but it's also easy to implement because most of the work is done by the physics engine. On the other hand, more work done by the engine means more CPU time used. Here is a typical example of how you might set up these particle bodies:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
  for (int i = 0; i < numRays; i++) {
      float angle = (i / (float)numRays) * 360 * DEGTORAD;
      b2Vec2 rayDir( sinf(angle), cosf(angle) );
  
      b2BodyDef bd;
      bd.type = b2_dynamicBody;
      bd.fixedRotation = true; // rotation not necessary
      bd.bullet = true; // prevent tunneling at high speed
      bd.linearDamping = 10; // drag due to moving through air
      bd.gravityScale = 0; // ignore gravity
      bd.position = center; // start at blast center
      bd.linearVelocity = blastPower * rayDir;
      b2Body* body = m_world->CreateBody( &bd );
  
      b2CircleShape circleShape;
      circleShape.m_radius = 0.05; // very small
  
      b2FixtureDef fd;
      fd.shape = &circleShape;
      fd.density = 60 / (float)numRays; // very high - shared across all particles
      fd.friction = 0; // friction not necessary
      fd.restitution = 0.99f; // high restitution to reflect off obstacles
      fd.filter.groupIndex = -1; // particles should not collide with each other
      body->CreateFixture( &fd );
  }
That's a lot of code just to create a body and add a fixture! But most of it is simply setting the necessary properties, because unlike most situations we have seen so far, many of the default properties are not what we want. The comments there should be self-explanatory... some things such as the friction and fixed rotation are not strictly necessary, but since there will be a lot of these particles it may help to do anything we can to reduce the calculation required for each of them.

The effects of this are difficult to show in static screenshots, so here is an animated gif showing the trail left by each particle. This shows 24 frames after the explosion, or about half a second: Explosions It's quite obvious that this method has all the advantages of the raycast method, plus a few more. Now we have a real blast 'wave' that reflects off obstacles, allowing the energy of the explosion to correctly get around corners, as in the case of the underground tunnel, and the diagonal plate beside the upper platform. This scattering of energy helps give the impression that the explosion is actually a physical space-occupying presence rather than the telekinetic impulse we have with the other methods. It also gives us something to base rendering (smoke textures etc) on.

You can vary the weight of the particles, their initial velocity, linear damping, restitution, and of course the number of them, to get just the right feel. The particles require some management to clear them after the explosion is finished, but it's not a lot of work. The only real drawback to this method is the CPU time required. Mobile devices may struggle a little with multiple explosions of a high number of particles, but for regular computers even 100 or more (128 in the screenshot below) is not a problem (at least with C++). Explosions Another nice side-effect is that the energy from the blast does not affect everything instantaneously - it takes a tiny bit longer for the particles to reach objects that are further from the blast center. It's only a few milliseconds of difference but those milliseconds do seem to add a touch of realism. For instance looking at the block stack immediately to the right of the explosion center, we can see the blast work its way up the stack over a few timesteps.

Finally, this method ensures that in tight spaces the expected energy does not just disappear. The particles can bounce around and hit many different surfaces before they dissipate. Explosions

Source code


Here is the source code for those who would like to try it out for themselves. This is a 'test' for the testbed, based on Box2D v2.3.0.

Testbed test: iforce2d_Explosions.zip

Linux 32-bit binary
Linux 64-bit binary
Windows binary
MacOSX binary

RUBE file for the test scene: explosionsRUBEScene.zip

YouTube video