ぬうぱんの備忘録

技術系のメモとかいろいろ

作った曲一覧はこちら

Box2Dでb2Bodyに力とかを加えるいくつかの方法

この記事は

Box2Dで物体に力とかを加えようと思った時に取ることが出来る方法のまとめです。自分は古典物理すら全くわからないマンなので必要な範囲で関連する知識とかもまとめます。

SetLinearVelocity()

ボディの速度(velocity, [m/s])を直接指定する方法。
加速度を加えるためには以下の手順を踏む。

    1. ボディの現在の速度ベクトルを得る(GetLinearVelocity())
    2. 速度ベクトルを合成(ベクトル同士の加算)
    3. 合成した速度ベクトルをボディに設定(SetLinearVelocity())

C++コードで書くと以下の要領

b2Body* pMyBody;
b2Vec2 Acc;
pMyBody->SetLinearVelocity(pMyBody->GetLiearVelocity()+Acc);

SetLinearVelocity()で指定するパラメータの単位は[m/s]なので、単位時間当たりの速度の変化であるAccの単位は[m/s^2]となる。つまり、加速度(Acceleration)をボディに適用していることになる。
この方法ではボディの持つ質量を無視している。少々周りくどいが、所望の加速度を得るために適切な力をボディに加えているという見方も出来る。
また、この方法で加速度を与えてもsleep状態のボディはsleepしたままであることに注意。

ApplyImpulse()

ボディに力積(Impulse, [kg{\cdot}m/s])を加える。
C++コードで書くと以下の要領

b2Body* pMyBody;
b2Vec2 Imp;
pMyBody->ApplyImpulse(Imp, pMyBody->GetPosition());

ボディのどこに力積を加えるのかをワールド座標系で指定出来る。
いろいろな量との関係はあるのだが、乱暴にまとめると、運動量(momentum, [kg{\cdot}m/s])に変化を与えていることになる。なので、質量と速度変化量の積を力積として適用すると、加速度を適用したのと同じ効果がある。C++コードで書くと以下の要領。

b2Body* pMyBody;
b2Vec2 Imp;
float32 Mass = pMyBody->GetMass();
Imp.x *= Mass;
Imp.y *= Mass;
pMyBody->ApplyImpluse(Imp, pMyBody->GetPosition());

物体と物体の衝突を表現したいときに使えば良さそう?

ApplyForce()

ボディに力(Force, kg{\cdot}m/s^2)を加える。
C++コードで書くと以下の要領

b2Body* pMyBody;
b2Vec2 Force;
pMyBody->ApplyImpulse(Force, pMyBody->GetPosition());

ApplyImpulse()と同様、ボディの何処に力を加えるのかを指定出来る。
ApplyImpulse()との違いは1ステップ当たりの運動量の変化を指定するという点と、ApplyForce()を呼び出した直後にはLinearVelocityが変化しないという点。ここらへんの詳しい話は後述。

3つの方法の違い

3つの方法の違いをまとめてみる。
まず、以下の3行はほとんど同じ結果をもたらす

b2Body* pMyBody;
b2Vec2 Vec;
pMyBody->SetLinearVelocity(pMyBody->GetLiearVelocity()+Vec);
pMyBody->ApplyImpulse(Vec*pMyBody->GetMass(), pMyBody->GetPosition());
pMyBody->ApplyForce(Vec*pMyBody->GetMass()*60, pMyBody->GetPosition());

ただし、b2Worldのフレームレートは60でるとする。

これらの動作の違いを表にまとめると

方法 sleep復帰 Velocityの変化 変化するパラメータ
SetLinearVelocity no 関数内で m_linearVelocity
ApplyImpulse yes 関数内で m_linearVelocity
ApplyForce yes フレーム更新時 m_force

ApplyImpulse()とApplyForce()の違い

力学の式とBox2Dのコードを並べてにらめっこしてもよくわからないし結果としてどういう違いがあるのかだけまとめると

    • ApplyImpulse(f*(1.f/60.f), ...)はApplyForce(f, ..)と同じ
    • ApplyImpulse()は関数内でボディの持つ速度に変化を与える
    • ApplyForce()はボディの持つ力に変化を与える
    • ApplyForce()の呼び出しによる速度への影響はワールドのステップ更新が行われるまで発生しない
    • ApplyImpulse()は瞬間的な力を与える時に使える
    • ApplyForce()は継続的な力を与える時に使える

感想

なんか難しいっすなぁ