日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

如何快速解决Unity中万向节死锁(gimbal lock)的问题

發布時間:2024/3/12 编程问答 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 如何快速解决Unity中万向节死锁(gimbal lock)的问题 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

如何快速解決Unity中萬向節死鎖(gimbal lock)的問題

轉載連接:https://www.jianshu.com/p/59acdd1c9db8

萬向節死鎖的根本問題是歐拉角(EulerAngles)保存的信息不足以描述空間中的唯一轉向,四元數(Quaternion)是可以的。
關于萬向節死鎖的產生原因,網上有非常多的文章解釋,這里不做過多闡述。
舉個簡單的例子,如果在Unity中設置一個物件(target)的Transform的Rotation的localEulerAngles為(90f, 180f,-90f),即下面的代碼:

target.localEulerAngles = new Vector3(90f, 180f,-90f);

此時:

  • 此時獲取target.localEulerAngles的值為(90,270,0)
  • 此時獲取target的Inspector面板值為(90,0,90)
  • 一個物件的3D空間可以用三種不同的值來表述。真是個災難。

    問題的產生

    項目中有個需求,需要實現一個類似Unity編輯器中Transform控制面板的功能,當拖動面板值時,能連續變換,并且能保存和恢復數據。效果如下:

    原始程序

    用戶輸入后,直接讀取面板值v1(Vector3),設置到target的localEulerAngles中;UI面板的值則實時從target.localEulerAngles獲取數據展示。

    這種情況會出現一種bug,先用代碼重現下:

    首先設置在Start中設置初始轉向為Vector3(0, 0, 90)
    Update中模擬用戶拖拽x軸,增長x值

    private void Start(){target.localEulerAngles = new Vector3(0, 0, 90);}private void Update(){StartCoroutine(ChangeRotation());}private IEnumerator ChangeRotation(){yield return new WaitForSeconds(0.1f);var original = target.localEulerAngles;original.x += 0.1f;target.localEulerAngles = original;}

    運行之后,當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為我們解決其中的死鎖以及顯示問題。
    程序如下

    private IEnumerator ChangeRotation3(){yield return new WaitForSeconds(1f);var original = ShowRotationLikeInspector(target);original.x += 0.1f;SetRotationLikeInspector(target, original);} 那么這個方案最大的問題是如何實現2和3步驟,查看[Unity 2018.1.0b12](https://github.com/Unity-Technologies/UnityCsReference)源碼可以發現這樣的代碼

    這正是我們需要的代碼,可惜的是在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)的问题的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。