Unity 3d Quaternion Вращение Lerp 3d объекта - PullRequest
0 голосов
/ 21 февраля 2020

Я работаю над проектом Unity. Есть автомобильный стеклоочиститель, и я хочу повернуть его так, чтобы, когда я нажал кнопку «Включение дворников», стеклоочистители начали вращаться с 0,0,0 до 0,0, -45 и начали лептеть. Но когда я нажимаю «выключатели стеклоочистителей», стеклоочистители должны повернуться обратно до 0,0,0. Например, если текущее вращение дворников составляет 0,0, -20, и я нажимаю клавишу «Выключение стеклоочистителей», они должны иметь значение rotateTowards 0,0,0. Кроме того, если я снова нажму «включение стеклоочистителей», стеклоочистители должны начать вращаться с 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);     
        }
    }
}

1 Ответ

0 голосов
/ 21 февраля 2020
  • Ваша проблема при включении заключается в том, что вы непосредственно используете 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;
}

enter image description here

...