也谈谈Unity的transform使用

一、Transform和transform

我们来详谈Unity的transform使用,这里所说的tansform不是类UnityEngine命名空间下的Transform,而是transform. 
Transform 是Unity中最常用的类了。 
其类的代码如下,代码贴出来太长也不是要说的重点:

#region 程序集 UnityEngine, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
// H:UnityProjectVRThemePark_03LibraryUnityAssembliesUnityEngine.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;
    //    }
    //}
}
原文地址:https://www.cnblogs.com/chenliyang/p/6558717.html