在Unity中,旋转一般能够用一个三维向量(x,y,z)表示。实际上这是欧拉角。三个份量分别是绕x轴、y轴和z轴的旋转角度。html
要对一个GameObject进行旋转,能够直接经过以下代码:web
transform.Rotate(xAngle, yAngle, zAngle);
那么有以下疑问:sql
下面一一解答。c#
首先,回答第一个问题,到底旋转轴是哪一个坐标系的基?分为以下三种状况。svg
对于这一个状况,Unity Doc 中有明确的说明,函数
The position, rotation and scale values of a Transform are measured relative to the Transform’s parent. If the Transform has no parent, the properties are measured in world space. 测试
即,Editor中Transform组件的旋转轴是父节点的模型空间坐标轴,若是没有父节点,则旋转轴是世界空间坐标轴。spa
上图显示了若是Transform有父节点,如图中的”Mesh”,则Position将是在其父节点(这里是”Cow”)的模型空间中的位置;若是没有父节点,Position就是在世界空间中的位置。一样,Transform中的Rotation和Scale也是相同的道理。.net
public void Rotate(Vector3 eulerAngles, Space relativeTo = Space.Self);
public void Rotate(float xAngle, float yAngle, float zAngle, Space relativeTo = Space.Self);
public void Rotate(Vector3 axis, float angle, Space relativeTo = Space.Self);
有上述三种重载函数,这里主要以第一种为例。其中第二个参数的取值有两种:Space.Self 或者 Space.World。3d
使用以下代码,测试上述函数的做用。
using UnityEngine;
using System.Collections;
public class Rotate : MonoBehaviour {
public Space m_RotateSpace;
public float m_RotateSpeed = 20f;
// Update is called once per frame
void Update()
{
transform.Rotate(Vector3.up * m_RotateSpeed * Time.deltaTime, m_RotateSpace);
}
}
场景中进行测试的是一个长方体,其父节点的旋转为(30,30,0),圆柱体的初始旋转为(0,0,0)。在Inspector中将Rotate Space设置为Self后,运行结果见下图。可见,长方体是绕着局部坐标系的Y轴旋转的。
得出结论:在Space.Self中进行旋转,旋转轴就是局部坐标系的坐标轴。
在Inspector中将Rotate Space设置为World后,运行结果见下图。这里咱们知道,长方体的父节点的Y轴不是World的Y轴,而这里的长方体是绕着世界坐标系下的Y轴旋转的。
因此得出结论:在Space.World中进行旋转,旋转轴是世界坐标系的坐标轴。
前面说到的旋转轴的问题,在数学上有对应的概念。这就是所谓的静态欧拉角和动态欧拉角。
所谓静态欧拉角,就是其旋转轴使用的是静止不动的参考系。动态欧拉角,使用的是刚体自己做为参考系,于是参考系会随着刚体的旋转而旋转。
所以,使用Space.World旋转,以及Inspector中的旋转是静态欧拉角;使用Space.Self旋转,是动态欧拉角。
来到第二个问题,因为Unity中局部坐标系和世界坐标系都是左手坐标系,因此这里旋转的正方向可由右手法则断定。
下面来看第三个问题,旋转的顺序,即咱们的欧拉角(xAngle, yAngle, zAngle)由三个份量组成,分别对应着绕x轴旋转,绕y轴旋转和绕z轴旋转,那么是如何绕着这三个轴进行旋转的呢?
这里也分为静态欧拉角和动态欧拉角的状况进行讨论。
这种状况对应着上面所述的使用Space.World进行旋转,以及Inspector中的旋转。即便旋转轴在旋转的过程当中保持不变,旋转的顺序会决定最后的旋转结果。咱们看下面的例子会很清晰的理解:
能够看到,因为旋转顺序的不一样,最终致使了旋转结果的不一样!(究其本质,是由于矩阵乘法不知足交换律)
对于旋转的顺序,通常没有定式,所以,须要在使用时明确的指定出其顺序。对此有一个专门的术语,称为顺规。若是在坐标系中的旋转,先绕x轴旋转,再绕y轴,最后再绕z轴,则称之为X-Y-Z顺规。以此类推。
对于Unity,从文档中能够看到,其transform.Rotate()使用的是Z-X-Y顺规。所以若是在Unity中,使用静态欧拉角旋转(90,90,0)获得情形一的状况。
这种状况对应着上面所述的使用Space.Self进行旋转。动态欧拉角除了上面说到的顺规问题(一样是Z-X-Y顺规),还有一个疑问:好比一个物体,初始状态记为A,以Z-X-Y顺规旋转(90,90,0),因为没有z轴旋转,第一步固然是绕着当前的x轴旋转90度,此时状态记为B,那么第二步要绕着y轴旋转90的时候,是绕着初始状态A时的y轴旋转,仍是绕着此时的B状态下的y轴旋转呢?
首先来看下二者的区别:
Unity中的状况究竟如何呢?直接运行下面的代码会看到结果:
void Start () { transform.Rotate(90, 90, 0, Space.Self);
}
能够发现Unity中的状况与情形一相同。因此第二步要绕着y轴旋转90的时候,是绕着初始状态A时的y轴旋转。
为了获得情形二中的效果,能够分两次旋转,运行以下代码:
void Start () {
transform.Rotate(90, 0, 0, Space.Self);
transform.Rotate(0, 90, 0, Space.Self);
}
能够发现,此时的效果与情形一中相同了。
最终,咱们的结论是:Unity中每次使用Space.Self进行Rotate时,都是绕着调用时刻的局部坐标系的坐标轴进行旋转的。
静态欧拉角和动态欧拉角是能够相互转换的。
转化规则就是:静态欧拉角中,在某一坐标系E下按照某一顺规如X-Y-Z旋转角度(a, b, c),等价于动态欧拉角中,在E下旋转(0, 0, c),在旋转后的坐标系E’中旋转(0, b, 0),在旋转后的新坐标系E”中旋转(a, 0, 0)。
在Space.Self中旋转以 Z-X-Y 顺规旋转角度(a, b, c),等价于在Space.Self中旋转(0, b, 0),在新的Space.Self中旋转(a, 0, 0),在更新的Space.Self中旋转(0, 0, c)。
下面咱们来证实上述两种旋转是等价的。经过复合旋转矩阵的方式。
记:
绕坐标系E下的Z轴旋转c的旋转矩阵为Rz,
绕坐标系E下X轴旋转a的旋转矩阵为Rx,
绕坐标系E下Y轴旋转b的旋转矩阵为Ry;
绕坐标系E下的Y轴旋转b的矩阵为Rb(Rb == Ry),
绕坐标系E在绕Y轴旋转b后的新坐标系E’下的X轴旋转a的旋转矩阵为Ra,
绕坐标系E’在绕X轴旋转a后的新坐标系E”下的Z轴旋转c的旋转矩阵为Rc。
另外,这里将矩阵R的逆记为R~。
求证:Rz * Rx * Ry == Rb * Ra * Rc
证实:
Rb == Ry,由定义相同可知。
Ra = (Rb~) * Rx * Rb,要获得绕坐标系E在绕Y轴旋转b后的新坐标系E’下的X轴旋转a的旋转矩阵Ra,先应用Rb~旋转到坐标系E下,而后绕坐标系E下的X轴旋转a,最后应用Rb转回到坐标系E’。
Rc = ((Rb * Ra)~) * Rz * (Rb * Ra),理由同上。
因此有,
右边 = Rb * Ra * Rc
= Rb * Ra * ((Rb * Ra)~) * Rz * (Rb * Ra)
= Rz * Rb * Ra
= Rz * Rb * (Rb~) * Rx * Rb
= Rz * Rx * Rb
= Rz * Rx * Ry = 左边
证毕!
从代码上来讲,就是下面两个函数是等价的。
private void RotateStatic(float a, float b, float c)
{
// 静态欧拉角,依次绕着调用Rotate时的局部坐标系的z,x,y轴旋转a,b,c角度
transform.Rotate(a, b, c, Space.Self);
}
private void RotateDynamic(float a, float b, float c)
{
// 动态欧拉角,绕着调用Rotate时的局部坐标系的y轴旋转b角度
transform.Rotate(0, b, 0, Space.Self);
// 动态欧拉角,绕着调用Rotate时的局部坐标系的y轴旋转a角度
transform.Rotate(a, 0, 0, Space.Self);
// 动态欧拉角,绕着调用Rotate时的局部坐标系的y轴旋转c角度
transform.Rotate(0, 0, c, Space.Self);
}