事情的起因还是因为一段代码,因为在做一个2D TileBase的游戏 所以需要有一个简单的 Tile坐标到世界坐标的变换
public static Vector3 GetTileWorldPosByTileIndex(int _tileIndexX, int _tileIndexY , Vector3 _result) { if(_result == null) { _result = new Vector3(); } _result.x = TileConst.TILE_WIDTH * _tileIndexX; _result.y = TileConst.TILE_HEIGHT * _tileIndexY; _result.z = 0; return _result; }
代码逻辑很简单,特殊的地方就是后面传入的Vector3,因为函数会被经常调用 所以不想每次都New出来一个新的Vector3. OK 运行..
Warning CS0472: The result of comparing value type `UnityEngine.Vector3‘ with null is `false‘
Unreachable code detected
WTF?! 哪里错了? Vector3 居然不能和null 判等? 嘿经过我一通测试 果真发现一些问题
来看如下的代码
public class Test01 : MonoBehaviour { void Start () { int inputInt = 500; int outputInt = SetIntWithRandom (inputInt); Debug.Log (inputInt); } public int SetIntWithRandom(int _input) { _input = Random.Range(-300,300); return _input; } }
这段应该很简单,弄出来一个int 类型然后传入函数内部, 然后在Log出来 看看是否有发生改变。 Ok 运行
Log结果 500,
说明没有发生任何改变。 也就是说 int 类型的变量是 传值不是传址的
再换下一组
public class Test01 : MonoBehaviour { void Start () { int[] inputIntArray = new int[2]; inputIntArray [0] = 500; int[] outputIntArray = SetIntArrayWithRandom (inputIntArray); Debug.Log (inputIntArray [0]); } public int[] SetIntArrayWithRandom(int[] _inputIntArray) { _inputIntArray[0] = Random.Range(-300,300); return _inputIntArray; } }
Log结果 -89 发生改变. 对于Array来说 是传址不是传值的.
Ok 来看 Vector3
public class Test01 : MonoBehaviour { void Start () { Vector3 inputV3 = new Vector3 (); inputV3.x = 500; Vector3 outputV3 =SetV3ValueWithRandom (inputV3); Debug.Log (inputV3.x); } public Vector3 SetV3ValueWithRandom (Vector3 _result) { _result.x = Random.Range (-300, 300); return _result; } }
Log结果 500.
也就是说呢, 虽然Vector3 初始化时候 需要用New 操作符, 但是Vector3 却是一个基础类型 和 float,int 一样
之前有很多类似的地方都是想节约内存不每次进行new操作,于是类中做了一个引用,然后函数时候将引用传过去。
Vector3 inputV3 = new Vector3 (); inputV3 =SetV3ValueWithRandom (inputV3)
现在看来,其实一点都没有省...
这个也解释了 为什么再给 transfrom的position赋值时候不能
transform.position.x = 100; 这样去做 会报错说
Error CS1612: Cannot modify a value type return value of `UnityEngine.Transform.position‘. Consider storing the value in a temporary variable (CS1612)
我又做了几个相关的测试,实在懒得写了 :) 就把相关结果说一下吧(有兴趣可以私聊哈)
1· 每次去New Vector3 对性能开销大么?
我Profile了一下, 在一个Update里面 循环去new 10w个 Vector3, CPU和内存都没有任何的波动.
vod Update() { Vector3 tmp; for(int i=0 ; i<100000;i++) { Vector3 tmp = new Vector3(); tmp.x = Random.Range (-300, 300); } transform.position = tmp }
也就是完全把它当int来看就好了,虽然使用的是New操作符 总感觉 要有很大动静似的...
vod Update() { int tmp; for(int i=0 ; i<100000;i++) { tmp = Random.Range (-300, 300); } }
2· 虽然开销很小 但是我还是想类中保留一个引用,然后不用每次去New出来 应该怎么做?
直接在函数的参数中改为ref即可, 感觉ref是C# 中很变态的东西 int啊 float啊什么的 都能ref (之前接触到得As3,Java是不行的 从C++上面继承来的特性吧 这个应该是)
public static void GetTileWorldPosByTileIndex(int _tileIndexX, int _tileIndexY , ref Vector3 _result) { _result.x = TileConst.TILE_WIDTH * _tileIndexX; _result.y = TileConst.TILE_HEIGHT * _tileIndexY; _result.z = 0; }
3· 注意一下 Nullable Type
可以看下这篇文章 http://unitypatterns.com/nullable-types/
两个问题,一个是说
Vector3 tmp; Debug.Log(tmp.x) // 这里会有结果,结果是0
也就是说 Vector3 在没有new操作时候 是有默认值的 和 布尔默认值是false, int默认值是0 一个道理
第二个 如果不希望这样的话 那就要使用 牛逼操作符 问号..
Vector3? tmp; if(tmp.HasValue) { Debug.Log(tmp.Value); }
在Vector3后面加一个问号 将其转变为Nullable Type 然后就可以用HasValue判断是否有值 然后用 xxx.Value获得这个值了
OK 继续搞游戏去了..
Best
Eran