事情的起因還是因為一段代碼,因為在做一個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
PS: 寫完以後被各種大神教育了一下,Struct問題 呵呵
其實Vector3是一個Struct 所以才有這種特性,如果有興趣可以看下MSDN
https://msdn.microsoft.com/en-us/library/aa288471%28v=vs.71%29.aspx
摘錄一段:
Structs may seem similar to classes, but there are important differences that you should be aware of. First of all, classes are reference types and structs are value types. By using structs, you can create objects that behave like the built-in types and enjoy their benefits as well.
When you call the New operator on a class, it will be allocated on the heap. However, when you instantiate a struct, it gets created on the stack. This will yield performance gains. Also, you will not be dealing with references to an instance of a struct as you would with classes. You will be working directly with the struct instance. Because of this, when passing a struct to a method, it's passed by value instead of as a reference.