5

体が移動できる最高速度を制限したい。

問題は、この回答が示唆するようなことをしても、

/* after applying forces from input for example */
b2Vec2 vel = body->GetLinearVelocity();
float speed = vel.Normalize();//normalizes vector and returns length
if ( speed > maxSpeed ) 
    body->SetLinearVelocity( maxSpeed * vel );

たとえば、速度を固定する直前に、体に大きな力を加えている場合はどうなるでしょうか? 直線速度が今のところ maxSpeed に制限されていても、次のタイムステップで Box2D は b2Body::m_force 値を考慮に入れ、実質的に maxSpeed よりも速く体を動かします。

だから私はこれを思いつきました(b2Body::m_forceをパブリックに移動する必要がありました):

if ( speed > maxSpeed ) {
    body->SetLinearVelocity( maxSpeed * vel );
    body->m_force = b2Vec2(0, 0)
}

それでも、これはまだ問題を適切に処理していません。

速度がmaxSpeed よりもわずかに小さいため、条件はヒットしませんが、それでも m_force 値が大きくなりすぎて速度が上がりすぎる場合はどうなるでしょうか?

ポイントは、デルタアキュムレータを使用してステップを踏んでいるため、力が速度にどのように影響するかについて正確な予測を行うことができず、現時点で必要な物理ステップの数がわからないことです。

Box2Dソースコードに位置を統合する前に速度を直接制限する以外に、これを処理する方法はありますか?

4

2 に答える 2

3

この問題を解決するための最初の試みは、上記のコードをループごとではなく、物理サブステップごとに単純に実行することでした。つまり、デルタ アキュムレータがn を実行する必要があることを通知した場合b2World::Step、速度をn回制限することもできます。

// source code taken form above link and modified for my purposes
for (int i = 0; i < nStepsClamped; ++ i)
{
    resetSmoothStates_ ();

    // here I execute whole systems that apply accelerations, drag forces and limit maximum velocities
    // ...
    if ( speed > maxSpeed ) 
         body->SetLinearVelocity( maxSpeed * vel );
    // ...

    singleStep_ (FIXED_TIMESTEP);

    // NOTE I'M CLEARING FORCES EVERY SUBSTEP to avoid excessive accumulation
    world_->ClearForces ();
}

これにより、フレームレートに関係なく一定の速度が得られますが(私の動きがぎくしゃくしていたため、これが私の主な関心事でした)、常にそうであるとは限りません<= maxSpeed. 同じシナリオ: 速度を制限してb2World::Step.

さて、力が次の検証まで一度だけ適用されることがわかっているので、現在の速度に応じて適用される実際の力を簡単に計算できますが、すでに述べた別の簡単な解決策があり、最終的に固執しました。

  1. Box2D\Dynamics\b2Body.h に移動します。
  2. float32 m_max_speedpublic メンバーを追加して初期化し-1.f、最初はどのボディの速度も制限しないようにします。
  3. Box2D\Dynamics\b2Island.cpp に移動します。
  4. 行番号 222 を見つけます。
  5. 次のif 条件を追加します。

    m_positions[i].c = c;
    m_positions[i].a = a;
    
    if (b->m_max_speed >= 0.f) {
        float32 speed = v.Normalize();
        if (speed > b->m_max_speed)
            v *= b->m_max_speed;
        else v *= speed;
    }
    
    m_velocities[i].v = v;
    m_velocities[i].w = w; 
    

これは、上で説明したサブステップがなくても機能しますが、空気抵抗をシミュレートする場合、サブステップごとに抗力を適用すると、フレームレートが変化してもシミュレーションの正確性が保証されることに注意してください。

于 2013-11-03T16:34:10.747 に答える
1

まず、体に力を加えることができる自分で答えてください。Box2D 自体は、接触と重力を介してボディに影響を与える可能性があります。コンタクトは力ではなくインパルスを使用しています。それらを管理するには、コンタクト リスナーを設定し、normalImpulses と tangentImpulsesを変更します。重力は体に大きな影響を与えることはできないと思いますが、b2BodyDef::gravityScale を介して制御することもできます。コードが手動の力を適用している場合、それらを管理するために何らかのプロキシ インターフェイスを導入すると便利な場合があります。

各ステップで box2d がいくつかの速度と位置の反復を行うため、簡単な方法はわかりません。そのため、ステップの開始時に力と衝撃が加えられると、それに応じて位置が変化します。

box2d のソース コードをハッキングせずに、どれほど厳密な速度が得られるか想像できません。ちなみに、悪いバリアントではないと思います。たとえば、Dynamics/b2Island.cpp:219 (b2Island::Solve) で制限を w 変数と v 変数に挿入します。

于 2013-11-01T18:08:26.503 に答える