Quaternion類
Quaternion(四元數(shù))用于計算Unity旋轉(zhuǎn)。它們計算緊湊高效,不受萬向節(jié)鎖的困擾,并且可以很方便快速地進行球面插值。 Unity內(nèi)部使用四元數(shù)來表示所有的旋轉(zhuǎn)。
Quaternion是基于復(fù)數(shù),并不容易直觀地理解。 不過你幾乎不需要訪問或修改單個四元數(shù)參數(shù)(x,y,z,w); 大多數(shù)情況下,你只需要獲取和使用現(xiàn)有的旋轉(zhuǎn)(例如來自“Transform”),或者用四元數(shù)來構(gòu)造新的旋轉(zhuǎn)(例如,在兩次旋轉(zhuǎn)之間平滑插入)。
大部分情況下,你可能會使用到這些函數(shù):
? ?Quaternion.LookRotation,
? ?Quaternion.Angle
? ?Quaternion.Euler
? ?Quaternion.Slerp
? ?Quaternion.FromToRotation
? ?Quaternion.identity。
Quaternion 是一個結(jié)構(gòu)體,本身成員變量相對簡單,可以作為函數(shù)參數(shù)高效傳遞。-
Unity默認方向
在深入了解API之前,我們需要先明確一些基本的概念,就是方向、旋轉(zhuǎn)究竟是如何表示的。
Unity中使用左手坐標系,假如把世界坐標系跟東南西北進行結(jié)合起來看,大致如下圖所示:
默認的方向?qū)?yīng)如下表:

假設(shè)以你自己身體為例,你站立在地面上,面朝北方,此時就是默認方向,也就是Unity中的方向就是面向+Z軸方向,那么此時+X軸在東方,+Y軸對應(yīng)正上方。此時對應(yīng)的歐拉角是(0,0,0),此時對應(yīng)的前方矢量是(0,0,1),上方矢量是(0,1,0)。
這里我區(qū)分了左右上下前后的概念,因為這些概念同時也對應(yīng)了Vector3類、Transform類中的相應(yīng)的方向函數(shù)。
方向的表示法
①歐拉角表示法
假如你使用一組歐拉角表示旋轉(zhuǎn),XYZ三個參數(shù)代表相應(yīng)軸向按照順歸YZX的旋轉(zhuǎn),因此(0、90、90)代表先進行+Z軸旋轉(zhuǎn)90度,再沿著+Y軸進行90度旋轉(zhuǎn),更多詳細內(nèi)容可以參考前述文章《【Unity編程】Unity中的歐拉旋轉(zhuǎn)》。
②前方上方矢量界定法
編程過程中,大部分需要明確指定方位的時候就需要使用這個方法。要確定一個朝向,我們可以使用兩個向量來確定:即前方矢量和上方矢量。當一個朝向的前方和上方確定之后,這個朝向也就完全確定了。
舉例來說,如果現(xiàn)在只提供一個朝向,就是你現(xiàn)在面朝北方,那么這個方向已經(jīng)完全確定了嗎?顯然沒有。因為你右側(cè)躺在地上,看向北方,還是在面朝北方,這時候就需要另外一個矢量,也就是上方。當給出上方之后,這個朝向就完全確定了。
上方需要嚴格給出嗎?
在Unity中,我們很多時候,不需要給出嚴格的上方朝向。比如,仍然是上面那個例子,如果我面朝北方,先給出(0,0,1)代表我的前方矢量。那么,如果我給出的方向不是嚴格的上方矢量,比如是(0,0.5,0.5),是否可以?答案也是可以的,因為這兩個矢量顯然已經(jīng)確定了一個方向,前方是嚴格的,而實際的上方可以通過前方朝著你給出的上方矢量旋轉(zhuǎn)90度得出。也就是說,你給(0,1,0)作為上方矢量,和給出在下圖中弧度范圍內(nèi)(不包含+Z和-Z)所有方向的矢量都是相同的結(jié)果。
③繞軸旋轉(zhuǎn)界定法
第三種定義旋轉(zhuǎn)的方法就是圍繞某個指定的軸向旋轉(zhuǎn)一定的角度。這個方法也可以確定一個相對旋轉(zhuǎn),它以從默認方向(此時前方+Z,上方+Y)出發(fā),沿著指定的軸向進行指定角度的旋轉(zhuǎn),旋轉(zhuǎn)后的前方和上方是確定的。因此這個方法也可以用來確定朝向。
④A向到B向相對旋轉(zhuǎn)表示法
還有一種方法就是從A向到B向的相對旋轉(zhuǎn),這種表示了一個旋轉(zhuǎn)的相對變化。比如A為(0,1,0),B為(0,0,1),也就是相對旋轉(zhuǎn)量代表原來的上方被旋轉(zhuǎn)到了前方,這樣的一個四元數(shù)也可以用歐拉角表示成(90,0,0),也就是沿著+X軸旋轉(zhuǎn)了90度。
注意上面四中表示方法中,有的明確表明了上方矢量,有的好像只明確了前方矢量,要明確的一點就是,它們都是從默認矢量出發(fā)的,如果沒有明確指定上方朝向,那么就是使用默認的上方,也就是+Y方向。
成員變量
? ?eulerAngles 歐拉角,返回當前四元數(shù)所對應(yīng)的歐拉角
? ?this[int] 可以使用類似數(shù)組和下標的形式從四元數(shù)中獲取四個四元數(shù)參數(shù)
? ?x、y、z、w 分別代表x、y、z、w 參數(shù),具體代表的內(nèi)容可以參考前文《【Unity編程】四元數(shù)(Quaternion)與歐拉角》,你最好不要通過修改四個參數(shù)來改變四元數(shù),除非你真的非常了解它們的含義。
靜態(tài)成員
identity 單位四元數(shù),也就是默認的無旋轉(zhuǎn)狀態(tài),此時與世界坐標相同,前方指向+Z,上方指向+Y
成員函數(shù)

說明:成員函數(shù)幾個set方法多用于將當前四元數(shù)設(shè)置成目標四元數(shù),目標四元數(shù)的構(gòu)建方法與對應(yīng)名稱的靜態(tài)函數(shù)相同。
靜態(tài)函數(shù)
函數(shù)形式解釋
static float Angle(Quaternion a, Quaternion b)計算兩個四元數(shù)前方矢量之間的夾角度數(shù)
static Quaternion AngleAxis(float angle, Vector3 axis)構(gòu)建一個四元數(shù),它表示沿著一個軸旋轉(zhuǎn)固定角度,即上述表示法③
static float Dot(Quaternion a, Quaternion b)計算兩個四元數(shù)之間的點積,返回一個標量,這個函數(shù)一般用不到,它的點積不代表什么具體的物理含義,具體定義方法見我的前述文章
static Quaternion Euler(float x, float y, float z)構(gòu)建一個四元數(shù),它用歐拉旋轉(zhuǎn)表示,即上述表示法①
static Quaternion FromToRotation(Vector3 fromDirection, Vector3 toDirection)構(gòu)建一個四元數(shù),它表示從指向fromDirection方向到指向toDirection方向的相對旋轉(zhuǎn)量,見上述表示法④
static Quaternion Inverse(Quaternion rotation)構(gòu)建一個四元數(shù),它是指定的四元數(shù)的逆,也就是逆向旋轉(zhuǎn),比如原四元數(shù)表示相對+X軸旋轉(zhuǎn)了90度,那么此函數(shù)結(jié)果就是相對+X軸旋轉(zhuǎn)了-90度
static Quaternion Lerp(Quaternion a, Quaternion b, float t)構(gòu)建一個四元數(shù),表示從四元數(shù)a到b的球面插值,所謂的插值也就是中間旋轉(zhuǎn)量,從a作為起點,此時對應(yīng)t為0,到b為終點,此時對應(yīng)t為1。當t取0-1之間的小數(shù)時,就代表了中間的插值結(jié)果。這個方法與Slerp相同,計算速度快,但是精度低,如果相對旋轉(zhuǎn)變化量很小,則效果不理想
static Quaternion LerpUnclamped(Quaternion a, Quaternion b, float t)與Lerp相同,區(qū)別是,Lerp的t值會被鉗制在[0,1]之間,而此方法則不會,t允許超出計算
static Quaternion LookRotation(Vector3 forward, Vector3 upwards = Vector3.up)構(gòu)建一個四元數(shù),使用前方上方矢量確定朝向,也就是上述表示法②
static Quaternion RotateTowards(Quaternion from, Quaternion to, float maxDegreesDelta)構(gòu)建一個四元數(shù),表示從一個四元數(shù)from(的前方)向著另外一個四元數(shù)(的前方)旋轉(zhuǎn),但不能超出指定的角度,也就是如果兩個前方矢量夾角超過指定角度,則旋轉(zhuǎn)到達指定角度時就停止,若是夾角本身不足的話,則結(jié)果直接為目標四元數(shù)to,與上述表示法④的意思很接近
static Quaternion Slerp(Quaternion a, Quaternion b, float t)球面插值,與Lerp功能相同,t值也被鉗制,計算精度高,但是速度相對較慢
static Quaternion SlerpUnclamped(Quaternion a, Quaternion b, float t)與Slerp功能相同,只是t值不被鉗制,允許超出計算
static Quaternion operator * (Quaternion lhs, Quaternion rhs)乘法運算符重載,當表示兩個連續(xù)的旋轉(zhuǎn)時,可以使用lhs * rhs的形式得出連續(xù)旋轉(zhuǎn)的結(jié)果,lhs為左值,rhs為右值。注意左值是先進行的旋轉(zhuǎn),疊加右值旋轉(zhuǎn)。用法示例:lhs = lhs * rhs;
static Vector3 operator *(Quaternion rotation, Vector3 point)乘法運算符重載,表示對一個矢量point施加旋轉(zhuǎn)rotation,得出旋轉(zhuǎn)后的結(jié)果矢量。用法示例:Vector3 result=rotation * point
驗證前方上方矢量表示法
為了驗證前方上方矢量表示法的實際上方會重新計算,我設(shè)計了以下小實驗。

在場景中設(shè)置三個物體,它們的朝向是打亂的,從左到右分別對應(yīng)1、2、3。可以使用以下代碼將三個物體朝向調(diào)整為一致。
//前方上方矢量界定法的實際上方會重新計算
m_t1.transform.rotation = Quaternion.LookRotation(Vector3.forward, Vector3.up);
m_t2.transform.rotation = Quaternion.LookRotation(Vector3.forward, new Vector3(0,0.5f,-0.5f));
m_t3.transform.rotation = Quaternion.LookRotation(Vector3.forward, new Vector3(0,0.5f,0.5f));
在start方法中執(zhí)行上述代碼后,如下:

三個物體朝向是一致的,也就說明了上方矢量確實是進行了重新計算。
總結(jié)幾種表示方法
下面使用代碼總結(jié)幾種表示法,對應(yīng)同樣的四元數(shù),大致有四種表示方法。
//旋轉(zhuǎn)量的4種表示形式
Quaternion q1=Quaternion.Euler(90, 0, 0);
Quaternion q2 = Quaternion.LookRotation(Vector3.down ,Vector3.forward);
Quaternion q3 = Quaternion.AngleAxis(90,Vector3.right);
Quaternion q4 = Quaternion.FromToRotation(Vector3.up, Vector3.forward);
showQ("q1",q1);
showQ("q2",q2);
showQ("q3",q3);
showQ("q4",q4);
它們的輸出結(jié)果是:

也就是說,這幾種形式表示的四元數(shù)結(jié)果完全相同。
將四元數(shù)旋轉(zhuǎn)應(yīng)用于子彈射擊示例
當槍管轉(zhuǎn)動起來,子彈仍然沿著正確的朝向發(fā)射出去,可以使用很簡單的幾句話,修改之前的代碼后如下:
Bullet_2 bullet = m_compPool.takeUnit();
//發(fā)射時,將子彈的初始位置為槍口的當前位置
bullet.m_transform.position = m_transform.position;
//將子彈的初始化旋轉(zhuǎn)設(shè)置為指向當前槍口前方
bullet.m_transform.rotation = Quaternion.LookRotation(m_transform.forward);
本文轉(zhuǎn)自:CSDN -Andrew的游戲世界,轉(zhuǎn)載此文目的在于傳遞更多信息,版權(quán)歸原作者所有。
原文鏈接:https://blog.csdn.net/AndrewFan/article/details/66676668