這是轉的別人的譯文http://www.cnblogs.com/zhaoqingqing/p/5677514.html
關於這些技巧
這些技巧不可能適用於每個項目。
- 這些是基於我的一些項目經驗,項目團隊的規模從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、考慮使用外部的關卡編輯工具
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之間的關係是不明確的。
20、使用安全的流程來處理Prefab分支
我們用一個名爲Player的Prefab來講解這個過程。
用下面這個流程來修改Player:
- 複製Player Prefab;
- 把複製出來的Prefab重命名爲__Player_Backup;
- 修改Player Prefab;
- 測試一切工作正常,刪除__Player_Backup;
不要把新複製的命名爲Player_New,然後修改它。
有些情況可能更復雜一些。例如,有些修改可能涉及到兩個人,上述過程有可能使得場景無法工作,而所有人必須停下來等他們修改完畢。如果修改能夠很快完成,那麼還用上面這個流程就可以。如果修改需要花很長時間,則可以使用下面的流程:
- 第一個人:
- 複製Player Prefab;
- 把它重命名爲__Player_WithNewFeature或者__Player_ForPerson2;
- 在複製的對象上做修改,然後提交給第二個人;
- 第二個人:
- 在新的Prefab上做修改;
- 複製Player Prefab,並命名爲__Player_Backup;
- 把__Player_WithNewFeature拖放到場景中,創建它的實例;
- 把這個實例拖放到原始的Player Prefab中;
- 如果一切工作正常,則可使刪除__Player_Backup和__Player_WithNewFeature;
擴展和MonoBehaviourBase
21、擴展一個自己的Mono Behaviour基類,然後自己的所有組件都從它派生
這可以使你方便的實現一些通用函數,例如類型安全的Invoke,或者是一些更復雜的調用(例如random等等)。
22、爲Invoke, StartCoroutine and Instantiate 定義安全調用方法
定義一個委託任務(delegate Task),用它來定義需要調用的方法,而不要使用字符串屬性方法名稱,例如:
-
public void Invoke(Task task, float time)
-
{
-
Invoke(task.Method.Name, time);
-
}
23、爲共享接口的組件擴展
有些時候把獲得組件、查找對象實現在一個組件的接口中會很方便。
下面這種實現方案使用了typeof,而不是泛型版本的函數。泛型函數無法在接口上工作,而typeof可以。下面這種方法把泛型方法整潔的包裝起來。
-
-
public I GetInterfaceComponent<I>() where I : class
-
{
-
return GetComponent(typeof(I)) as I;
-
}
-
-
public static List<I> FindObjectsOfInterface<I>() where I : class
-
{
-
MonoBehaviour[] monoBehaviours = FindObjectsOfType<MonoBehaviour>();
-
List<I> list = new List<I>();
-
-
foreach(MonoBehaviour behaviour in monoBehaviours)
-
{
-
I component = behaviour.GetComponent(typeof(I)) as I;
-
-
if(component != null)
-
{
-
list.Add(component);
-
}
-
}
-
-
return list;
-
}
24、使用擴展來讓代碼書寫更便捷
例如:
-
public static class CSTransform
-
{
-
public static void SetX(this Transform transform, float x)
-
{
-
Vector3 newPosition =
-
new Vector3(x, transform.position.y, transform.position.z);
-
-
transform.position = newPosition;
-
}
-
...
-
}
25、使用防禦性的GetComponent()
有些時候強制性組件依賴(通過RequiredComponent)會讓人蛋疼。例如,很難在Inspector中修改組件(即使他們有同樣的基類)。下面是一種替代方案,當一個必要的組件沒有找到時,輸出一條錯誤信息。
-
public static T GetSafeComponent<T>(this GameObject obj) where T : MonoBehaviour
-
{
-
T component = obj.GetComponent<T>();
-
-
if(component == null)
-
{
-
Debug.LogError("Expected to find component of type "
-
+ typeof(T) + " but found none", obj);
-
}
-
-
return component;
-
}
風格
26、避免對同一件事使用不同的處理風格
在很多情況下,某件事並不只有一個慣用手法。在這種情況下,在項目中明確選擇其中的一個來使用。下面是原因:
- 一些做法並不能很好的一起協作。使用一個,能強制統一設計方向,並明確指出不是其他做法所指的方向;
- 團隊成員使用統一的風格,可能方便大家互相的理解。他使得整體結構和代碼都更容易理解。這也可以減少錯誤;
幾組風格的例子:
- 協程與狀態機(Coroutines vs. state machines);
- 嵌套的Prefab、互相鏈接的Prefab、超級Prefab(Nested prefabs vs. linked prefabs vs. God prefabs);
- 數據分離的策略;
- 在2D遊戲的使用Sprite的方法;
- Prefab的結構;
- 對象生成策略;
- 定位對象的方法:使用類型、名稱、層、引用關係;
- 對象分組的方法:使用類型、名稱、層、引用數組;
- 找到一組對象,還是讓它們自己來註冊;
- 控制執行次序(使用Unity的執行次序設置,還是使用Awake/Start/Update/LateUpdate,還是使用純手動的方法,或者是次序無關的架構);
- 在遊戲中使用鼠標選擇對象/位置/目標:SelectionManager或者是對象自主管理;
- 在場景變換時保存數據:通過PlayerPrefs,或者是在新場景加載時不要銷燬的對象;
- 組合動畫的方法:混合、疊加、分層;
時間
27、維護一個自己的Time類,可以使遊戲暫停更容易實現
做一個“Time.DeltaTime”和""Time.TimeSinceLevelLoad"的包裝,用來實現暫停和遊戲速度縮放。這使用起來略顯麻煩,但是當對象運行在不同的時鐘速率下的時候就方便多了(例如界面動畫和遊戲內動畫)。
生成對象
28、不要讓遊戲運行時生成的對象搞亂場景層次結構
在遊戲運行時,爲動態生成的對象設置好它們的父對象,可以讓你更方便的查找。你可以使用一個空的對象,或者一個沒有行爲的單件來簡化代碼中的訪問。可以給這個對象命名爲“DynamicObjects”。
類設計
29、使用單件(Singleton)模式
從下面這個類派生的所有類,將自動獲得單件功能:
-
public class Singleton<T> : MonoBehaviour where T : MonoBehaviour
-
{
-
protected static T instance;
-
-
-
-
-
public static T Instance
-
{
-
get
-
{
-
if(instance == null)
-
{
-
instance = (T) FindObjectOfType(typeof(T));
-
-
if (instance == null)
-
{
-
Debug.LogError("An instance of " + typeof(T) +
-
" is needed in the scene, but there is none.");
-
}
-
}
-
-
return instance;
-
}
-
}
-
}
單件可以作爲一些管理器,例如ParticleManager或者AudioManager亦或者GUIManager。
- 對於那些非唯一的prefab實例使用單件管理器(例如Player)。不要爲了堅持這條原則把類的層次關係複雜化,寧願在你的GameManager(或其他合適的管理器中)中持有一個它們的引用。
- 對於外部經常使用的共有變量和方法定義爲static,這樣你可以這樣簡便的書寫“GameManager.Player”,而不用寫成“GameManager.Instance.player”。
30、在組件中不要使用public成員變量,除非它需要在inspector中調節
除非需要設計師(策劃or美術)去調節的變量,特別是它不能明確表明自己是做什麼的變量,不要聲明爲public。如果在這些特殊情況下,無法避免,則可使用兩個甚至四個下劃線來表明不要從外部調節它,例如:
-
public float __aVariable;
31、把界面和遊戲邏輯分開
這一條本質上就是指的MVC模式。
所有的輸入控制器,只負責向相應的組件發送命令,讓它們知道控制器被調用了。舉一個控制器邏輯的例子,一個控制器根據玩家的狀態來決定發送哪個命令。但是這樣並不好(例如,如果你添加了多個控制器,那將會導致邏輯重複)。相反的,玩家對象應該根據當前狀態(例如減速、驚恐)來設置當前的速度,並根據當前的面朝向來計算如何向前移動。控制器只負責做他們自己狀態相關的事情,控制器不改變玩家的狀態,因此控制前甚至可以根本不知道玩家的狀態。另外一個例子,切換武器。正確的方法是,玩家有一個函數:“SwitchWeapon(Weapon
newWeapon)”供GUI調用。GUI不應該維護所有對象的Transform和他們之間的父子關係。
所有界面相關的組件,只負責維護和處理他們自己狀態相關的數據。例如,顯示一個地圖,GUI可以根據玩家的位移計算地圖的顯示。但是,這是遊戲狀態數據,它不屬於GUI。GUI只是顯示遊戲狀態數據,這些數據應該在其他地方維護。地圖數據也應該在其他地方維護(例如GameManager)。
遊戲玩法對象不應該關心GUI。有一個例外是處理遊戲暫停(可能是通過控制Time.timeScale,其實這並不是個好主意)。遊戲玩法對象應該知道遊戲是否暫停。但是,這就是全部了。另外,不要把GUI組件掛到遊戲玩法對象上。
這麼說吧,如果你把所有的GUI類都刪了,遊戲應該可以正確編譯。
你還應該達到:在不需要重寫遊戲邏輯的前提下,重寫GUI和輸入控制。
32、分離狀態控制和簿記變量
簿記變量只是爲了使用起來方便或者提高查找速度,並且可以根據狀態控制來覆蓋。將兩者分離可以簡化:
實現方法之一是爲每個遊戲邏輯定義一個”SaveData“類,例如:
-
[Serializable]
-
PlayerSaveData
-
{
-
public float health;
-
}
-
-
Player
-
{
-
-
-
-
private PlayerSaveData playerSaveData;
-
}
33、分離特殊的配置
假設我們有兩個敵人,它們使用同一個Mesh,但是有不同的屬性設置(例如不同的力量、不同的速度等等)。有很多方法來分離數據。下面是我比較喜歡的一種,特別是對於對象生成或者遊戲存檔時,會很好用。(屬性設置不是狀態數據,而是配置數據,所以我們不需要存檔他們。當對象加載或者生成是,屬性設置會自動加載。)
- 爲每一個遊戲邏輯類定義一個模板類。例如,對於敵人,我們來一個“EnemyTemplate”,所有的屬性設置變量都保存在這個類中。
- 在遊戲邏輯的類中,定義一個上述模板類型的變量。
- 製作一個敵人的Prefab,以及兩個模板的Prefab:“WeakEnemyTemplate”和"StrongEnemyTemplate"。
- 在加載或者生成對象是,把模板變量正確的複製。
這種方法可能有點複雜(在一些情況下,可能不需要這樣)。
舉個例子,最好使用泛型,我們可以這樣定義我們的類:
-
public class BaseTemplate
-
{
-
...
-
}
-
-
public class ActorTemplate : BaseTemplate
-
{
-
...
-
}
-
-
public class Entity<EntityTemplateType> where EntityTemplateType : BaseTemplate
-
{
-
EntityTemplateType template;
-
...
-
}
-
-
public class Actor : Entity <ActorTemplate>
-
{
-
...
-
}
34、除了顯示用的文本,不要使用字符串
特別是不要用字符串作爲對象或者prefab等等的ID標識。一個很遺憾的例外是動畫系統,需要使用字符串來訪問相應的動畫。
35、避免使用public的數組
舉例說明,不要定義一個武器的數組,一個子彈的數組,一個粒子的數組,這樣你的代碼看起來像這樣:
-
public void SelectWeapon(int index)
-
{
-
currentWeaponIndex = index;
-
Player.SwitchWeapon(weapons[currentWeapon]);
-
}
-
-
public void Shoot()
-
{
-
Fire(bullets[currentWeapon]);
-
FireParticles(particles[currentWeapon]);
-
}
這在代碼中還不是什麼大問題,但是在Inspector中設置他們的值的時候,就很難不犯錯了。
我們可以定義一個類,來封裝這三個變量,然後使用一個它的實例數組:
-
[Serializable]
-
public class Weapon
-
{
-
public GameObject prefab;
-
public ParticleSystem particles;
-
public Bullet bullet;
-
}
這樣代碼看起來很整潔,但是更重要的是,在Inspector中設置時就不容易犯錯了。
36、在結構中避免使用數組
舉個例子,一個玩家可以有三種攻擊形式,每種使用當前的武器,併發射不同的子彈、產生不同的行爲。
你可以把三個子彈作爲一個數組,並像下面這樣組織邏輯:
-
public void FireAttack()
-
{
-
-
Fire(bullets[0]);
-
}
-
-
public void IceAttack()
-
{
-
-
Fire(bullets[1]);
-
}
-
-
public void WindAttack()
-
{
-
-
Fire(bullets[2]);
-
}
使用枚舉值可以讓代碼看起來更好一點:
-
public void WindAttack()
-
{
-
-
Fire(bullets[WeaponType.Wind]);
-
}
但是這對Inspector一點也不好。
最好使用單獨的變量,並且起一個好的變量名,能夠代表他們的內容的含義。使用下面這個類會更整潔。
-
[Serializable]
-
public class Bullets
-
{
-
public Bullet FireBullet;
-
public Bullet IceBullet;
-
public Bullet WindBullet;
-
}
這裏假設沒有其他的Fire、Ice、Wind的數據。
37、把數據組織到可序列化的類中,可以讓inspector更整潔
有些對象有一大堆可調節的變量,這種情況下在Inspector中找到某個變量簡直就成了噩夢。爲了簡化這種情況,可以使用一下的步驟:
- 把這些變量分組定義到不同的類中,並讓它們聲明爲public和serializable;
- 在一個主要的類中,把上述類的實例定義爲public成員變量;
- 不用在Awake或者Start中初始化這些變量,因爲Unity會處理好它們;
- 你可以定義它們的默認值;
這可以把變量分組到Inspector的分組頁籤中,方便管理。
-
[Serializable]
-
public class MovementProperties
-
{
-
public float movementSpeed;
-
public float turnSpeed = 1;
-
}
-
-
public class HealthProperties
-
{
-
public float maxHealth;
-
public float regenerationRate;
-
}
-
-
public class Player : MonoBehaviour
-
{
-
public MovementProperties movementProeprties;
-
public HealthPorperties healthProeprties;
-
}
文本
38、如果你有很多的劇情文本,那麼把他們放到一個文件裏面。
不要把他們放到Inspector的字段中去編輯。這些需要做到不打開Unity,也不用保存Scene就可以方便的修改。
39、如果你計劃實現本地化,那麼把你的字符串分離到一個統一的位置。
有很多種方法來實現這點。例如,定義一個文本Class,爲每個字符串定義一個public的字符串字段,並把他們的默認值設爲英文。其他的語言定義爲子類,然後重新初始化這些字段爲相應的語言的值。
另外一種更好的技術(適用於文本很大或者支持的語言數量衆多),可以讀取幾個單獨的表單,然後提供一些邏輯,根據所選擇的語言來選取正確的字符串。
測試與調試
40、實現一個圖形化的Log用來調試物理、動畫和AI。
41、實現一個HTML的Log。
在很多情況下,日誌是非常有用的。擁有一個便於分析的Log(顏色編碼、有多個視圖、記錄屏幕截圖等)可以使基於Log的調試變動愉悅。詳見這裏。
42、實現一個你自己的幀速率計算器。
沒有人知道Unity的FPS計算器在做什麼,但是肯定不是計算幀速率。實現一個你自己的,讓數字符合直覺並可視化。
43、實現一個截屏的快捷鍵。
很多BUG是圖形化的,如果你有一個截圖,就很容易報告它。一個理想的系統,應該在PlayerPrefes中保存一個計數,並根據這個計數,使得所有成功保存的截屏文件都不被覆蓋掉。截屏文件應該保存在工程文件夾之外,這可以防止人們不小心把它提交到版本庫中。
44、實現一個打印玩家座標的快捷鍵。
這可以在彙報位置相關的BUG時明確它發生在世界中的什麼位置,這可以讓Debug容易一些。
45、實現一些Debug選項,用來方便測試。
一些例子:
- 解鎖所有道具;
- 關閉所有敵人;
- 關閉GUI;
- 讓玩家無敵;
- 關閉所有遊戲邏輯;
46、爲每一個足夠小的團隊,創建一個適合他們的Debug選項的Prefab。
設置一個用戶標識文件,單不要提交到版本庫,在遊戲運行時讀取它。下面是原因:
- 團隊的成員不會因爲意外的提交了自己的Debug設置而影響到其他人。
- 修改Debug設置不需要修改場景。
47、維護一個包含所有遊戲元素的場景。
例如,一個場景,包括所有的敵人,所有可以交互的對象等等。這樣可以不用玩很久,而進行全面的功能測試。
48、定義一些Debug快捷鍵常量,並把他們保存在統一的地方。
Debug鍵通常(方便起見)在一個地方來處理,就像其他的遊戲輸入一樣。爲了避免快捷鍵衝突,在一箇中心位置定義所有常量。一種替代方案是,在一個地方處理所有按鍵輸入,不管他是否是Debug鍵。(負面作用是,這個類可能需要引用更多的其他對象)
文檔
49、爲你的設置建立文檔。
代碼應該擁有最多的文檔,但是一些代碼之外的東西也必須建立文檔。讓設計師們通過代碼去看如果進行設置是浪費時間。把設置寫入文檔,可以提高效率(如果文檔的版本能夠及時更新的話)。
用文檔記錄下面這些:
- Layer的使用(碰撞、檢測、射線檢測——本質上說,什麼東西應該在哪個Layer裏);
- Tag的使用;
- GUI的depth層級(說什麼應該顯示在什麼之上);
- 慣用的處理方式;
- Prefab結構;
- 動畫Layer。
命名規則和目錄結構
50、遵從一個命名規範和目錄結構,並建立文檔
命名和目錄結構的一致性,可以方便查找,並明確指出什麼東西在哪裏。
你很有可能需要創建自己的命名規則和目錄結構,下面的例子僅供參考。
普遍的命名規則
- 名字應該代表它是什麼,例如鳥就應該叫做Bird。
- 選擇可以發音、方便記憶的名字。如果你在製作一個瑪雅文化相關的遊戲,不要把關卡命名爲QuetzalcoatisReturn。
- 保持唯一性。如果你選擇了一個名字,就堅持用它。
- 使用Pascal風格的大小寫,例如ComplicatedVerySpecificObject。
不要使用空格,下劃線,或者連字符,除了一個例外(詳見爲同一事物的不同方面命名一節)。 - 不要使用版本數字,或者標示他們進度的名詞(WIP、final)。
- 不要使用縮寫:DVamp@W應該寫成DarkVampire@Walk。
- 使用設計文檔中的術語:如果文檔中稱呼一個動畫爲Die,那麼使用DarkVampire@Die,而不要用DarkVampire@Death。
- 保持細節修飾詞在左側:DarkVampire,而不是VampireDark;PauseButton,而不是ButtonPaused。舉例說明,在Inspector中查找PauseButton,比所有按鈕都以Button開頭方便。(很多人傾向於相反的次序,認爲那樣名字可以自然的分組。然而,名字不是用來分組的,目錄纔是。名字是用來在同一類對象中可以快速辨識的。)
- 爲一個序列使用同一個名字,並在這些名字中使用數字。例如PathNode0, PathNode1。永遠從0開始,而不是1。
- 對於不是序列的情況,不要使用數字。例如 Bird0, Bird1, Bird2,本應該是Flamingo, Eagle, Swallow。
- 爲臨時對象添加雙下劃線前綴,例如__Player_Backup。
爲同一事物的不同方面命名
在覈心名稱後面添加下劃線,後面的部分代表哪個方面。例如
- GUI中的按鈕狀態:EnterButton_Active、EnterButton_Inactive
- 貼圖: DarkVampire_Diffuse, DarkVampire_Normalmap
- 天空盒:JungleSky_Top, JungleSky_North
- LOD分組:DarkVampire_LOD0, DarkVampire_LOD1
結構
場景組織、工程目錄、腳本目錄應該使用相似的模式。
目錄結構
-
Materials
-
GUI
-
Effects
-
Meshes
-
Actors
-
DarkVampire
-
LightVampire
-
...
-
Structures
-
Buildings
-
...
-
Props
-
Plants
-
...
-
...
-
Plugins
-
Prefabs
-
Actors
-
Items
-
...
-
Resources
-
Actors
-
Items
-
...
-
Scenes
-
GUI
-
Levels
-
TestScenes
-
Scripts
-
Textures
-
GUI
-
Effects
-
...
場景結構
-
Cameras
-
Dynamic Objects
-
Gameplay
-
Actors
-
Items
-
...
-
GUI
-
HUD
-
PauseMenu
-
...
-
Management
-
Lights
-
World
-
Ground
-
Props
-
Structure
-
...
腳本目錄結構
-
ThirdParty
-
...
-
MyGenericScripts
-
Debug
-
Extensions
-
Framework
-
Graphics
-
IO
-
Math
-
...
-
MyGameScripts
-
Debug
-
Gameplay
-
Actors
-
Items
-
...
-
Framework
-
Graphics
-
GUI
-
...