0

我正在开发一个 Unity 项目。有一个汽车雨刷器,我想旋转它,当我按下“雨刷器开启”时,雨刷器开始从 0,0,0 旋转到 0,0,-45 并开始转动。但是当我按下“雨刷关闭”时,雨刷必须旋转回 0,0,0。例如,如果当前雨刷旋转为 0,0,-20,并且我按下“雨刷切换关闭”键,则雨刷必须向 0,0,0 旋转。此外,如果我再次按下“wipers toggle on”,则雨刷必须开始从 0,0,0 旋转到 0,0,-45。现在,情况是雨刷器正在旋转,但是当我按下“雨刷器关闭”时,雨刷器停止在完全相同的当前旋转点,例如(0,0,-30)。当我再次按下“雨刷器开启”时,雨刷器从奇怪的不同旋转点开始。这是我的代码:

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

1 回答 1

0
  • 您打开时的问题是您直接使用Time.realtimeSinceStartup它当然在例程关闭时也会继续运行!所以你永远不知道雨刷器会在什么时候打开。

    → 您宁愿使用自雨刷器启动以来的时间。

  • 关闭时的问题当然是您立即停止移动。

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

Update我建议不要这样做,而是使用Coroutine。协程比直接做事情更容易控制和维护Update。实际上,它们就像例程一样的小型临时更新,并在实际Update完成后立即执行。

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

在更新中,您只需要检查用户输入并启动一个例程(如果没有一个正在运行),该例程可以独立运行和终止一个完整的循环:

[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;
}

在此处输入图像描述

于 2020-02-21T07:03:42.753 回答