实际上限制了Box2D中的最大速度

Actually limiting maximum speed in Box2D

本文关键字:速度 Box2D 实际上      更新时间:2023-10-16

我想限制身体可以行驶的最大速度。

问题是,即使我做这样的事情,这个答案也建议:

/* 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 源代码中集成位置之前直接限制速度之外,还有什么方法可以解决这个问题吗?

我解决这个问题的第一次尝试是简单地执行上面的代码片段,不是每个循环,而是每个物理子步骤,这意味着如果我的增量累加器告诉我必须执行 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_speed公共成员并使用-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; 
    

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

首先,自己回答,谁可以对身体施加力量。Box2D本身可以通过接触和重力撞击物体。接触不是使用力,而是冲动。要管理它们,请设置联系人侦听器并修改正常脉冲和切线脉冲。我认为重力不能对身体产生很大影响,但它也可以通过b2BodyDef::gravityScale进行控制。如果您的代码采用一些手动强制,那么引入一些代理接口来管理它们可能会很有用。

我看不到一些简单的方法,因为每一步 box2d 都会进行几次速度和位置迭代。因此,在步骤开始时施加在其上的力和脉冲将相应地导致位置变化。

我无法想象这种方式,如果不破解 box2d 源代码,速度有多严格。顺便说一句,我认为它不是坏的变体。例如,在 Dynamics/b2Island.cpp:219 (b2Island::Solve) 中插入对 w 和 v 变量的限制。