unity獲取真實旋轉數據


今天做工具的時候遇到一個神奇的問題,具體就是在使用transform.eulerAngles拿到的數據是經過計算轉換的,簡單的說就是你面板上的數據是vector3(-100, 0, 0),而訪問transform對象拿到的eulerAngles屬性是vector3(260, 0, 0),並且會把大於360的值限制在360內,我做的工具是用animationCurve進行曲線旋轉,3個軸分別各自插值,但是拿到的這個eulerAngles屬性就沒法做反向插值旋轉,比如我要從x=10插值到x=-10,結果成了x=10到x=350的插值了,並且沒辦法做到旋轉很多圈。

拿到這個問題,我以為是我的工具在其他地方有設置去限定這個的值,因為我看到transform的檢視面板上是明明可以看到負值的。后來才發現監視面板上面是負值,但是調用transform對象拿到的eulerAngles的值就已經是轉換后的了。那不用說,肯定是Unity在背后做了工作,於是果斷反編譯了unity的transformInspactor類查看。
 
transformInspactor源碼:
 1 namespace UnityEditor
 2 {
 3     using System;
 4     using UnityEngine;
 5 
 6     [CustomEditor(typeof(Transform)), CanEditMultipleObjects]
 7     internal class TransformInspector : Editor
 8     {
 9         private SerializedProperty m_Position;
10         private TransformRotationGUI m_RotationGUI;
11         private SerializedProperty m_Scale;
12         private static Contents s_Contents;
13 
14         private void Inspector3D()
15         {
16             EditorGUILayout.PropertyField(this.m_Position, s_Contents.positionContent, new GUILayoutOption[0]);
17             this.m_RotationGUI.RotationField();
18             EditorGUILayout.PropertyField(this.m_Scale, s_Contents.scaleContent, new GUILayoutOption[0]);
19         }
20 
21         public void OnEnable()
22         {
23             this.m_Position = base.serializedObject.FindProperty("m_LocalPosition");
24             this.m_Scale = base.serializedObject.FindProperty("m_LocalScale");
25             if (this.m_RotationGUI == null)
26             {
27                 this.m_RotationGUI = new TransformRotationGUI();
28             }
29             this.m_RotationGUI.OnEnable(base.serializedObject.FindProperty("m_LocalRotation"), new GUIContent(LocalizationDatabase.GetLocalizedString("Rotation")));
30         }
31 
32         public override void OnInspectorGUI()
33         {
34             if (s_Contents == null)
35             {
36                 s_Contents = new Contents();
37             }
38             if (!EditorGUIUtility.wideMode)
39             {
40                 EditorGUIUtility.wideMode = true;
41                 EditorGUIUtility.labelWidth = EditorGUIUtility.currentViewWidth - 212f;
42             }
43             base.serializedObject.Update();
44             this.Inspector3D();
45             Transform target = base.target as Transform;
46             Vector3 position = target.position;
47             if (((Mathf.Abs(position.x) > 100000f) || (Mathf.Abs(position.y) > 100000f)) || (Mathf.Abs(position.z) > 100000f))
48             {
49                 EditorGUILayout.HelpBox(s_Contents.floatingPointWarning, MessageType.Warning);
50             }
51             base.serializedObject.ApplyModifiedProperties();
52         }
53 
54         private class Contents
55         {
56             public string floatingPointWarning = LocalizationDatabase.GetLocalizedString("Due to floating-point precision limitations, it is recommended to bring the world coordinates of the GameObject within a smaller range.");
57             public GUIContent positionContent = new GUIContent(LocalizationDatabase.GetLocalizedString("Position"), LocalizationDatabase.GetLocalizedString("The local position of this Game Object relative to the parent."));
58             public GUIContent scaleContent = new GUIContent(LocalizationDatabase.GetLocalizedString("Scale"), LocalizationDatabase.GetLocalizedString("The local scaling of this Game Object relative to the parent."));
59         }
60     }
61 }

可以看到里面的旋轉屬性在檢視面板的顯示是由TransformRotationGUI這個類處理的

TransformRotationGUI源碼:

 1 namespace UnityEditor
 2 {
 3     using System;
 4     using UnityEngine;
 5 
 6     [Serializable]
 7     internal class TransformRotationGUI
 8     {
 9         private Vector3 m_EulerAngles;
10         private Vector3 m_OldEulerAngles = new Vector3(1000000f, 1E+07f, 1000000f);
11         private RotationOrder m_OldRotationOrder = RotationOrder.OrderZXY;
12         private SerializedProperty m_Rotation;
13         private GUIContent rotationContent = new GUIContent("Rotation", "The local rotation of this Game Object relative to the parent.");
14         private static int s_FoldoutHash = "Foldout".GetHashCode();
15         private Object[] targets;
16 
17         public void OnEnable(SerializedProperty m_Rotation, GUIContent label)
18         {
19             this.m_Rotation = m_Rotation;
20             this.targets = m_Rotation.serializedObject.targetObjects;
21             this.m_OldRotationOrder = (this.targets[0] as Transform).rotationOrder;
22             this.rotationContent = label;
23         }
24 
25         public void RotationField()
26         {
27             this.RotationField(false);
28         }
29 
30         public void RotationField(bool disabled)
31         {
32             Transform transform = this.targets[0] as Transform;
33             Vector3 localEulerAngles = transform.GetLocalEulerAngles(transform.rotationOrder);
34             if (((this.m_OldEulerAngles.x != localEulerAngles.x) || (this.m_OldEulerAngles.y != localEulerAngles.y)) || ((this.m_OldEulerAngles.z != localEulerAngles.z) || (this.m_OldRotationOrder != transform.rotationOrder)))
35             {
36                 this.m_EulerAngles = transform.GetLocalEulerAngles(transform.rotationOrder);
37                 this.m_OldRotationOrder = transform.rotationOrder;
38             }
39             bool flag = false;
40             bool flag2 = false;
41             for (int i = 1; i < this.targets.Length; i++)
42             {
43                 Transform transform2 = this.targets[i] as Transform;
44                 Vector3 vector2 = transform2.GetLocalEulerAngles(transform2.rotationOrder);
45                 flag |= ((vector2.x != localEulerAngles.x) || (vector2.y != localEulerAngles.y)) || !(vector2.z == localEulerAngles.z);
46                 flag2 |= transform2.rotationOrder != transform.rotationOrder;
47             }
48             Rect totalPosition = EditorGUILayout.GetControlRect(true, EditorGUIUtility.singleLineHeight * (!EditorGUIUtility.wideMode ? ((float) 2) : ((float) 1)), new GUILayoutOption[0]);
49             GUIContent label = EditorGUI.BeginProperty(totalPosition, this.rotationContent, this.m_Rotation);
50             EditorGUI.showMixedValue = flag;
51             EditorGUI.BeginChangeCheck();
52             int id = GUIUtility.GetControlID(s_FoldoutHash, FocusType.Keyboard, totalPosition);
53             string str = "";
54             if (AnimationMode.InAnimationMode() && (transform.rotationOrder != RotationOrder.OrderZXY))
55             {
56                 if (flag2)
57                 {
58                     str = "Mixed";
59                 }
60                 else
61                 {
62                     str = transform.rotationOrder.ToString();
63                     str = str.Substring(str.Length - 3);
64                 }
65                 label.text = label.text + " (" + str + ")";
66             }
67             totalPosition = EditorGUI.MultiFieldPrefixLabel(totalPosition, id, label, 3);
68             totalPosition.height = EditorGUIUtility.singleLineHeight;
69             using (new EditorGUI.DisabledScope(disabled))
70             {
71                 this.m_EulerAngles = EditorGUI.Vector3Field(totalPosition, GUIContent.none, this.m_EulerAngles);
72             }
73             if (EditorGUI.EndChangeCheck())
74             {
75                 Undo.RecordObjects(this.targets, "Inspector");
76                 foreach (Transform transform3 in this.targets)
77                 {
78                     transform3.SetLocalEulerAngles(this.m_EulerAngles, transform3.rotationOrder);
79                     if (transform3.parent != null)
80                     {
81                         transform3.SendTransformChangedScale();
82                     }
83                 }
84                 this.m_Rotation.serializedObject.SetIsDifferentCacheDirty();
85             }
86             EditorGUI.showMixedValue = false;
87             if (flag2)
88             {
89                 EditorGUILayout.HelpBox("Transforms have different rotation orders, keyframes saved will have the same value but not the same local rotation", MessageType.Warning);
90             }
91             EditorGUI.EndProperty();
92         }
93     }
94 }

再仔細看里面得到eulerAngles的方法:

1 // 拿到transform對象
2 Transform transform = this.targets[0] as Transform;
3 
4 // 調用transform的GetLocalEulerAngles方法獲得原生值(此方法是直接調用到unity的native層)
5 Vector3 localEulerAngles = transform.GetLocalEulerAngles(transform.rotationOrder);

再看反編譯的Transform類的GetLocalEulerAngles方法和eulerAngles屬性:

public Vector3 eulerAngles
{
    get
        {
            return this.rotation.eulerAngles;
        }
        set
        {
                this.rotation = Quaternion.Euler(value);
        }
}

eulerAngles是用的緩存的四元素,GetLocalEulerAngles是直接訪問去了底層。

於是用反射去調用GetLocalEulerAngles,最終結果果然就是檢視面板上面的真實值。

請看結果:

最后上調用該方法的代碼:

 1 // 獲取原生值
 2 System.Type transformType = transform.GetType();
 3 PropertyInfo m_propertyInfo_rotationOrder = transformType.GetProperty("rotationOrder", BindingFlags.Instance | BindingFlags.NonPublic);
 4 object m_OldRotationOrder = m_propertyInfo_rotationOrder.GetValue(transform, null);
 5 MethodInfo m_methodInfo_GetLocalEulerAngles = transformType.GetMethod("GetLocalEulerAngles", BindingFlags.Instance | BindingFlags.NonPublic);
 6 object value = m_methodInfo_GetLocalEulerAngles.Invoke(transform, new object[] { m_OldRotationOrder });
 7 
 8 
 9 Debug.LogError("反射調用GetLocalEulerAngles方法獲得的值:" + value.ToString());
10 Debug.LogError("transform.localEulerAngles獲取的值:" + transform.localEulerAngles.ToString());

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM