Unity 3d Quaternion Rotation Lerp of 3d Object

Unity 3d Quaternion Rotation Lerp of 3d Object

我正在处理 Unity 项目。有一个汽车雨刷器,我想以一种方式旋转它,当我按下 "wipers toggle on" 雨刷器开始从 0,0,0 旋转到 0,0,-45 并开始 lerping。但是当我按下 "wipers toggle off" 时,雨刷必须旋转回 0,0,0。例如,如果当前雨刷旋转为 0,0,-20 并且我按下 "wipers toggle off" 键,它们雨刷必须旋转到 0,0,0。此外,如果我再次按 "wipers toggle on",雨刷必须从 0,0,0 开始旋转到 0,0,-45。现在,情况是刮水器正在旋转,但是当我按下 "wipers toggle off" 时,刮水器停在完全相同的当前旋转点,例如 (0,0,-30)。当我再次按下 "wipers toggle on" 时,雨刷器从奇怪的不同旋转点开始。这是我的代码:

using UnityEngine;
using System.Collections;

public class Wipers : MonoBehaviour 
{
    [SerializeField] protected Vector3 m_from = new Vector3(0.0F, 0.0F, 0.0F);
    [SerializeField] protected Vector3 m_to = new Vector3(0.0F, -45.0F, 0.0F);
    [SerializeField] protected float m_frequency = 1.0F;

    protected virtual void Update() 
    {
        if (ControlFreak2.CF2Input.GetAxis ("wipers") != 0) 
        {
            Quaternion from = Quaternion.Euler(this.m_from);
            Quaternion to = Quaternion.Euler(this.m_to);

            float lerp = 0.5F * (1.0F + Mathf.Sin(Mathf.PI * Time.realtimeSinceStartup * this.m_frequency));
            this.transform.localRotation = Quaternion.Lerp(from, to, lerp);     
        }
    }
}
  • 您打开时的问题是您直接使用 Time.realtimeSinceStartup,这当然也会在例程关闭时继续 运行ning!所以你永远不知道雨刷会在什么时候打开。

    →您宁愿使用雨刷启动后的时间。

  • 你关机的问题当然是立马不动了。

    →您宁愿在雨刮器关闭后始终完成一个完整的循环。

与其在 Update 中执行此操作,我建议使用 Coroutine。协程比直接在 Update 中做事更容易控制和维护。事实上,它们就像例程一样的小型临时更新,并在真正的 Update 完成后立即执行。

一个协程,你可以让它继续并完成一个完整的循环,直到它真正完成。

在 Update 中你只会检查用户输入并启动一个例程(如果 none 已经 运行ning)并且例程可以 运行 并独立终止一个完整的循环:

[SerializeField] protected Vector3 m_from = new Vector3(0.0F, 0.0F, 0.0F);
[SerializeField] protected Vector3 m_to = new Vector3(0.0F, -45.0F, 0.0F);
[SerializeField] protected float m_frequency = 1.0F;

// TODO only for debugging
[SerializeField] private bool ControlFreak2Dummy;

private bool wipersOn;
private Quaternion from;
private Quaternion to;

private void Awake()
{
    // Store these once -> more efficient then recalculate them everytime
    from = Quaternion.Euler(m_from);
    to = Quaternion.Euler(m_to);
}

protected virtual void Update()
{
    // TODO switch these back
    //if (ControlFreak2.CF2Input.GetAxis ("wipers") != 0)
    if (ControlFreak2Dummy)
    {
         if(!wipersOn)  
         {
            StartCoroutine(Wipers());
         }
    }
}

// To make things easier for us this in itself closed routine is exactly ONE FULL wipers cycle
// so we can determine exactly when a full cycle is done or not
private IEnumerator Wipers()
{
    // block concurrent routines
    wipersOn = true;

    var duration = 1f / m_frequency;
    var timePassed = 0f;

    while (timePassed <= duration)
    {
        // Note: Your sinus factor calculation was a bit strange
        // (or maybe this early I'm way too stupid for maths lol ^^)
        // It was always minimum 0.5 and maximum 1, you rather want to pingpong between 0 and 1
        //
        // This now moves forth and back exactly once between 0 and 1
        var factor = Mathf.Sin(Mathf.PI *  timePassed / duration);

        transform.localRotation = Quaternion.Lerp(from, to, factor);

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

        // allows Unity to "pause" here, render this frame and
        // continue from here in the next frame
        yield return null;
    }

    // make sur it really terminates in 0
    transform.localRotation = from;

    // tell others that this routine is done
    wipersOn = false;
}