unity 旋转

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

欧拉角
百度定义:
在这里插入图片描述
形象点其实就是浑天仪,具体的旋转了多少角度。

优点:简单形象,易理解
缺点是可能会有万向节死锁问题。




四元数
百度定义:
在这里插入图片描述

优点:无万向节死锁问题
缺点:对于初学者来说有些复杂,不易理解




四元数的概念观看B站3Blue1Brown的视频,有助于理解。
3Blue1Brown
不想看视频的可以看GitHub的Krasjet*大佬的关于四元数的文档:
Krasjet*
当然你也可以不用理解四元数,在unity中有四元数与欧拉角的转换函数,会用就行。



一、Rotate

public void Rotate(Vector3 eulers,Space relativeTo = Space.Self)

前面一个参数是要旋转的欧拉角度,后面一个是旋转的欧拉角度相对的空间坐标系,默认为局部坐标系。
下面是一个物体旋转代码,当没有按下空格键时,物体按照局部坐标的y轴旋转,按住空格键后按照全局坐标的y轴旋转。

using System.Collections;
using System.Collections.Generic;
using Unity.Mathematics;
using UnityEngine;

public class CoordinateSystem1 : MonoBehaviour
{
  
    void Update()
    {
   		 //Vector3.up为一个静态属性,其值为(0,1,0);
        transform.Rotate(Vector3.up * 90 * Time.deltaTime);//默认局部坐标系,每秒旋转90度
        //Vector3.up为y轴
        //transform.Rotate(Vector3.up * 90 * Time.deltaTime, Space.Self);//局部坐标系
        if (Input.GetKey(KeyCode.Space))//按住空格后以全局坐标系的轴为参考旋转
        {
            transform.Rotate(Vector3.up * 90 * Time.deltaTime, Space.World);//全局坐标系
        }
    }
    
}

没有按住空格时,以物体局部坐标的y轴为轴心旋转
在这里插入图片描述

在按住空格后,物体不再绕着局部坐标系的y轴旋转,将会绕着全局坐标系的y轴旋转。

在这里插入图片描述

二、设置欧拉角

transform.eulerAnges=new Vector3 euler

using System.Collections;
using System.Collections.Generic;
using Unity.Mathematics;
using UnityEngine;

public class CoordinateSystem1 : MonoBehaviour
{
    void Update()
    {
        transform.eulerAngles+=new Vector3(0, 1, 0);
    }
}

在这里插入图片描述
transform.localEulerAngles=new Vector3 euler

using System.Collections;
using System.Collections.Generic;
using Unity.Mathematics;
using UnityEngine;

public class CoordinateSystem1 : MonoBehaviour
{
    void Update()
    {
        transform.localEulerAngles+=new Vector3(0, 1, 0);      
    }
}

在这里插入图片描述
当我们使用localEulerAngles时,发现它并没有像我们沿着我们预想的和rotate的局部坐标那样的旋转。

这是因为localEulerAngles是绝对赋值,Unity会以初始局部坐标系(即无父物体时的世界坐标系)为基准,重新计算旋转。
而Rotate是增量操作,基于物体当前局部坐标系进行旋转,坐标系会随物体旋转动态变化。

三、Quaternion

using System.Collections;
using System.Collections.Generic;
using Unity.Mathematics;
using UnityEngine;

public class CoordinateSystem1 : MonoBehaviour
{
    void Start()
    {
        Debug.Log(transform.rotation);
    }
}

其实我们只要打印一下transform的rotation属性就不难发现其实rotation就是一个四元数。
在这里插入图片描述
所有如果我们给transform.rotation赋值需要用四元数。

1.欧拉角转四元数
Quaternion.Euler(Vector3 euler);

using System.Collections;
using System.Collections.Generic;
using Unity.Mathematics;
using UnityEngine;

public class CoordinateSystem1 : MonoBehaviour
{
    private Vector3 _euler;
    private Vector3 _rSpeed;
    void Start()
    {
        _euler = transform.rotation.eulerAngles;
        _rSpeed=new Vector3(0, 10,0);
    }
    void Update()
    {
        _euler += _rSpeed;
        Quaternion _rotation = Quaternion.Euler(_euler);
        transform.rotation=_rotation;
        Debug.Log(_euler);
    }
}

在这里插入图片描述
在这里插入图片描述

2.朝向旋转
LookRotation(Vector3 forward, Vector3 upwards = Vector3.up)
LookRotation(Vector3 dir);

using System.Collections;
using System.Collections.Generic;
using Unity.Mathematics;
using Unity.VisualScripting.FullSerializer;
using UnityEditorInternal;
using UnityEngine;

public class CoordinateSystem1 : MonoBehaviour
{
    public Transform target;
    private Vector3 _dir;
    void Start()
    {
        _dir= target.transform.position - transform.position;
    }
    void Update()
    {
        _dir = target.transform.position-transform.position;
        transform.rotation = Quaternion.LookRotation(_dir);
    }
}

在这里插入图片描述
.可以实现和LookAt类似的功能。

3.插值旋转 Lerp&&Slerp
Lerp(Quaternion a,Quaternion b,float t)

线性插值,在小角度时匀速较准确>90°后可能会出现速度不均,性能相对Slerp更优

using System.Collections;
using System.Collections.Generic;
using Unity.Mathematics;
using Unity.VisualScripting.FullSerializer;
using UnityEditorInternal;
using UnityEngine;

public class CoordinateSystem1 : MonoBehaviour
{
    private Quaternion _aRotation;//起始四元数
    private Quaternion _bRotation;//终点四元数
    private float _time;//设置累计时间
    private float totalTime;//设置匀速旋转的总时间
    private float t;//插值系数
    void Start()
    {
        _aRotation = transform.rotation;
        _bRotation = Quaternion.Euler(0, 90, 0);//旋转到(0,90,0)转换为四元数
        _time = 0f;//初始累计时间为0s
        totalTime = 5f;//旋转完这一过程需要五秒
    }
    void Update()
    {
        _time += Time.deltaTime;//累计分子
        t = _time / totalTime;//算插值t
        transform.rotation=Quaternion.Lerp(_aRotation, _bRotation,t);//根据插值算四元数旋转位置
        
    }
}

在这里插入图片描述

Slerp(Quaternion a, Quaternion b, float t)

球面线性插值,根据弧度的比例旋转,路径准确,速率恒定。

using System.Collections;
using System.Collections.Generic;
using Unity.Mathematics;
using Unity.VisualScripting.FullSerializer;
using UnityEditorInternal;
using UnityEngine;

public class CoordinateSystem1 : MonoBehaviour
{
    private Quaternion _aRotation;//起始四元数
    private Quaternion _bRotation;//终点四元数
    private float _time;//设置累计时间
    private float totalTime;//设置匀速旋转的总时间
    private float t;//插值系数
    void Start()
    {
        _aRotation = transform.rotation;
        _bRotation = Quaternion.Euler(0, 90, 0);//旋转到(0,90,0)转换为四元数
        _time = 0f;//初始累计时间为0s
        totalTime = 5f;//旋转完这一过程需要五秒
    }
    void Update()
    {
        _time += Time.deltaTime;//累计分子
        t = _time / totalTime;//算插值t
        transform.rotation = Quaternion.Slerp(_aRotation, _bRotation,t);//根据插值算四元数旋转位置

    }
}

在这里插入图片描述

但就我个人而言,并不是很能感觉这俩的差异在哪。好像感觉都差不多。感觉没有Vector3里面的Lerp和Slerp的差异看的明显。

上面两个例子都是匀速的写法,如果没有将总时间和插值t相关联,单纯以某个固定的t值写的话就能实现非匀速的旋转了。指数增长的非匀速旋转。

四、rigibody

1.通过扭矩力旋转AddTorque
AddTorque(Vector3 torque, ForceMode mode = ForceMode.Force);

torque就是扭矩。

类型:Vector3

作用:定义扭矩的方向和大小。

方向:扭矩轴的朝向(如Vector3.up表示绕Y轴旋转)。

大小:扭矩的强度(单位:牛顿·米,N·m)。

ForceMode施加力的方式,有四种;
不再赘述,移动篇有大致区别分类
移动篇

using System.Collections;
using System.Collections.Generic;
using Unity.Mathematics;
using Unity.VisualScripting.FullSerializer;
using UnityEditorInternal;
using UnityEngine;

public class CoordinateSystem1 : MonoBehaviour
{
    private Rigidbody _rigidbody;

    void Start()
    {
        _rigidbody=GetComponent<Rigidbody>();   
    }
    void Update()
    {
        _rigidbody.AddTorque(Vector3.up *5);

    }
}

在这里插入图片描述

鼠标滑动物体检视物体的效果

需要先给物体加上刚体组件并取消重力应用(use gravity)。
并且要注意鼠标Mouse X,Mouse Y和torque的关系.
Mouse X [-1,1] 左右
Mouse Y [-1,1] 上下

你可以试着转动Inspector里面的rotation的xyz。

如果是上下转的话你要转动rotation的x,对应鼠标Mouse Y。
进一步对应,rotation的x负值对应向下转,对应的Mouse Y的负值,向下。向上转亦然。

如果是上下转的话你要转动rotation的y,对应鼠标Mouse X。
进一步对应,rotation的y负值对应向右转,对应的Mouse Y的正值,向右。向左转亦然。

using System.Collections;
using System.Collections.Generic;
using Unity.Mathematics;
using Unity.VisualScripting.FullSerializer;
using UnityEditorInternal;
using UnityEngine;

public class CoordinateSystem1 : MonoBehaviour
{
    private Rigidbody _rigidbody;
    public float rotX;
    public float rotY;
    public float strength = 10;
    bool applyForce;
    void Start()
    {
        _rigidbody= GetComponent<Rigidbody>();
    }
    void Update()
    {
        if (Input.GetMouseButton(0))
        {
            applyForce= true;
            rotX=Input.GetAxis("Mouse X")*strength;
            rotY= Input.GetAxis("Mouse Y")*strength;
        }
        else
            applyForce = false;
        
    }
    void FixedUpdate()
    {
        if (applyForce)
        {   
        	//根据上面的分析,对应填入相应xy
            _rigidbody.AddTorque(new Vector3(rotY,-rotX,0),ForceMode.Force);
        }
    }
}

在这里插入图片描述

2.调整旋转角速度
rigidbody.angularVelocity = new Vector3 _aVelocity;

using System.Collections;
using System.Collections.Generic;
using Unity.Mathematics;
using Unity.VisualScripting.FullSerializer;
using UnityEditorInternal;
using UnityEngine;

public class CoordinateSystem1 : MonoBehaviour
{
    private Rigidbody _rigidbody;
    void Start()
    {
        _rigidbody=GetComponent<Rigidbody>();   
    }
    void Update()
    {
        _rigidbody.angularVelocity = new Vector3(0, 5, 0);
    }
}

在这里插入图片描述

五、RotateAround

RotateAround(Vector3 point, Vector3 axis, float angle);

point围绕的中心
axis围绕的轴
angle每次旋转的角度

using System.Collections;
using System.Collections.Generic;
using Unity.Mathematics;
using Unity.VisualScripting.FullSerializer;
using UnityEditorInternal;
using UnityEngine;

public class CoordinateSystem1 : MonoBehaviour
{
    public Transform _target;
    private float _angle;
  
    void Start()
    {
        _angle = 2f;
    }
    void Update()
    {
        transform.RotateAround(_target.position,Vector3.up,_angle);
    }
}

在这里插入图片描述

绕某物体的局部坐标的轴旋转:

using System.Collections;
using System.Collections.Generic;
using Unity.Mathematics;
using Unity.VisualScripting.FullSerializer;
using UnityEditorInternal;
using UnityEngine;

public class CoordinateSystem1 : MonoBehaviour
{
    public Transform _target;
    private float _angle;
    private Vector3 localYaxis;
    void Start()
    {
        _angle = 2f;
    }
    void Update()
    {
        localYaxis=_target.TransformDirection(Vector3.up);
        //将局部坐标的轴方向向量转为全局坐标方向向量
        transform.RotateAround(_target.position, localYaxis, _angle);
    }
}

在这里插入图片描述

posted @ 2025-02-23 21:50  Y0417  阅读(3)  评论(0)    收藏  举报  来源