您現在的位置是:首頁 > 舞蹈首頁舞蹈

學前班遊戲開發入門3:Unity3D中的旋轉與引出的四元數知識

由 clq的程式設計師學前班 發表于 舞蹈2021-06-14
簡介position - transform

為什麼3dmax不能旋轉物體

我們百家號“運營”這麼一段時間以來,感覺太過具體的或者說過於專業的內容不是太受歡迎。比如前面郵件收發相減的內容,那系列在 cnblogs 可是異常火爆的,而且筆者花的心思是很多的,但在百家號中的閱讀量則很是慘淡 。。。 我也不明白其中的原因啊,也許是百家號都是手機來的使用者沒有太多的時間去作達深的思考,所以經驗總結類的比較受歡迎,具體技巧的則大家就沒時間多看了。這系列的 Unity3D 也是這樣,其實這些知識對於入門來說還是很重要的,要知道 Unity3D 的坑那是一點都不少 。。。 。。。 不過也不要緊,大家可以先大概瞭解一下,以後用到時記得回來仔細看就行了。

今天要說的是 Unity3D 中的旋轉,普通的旋轉在 u3d 中是相當簡單的,例如這樣:

obj。transform。Rotate(0, 0, 90);

其中的三個引數就是三維方向上的角度度數,只要學過初中的知識這些很容易就能用起來了。但在 u3d 中有一個操作非常的常見,那就是讓一個物體看向另外一個物體,比如讓攝像機一直跟著主角色。這在 u3d 中也是非常的簡單,例如:

obj。transform。LookAt(target);//看向某個物體

好了,當我們把這種方法應用到讓一個物體跟著另外一個物體走的時候就會出現奇怪的現象:這時候很可能不是物體的正面面向了另外一個物體,而是反面或者是側面面對的其他物體。這是為什麼呢?原因是因為這個 lookat 函式預設是讓物體的 z 軸對向物體,而我們要操作的這個物體很可能是從 3dmax 或者 maya 中匯入的,它們的座標軸和 u3d 的通常是不一樣的,所以很多教程在文章說會特別說明在匯出模型時要先設定軸的一些引數。這個辦法當然不錯,但有時候這個模型是別人已經匯出的,透過還附有別的引數,是不能重新匯出軸引數的。這時候就要先計算出旋轉的引數後再繞著自身的座標再旋轉那個軸與 u3d 軸的差值。這個演算法如果要用我們高中學過的三角函式來算的話相當的繁瑣,在網上的解決方案五花八門,可惜都不是太正確,如果真要算的話,正確的程式碼如下:

//obj。transform。rotation = Quaternion。LookRotation(target。transform。position - obj。transform。position, Vector3。up); //先看向目標

//obj。transform。rotation = obj。transform。rotation * Quaternion。Euler(new Vector3(90, 0, 0)); //再調整自身的座標

q4 = Quaternion。LookRotation(target。transform。position - obj。transform。position, Vector3。up); //先看向目標

q4 = q4 * Quaternion。Euler(new Vector3(90, 0, 0)); //再調整自身的座標

obj。transform。rotation = q4;

程式碼用了兩種方向,如果直接操作物體的旋轉引數的話,那麼會看到物體動來動去顯然不行,所以要計算出兩步合成的結果後再一次性賦值給旋轉引數比較好。

而要把兩步的結果合成成一個,用三角函式算的話大家基本上算不出來吧 。。。 。。。 也不用自卑,數字家們也曾經頭痛過很長時間,據說解決這個問題的數字家是愛爾蘭的數學家Hamilton,他用到的解決方法就是我們這次要說的4元數。也不用害怕這個沒見過的“4元數”,它的意思其實只是說一個數據用4個數字表示,也不一定就用來表示角度,只不過在這裡我們特指一個旋轉的3維空間下的引數用4個浮點來表示,也就是一個角度用4個數據才能表示出來,而我們以前學的幾何中則是用3個數據就表示出來了(分別指定 x,y,z 三個軸上要旋轉的角度就可以了)。按一般的教程,這裡要狠狠地說一個4元數的各個解釋,不過我想說的是現在的軟體開發,特別是遊戲開發是應用科學,不應該也不用去細究那麼多,我們只要理解它是一個表示旋轉角度的引數就可以,然後記住操作它的那幾個函式就可以了(沒有幾個)。具體的函式大家可以再去查資料,這其中有一個方法一定要告訴大家,在 u3d 裡,兩個4元數相乘就是物體先旋轉到第一個角度然後再旋轉到第二個角度。這種用運算子號來表示某種結果的做法在 u3d 中很普遍,我個人覺得很不好,雖然用習慣了會覺得很方便,但是假如有一個沒學過的人看以上程式碼肯定是不明白的,所以我覺得應該封裝成函式,不應該暴露那麼多數字的細節。類似的兩個三維空間位置相減會得到它們的向量差(例如前面的 target。transform。position - transform。position),這也是很讓初學者崩潰的。另外向量這個詞也很讓人要回憶高中數字的,其實從應用的角度來說,可以認為是用三個方向上的數值來表示的角度 —— 所以在 u3d 有多種表示角度的方法:Quaternion(四元數)、尤拉角(其實就是三個軸上分別轉動的角度)、向量(其實就是在三個方向上的長度)。這當中當然是四元數最難理解了,所以我們乾脆就不要去理解好了。

當然了,以上是學院派的做法,實際上更多的做法是先建立一個空物體,然後把模型拉成空物體的子級(見下圖),然後在設計期間在裡面旋轉調整模型的角度就可以了,而外層空物體當做正常模型來使用就可以了。讓它直接 lookat 目標就行,不用兩次程式碼進行調整:

學前班遊戲開發入門3:Unity3D中的旋轉與引出的四元數知識

空物體做父級可輕鬆解決匯出模型軸問題

大家肯定鬆了一口氣。但這還不算完,如果是操作攝像機那沒什麼好說的,如果是操作模型,直接轉向是很生硬的,通常會加上一個漸變的過程,所以具體的程式碼實際上要變成正面這樣:

ship1。transform。rotation = Quaternion。Slerp(

ship1。transform。rotation,

Quaternion。LookRotation(target。transform。position - ship1。transform。position), //兩個位置在前後是不一樣的,原物體位置在前是最好的

//還可以這樣在最後的結果上再做調整,兩個角度相乘就是角度疊加了

//Quaternion。LookRotation(target。transform。position - ship1。transform。position) * Quaternion。Euler(new Vector3(90, 0, 0)), //尤拉角度轉 Quaternion, //兩個位置在前後是不一樣的,原物體位置在前是最好的

//10 * Time。deltaTime);

2。5f * Time。deltaTime);//速度

//transform。Rotate(0, 0, 90);//ok//不行,很抖動

具體的大家可以查 Quaternion。Slerp() 函式的用法,總的來說它就是在兩個角度狀態之間根據不同的速度計算出二者的漸進值。要特別說明的是原物體的位置最好作為第一個引數,要不可能是沒有漸變平滑效果出來的。

好了,這就是我們今天要說的內容。我知道大家一定都沒聽懂,不過沒關係,大家記得需要的時候回來這裡檢視就行了。