Explosions in Box2D

    My telegram channel: https://t.me/winc0de .
    In this article, we will look at several types of explosions in the Box2D physics engine.
    Explosion simulation comes down to finding bodies that are within the radius of the blast wave and applying force to them to push them away from the center of the explosion.

    We will consider three types of explosions of varying complexity:
    • Finding bodies in an explosion radius
    • Raycast - finding bodies in a radius of rays
    • Particles - the spread of many small bodies from the epicenter of an explosion


    In the case of particles, we do not need to look for bodies and somehow directly influence them: the physical engine will do everything for us. For verification, 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 technical details and formulas (for example, how the force changes relative to the distance from the point of explosion). An explosion causes a limited volume of gas to expand until the ambient air pressure stabilizes. The pressure should decrease inversely with the square of the radius to the center of the explosion.

    Everything described above can be expressed with 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 an explosion radius


    The simplest method for implementing explosions is to find all the bodies within a certain radius of the explosion relative to its center. A little clarification: we need bodies with their centers of mass within the specified explosion range. For these purposes, Box2D has a 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 such an explosion. The picture shows the bodies that will receive an impulse after the explosion:


    For a start, not bad, 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 stage. It does not fall under 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 an impulse 4 times greater than the body on the left.

    Raycast method


    We can remove all the problems found in the first method, using the rays to search for the 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 is done in order to make it easier to select the number of rays without changing the total strength of the explosion pulse. Let's look at an explosion 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 selected so that the explosion passes through small holes.

    Particle method


    The last method is very different from the first two. Instead of searching for bodies within the explosion zone, we simply create a 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 physical engine. On the other hand, this method is more expensive 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 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 correctly.

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


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


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

    Also popular now: