一、Transform和transform
我們來詳談Unity的transform使用,這里所說的tansform不是類UnityEngine命名空間下的Transform,而是transform.
Transform 是Unity中最常用的類了。
其類的代碼如下,代碼貼出來太長也不是要說的重點:
#region 程序集 UnityEngine, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null // H:\Unity\Project\VRThemePark_03\Library\UnityAssemblies\UnityEngine.dll #endregion using System; using System.Collections; using UnityEngine.Internal; namespace UnityEngine { // // 摘要: // /// // Position, rotation and scale of an object. // /// public class Transform : Component, IEnumerable { protected Transform(); // // 摘要: // /// // The number of children the Transform has. // /// public int childCount { get; } // // 摘要: // /// // The rotation as Euler angles in degrees. // /// public Vector3 eulerAngles { get; set; } // // 摘要: // /// // The blue axis of the transform in world space. // /// public Vector3 forward { get; set; } // // 摘要: // /// // Has the transform changed since the last time the flag was set to 'false'? // /// public bool hasChanged { get; set; } // // 摘要: // /// // The transform capacity of the transform's hierarchy data structure. // /// public int hierarchyCapacity { get; set; } // // 摘要: // /// // The number of transforms in the transform's hierarchy data structure. // /// public int hierarchyCount { get; } // // 摘要: // /// // The rotation as Euler angles in degrees relative to the parent transform's rotation. // /// public Vector3 localEulerAngles { get; set; } ..... public void Translate(float x, float y, float z, Transform relativeTo); } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
我們所說的是常用的還有對象組件自身的transform,他里面包含了位置,旋轉,縮放參數。
在常用組件Compnent的代碼中:
// // 摘要: // /// // The Transform attached to this GameObject (null if there is none attached). // /// public Transform transform { get; }
- 1
- 2
- 3
- 4
- 5
- 6
- 1
- 2
- 3
- 4
- 5
- 6
注意這個東西是屬性,有get,沒有set.
當然命名空間仍舊為UnityEngine。
二、transform用法及其原因
我們先來看看,這個WrapperlessIcall ,它是unity中一個屬性字段,他有什么用呢?
WrapperlessIcall 內部實現,非公開方法。
大家來看看如下代碼:
private Transform myTransform; void Awake() { myTransform = transform; }
- 1
- 2
- 3
- 4
- 1
- 2
- 3
- 4
看起來稀松平常,波瀾不驚,但是下面水還是蠻深的。
使用myTransform替代this.transform。如果你不知道u3d內部實現獲取方式你肯定會以為這人腦抽水了,有直接的不用,還自己保存起來。
this.transform並不是變量,而是一個get/set屬性(property)
他是一個C++寫的代碼,在Mono中被調用。調用是intenal method的調用,其效率本身不是高。
比如,transform 經常需要保存在本地,然后在使用。
namespace UnityEngine
{
public class Component : Object { public extern Transform transform { [WrapperlessIcall] [MethodImpl(MethodImplOptions.InternalCall)] get; } } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
值得注意的是這個調用方法略慢,因為你需要調用外部的CIL(aka interop),花費了額外的性能。
三、新的版本會不會做了優化呢?
個人覺得這個是之前的unity版本的東西,可能效率和性能沒有做優化。
WrapperlessIcall] [MethodImpl(MethodImplOptions.InternalCall)]
- 1
- 2
- 1
- 2
就這些屬性來說,有的是直接調用C++代碼,有的則是調用.net的內部函數到Unity中。
對於新的版本是不是有的優化處理呢,自己做了測試:
先看看現在Compnent的代碼:
// // 摘要: // /// // The Transform attached to this GameObject (null if there is none attached). // /// public Transform transform { get; }
- 1
- 2
- 3
- 4
- 5
- 6
- 1
- 2
- 3
- 4
- 5
- 6
然后是測試代碼:
using UnityEngine; using System.Collections; using System.Diagnostics; using System.Reflection; using System; public class CacheTest : MonoBehaviour { const int ITERATIONS = 1000000; // Use this for initialization Transform cached; IEnumerator Start() { cached = transform; UnityEngine.Debug.Log("test........."); while(true) { yield return null; if (Input.GetKeyDown(KeyCode.T)) break; var sw1 = Stopwatch.StartNew(); { Transform trans1; for (int i = 0; i < ITERATIONS; i++) trans1 = GetComponent<Transform>(); } sw1.Stop(); var sw2 = Stopwatch.StartNew(); { Transform trans2; for (int i = 0; i < ITERATIONS; i++) trans2 = transform; } sw2.Stop(); var sw3 = Stopwatch.StartNew(); { Transform trans3; for (int i = 0; i < ITERATIONS; i++) trans3 = cached; } sw3.Stop(); var sw4 = Stopwatch.StartNew(); { Transform trans4; for (int i = 0; i < ITERATIONS; i++) trans4 = this.transform; } sw4.Stop(); UnityEngine.Debug.Log(ITERATIONS + " iterations"); UnityEngine.Debug.Log("GetComponent " + sw1.ElapsedMilliseconds + "ms"); UnityEngine.Debug.Log("this.transform" + sw4.ElapsedMilliseconds + "ms"); UnityEngine.Debug.Log("CachedMB " + sw2.ElapsedMilliseconds + "ms"); UnityEngine.Debug.Log("Manual Cache " + sw3.ElapsedMilliseconds + "ms"); } } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
結果還是一樣的。還是需要做處理的。
效率有手動cache (4ms)>>transform(20ms)>>this.tranform(22ms)>> GetComponent()(54ms)
但是原來的測試結果為:
1000000 次的Iterations ● GetComponent = 619ms ● Monobehaviour = 60ms ● CachedMB = 8ms ● Manual Cache = 3ms
- 1
- 2
- 3
- 4
- 5
- 1
- 2
- 3
- 4
- 5
看來這其中還是有奧秘的。
我電腦配置目前還算可以,win7 64 位,I7-3770 + 960顯卡+ 16G內存。
結果對比,相對與之前2012年Unity版本,可能mono做了很大的優化,當然我們的電腦可能還是不一樣,沒有辦法直接做對比,也只能猜測而已。
但是結論還是一樣的:
在以后的使用中,若大量使用,還是需把transform給手動保存下來吧。
說明:
代碼做了修改:
原來的代碼中有這些:
1. Transform _transform; 2. public Transform transform 3. { 4. get { return _transform ?? (_transform = base.transform); } 5. } 6. 7. 8. //for testing 9. public Transform 10. { 11. get { return base.transform; } 12. }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
四、強制拔高啦!!
我還想努力一把!!
讓別人可以直接用,但是有不修改原有代碼:
怎么辦呢?
既然大家都要繼承monobehaviour,那我就在他上面想辦法。
4.1 方法一,實現一個擴展方法:
public static class ExtendMono { public static Transform tranform(this MonoBehaviour tsf) { Transform _tsf; _tsf = tsf.transform; return _tsf; } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
但是這個擴展方法,必須靜態的,所以沒有辦法做一個靜態的臨時變量啊,這個不靠譜啊。
若寫成上面的代碼效率並沒有太多提高。每次還是需要賦值。
所以這個路走不通啊!!
來看方法二吧。
4.2 方法二,乾坤大挪移,重新命名類
public class MonoBehaviour : UnityMonoBehaviour { Transform _transform; public Transform transform { get { return _transform ?? (_transform = base.transform); } } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
UnityMonoBehaviour 這個是啥呢?哈哈
看using UnityMonoBehaviour = UnityEngine.MonoBehaviour;
我們來看看結果:
直接使用tranform 和this.tranform花費時間為9ms,比上面的20多ms,那是降低了很多。
但是,這個是結論啊,還是沒有手動緩存的效果高啊,依舊為4ms。
放出所有代碼:
using UnityMonoBehaviour = UnityEngine.MonoBehaviour; using UnityEngine; using System.Collections; using System.Diagnostics; using System.Reflection; using System; namespace aa { public class CacheTest : MonoBehaviour { const int ITERATIONS = 1000000; // Use this for initialization Transform cached; IEnumerator Start() { cached = transform; UnityEngine.Debug.Log("test........."); while (true) { yield return null; if (Input.GetKeyDown(KeyCode.T)) break; var sw1 = Stopwatch.StartNew(); { Transform trans1; for (int i = 0; i < ITERATIONS; i++) trans1 = GetComponent<Transform>(); } sw1.Stop(); var sw2 = Stopwatch.StartNew(); { Transform trans2; for (int i = 0; i < ITERATIONS; i++) trans2 = transform; } sw2.Stop(); var sw3 = Stopwatch.StartNew(); { Transform trans3; for (int i = 0; i < ITERATIONS; i++) trans3 = cached; } sw3.Stop(); var sw4 = Stopwatch.StartNew(); { Transform trans4; for (int i = 0; i < ITERATIONS; i++) trans4 = this.transform; } sw4.Stop(); UnityEngine.Debug.Log(ITERATIONS + " iterations"); UnityEngine.Debug.Log("GetComponent " + sw1.ElapsedMilliseconds + "ms"); UnityEngine.Debug.Log("this.transform " + sw4.ElapsedMilliseconds + "ms"); UnityEngine.Debug.Log("CachedMB " + sw2.ElapsedMilliseconds + "ms"); UnityEngine.Debug.Log("Manual Cache " + sw3.ElapsedMilliseconds + "ms"); } } } public class MonoBehaviour : UnityMonoBehaviour { Transform _transform; public Transform transform { get { return _transform ?? (_transform = base.transform); } } } //public static class ExtendMono //{ // public static Transform tranform(this MonoBehaviour tsf) // { // Transform _tsf; // _tsf = tsf.transform; // return _tsf; // } //} }
