如何快速解决Unity中万向节死锁(gimbal lock)的问题
如何快速解決Unity中萬向節死鎖(gimbal lock)的問題
轉載連接:https://www.jianshu.com/p/59acdd1c9db8
萬向節死鎖的根本問題是歐拉角(EulerAngles)保存的信息不足以描述空間中的唯一轉向,四元數(Quaternion)是可以的。
關于萬向節死鎖的產生原因,網上有非常多的文章解釋,這里不做過多闡述。
舉個簡單的例子,如果在Unity中設置一個物件(target)的Transform的Rotation的localEulerAngles為(90f, 180f,-90f),即下面的代碼:
此時:
一個物件的3D空間可以用三種不同的值來表述。真是個災難。
問題的產生
項目中有個需求,需要實現一個類似Unity編輯器中Transform控制面板的功能,當拖動面板值時,能連續變換,并且能保存和恢復數據。效果如下:
原始程序
用戶輸入后,直接讀取面板值v1(Vector3),設置到target的localEulerAngles中;UI面板的值則實時從target.localEulerAngles獲取數據展示。
這種情況會出現一種bug,先用代碼重現下:
首先設置在Start中設置初始轉向為Vector3(0, 0, 90)
Update中模擬用戶拖拽x軸,增長x值
運行之后,當x值增長到90°時,就“卡”住不動了
查看Console值,當輸入值增長到90附近時,target.localEulerAngles發生了劇烈變化,導致值在死循環,所以就卡住了
改進程序A
private Vector3 start = new Vector3(0, 0, 90);//假設為數據恢復后的值private IEnumerator ChangeRotation2(){yield return new WaitForSeconds(0.01f);start.x += 0.1f;target.localEulerAngles = start;}
將用戶輸入的UI數據與target.localEulerAngles變成單向關聯,數據恢復后的值直接顯示到UI上,用戶更改的值(同樣也是UI顯示的值)用于存儲,并作用于target.localEulerAngles。
結果如下
卡死的問題雖然結局了,但是存儲的值是Vector3類型的歐拉角,也就意味著這種情況的死鎖問題解決了,今后可能會遇到其他情況的死鎖問題。
例如而且這里可以發現,start的x值在無限增長,而Inspector面板中的值有個[-180,180]的區間(這里是因為調快了步進值,所以看上去只有175)
改進程序B
首先要明確的一點就是需要把描述轉向的四元數存儲下來,其次要解決Inspector面板中的值在[-180,180]的區間的問題。
那么我們首先觀察下Unity本身Inspector是怎么表現的。
可以看到在x值是可以無限增長的。
那么我們的程序可以這樣設計:借助UnityEngine的Inspector來實現同樣的功能。
1、存儲的Unity Transform的localRotation值(Quaternion),恢復的時候,直接調用target.localRotation = q;
2、UI顯示的時候,直接從Inspector面板獲取該值顯示。
3、用戶輸入的時候,直接“設置到Inspector面板中”讓UnityEngine為我們解決其中的死鎖以及顯示問題。
程序如下
這正是我們需要的代碼,可惜的是在5.x版本中并沒有這樣的接口。好在這2個方法的底層方法都是有的,只不過是internal,那么只需要使用反射,就可以實現這樣的方法,代碼如下
public Vector3 ShowRotationLikeInspector(Transform t){var type = t.GetType();var mi = type.GetMethod("GetLocalEulerAngles", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);var rotationOrderPro = type.GetProperty("rotationOrder", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);var rotationOrder = rotationOrderPro.GetValue(t, null);var EulerAnglesInspector = mi.Invoke(t, new[] {rotationOrder});return (Vector3) EulerAnglesInspector;}public void SetRotationLikeInspector(Transform t, Vector3 v){var type = t.GetType();var mi = type.GetMethod("SetLocalEulerAngles", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);var rotationOrderPro = type.GetProperty("rotationOrder", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);var rotationOrder = rotationOrderPro.GetValue(t, null);mi.Invoke(t, new[] {v, rotationOrder});}最終運行起來,效果如下,這里借助Unity的內部機制,取巧地解決了萬向節死鎖(gimbal lock)的問題
總結
以上是生活随笔為你收集整理的如何快速解决Unity中万向节死锁(gimbal lock)的问题的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【斜率优化】【决策单调】xjb讲课
- 下一篇: 电化学传感器电路设计