概述
在3D軟件中旋轉(zhuǎn)通常用四元數(shù)或者歐拉角來(lái)表示,各有優(yōu)缺點(diǎn)。Unity內(nèi)部存儲(chǔ)使用四元數(shù),但是為了方便我們編輯,在面板上顯示成對(duì)應(yīng)的歐拉角的值。
Euler Angles 歐拉角
歐拉角簡(jiǎn)單的理解就是有三個(gè)角度X,Y,Z,然后按順序在對(duì)應(yīng)的軸上進(jìn)行旋轉(zhuǎn),最后物體的方向就是這個(gè)歐拉角。
- 優(yōu)點(diǎn):容易直觀的理解值的意義
- 缺點(diǎn):會(huì)導(dǎo)致萬(wàn)向節(jié)鎖。我理解是,當(dāng)依次旋轉(zhuǎn)時(shí),會(huì)出現(xiàn)有個(gè)軸無(wú)法旋轉(zhuǎn),即旋轉(zhuǎn)失效 ,詳細(xì)可查看
Quaternions 四元數(shù)
四元數(shù)也可以用來(lái)表示物體的旋轉(zhuǎn)或者方向,顧名思義,是由四個(gè)數(shù)組成,在Unity3D中是x,y,z,w。不過(guò),這些數(shù)字并不是表示角度和旋轉(zhuǎn)軸,而且你絕不會(huì)直接使用它們。除非你特別想知道四元數(shù)的原理,否則僅僅知道它在3D世界中表示旋轉(zhuǎn)即可,完全不用理解這四個(gè)數(shù)的意義或者嘗試修改它。如果你確實(shí)想了解四元數(shù)的前世今生,詳細(xì)可查看,作者寫的特別詳細(xì)。
好比一個(gè)Vector(矢量)可以表達(dá)位置或者方向(從坐標(biāo)原點(diǎn)看),一個(gè)四元數(shù)也可以同時(shí)表示指向或者旋轉(zhuǎn)角度(從世界坐標(biāo)系的原點(diǎn)或者特定坐標(biāo)系的原點(diǎn)作為參考點(diǎn))。因?yàn)樾D(zhuǎn)是這么計(jì)算的-從一個(gè)方向到另一個(gè)方向,所以一個(gè)四元數(shù)無(wú)法表示一個(gè)超過(guò)180度的旋轉(zhuǎn)。
- 優(yōu)點(diǎn):無(wú)萬(wàn)向節(jié)鎖
- 缺點(diǎn):?jiǎn)我凰脑獢?shù)無(wú)法表示任何方向上超過(guò)180度的旋轉(zhuǎn)
- 缺點(diǎn):四元數(shù)的四個(gè)數(shù)字無(wú)法直觀的理解
權(quán)衡利弊后,在Unity里,所有游戲物體旋轉(zhuǎn)狀態(tài)都是以四元數(shù)的方式存儲(chǔ)的
不過(guò)在Transform的面板中,還是用歐拉角的方式展示數(shù)據(jù),因?yàn)檫@種方式還是更易于理解和編輯。每當(dāng)輸入一個(gè)旋轉(zhuǎn)的值,都會(huì)在后臺(tái)被轉(zhuǎn)存為一個(gè)新的四元數(shù)。
副作用就是,有可能在旋轉(zhuǎn)面板上輸入“X: 0, Y: 365, Z: 0”。這個(gè)歐拉角不可能以四元數(shù)來(lái)表示,所以當(dāng)你點(diǎn)擊"Play",你會(huì)看到面板上的值變成了" X: 0, Y: 5, Z: 0 "(左右)。這是因?yàn)?65度是無(wú)法用四元數(shù)表示的,所以簡(jiǎn)單的用前面的結(jié)果取代了它。
腳本相關(guān)
當(dāng)你在腳本中處理旋轉(zhuǎn)時(shí),你應(yīng)該用到Quaternion類以及它的方法去創(chuàng)建和修改旋轉(zhuǎn)相關(guān)的參數(shù)。有一些場(chǎng)景需要合理的使用歐拉角,但是你要記?。耗銘?yīng)該使用Quaternion類以及方法去處理歐拉角,獲取,修改和重復(fù)使用歐拉值會(huì)出現(xiàn)無(wú)意的副作用。
直接創(chuàng)建和操作四元數(shù)
Unity的Quaternion類有許多創(chuàng)建和操作旋轉(zhuǎn)的方法,這些完全不必用到歐拉角。比如:
- 創(chuàng)建
- Quaternion.LookRotation
- Quaternion.AngleAxis
- Quaternion.FromToRotation
- Manipulating:
- Quaternion.Slerp
- Quaternion.Inverse
- Quaternion.RotateTowards
- Transform.Rotate & Transform.RotateAround
不過(guò),有時(shí)在你的腳本里使用歐拉角時(shí)合適的。但是需要格外留心的是,用新的變量保存角度,僅僅在需要改變物體旋轉(zhuǎn)時(shí)使用下歐拉角。同時(shí)也有可能從四元數(shù)里獲取當(dāng)前的歐拉角,當(dāng)你獲取,修改,重使用歐拉角時(shí),問(wèn)題就出現(xiàn)了。因?yàn)橹禃?huì)在轉(zhuǎn)化成四元數(shù)時(shí)會(huì)進(jìn)行角度的變化。比如360度和0度之類。。。
以下是一些錯(cuò)誤事例,假設(shè)每秒鐘讓一個(gè)物體繞X軸旋轉(zhuǎn)10度。這是是我們需要避免的:
- 錯(cuò)誤一
直接修改Quaternion的x值,但是這個(gè)值并不是表示角度,所以就不會(huì)產(chǎn)生預(yù)期的結(jié)果
void Update () {
var rot = transform.rotation;
rot.x += Time.deltaTime * 10;
transform.rotation = rot;
}
- 錯(cuò)誤二
讀、寫和修改一個(gè)Quaternion中的歐拉值.因?yàn)檫@些值都是從Quaternion中計(jì)算得出,所以每一次新的旋轉(zhuǎn)可能會(huì)產(chǎn)生極不相同的歐拉角,這有可能引發(fā)萬(wàn)向節(jié)鎖。
void Update () {
var angles = transform.rotation.eulerAngles;
angles.x += Time.deltaTime * 10;
transform.rotation = Quaternion.Euler(angles);
}
- 正確示例
正確的在腳本中使用歐拉角進(jìn)行旋轉(zhuǎn)。我們使用一個(gè)類成員變量存儲(chǔ)了當(dāng)前的歐拉角,并且只在賦值給Quaternion時(shí)使用,并且不會(huì)從Quaternion中讀取這個(gè)值。
float x;
void Update () {
x += Time.deltaTime * 10;
transform.rotation = Quaternion.Euler(x,0,0);
}