Unity3D遊戲開發最佳實踐20技巧(一)

七大黍發表於2014-10-17


關於這些技巧這些技巧不可能適用於每個專案。

  • 這些是基於我的一些專案經驗,專案團隊的規模從3人到20人不等;
  • 框架結構的可重用性、清晰程度是有代價的——團隊的規模和專案的規模決定你要在這個上面付出多少;
  • 很多技巧是品味的問題(這裡所列的所有技巧,可能有同樣好的技術替代方案);
  • 一些技巧可能是對傳統的Unity開發的一個衝擊。例如,使用prefab替代物件例項並不是一個傳統的Unity風格,並且這樣做的代價還挺高的(需要很多的preffab)。也許這些看起來有些瘋狂,但是在我看來是值得的。

【流程】

1、避免Assets分支
所有的Asset都應該只有一個唯一的版本。如果你真的需要一個分支版本的Prefab、Scene或是Mesh,那你要制定一個非常清晰的流程,來確定哪個是正確的版本。錯誤的分支應該起一個特別的名字,例如雙下劃線字首:__MainScene_Backup。Prefab版本分支需要一個特別的流程來保證安全(詳見Prefabs一節)。
2、如果你在使用版本控制的話,每個團隊成員都應該保有一個專案的Second Copy用來測試修改之後,Second Copy和Clean Copy都應該被更新和測試。大家都不要修改自己的Clean Copy。這對於測試Asset丟失特別有用。
3、考慮使用外部的關卡編輯工具
不是一個完美的關卡編輯器。例如,我們使用TuDee來建立3D Tile-Based的遊戲,這使我們可以獲得對Tile友好的工具的益處(網格約束,90度倍數的旋轉,2D檢視,快速Tile選擇等)。從一個XML檔案來例項化Prefab也很簡單。詳見Guerrilla Tool Development。
4、考慮把關卡儲存為XML,而非scene
這是一種很奇妙的技術:
  • 它可以讓你不必每個場景都設定一遍;
  • 他可以載入的更快(如果大多數物件都是在場景之間共享的)。
  • 它讓場景的版本合併變的簡單(就算是Unity的新的文字格式的Scene,也由於資料太多,而讓版本合併變的不切實際)。
  • 它可以使得在關卡之間保持資料更簡便。
你仍就可以使用Unity作為關卡編輯器(儘管你用不著了)。你需要寫一些你的資料的序列化和反序列化的程式碼,並實現在編輯器和遊戲執行時載入關卡、在編輯器中儲存關卡。你可能需要模仿Unity的ID系統來維護物件之間的引用關係。

5、考慮編寫通用的自定義Inspector程式碼
實現自定義的Inspector是很直截了當的,但是Unity的系統有很多的缺點:
  • 它不支援從繼承中獲益;
  • 它不允許定義欄位級別的Inspector元件,而只能是class型別級別。舉個例子,如果沒有遊戲物件都有一個ScomeCoolType欄位,而你想在Inspector中使用不同的渲染,那麼你必須為你的所有class寫Inspector程式碼。
你可以通過從根本上重新實現Inspector系統來處理這些問題。通過一些反射機制的小技巧,他並不像看上去那麼看,文章底部(日後另作翻譯)將提供更多的實現細節。

                                                                                                                                         
【場景組織】
6、使用命名的空Game Object來做場景目錄
仔細的組織場景,就可以方便的找到任何物件。
7、把控制物件和場景目錄(空Game Objec)放在原點(0,0,0)
如果位置對於這個物件不重要,那麼就把他放到原點。這樣你就不會遇到處理Local Space和World Space的麻煩,程式碼也會更簡潔。
8、儘量減少使用GUI元件的offset
通常應該由控制元件的Layout父物件來控制Offset;它們不應該依賴它們的爺爺節點的位置。位移不應該互相抵消來達到正確顯示的目的。做基本上要防止了下列情況的發生:
父容器被放到了(100,-50),而位元組點應該在(10,10),所以把他放到(90,60)[父節點的相對位置]。
這種錯誤通常放生在容器不可見時。
9、把世界的地面放在Y=0
這樣可以更方便的把物件放到地面上,並且在遊戲邏輯中,可以把世界作為2D空間來處理(如果合適的話),例如AI和物理模擬。
10、使遊戲可以從每個Scene啟動
這將大大的降低測試的時間。為了達到所有場景可執行,你需要做兩件事:
首先,如果需要前面場景執行產生的一些資料,那麼要模擬出它們。
其次,生成在場景切換時必要儲存的物件,可以是這樣:
myObject = FindMyObjectInScene(); if (myObjet == null){   myObject = SpawnMyObject();}
                                                                                                                                                                  
【美術】
11、把角色和地面物體的中心點(Pivot)放在底部,不要放在中間
這可以使你方便的把角色或者其他物件精確的放到地板上。如果合適的話,它也可能使得遊戲邏輯、AI、甚至是物理使用2D邏輯來表現3D。
12、統一所有的模型的面朝向(Z軸正向或者反向)
對於所有具有面朝向的物件(例如角色)都應該遵守這一條。在統一面朝向的前提下,很多演算法可以簡化。
13、在開始就把Scale搞正確
請美術把所有匯入的縮放係數設定為1,並且把他們的Transform的Scale設定為1,1,1。可以使用一個參考物件(一個Unity的Cube)來做縮放比較。為你的遊戲選擇一個世界的單位係數,然後堅持使用它。
14、為GUI元件或者手動建立的粒子製作一個兩個面的平面模型
設定這個平面面朝向Z軸正向,可能簡化Billboard和GUI建立。
15、製作並使用測試資源
  • 為SkyBox建立帶文字的方形貼圖;
  • 一個網格(Grid);
  • 為Shader測試使用各種顏色的平面:白色,黑色,50%灰度,紅,綠,藍,紫,黃,青;
  • 為Shader測試使用漸進色:黑到白,紅到綠,紅到藍,綠到藍;
  • 黑白格子;
  • 平滑的或者粗糙的法線貼圖;
  • 一套用來快速搭建場景的燈光(使用Prefa);

                                                                                                                                                                  
【Prefabs】
16、所有東西都使用Prefab
只有場景中的“目錄”物件不使用Prefab。甚至是那些只使用一次的唯一物件也應該使用Prefab。這樣可以在不動用場景的情況下,輕鬆修改他們。(一個額外的好處是,當你使用EZGUI時,這可以用來建立穩定的Sprite Atlases)
17、對於特例使用單獨的Prefab,而不要使用特殊的例項物件
如果你有兩種敵人的型別,並且只是屬性有區別,那麼為不同的屬性分別建立Prefab,然後連結他們。這可以:
  • 在同一個地方修改所有型別
  • 在不動用場景的情況下進行修改
如果你有很多敵人的型別,那麼也不要在編輯器中使用特殊的例項。一種可選的方案是程式化處理它們,或者為所有敵人使用一個核心的檔案/Prefab。使用一個下拉選單來建立不同的敵人,或者根據敵人的位置、玩家的進度來計算。
18、在Prefab之間連結,而不要連結例項物件
當Prefab放置到場景中時,它們的連結關係是被維護的,而例項的連結關係不被維護。儘可能的使用Prefab之間的連結可以減少場景建立的操作,並且減少場景的修改。
19、如果可能,自動在例項物件之間產生連結關係
如果你確實需要在例項之間連結,那麼應該在程式程式碼中去建立。例如,Player物件在Start時需要把自己註冊到GameManager,或者GameManager可以在Start時去查詢Player物件。
對於需要新增指令碼的Prefab,不要用Mesh作為根節點。當你需要從Mesh建立一個Prefab時,首先建立一個空的GameObject作為父物件,並用來做根節點。把指令碼放到根節點上,而不要放到Mesh節點上。使用這種方法,當你替換Mesh時,就不會丟失所有你在Inspector中設定的值了。
使用互相連結的Prefab來實現Prefab巢狀。Unity並不支援Prefab的巢狀,在團隊合作中第三方的實現方案可能是危險的,因為巢狀的Prefab之間的關係是不明確的。

相關文章