原文链接: http://www.iforce2d.net/b2dtut/explosions
这篇是个有趣的主题。
Explosion:
这篇文章会找一些方法去实现box2d的爆炸效果,这个主题覆盖很多以前我们学过的东西,所以我们先不细看代码部分,先看实现的思想。现在你的脑袋里面充满了box2d的东西,你要戴上耳机来集中精力。
为了模拟爆炸效果,我们必须找到有哪些刚体在爆炸范围内,然后给它们一个冲量,让它们远离爆炸中心。
下面三个方法都可以模拟爆炸效果,但是难易是不同的:
1.用距离的方法来找到爆炸区域内的刚体
2.用射线的方法找到视野内的刚体
3.从爆炸中心点发射很多微小的刚体粒子
实际上最后一种方法我们不用找到爆炸范围内的刚体,因为物理引擎已经为了我们做了,让我们来看看上面三种方法的细节,而且用下面这篇图片来显示三种方法的供给
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 );
}
这是个相当好的开始,但是还是有很多问题的,最明显的问题是爆炸会穿透障碍物,另一个问题是左边最大的物体在这个爆炸区域内,但是它的质心并不在爆炸区域内:
两边的物体有着相同的质量,并且表示相同的受爆炸的区域,但是右边会受到四倍的爆炸力量,还有下面的情况:
这样的爆炸只会产生推力,却不会产生扭矩,这实在看起来有点奇怪,虽然这种办法有种种缺点,但是很多不是有特殊要求的情况下,还算是不错的解决方案。
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));
}
相当不错,现在没有别扭的爆炸力了,所以现在爆炸不会穿透阻碍物了,第二种情况也得到了很好的改善,因为每个边的表面区域都计算进来了。
最后,冲量施加在射线跟夹具的交点上,而这些交点又不是对称的点,所以这样会产生扭矩,从而会旋转物体而产生更有趣的效果
而射线的个数你可以做一个平衡的处理,因为太多会影响cpu的使用,而太少也会忽略一些细小的物体