box2d- explosion

原文链接: http://www.iforce2d.net/b2dtut/explosions

这篇是个有趣的主题。

Explosion:

     这篇文章会找一些方法去实现box2d的爆炸效果,这个主题覆盖很多以前我们学过的东西,所以我们先不细看代码部分,先看实现的思想。现在你的脑袋里面充满了box2d的东西,你要戴上耳机来集中精力。

    为了模拟爆炸效果,我们必须找到有哪些刚体在爆炸范围内,然后给它们一个冲量,让它们远离爆炸中心。

    下面三个方法都可以模拟爆炸效果,但是难易是不同的:

    1.用距离的方法来找到爆炸区域内的刚体

    2.用射线的方法找到视野内的刚体

    3.从爆炸中心点发射很多微小的刚体粒子

    实际上最后一种方法我们不用找到爆炸范围内的刚体,因为物理引擎已经为了我们做了,让我们来看看上面三种方法的细节,而且用下面这篇图片来显示三种方法的供给

box2d- explosion_第1张图片


Applying a blast impulse

    在这段里面我不会讲太多的距离跟爆炸力大小的公式,但是我们知道的是爆炸范围的大小会逐渐增大的,

爆炸产生一个强气流向外扩散,直到它跟普遍大气压相等。

总之,我们可以产生下面的公式:

  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

    最简单的实现方法是以爆炸点为中心,以一个给定的半径来找出在这个圆中所有的半径。我们可以用area query的方法去高效地找到所有的被包含的刚体。

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

下面让我们来看看结果,下面是个截图,显示哪些刚体需要被施加一个冲量。

box2d- explosion_第2张图片

这是个相当好的开始,但是还是有很多问题的,最明显的问题是爆炸会穿透障碍物,另一个问题是左边最大的物体在这个爆炸区域内,但是它的质心并不在爆炸区域内:

box2d- explosion_第3张图片

两边的物体有着相同的质量,并且表示相同的受爆炸的区域,但是右边会受到四倍的爆炸力量,还有下面的情况:

box2d- explosion_第4张图片


这样的爆炸只会产生推力,却不会产生扭矩,这实在看起来有点奇怪,虽然这种办法有种种缺点,但是很多不是有特殊要求的情况下,还算是不错的解决方案。


Raycast method

    我们可以用现在这种方法来改善上个方法的不足,用射线来找到与爆炸区域有相交的刚体,你可以去ray casting跟world querying主题看更多的细节。

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

需要注意的是:我们分割爆炸来产生一定数据的射线,这样会更容易实现,我们先用32根射线来模拟:

box2d- explosion_第5张图片


相当不错,现在没有别扭的爆炸力了,所以现在爆炸不会穿透阻碍物了,第二种情况也得到了很好的改善,因为每个边的表面区域都计算进来了。

box2d- explosion_第6张图片

最后,冲量施加在射线跟夹具的交点上,而这些交点又不是对称的点,所以这样会产生扭矩,从而会旋转物体而产生更有趣的效果

box2d- explosion_第7张图片

而射线的个数你可以做一个平衡的处理,因为太多会影响cpu的使用,而太少也会忽略一些细小的物体

你可能感兴趣的