📜 ⬆️ ⬇️

Box2D explosions

In this article we will look at several types of explosions in the physics engine Box2D.
The simulation of an explosion is reduced to finding bodies that are within the range of the blast wave and applying force to them to throw them away from the center of the explosion.

We will consider three types of explosions of varying complexity:


In the case of particles, we do not need to search for bodies and somehow act on them directly: the physics engine will do everything for us. To check, we take the following scene, with which you can see and understand all the pros and cons of each type of explosion:


')

Impulse application


In this topic, I will not delve into the technical details and formulas (for example, how the force varies relative to the distance from the point of explosion). The explosion causes a limited amount of gas to expand until the ambient air pressure stabilizes. The pressure should decrease in inverse proportion to the square of the radius to the center of the explosion.

Everything described above can be expressed by the following code:
void applyBlastImpulse(b2Body* body, b2Vec2 blastCenter, b2Vec2 applyPoint, float blastPower) { b2Vec2 blastDir = applyPoint - blastCenter; float distance = blastDir.Normalize(); //  ,      if ( distance == 0 ) return; float invDistance = 1 / distance; float impulseMag = blastPower * invDistance * invDistance; body->ApplyLinearImpulse( impulseMag * blastDir, applyPoint ); } 


Finding bodies in the blast radius


The simplest method of explosions realization is to find all the bodies inside a certain explosion radius relative to its center. A bit of clarification: we need bodies with their centers of mass within the specified explosion range. For this purpose, Box2D has the QueryAABB method:

 MyQueryCallback queryCallback; b2AABB aabb; aabb.lowerBound = center - b2Vec2( blastRadius, blastRadius ); aabb.upperBound = center + b2Vec2( blastRadius, blastRadius ); m_world->QueryAABB( &queryCallback, aabb ); //        ,         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 ); } 


Let's look at the result of this implementation of the explosion. The picture shows the bodies that will get a boost after the explosion:


Not bad for a start, but there are a couple of problems with this method. The biggest of them: the blast wave passes through platforms and walls. Also look at the large body on the left of the scene. It is not affected by the explosion because its center of mass is outside the radius of the explosion.
Consider the following situation:

Objects on both sides of the center of the explosion have the same mass, but the bodies on the right will receive a pulse 4 times larger than the body on the left.

Raycast method


We can remove all the problems found in the first method, using rays to search for bodies with which the blast wave will interact.

 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; RayCastClosestCallback callback; m_world->RayCast(&callback, center, rayEnd); if ( callback.m_body ) applyBlastImpulse(callback.body, center, callback.point, (m_blastPower / (float)numRays)); } 


Please note we divide the force of the explosion by the number of rays. This was done to make it easier to pick up the number of rays without changing the total force of the impulse of the explosion. Let's look at the blast using 32 rays:

Much better: the blast wave does not pass through the platforms. The second problem is also solved, because the full surface on each side is taken into account:

The number of rays can be chosen so that the explosion passes through small holes.

Particle method


The latter method is very different from the first two. Instead of searching for bodies that are in the blast zone, we will simply create a certain number of small bodies and launch them in different directions. This is the most realistic behavior regarding a real explosion.
This method gives not only good results, but also greatly simplifies the code, because most of the work is done by the physics engine. On the other hand, this method is more costly for calculations.

 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; //   bd.bullet = true; bd.linearDamping = 10; bd.gravityScale = 0; //   bd.position = center; //      bd.linearVelocity = blastPower * rayDir; b2Body* body = m_world->CreateBody( &bd ); b2CircleShape circleShape; circleShape.m_radius = 0.05; //      b2FixtureDef fd; fd.shape = &circleShape; fd.density = 60 / (float)numRays; fd.friction = 0; //   fd.restitution = 0.99f; //    fd.filter.groupIndex = -1; //        body->CreateFixture( &fd ); } 

So much code just because we create the body and add a fixture to it.


It is quite obvious that this method has all the advantages of the raycast method. Now we have a real blast wave, which is reflected from the bodies, allowing the energy of the explosion to pass obstacles properly.

You can change the weight of the particles, their initial acceleration, friction and, of course, their number. This method also requires cleaning the world from particles after an explosion. The only and biggest disadvantage of this method: the load on the CPU. For mobile platforms, multiple explosions will be a problem, but the average computer will easily cope with this task:


Another side effect: the force of the explosion does not apply simultaneously to all bodies. Particle propagation takes some time.


The original author (English) is my good friend iforce2d, author of the physical editor RUBE for Box2D.

Source: https://habr.com/ru/post/238753/


All Articles