在BOX2D 中如何控制body 自然的旋轉到一個指定角度?

這個問題在許多遊戲中控制角度時都會遇到,但是在BOX2D中,你必須考慮到如果轉動中與其他body碰撞等物理因素。

能夠想到的解決方案有三種:

1 在update方法裏不斷更改body的角度,使他接近於要設定的角度。

b2vec2 clickedPoint;//設定點的向量
float bodyAngle = body->GetAngle();//取得物體自身的弧度
b2Vec2 toTarget = clickedPoint - body->GetPosition();//計算角度差
float desiredAngle = atanf( -toTarget.x, toTarget.y );//計算設定的弧度
float totalRotation = desiredAngle - bodyAngle;//計算需要旋轉的弧度
while ( totalRotation < -180 * DEGTORAD ) totalRotation += 360 * DEGTORAD;//調整設定角度到-180到180度之間
while ( totalRotation >  180 * DEGTORAD ) totalRotation -= 360 * DEGTORAD;
float change = 1 * DEGTORAD; //每次間隔允許最大旋轉角度
float newAngle = bodyAngle + min( change, max(-change, totalRotation));
body->SetTransform( body->GetPosition(), newAngle );

 

很明顯,第一種方法並不適合在物理引擎中使用,因爲不斷設定body的角度會打亂BOX2D中的仿真效果。

2 在update方法裏不斷給body施加一個能使body轉到設定角度的力矩。

剛開始肯定會想到這樣做:

float totalRotation = desiredAngle - bodyAngle;

while ( totalRotation < -180 * DEGTORAD ) totalRotation += 360 * DEGTORAD;

while ( totalRotation >  180 * DEGTORAD ) totalRotation -= 360 * DEGTORAD;

body->ApplyTorque( totalRotation);

但是運行看看?你會發現一個問題,物體始終受到一個朝向設定點角度方向的力矩,直到物體轉到這個角度,乍看好像沒問題,但是實際上,物體在到達設定角度時角速度並不是零,所以物體將繼續轉過這個角度,並受到一個反向的力矩,然後到達設定角度後又一次超過設定角度,這樣永遠循環擺動,卻永遠到達不了設定角度。

總結了下原因沒有考慮到自身本身的角速度影響。

真的非常難解釋的非常清楚,可能我也理解的不太夠吧,直接給出解決方案。

方法是假設一定時間間隔dt內body不受任何轉矩,計算出dt間隔後的body角度,用來替換現在的body角度:

 

float dt = 1.0f / 60.0f;
float nextAngle = bodyAngle + body->GetAngularVelocity() *dt;
float totalRotation = desiredAngle - nextAngle;
while ( totalRotation < -180 * DEGTORAD ) totalRotation += 360 * DEGTORAD;
while ( totalRotation >  180 * DEGTORAD ) totalRotation -= 360 * DEGTORAD;
float desiredAngularVelocity = totalRotation / dt;
float torque = body->GetInertia() * desiredAngularVelocity / dt;
body->ApplyTorque( torque );

但是這樣還不是最好的方法,body仍然需要來回晃動數個來回才能最終停下來。

3 在update方法裏不斷給body施加一個能使body轉到設定角度的慣性衝量。

最終的解決方案是通過施加一個衝量,和上面的方法相似:

float dt = 1.0f / 60.0f;
float nextAngle = bodyAngle + body->GetAngularVelocity() *dt;
float totalRotation = desiredAngle - nextAngle;
while ( totalRotation < -180 * DEGTORAD ) totalRotation += 360 * DEGTORAD;
while ( totalRotation >  180 * DEGTORAD ) totalRotation -= 360 * DEGTORAD;
float desiredAngularVelocity = totalRotation / dt;
float impulse = body->GetInertia() * desiredAngularVelocity;// disregard time factor
body->ApplyAngularImpulse( impulse );

此外,如果你想在旋轉過程中設定一個最大旋轉速度,可以添加一個change值

 

float dt = 1.0f / 60.0f;
float nextAngle = bodyAngle + body->GetAngularVelocity() *dt;
float totalRotation = desiredAngle - nextAngle;
while ( totalRotation < -180 * DEGTORAD ) totalRotation += 360 * DEGTORAD;
while ( totalRotation >  180 * DEGTORAD ) totalRotation -= 360 * DEGTORAD;
float desiredAngularVelocity = totalRotation / dt;
float change = 1 * DEGTORAD; //allow 1 degree rotation per time step
desiredAngularVelocity = min( change, max(-change, desiredAngularVelocity));
float impulse = body->GetInertia() * desiredAngularVelocity;// disregard time factor
body->ApplyAngularImpulse( impulse );

 

至此,基本完美的解決了這個問題,並且可以通過調整dt的值,可以實現不同精度的旋轉body到指定角度,通過調整change的值,改變旋轉的最大速度。

對應的,此方法還可以實現控制body自然的加速到一個指定速度,我將在下次詳細講下方法。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章