声明:此篇文章是个人学习笔记,并非教程,所以内容可能不够严谨。可作参考,但不保证绝对正确。如果你发现我的文章有什么错误,非常欢迎指正,谢谢哦。
Unity的Inspector面板上的rotation是欧拉角,简单来说欧拉角由三个旋转值组成。当刚开始接触Unity时,在Inspector面板上看到Transform组件,会想当然的认为它的rotation要么是指物体绕其惯性坐标轴的旋转度数,要么是物体绕其局部坐标轴的旋转度数。简简单单,X分量就是绕X轴,Y分量就是绕Y轴,Z分量就是绕Z轴而已嘛。
但是当我们试着调整Inspector面板上的rotation时,有时候又会出现奇怪的现象。
物体旋转既不是绕自身坐标轴旋转,也不是绕惯性坐标轴旋转。
显示的自身坐标轴 | 显示的惯性坐标轴 | Inspector面板 |
---|---|---|
有时候,我们直接在场景中调整物体的单个旋转轴(无论惯性坐标轴还是自身坐标轴),但在Inspector面板上却能看到rotation三个数值在同时变化。
场景 | Inspector面板 |
---|---|
更严重的是,有时候会出现一个旋转方向丢失的现象,比如当我们将X旋转90°,再去调整Z和Y的值时会发现:调整Z和Y的效果一样,也就是说我们三个旋转方向变两个了。
将X旋转至90°,再旋转Z轴 | 将X旋转至90°,再旋转Y轴 |
---|---|
为什么会出现这些奇怪的现象,难道是因为Unity出了什么Bug?在解决这个问题之前,先要了解一下万向节。
如果以前没有了解过万向节,可以看这里:万向节和万向节锁
万向节有三个坐标轴X,Y,Z,刚开始时,三个坐标轴是相互垂直的,这似乎很常见。
但要注意的一点是,它的三个坐标轴有父子关系,父轴的转动会带动子轴的转动,类似于我们在Unity的Hierachy面板上构建的父子关系(如下图)。试着排列三个坐标轴的父子关系,会发现三个轴的父子关系有6种,xyz\xzy\yxz\yzx\zxy\zyx,在不同的应用场景中,人们会采用不同的关系。下面以yxz为例子:
现在假设三个轴的层次关系是这样的: Z在最底层,X是Z的父项,Y是X的父项。也就是说,当我们旋转万向节中的三个轴时,会有以下现象:
Z轴旋转时,另外两个轴不旋转
X轴旋转时,Z轴随X轴旋转而旋转(相对静止),Y轴不动
Y轴旋转时,X,Z都随Y轴旋转而旋转(相对静止)
文字性的描述可能不够清晰,下面的图来自我自己在Unity中编写的万向节模拟系统,物体的旋转使用万向节来实现(其中蓝色的环表示物体的Z旋转轴,红色表示X,绿色表示Y):
旋转的轴 | 图片 | 描述 |
---|---|---|
Z | 蓝色的轴是Z轴,当旋转Z轴时,X和Y轴保持原有状态不动。因为Z轴在最底层。 | |
X | 红色的轴是X轴,因为Z轴是X轴的子项,所以当旋转X轴时,Z轴会随之运动(保持相对静止)。而Y轴保持不动 | |
Y | 绿色的轴是Y轴,因为X轴是Y轴的子项,而Z轴是Y轴子项的子项,所以旋转Y轴时,X和Z均会随之旋转从而与Y轴保持相对静止 |
欧拉角内部就是使用的万向节的规则(为什么使用这种规则,为什么不采用三个轴互不影响的策略,可以考虑参考我的下一篇笔记)。可以想象成每个游戏物体有一个隐藏的万向节坐标系统(当然,这是不准确的,这里这么说只是为了帮助理解),就像上面图上的飞机一样。而Inspector面板上的rotation就是欧拉角,也就是采用的万向节的规则。
万向节的特点是“轴与轴之间存在父子关系,每个轴对另外两个轴的影响各不相同”,也正是因为这个特点,导致出现了一些缺点。
比如当我们将X轴旋转90°后,Z轴和Y轴会变成平行状态,然后我们会发现旋转Z轴和Y轴的效果竟然是一模一样的,这就导致当我们想让图中的飞机实现转向操作时就找不到轴了(这似乎与我们的直觉相违背,三个轴变成了两个)
将X轴旋转90° | 旋转Z和Y对物体的旋转效果一样 | 利用万向节中的轴无法完成的旋转 |
---|---|---|
前面提到,欧拉角运用的就是万向节的规则。经过我的实验证明,Unity中的欧拉角采用的是yxz的层次关系(即y在最外层,如上面的例子),Inspector面板上显示的并不是物体绕自身局部坐标轴旋转的度数,也不是绕惯性坐标轴旋转的度数,而是隐藏的万向节旋转轴旋转的度数。那么一切都说得通了,真相只有一个!
物体旋转既不是绕自身坐标轴旋转,也不是饶世界坐标轴旋转。
解释:这是因为物体绕得是万向节的三个轴,既不同于Unity物体自身坐标轴也不同于惯性坐标轴。
有时候,我们直接在场景中调整物体的单个旋转轴(无论惯性坐标轴还是自身坐标轴),但在Inspector面板上却能看到rotation三个数值在同时变化。
场景中的旋转轴要么是使用的物体局部坐标系,要么使用的是惯性坐标系,这二者与物体的万向节坐标系不同。当使用场景中的旋转轴进行旋转时,Unity内部应该是使用的四元数进行处理,然后将其转换为欧拉角(万向节坐标系)的结果显示在Inspector面板上。等价转换之后,可能旋转场景中一个轴的效果需要动用欧拉角的三个轴。
更严重的是,有时候会出现一个旋转方向丢失的现象,比如当我们将X旋转90°,再去调整Z和Y的值时会发现:调整Z和Y的效果一样,也就是说我们三个旋转方向变两个了。
这一现象已经在前面解释过了,物体隐含有一个万向节系统, 有些情况下就会出现万向节锁(Gimbal Lock)。
当我们在Inspector面板上按照Z、X、Y的顺序调整rotation的值时会发现,此时场景中物体的旋转效果是绕着物体的惯性坐标轴进行旋转的。这是因为初始状态下(0,0,0),物体的万向节系统的三个轴是和物体的惯性坐标轴重合的,旋转Z轴不影响X,Y轴的位置和角度,所以旋转完Z轴后,X、Y轴依然与惯性坐标轴重合。
如果你发现结果和我说的不一样,可能要检查一下物体中心和坐标轴的设定是不是如此:
当我们在Inspector面板上按照Y、X、Z的顺序调整rotation的值时会发现,此时场景中物体的旋转效果是绕着物体的局部坐标轴进行旋转的。这是因为初始状态下(0,0,0),物体的万向节系统的三个轴不仅是和物体的惯性坐标轴重合,也是和物体的局部坐标轴重合的。旋转Y轴时会带动X和Z轴旋转,三个轴保持相对静止,这和局部坐标轴是一样一样滴。
最后再缕一缕关于Unity中欧拉角的思路。
欧拉角由三个旋转值组成,三个旋转值会根据万向节的规则确定。Unity中Inspector面板上的rotation的三个值对应的是物体的欧拉角,即通过万向节规则确定的三个旋转值。当我们调节Inspector面板上的rotation的值时,Unity根据万向节规则计算出物体当前的朝向。
我在不同的文章或者视频中看到了两种万向节的示例图,第一次见感觉这两种万向节似乎相互矛盾,但二者其实本质上是一样的。
这张图示是我在维基百科英文版Wikipedia中看到的,它的旋转方式是让环绕着横插过环的轴进行旋转的。
这种图示是我在讲解万向节锁的视频中看到的,也是上面我自己做的图中采用的方式,即像轮胎一样沿着环进行旋转。这里就懒得放图了。
这两种不同的旋转方式并不影响它们表达相同的本质:父轴的旋转会带动子轴的旋转,而子轴的旋转并不带动父轴的旋转。
另外,我还在一些文章中看到了最里层的轴会带动外层的轴旋转,外层的轴不会带动里层的轴旋转(和上面相反),其本质也是一样一样滴。
这里Unity中关于欧拉角和万向节的讨论依然不够深入,停留在表面的理解上。我将在我的下一篇文章中说说我对“欧拉角为什么会符合万向节的规则”的理解。
①清晰易懂的万向节和万向锁的视频(前面已经提到): https://v.youku.com/v_show/id_XNjk1MTkzMTM2.html#paction
②大佬关于欧拉角的讲解文章:
Ⅰ.https://blog.csdn.net/andrewfan/article/details/60866636
Ⅱ.https://blog.csdn.net/AndrewFan/article/details/60981437#comments_14584533