实际上限制Box2D的最大速度

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

假设在限制速度之前,我对物体施加了巨大的力量,怎么办呢?即使线性速度此刻被限制为最大速度,下一个时间步骤中,Box2D将考虑b2Body::m_force值,并有效地使我的物体移动得比最大速度更快。
因此,我想到了这个解决方案(不得不将b2Body::m_force移到公共位置):
if ( speed > maxSpeed ) {
    body->SetLinearVelocity( maxSpeed * vel );
    body->m_force = b2Vec2(0, 0)
}

然而,这仍然不能很好地解决问题。

如果速度略小于最大速度,则条件不会被触发,但m_force值仍将足够大,以致于增加速度过多?

关键是我无法准确预测力量如何影响速度,因为我使用delta累加器进行步进,并且我不知道需要多少物理步骤才能到达目标。

除了在Box2D源代码中直接限制积分位置之前的速度之外,还有其他处理方式吗?


1
为什么不只是把施加的力相加,而是每一步都只施加到您首选的最大值(并通过最大值减少施加的力)? - Wilbert
为什么不在时间步骤之后进行夹紧呢? - iforce2d
2个回答

3

我第一次尝试解决这个问题是通过简单地执行以上代码片段,而不是每个循环,而是每个物理子步骤,这意味着如果我的delta accumulator 告诉我必须执行nb2World :: 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 ();
}

现在,这使我保持恒定的速度,而不受帧率的影响(这是我的主要关注点,因为我的移动很抖动),但并不总是小于或等于最大速度。同样的情况:想象一下,在限制速度并执行b2World :: Step之前施加了巨大的力量。
现在,我可以根据当前速度计算应该施加的实际力量,因为我知道这个力量只会被施加一次,直到下一次验证,但是还有另一个简单的解决方案,我已经提到并最终坚持使用:
  1. Go to Box2D\Dynamics\b2Body.h
  2. Add float32 m_max_speed public member and initialize it with -1.f so initially we don't limit velocities for any body.
  3. Go to Box2D\Dynamics\b2Island.cpp.
  4. Locate line number 222.
  5. Add following if condition:

    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; 
    

即使没有我上面描述的子步骤,这也可以正常工作,但请记住,如果您要模拟空气阻力,每个子步骤应用阻力力将确保模拟的正确性,即使帧速率不同。


我喜欢解决方案1。嗨,Patryk,使用substep之前是否需要设置一些选项?例如设置autoClearForces = false等等,还有其他的吗?因为我尝试了几次,似乎失败了,固定时间步长效果不是很好。你有一些演示代码吗?非常感谢! - SexyBoooom

1
首先,你需要自问谁可以对一个物体施加力。Box2D本身可以通过接触和重力影响物体。接触不使用力,而是脉冲。要管理它们,设置联系人侦听器并修改normalImpulses and tangentImpulses。我认为重力不能太大地影响身体,但也可以通过b2BodyDef::gravityScale进行控制。如果您的代码应用了一些手动力,则引入一些代理接口来管理它们可能会有用。
我看不到任何简单的方法,因为在每个步骤中,box2d都会进行几个速度和位置迭代。因此,在步骤开始时施加的力和脉冲将相应地导致位置的变化。
我无法想象严格的速度方式,除非黑客box2d源代码。顺便说一下,我认为这不是一个坏的选择。例如,在Dynamics/b2Island.cpp:219(b2Island::Solve)中插入对w和v变量的限制。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接