Box2Dの爆発

私の電報チャンネル: https : //t.me/winc0de

この記事では、Box2D物理エンジンのいくつかのタイプの爆発について説明します。

爆発シミュレーションは、爆発波の半径内にある物体を見つけ、爆発の中心から離れるように力を加えることで終わります。



さまざまな複雑さの3種類の爆発を検討します。





パーティクルの場合、身体を探す必要はなく、何らかの形で身体に直接影響を与える必要があります。物理エンジンがすべてを行います。 検証のために、以下のシーンを取り上げます。これにより、各タイプの爆発のすべての長所と短所を確認できます。







インパルスアプリケーション



このトピックでは、技術的な詳細や公式(たとえば、爆発点からの距離に応じて力がどのように変化するか)については掘り下げません。 爆発により、周囲の空気圧が安定するまで、限られた量のガスが膨張します。 圧力は、爆発の中心までの半径の2乗に反比例して減少するはずです。



上記のすべては、次のコードで表現できます。

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







爆発半径内の遺体を見つける



爆発を実装する最も簡単な方法は、爆発の中心から特定の半径内にあるすべての物体を見つけることです。 少し明確化:指定された爆発範囲内に重心を持つボディが必要です。 これらの目的のために、Box2DにはQueryAABBメソッドがあります。



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







そのような爆発の結果を見てみましょう。 写真は、爆発後に勢いを受ける体を示しています。





そもそも悪くはありませんが、この方法にはいくつかの問題があります。 それらの最大のもの:爆風はプラットフォームと壁を通過します。 ステージの左側にある大きな体も見てください。 重心が爆発の半径の外側にあるため、爆発の下に落ちません。

次の状況を考慮してください。



爆発の中心の両側の物体は同じ質量を持っていますが、右側の物体は左側の物体の4倍の衝撃を受けます。



レイキャスト法



光線を使用して爆風が相互作用する物体を検索し、最初の方法で見つかったすべての問題を取り除くことができます。



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







爆発の力を光線の数で割ることに注意してください。 これは、爆発パルスの合計強度を変更せずに光線の数を選択しやすくするために行われます。 32の光線を使用して爆発を見てみましょう。



はるかに良い:爆風はプラットフォームを通過しません。 2番目の問題も解決されます。これは、両側の表面全体が考慮されるためです。



爆発が小さな穴を通過するように、光線の数を選択できます。



粒子法



最後の方法は、最初の2つの方法とは大きく異なります。 爆発の領域で死体を検索する代わりに、単純に多数の小さな死体を作成し、異なる方向に発射します。 これは、実際の爆発に関して最も現実的な動作です。

この方法では、良い結果が得られるだけでなく、コードが大幅に簡素化されます。これは、ほとんどの作業が物理エンジンによって行われるためです。 一方、この方法は計算にコストがかかります。



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





ボディを作成してフィクスチャを追加するという理由だけで、多くのコードがあります。





この方法には、レイキャスト方法のすべての利点があることは明らかです。 これで実際の爆風が発生し、これが体から反射され、爆発のエネルギーが障害物を正しく通過できるようになります。



パーティクルの重量、初期加速、摩擦、そしてもちろんパーティクルの数を変更できます。 また、この方法では、爆発後の粒子から世界をきれいにする必要があります。 この方法の唯一の最大のマイナス点は、CPU負荷です。 モバイルプラットフォームでは、いくつかの爆発が問題になりますが、平均的なコンピューターはこのタスクに簡単に対処できます。





別の副作用:爆発の力がすべてのボディに同時に適用されるわけではありません。 粒子の伝播には時間がかかります。





オリジナル(英語)の著者は、Box2DのRUBE物理エディターの著者である私の良き友人iforce2dです。



All Articles