zoukankan      html  css  js  c++  java
  • 也谈谈Unity的transform使用

    谈谈Unity的transform使用


    本文章由cartzhang编写,转载请注明出处。 所有权利保留。
    文章链接:http://blog.csdn.net/cartzhang/article/details/52862118
    作者:cartzhang

    一、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);
        }
    }


    我们所说的是常用的还有对象组件自身的transform,他里面包含了位置,旋转,缩放参数。
    在常用组件Compnent的代码中:

    //
    // 摘要:
     //     ///
     //     The Transform attached to this GameObject (null if there is none attached).
     //     ///
     public Transform transform { get; }

    注意这个东西是属性,有get,没有set.
    当然命名空间仍旧为UnityEngine。

    二、transform用法及其原因

    我们先来看看,这个WrapperlessIcall ,它是unity中一个属性字段,他有什么用呢?
    WrapperlessIcall 内部实现,非公开方法。
    大家来看看如下代码:

    private Transform myTransform;
    void Awake() {
        myTransform = transform;
    }


    看起来稀松平常,波澜不惊,但是下面水还是蛮深的。
    使用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;
            }
        }
    }


    值得注意的是这个调用方法略慢,因为你需要调用外部的CIL(aka interop),花费了额外的性能。

    三、新的版本会不会做了优化呢?

    个人觉得这个是之前的unity版本的东西,可能效率和性能没有做优化

    WrapperlessIcall]
    [MethodImpl(MethodImplOptions.InternalCall)]


    就这些属性来说,有的是直接调用C++代码,有的则是调用.net的内部函数到Unity中。
    对于新的版本是不是有的优化处理呢,自己做了测试:
    先看看现在Compnent的代码:

    //
    // 摘要:
    //     ///
    //     The Transform attached to this GameObject (null if there is none attached).
    //     ///
    public Transform transform { get; }


    然后是测试代码:

    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");
            }
        }
    }


    结果还是一样的。还是需要做处理的。

    这里写图片描述
    效率有手动cache (4ms)>>transform(20ms)>>this.tranform(22ms)>> GetComponent()(54ms)
    但是原来的测试结果为:

    1000000 次的Iterations
      ● GetComponent = 619ms
      ● Monobehaviour = 60ms
      ● CachedMB = 8ms
      ● Manual Cache = 3ms


    看来这其中还是有奥秘的。
    我电脑配置目前还算可以,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.     }

    四、强制拔高啦!!

    我还想努力一把!!
    让别人可以直接用,但是有不修改原有代码:
    怎么办呢?
    既然大家都要继承monobehaviour,那我就在他上面想办法。

    4.1 方法一,实现一个扩展方法:

      public static class ExtendMono
        {
            public static Transform tranform(this MonoBehaviour tsf)
            {
                Transform _tsf;
                _tsf = tsf.transform;
                return _tsf;
            }
        }


    但是这个扩展方法,必须静态的,所以没有办法做一个静态的临时变量啊,这个不靠谱啊。
    若写成上面的代码效率并没有太多提高。每次还是需要赋值。
    所以这个路走不通啊!!
    来看方法二吧。

    4.2 方法二,乾坤大挪移,重新命名类

     public class MonoBehaviour : UnityMonoBehaviour
        {
            Transform _transform;
            public Transform transform
            {
                get { return _transform ?? (_transform = base.transform); }
            }
        }


    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://forum.unity3d.com/threads/cachedmb.130365/
    http://pastebin.com/vYKK4pat
    http://blog.sina.com.cn/s/blog_5b6cb9500101fkal.html
    http://answers.unity3d.com/questions/700468/where-do-methods-mocked-with-wrapperlessicall-meth.html


    ————————-THE———END——————————
    若有问题,请随时联系!!
    非常感谢!!

  • 相关阅读:
    vscode写python时的代码错误提醒和自动格式化
    Python使用requests发送post请求的三种方式
    unittest参数化(paramunittest)
    pycharm快捷键及一些常用设置
    Navicat破解
    Python3 os.path() 模块笔记
    Python使用SMTP模块、email模块发送邮件
    五笔字典86版wubi拆字图编码查询
    根据字符串从资源中取出对应的资源ResourceManager.GetObject
    判断字符串是否是由相同的字符组成
  • 原文地址:https://www.cnblogs.com/qitian1/p/6461894.html
Copyright © 2011-2022 走看看