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

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



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



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

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++).

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.

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