物体的几种旋转方式

2020-10-27  本文已影响0人  烂醉花间dlitf

一种错误的写法

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Rotation : MonoBehaviour
{
    void Update()
    {
        Vector3 euler_rotation = transform.rotation.eulerAngles;
        euler_rotation += new Vector3(20,20,0) * Time.deltaTime;
        transform.rotation = Quaternion.Euler(euler_rotation);
    }
}

错误写法的运行结果

可以看到物体只有一开始在 X 轴上是有旋转的,后面就 “卡住了”,具体的原因可以在学习了四元数与欧拉角的转换之后再探究,现在只需要知道如果想要使用欧拉角旋转,需要将整个旋转角度存在一个变量里面,不能直接从四元数获取。具体写法参考如下:

控制欧拉角的写法

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Rotation : MonoBehaviour
{
    private Vector3 rotation;
    private Vector3 EulerRotation
    {
        get { return rotation; }
        set {
            Vector3 temp = value;
            if (temp.x > 360)
            {
                temp.x -= 360;
            }
            if (temp.y > 360)
            {
                temp.y -= 360;
            }
            if(temp.z > 360)
            {
                temp.z -= 360;
            }
            rotation = temp;
            }
    }
    // Start is called before the first frame update
    void Start()
    {
        EulerRotation = transform.rotation.eulerAngles;
    }

    // Update is called once per frame
    void Update()
    {
        EulerRotation += new Vector3(20,20,0) * Time.deltaTime;
        transform.rotation = Quaternion.Euler(EulerRotation);
    }
}

可以很直观的从欧拉角知道旋转的效果。这里其实就是将目标欧拉角一直存储在一个单独的 Vertor3 里面。
在这个过程中物体在 Inspector 面板的旋转会基本跟预期的一样,也就是说如果设置的旋转是 Vector3(20,20,0) * Time.deltaTime ,那么它在 Z 轴的旋转就不会变。看图可以看到一开始 x 和 y 都是同步增长的,但后面就不一样了,过一段时间之后又会恢复到同步增长的状态。

直接赋值欧拉角

如果使用 Rotate 函数,也就是只在 update 里面写一句 transform.Rotate(new Vector3(20, 20, 0) * Time.deltaTime,Space.World); 那么结果会跟你想象的不太一样。可以看到他的旋转不仅 Z 轴是变化的,而且 X 和 Y 也没有保持统一。

使用 Rotate 函数

对比一下看看,旋转都是 new Vector3(20, 20, 0) * Time.deltaTime,Space.World;

[左]使用 Rotate [右]没使用 Rotate

先站立,再旋转

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Rotation : MonoBehaviour
{
    private Vector3 point1; // 正方体的一个顶点
    private Vector3 point2; // 正方体的相对的那个顶点
    private Quaternion targetRotation;
    private Quaternion originRotation;
    private float timeCount = 0.0f;
    private void Start()
    {
        Renderer render= GetComponent<Renderer>();
        point1 = render.bounds.center;
        point2 = render.bounds.center;
        point1 += render.bounds.extents; // 这里的 extents 是包含缩放的
        point2 -= render.bounds.extents;
        Vector3 origin_dir = point1 - point2;
        originRotation = transform.rotation;
        targetRotation = originRotation * Quaternion.FromToRotation(origin_dir, Vector3.up);
    }

    private void Update()
    {
        if(timeCount > 1) // 已经到位置了
        {
            // 写法一:
            transform.Rotate(new Vector3(0, 20 * Time.deltaTime, 0), Space.World);
            
            // 写法二:// 两种写法效果一样的
            // Vector3 axis = transform.InverseTransformDirection(Vector3.up); // 将世界方向的 up 转换到本地方向
            // transform.rotation *= Quaternion.AngleAxis(20 * Time.deltaTime, axis);
        }
        else // 没有旋转到对应位置
        {
            timeCount += Time.deltaTime;
            transform.rotation = Quaternion.Slerp(originRotation, targetRotation, timeCount);
        }
    }
}

运行结果

这个效果即使物体有缩放,也不受影响。先获得两个相对的顶点,然后算出应该旋转到的位置。Slerp 参考 lerp 也比较好理解。

上一篇 下一篇

猜你喜欢

热点阅读