无论在 Unity 中旋转的角度如何,确保旋转花费相同的时间

Ensure rotation takes the same amount of time regardless of angle to be rotated in Unity

我希望有人能提供帮助。我有一个旋转地球仪的脚本,一架飞机(静止的)从一个国家飞到另一个国家。这一切都很好,并通过以下脚本实现

while (!DestinationReached)
    {
        Vector3 planetToCountry = (Destination.localPosition - Vector3.zero);//.normalized; //vector that points from planet to country
        Vector3 planetToCamera = (camTr.position - tr.position).normalized; //vector that points from planet to camera/plane

        Quaternion a = Quaternion.LookRotation(planetToCamera);
        Quaternion b = Quaternion.LookRotation(planetToCountry);

        b = Quaternion.Inverse(b);

        newRotation = a * b;
        tr.rotation = Quaternion.Slerp(tr.rotation, newRotation, 0.01f);

        if (Approximately(tr.rotation, newRotation, 0.0001f)) //if here the plane has arrived
        {
            Debug.Log("Destination reached");
            DestinationReached = true;
        }
        yield return new WaitForEndOfFrame();
        
    }

它本质上是计算飞机(相机连接到飞机 GO 并从上方查看)与地球仪需要旋转到的目的地之间的角度,以便飞机看起来就像飞到目的地一样。

我遇到的问题是无论地球必须旋转的角度如何,我都需要使飞行时间统一,所以假设它必须是 5 秒,无论飞机是从巴黎飞往爱尔兰还是从巴黎飞往澳大利亚。任何人都对如何执行此操作有任何想法。

我不得不承认,我为网络创建了这个脚本,因为我的向量和四元数数学是无望的:)

所以这里的问题是 t 在你的 Quaternion.Slerp 方法上使用,它是不变的。 t 是 slerp 将执行的“步骤”,因此如果它是恒定的,它将不取决于时间,而是取决于距离。

尝试做这样的事情,将 timeToTransition 设置为您希望每次旋转都匹配的时间:

public IEnumerator RotatintCoroutine(float timeToTransition)
{
    float step = 0f;
    while (step < 1)
    {
        step += Time.deltaTime / timeToTransition;
        //...other stuff
        tr.rotation = Quaternion.Slerp(tr.rotation, newRotation, step);
        //...more stuff if you want
        yield return null;
    }
}

编辑:添加到您的代码中应该如下所示

float timeToFly = 5f;
while (!DestinationReached)
{
    step += Time.deltaTime / timeToTransition;

    Vector3 planetToCountry = (Destination.localPosition - Vector3.zero);//.normalized; //vector that points from planet to country
    Vector3 planetToCamera = (camTr.position - tr.position).normalized; //vector that points from planet to camera/plane

    Quaternion a = Quaternion.LookRotation(planetToCamera);
    Quaternion b = Quaternion.LookRotation(planetToCountry);

    b = Quaternion.Inverse(b);

    newRotation = a * b;
    tr.rotation = Quaternion.Slerp(tr.rotation, newRotation, step);

    if (step >= 1) //if here the plane has arrived
    {
        Debug.Log("Destination reached");
        DestinationReached = true;
    }
    yield return null();    
}

如果您想灵活一点,例如在开始和结束时添加一些缓动但仍然在固定的持续时间内完成我会这样做(我只是假设你计算最终旋转按预期工作)

// Adjust the duration via the Inspector
[SerializeField] private float duration = 5f;

private IEnumerator RotateRoutine()
{
    // calculate these values only once!

    // store the initial rotation
    var startRotation = tr.rotation;

    // Calculate and store your target ratoation
    var planetToCountry = (Destination.localPosition - Vector3.zero);
    var planetToCamera = (camTr.position - tr.position);

    var a = Quaternion.LookRotation(planetToCamera);
    var b = Quaternion.LookRotation(planetToCountry);
    b = Quaternion.Inverse(b);

    var targetRotation = a * b;

    if(duration <= 0)
    {
        Debug.LogWarning("Rotating without duration!", this);
    }
    else
    {
        // track the time passed in this routine
        var timePassed = 0f;
        while (timePassed < duration)
        {
            // This will be a factor from 0 to 1
            var factor = timePassed / duration;
            // Optionally you can alter the curve of this factor
            // and e.g. add some easing-in and - out
            factor = Mathf.SmoothStep(0, 1, factor);
   
            // rotate from startRotation to targetRotation via given factor
            tr.rotation = Quaternion.Slerp(startRotation, targetRotation, factor);

            // increase the timer by the time passed since last frame
            timePassed += Time.deltaTime;

            // Return null simply waits one frame
            yield return null;   
        }
    }

    // Assign the targetRotation fix in order to eliminate 
    // an offset in the end due to time imprecision
    tr.rotation = targetRotation;

    Debug.Log("Destination reached");
}