余弦加速度曲线的潜在浮点问题

时间:2013-12-19 13:46:05

标签: math floating-point unity3d game-physics

我正在使用余弦曲线对范围[0,pi]之间的对象施加力。根据我的计算,这应该给出一个速度的正弦曲线,t=pi/2的速度应该是1.0f

然而,对于最简单的例子,我的最高速度为0.753。

现在如果这是一个浮点问题,那很好,但这是一个非常重要的错误,所以我很难接受它(如果是的话,为什么会有这么大的错误来计算这些值)。

一些代码:

// the function that gives the force to apply (totalTime = pi, maxForce = 1.0 in this example)
return ((Mathf.Cos(time * (Mathf.PI / totalTime)) * maxForce));


// the engine stores this value and in the next fixed update applies it to the rigidbody
// the mass is 1 so isn't affecting the result
engine.ApplyAccelerateForce(applyingForce * ship.rigidbody2D.mass);

更新

没有重力应用于对象,世界上没有其他对象可以与之交互,也没有拖动。我也使用RigidBody2D,因此对象只在飞机上移动。

更新2

好的尝试了一个非常简单的例子,我得到了我期待的结果,所以我的代码中必定有一些东西。一旦我发现了不同的东西,我会更新。

为了记录,超级简单的代码:

float forceThisFrame;
float startTime;

// Use this for initialization
void Start () {
    forceThisFrame = 0.0f;
    startTime = Time.fixedTime;
}

// Update is called once per frame
void Update () {
    float time = Time.fixedTime - startTime;

    if(time <= Mathf.PI)
    {

        forceThisFrame = Mathf.Cos (time);

        if(time >= (Mathf.PI /2.0f)- 0.01f && time <= (Mathf.PI /2.0f) + 0.01f)
        {
            print ("Speed: " + rigidbody2D.velocity);
        }
    }
    else
    {
        forceThisFrame = 0.0f;
    }
}

void FixedUpdate()
{
    rigidbody2D.AddForce(forceThisFrame * Vector2.up);
}

更新3

我已经更改了我的原始代码以匹配上面的示例尽可能接近(下面列出的其余差异)并且仍然存在差异。

这里are my results of velocity against time。它们都不会对我有意义,使用1N的恒定力,这应该导致线性速度函数v(t) = t,但这不是任何一个例子产生的。

剩余差异:

  • “计算”力(现在只返回1)的代码正在通过非统一DLL运行,尽管代码本身位于Unity DLL中(可以解释更多,但不能相信这是相关的! )
  • 将力施加到刚体上的行为是一种单独的行为。
  • 一个是在一个空的环境中移动一个立方体,另一个是移动Model3D并且附近有一个平面 - 在破碎的项目中尝试了一个具有相同代码的立方体,同样的问题

除此之外,我看不出任何差异,我当然也看不出为什么会有这些影响。它们都在每次固定更新时对对象施加1的力。

2 个答案:

答案 0 :(得分:2)

对于余弦情况,这不是一个浮点问题,本身就是一个集成问题。

[在你的'固定'加速情况下,显然也存在轻微的浮点问题。)

显然,加速度与力成正比( F = ma ),但你不能简单地添加加速度来获得速度,特别是如果帧之间的时间间隔不恒定。 / p>

通过假设帧间加速度不变来简化事情,因此遵循 v = u + at (或者 ∂v = a.∂t ),你需要缩放加速度的效果与自最后一帧以来经过的时间成比例。因此,较小的 ∂t ,您的整合就越准确。

答案 1 :(得分:0)

这是一个多部分问题,首先我没有完全理解Unity中的UpdateFixedUpdate,有关该部分的更多信息,请参阅this question on GameDev.SE

我的“修复”就是推进了一个带有固定更新的计时器,以便不会施加错误的力量。正如Eric Postpischil所证明的那样,问题是因为FixedUpdate,尽管它的名字,并不是每0.02s调用一次,而是最多每0.02s调用一次。对此的修复是,在我的更新中,将一些缩放应用于强制应用以适应错过的固定更新。我的代码最终看起来像:

从更新中调用

float oldTime = time;
time = Time.fixedTime - startTime;
float variableFixedDeltaTime = time - oldTime;
float fixedRatio = variableFixedDeltaTime / Time.fixedDeltaTime;

if(time <= totalTime)
{
    applyingForce = forceFunction.GetValue(time) * fixedRatio;

    Vector2 currentVelocity = ship.rigidbody2D.velocity;
    Vector2 direction = new Vector2(ship.transform.right.x, ship.transform.right.y);
    float velocityAlongDir = Vector2.Dot(currentVelocity, direction);
    float velocityPrediction = velocityAlongDir + (applyingForce * lDeltaTime);

    if(time > 0.0f && // we are not interested if we are just starting
       ((velocityPrediction < 0.0f  && velocityAlongDir > 0.0f ) ||
       (velocityPrediction > 0.0f  && velocityAlongDir < 0.0f ) ))
    {
        float ratio = Mathf.Abs((velocityAlongDir / (applyingForce * lDeltaTime)));
        applyingForce = applyingForce * ratio;

        // We have reversed the direction so we must have arrived
        Deactivate();
    }

    engine.ApplyAccelerateForce(applyingForce);
}

ApplyAccelerateForce在哪里:

public void ApplyAccelerateForce(float requestedForce)
{
    forceToApply += requestedForce;
}

从FixedUpdate调用

rigidbody2D.AddForce(forceToApply * new Vector2(transform.right.x, transform.right.y));
forceToApply = 0.0f;