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;
}
我正在处理 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;
}