本文章由cartzhang编写,转载请注明出处。 所有权利保留。
文章链接:http://blog.csdn.net/cartzhang/article/details/52862118
作者:cartzhang
我们来详谈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);
}
}
我们所说的是常用的还有对象组件自身的transform,他里面包含了位置,旋转,缩放参数。
在常用组件Compnent的代码中:
//
// 摘要:
// ///
// The Transform attached to this GameObject (null if there is none attached).
// ///
public Transform transform { get; }
注意这个东西是属性,有get,没有set.
当然命名空间仍旧为UnityEngine。
我们先来看看,这个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,那我就在他上面想办法。
public static class ExtendMono
{
public static Transform tranform(this MonoBehaviour tsf)
{
Transform _tsf;
_tsf = tsf.transform;
return _tsf;
}
}
但是这个扩展方法,必须静态的,所以没有办法做一个静态的临时变量啊,这个不靠谱啊。
若写成上面的代码效率并没有太多提高。每次还是需要赋值。
所以这个路走不通啊!!
来看方法二吧。
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——————————
若有问题,请随时联系!!
非常感谢!!