随时间旋转游戏对象

Rotate GameObject over time

我是新来的,我尝试开始使用 Unity 引擎。

有人可以向我解释一下吗,如何运作 Quaternion.Slerp?因为我想以不同的角度旋转一些对象 90、180 和 270。我的代码你可以在下面看到。不幸的是,当我添加 180 度时,对象会做出疯狂的事情,而不是将这个游戏对象旋转到 (0, 180, 180)。我想得到 (180,0,0)

    public float speed = 0.1F;
    private float rotation_x;
    void Update()
    {
        if (Input.GetButtonDown("Fire1"))
        {
            rotation_x = transform.rotation.eulerAngles.x;
            rotation_x += 180;
        }
        transform.rotation = Quaternion.Slerp(transform.rotation, Quaternion.Euler(rotation_x, transform.eulerAngles.y, transform.eulerAngles.z), Time.time * speed);

    }

大多数示例,包括来自其官方网站的 Unity 示例,都以错误的方式使用了 Lerp。他们甚至懒得在 API 文档中描述它是如何工作的。他们只是在 Update() 函数中加注淀粉,然后收工。

Mathf.LerpVector3.LerpQuaternion.Slerp 通过从一个 position/rotation 更改为另一个具有 t 值(最后一个参数)被传递 in.That t 值也称为时间。

t的最小值是0f,最大值是1f

我会用Mathf.Lerp解释一下,这样更容易理解。 Lerp 函数对于 Mathf.LerpVectorQuaternion 都是相同的。

请记住,Lerp 有两个值和它们之间的 returns 个值。如果我们有 110 的值,我们对它们进行 Lerp:

 float x = Mathf.Lerp(1f, 10f, 0f); will return 1.
 float x = Mathf.Lerp(1f, 10f, 0.5f); will return 5.5
 float x = Mathf.Lerp(1f, 10f, 1f);  will return 10

可以看到,t(0)return是传入的数字的mint(1)return s 传入的 max 值和 t(0.5) 将 return mid 点介于 minmax 值。当您传递 t 值为 < 0> 1 时,您做错了。 Update() 函数中的代码就是这样做的。 Time.time 每秒都会增加,然后会在一秒钟内增加 > 1,因此您遇到了问题。

建议在另一个 function/Coroutine 中使用 Lerp 而不是更新函数。

注意

使用 Lerp 在旋转方面有不好的一面。 Lerp不知道如何用最短路径旋转Object。所以请记住这一点。例如,您有一个位置为 0,0,90 的对象。假设您想将旋转从那个移动到 0,0,120 Lerp 有时可以向左而不是向右旋转以到达新位置,这意味着到达该距离需要更长的时间。

假设我们想要根据当前旋转进行旋转 (0,0,90)。下面的代码将在 3 秒内将旋转更改为 0,0,90

随时间旋转

void Start()
{
    Quaternion rotation2 = Quaternion.Euler(new Vector3(0, 0, 90));
    StartCoroutine(rotateObject(objectToRotate, rotation2, 3f));
}

bool rotating = false;
public GameObject objectToRotate;
IEnumerator rotateObject(GameObject gameObjectToMove, Quaternion newRot, float duration)
{
    if (rotating)
    {
        yield break;
    }
    rotating = true;

    Quaternion currentRot = gameObjectToMove.transform.rotation;

    float counter = 0;
    while (counter < duration)
    {
        counter += Time.deltaTime;
        gameObjectToMove.transform.rotation = Quaternion.Lerp(currentRot, newRot, counter / duration);
        yield return null;
    }
    rotating = false;
}

增量 ANGULAR 随时间旋转:

并且只需将对象沿 z 轴旋转 90 度,下面的代码就是一个很好的例子。请理解将对象移动到新的旋转点和仅仅旋转它是有区别的。

void Start()
{
    StartCoroutine(rotateObject(objectToRotate, new Vector3(0, 0, 90), 3f));
}

bool rotating = false;
public GameObject objectToRotate;

IEnumerator rotateObject(GameObject gameObjectToMove, Vector3 eulerAngles, float duration)
{
    if (rotating)
    {
        yield break;
    }
    rotating = true;

    Vector3 newRot = gameObjectToMove.transform.eulerAngles + eulerAngles;

    Vector3 currentRot = gameObjectToMove.transform.eulerAngles;

    float counter = 0;
    while (counter < duration)
    {
        counter += Time.deltaTime;
        gameObjectToMove.transform.eulerAngles = Vector3.Lerp(currentRot, newRot, counter / duration);
        yield return null;
    }
    rotating = false;
}

我所有的例子都是基于设备的帧率。您可以通过将 Time.deltaTime 替换为 Time.delta 来使用实时,但需要更多计算。

首先,您不能像这样在欧拉角上加上 180,这主要是导致您出现问题的原因。您最好直接使用四元数,或者自己处理转换。

您可以将四元数视为 space 中的方向。与所说的相反,如果可以的话,我确实建议学习如何使用它们。但是,我根本不建议使用欧拉角……因为它们受制于不同的书写约定,并且有时会失败。如果您想了解详细信息,可以查看 'gimbal lock'。

slerp 或 lerp(分别代表球面线性插值或线性插值)是一种插值方法(从一个方向到另一个方向,通过将 t 从 0 增加到 1,在协程或其他任何地方)在方向 A 和 B 之间。两者之间的区别在于 slerp 为您提供从 A 到 B 的最短路径。

最后,当 t = 1 时,lerp(A,B,t) 和 slerp(A,B,t) 会给你 B。

在您的情况下,如果您想立即将 space 中的对象旋转到特定方向,我建议您使用 Quaternion.AngleAxis,这是最先进的数学描述四元数的方法。

如果你想添加一个旋转,比如 90° 到你的实际方向(两者之间没有动画),你可以这样做:

transform.rotation *= Quaternion.AngleAxis(axis_of_rotation, 角度) 或使用 transform.rotate(取决于参数,它可以是右乘,或左乘:局部或世界变换)。

程序员的回答详细说明了如何为变换设置动画。但我确实建议您自己研究四元数,因为它会让您全面了解 space 变换。