日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

用Unity3D的50个技巧:Unity3D最佳实践

發(fā)布時間:2025/5/22 编程问答 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 用Unity3D的50个技巧:Unity3D最佳实践 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.


http://bbs.9ria.com/thread-416805-1-1.html

剛開始學(xué)習(xí)Unity3D時間不長,在看各種資料。除了官方的手冊以外,其他人的經(jīng)驗也是非常有益的。偶爾看到老外這篇文章,覺得還不錯,于是翻譯過來和大家共享。原文地址:http://devmag.org.za/2012/07/12/50-tips-for-working-with-unity-best-practices/,下面是譯文。

歡迎轉(zhuǎn)載,請注明出處:燕良@游戲開發(fā),http://blog.csdn.net/neil3d/article/details/38534809。另外,歡迎各路高手加入我的QQ群:264656505,切磋交流技術(shù)。

關(guān)于這些技巧這些技巧不可能適用于每個項目。

  • 這些是基于我的一些項目經(jīng)驗,項目團隊的規(guī)模從3人到20人不等;

  • 框架結(jié)構(gòu)的可重用性、清晰程度是有代價的——團隊的規(guī)模和項目的規(guī)模決定你要在這個上面付出多少;

  • 很多技巧是品味的問題(這里所列的所有技巧,可能有同樣好的技術(shù)替代方案);

  • 一些技巧可能是對傳統(tǒng)的Unity開發(fā)的一個沖擊。例如,使用prefab替代對象實例并不是一個傳統(tǒng)的Unity風(fēng)格,并且這樣做的代價還挺高的(需要很多的preffab)。也許這些看起來有些瘋狂,但是在我看來是值得的。

流程
1、避免Assets分支

所有的Asset都應(yīng)該只有一個唯一的版本。如果你真的需要一個分支版本的Prefab、Scene或是Mesh,那你要制定一個非常清晰的流程,來確定哪個是正確的版本。錯誤的分支應(yīng)該起一個特別的名字,例如雙下劃線前綴:__MainScene_Backup。Prefab版本分支需要一個特別的流程來保證安全(詳見Prefabs一節(jié))。

2、如果你在使用版本控制的話,每個團隊成員都應(yīng)該保有一個項目的Second Copy用來測試修改之后,Second Copy和Clean Copy都應(yīng)該被更新和測試。大家都不要修改自己的Clean Copy。這對于測試Asset丟失特別有用。
3、考慮使用外部的關(guān)卡編輯工具Unity不是一個完美的關(guān)卡編輯器。例如,我們使用TuDee來創(chuàng)建3D Tile-Based的游戲,這使我們可以獲得對Tile友好的工具的益處(網(wǎng)格約束,90度倍數(shù)的旋轉(zhuǎn),2D視圖,快速Tile選擇等)。從一個XML文件來實例化Prefab也很簡單。詳見Guerrilla Tool Development。
4、考慮把關(guān)卡保存為XML,而非scene這是一種很奇妙的技術(shù):

  • 它可以讓你不必每個場景都設(shè)置一遍;

  • 他可以加載的更快(如果大多數(shù)對象都是在場景之間共享的)。

  • 它讓場景的版本合并變的簡單(就算是Unity的新的文本格式的Scene,也由于數(shù)據(jù)太多,而讓版本合并變的不切實際)。

  • 它可以使得在關(guān)卡之間保持?jǐn)?shù)據(jù)更簡便。

你仍就可以使用Unity作為關(guān)卡編輯器(盡管你用不著了)。你需要寫一些你的數(shù)據(jù)的序列化和反序列化的代碼,并實現(xiàn)在編輯器和游戲運行時加載關(guān)卡、在編輯器中保存關(guān)卡。你可能需要模仿Unity的ID系統(tǒng)來維護對象之間的引用關(guān)系。

5、考慮編寫通用的自定義Inspector代碼實現(xiàn)自定義的Inspector是很直截了當(dāng)?shù)?#xff0c;但是Unity的系統(tǒng)有很多的缺點:

  • 它不支持從繼承中獲益;

  • 它不允許定義字段級別的Inspector組件,而只能是class類型級別。舉個例子,如果沒有游戲?qū)ο蠖加幸粋€ScomeCoolType字段,而你想在Inspector中使用不同的渲染,那么你必須為你的所有class寫Inspector代碼。

你可以通過從根本上重新實現(xiàn)Inspector系統(tǒng)來處理這些問題。通過一些反射機制的小技巧,他并不像看上去那么看,文章底部(日后另作翻譯)將提供更多的實現(xiàn)細(xì)節(jié)。

? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???
場景組織6、使用命名的空Game Object來做場景目錄仔細(xì)的組織場景,就可以方便的找到任何對象。
7、把控制對象和場景目錄(空Game Objec)放在原點(0,0,0)如果位置對于這個對象不重要,那么就把他放到原點。這樣你就不會遇到處理Local Space和World Space的麻煩,代碼也會更簡潔。
8、盡量減少使用GUI組件的offset通常應(yīng)該由控件的Layout父對象來控制Offset;它們不應(yīng)該依賴它們的爺爺節(jié)點的位置。位移不應(yīng)該互相抵消來達到正確顯示的目的。做基本上要防止了下列情況的發(fā)生:
父容器被放到了(100,-50),而字節(jié)點應(yīng)該在(10,10),所以把他放到(90,60)[父節(jié)點的相對位置]。
這種錯誤通常放生在容器不可見時。
9、把世界的地面放在Y=0這樣可以更方便的把對象放到地面上,并且在游戲邏輯中,可以把世界作為2D空間來處理(如果合適的話),例如AI和物理模擬。
10、使游戲可以從每個Scene啟動這將大大的降低測試的時間。為了達到所有場景可運行,你需要做兩件事:
首先,如果需要前面場景運行產(chǎn)生的一些數(shù)據(jù),那么要模擬出它們。
其次,生成在場景切換時必要保存的對象,可以是這樣:
myObject = FindMyObjectInScene(); if (myObjet == null){? ?myObject = SpawnMyObject();}



? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?
美術(shù)11、把角色和地面物體的中心點(Pivot)放在底部,不要放在中間這可以使你方便的把角色或者其他對象精確的放到地板上。如果合適的話,它也可能使得游戲邏輯、AI、甚至是物理使用2D邏輯來表現(xiàn)3D。
12、統(tǒng)一所有的模型的面朝向(Z軸正向或者反向)對于所有具有面朝向的對象(例如角色)都應(yīng)該遵守這一條。在統(tǒng)一面朝向的前提下,很多算法可以簡化。
13、在開始就把Scale搞正確請美術(shù)把所有導(dǎo)入的縮放系數(shù)設(shè)置為1,并且把他們的Transform的Scale設(shè)置為1,1,1。可以使用一個參考對象(一個Unity的Cube)來做縮放比較。為你的游戲選擇一個世界的單位系數(shù),然后堅持使用它。
14、為GUI組件或者手動創(chuàng)建的粒子制作一個兩個面的平面模型設(shè)置這個平面面朝向Z軸正向,可能簡化Billboard和GUI創(chuàng)建。
15、制作并使用測試資源

  • 為SkyBox創(chuàng)建帶文字的方形貼圖;

  • 一個網(wǎng)格(Grid);

  • 為Shader測試使用各種顏色的平面:白色,黑色,50%灰度,紅,綠,藍(lán),紫,黃,青;

  • 為Shader測試使用漸進色:黑到白,紅到綠,紅到藍(lán),綠到藍(lán);

  • 黑白格子;

  • 平滑的或者粗糙的法線貼圖;

  • 一套用來快速搭建場景的燈光(使用Prefa);




? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?
Prefabs16、所有東西都使用Prefab只有場景中的“目錄”對象不使用Prefab。甚至是那些只使用一次的唯一對象也應(yīng)該使用Prefab。這樣可以在不動用場景的情況下,輕松修改他們。(一個額外的好處是,當(dāng)你使用EZGUI時,這可以用來創(chuàng)建穩(wěn)定的Sprite Atlases)
17、對于特例使用單獨的Prefab,而不要使用特殊的實例對象如果你有兩種敵人的類型,并且只是屬性有區(qū)別,那么為不同的屬性分別創(chuàng)建Prefab,然后鏈接他們。這可以:

  • 在同一個地方修改所有類型

  • 在不動用場景的情況下進行修改

如果你有很多敵人的類型,那么也不要在編輯器中使用特殊的實例。一種可選的方案是程序化處理它們,或者為所有敵人使用一個核心的文件/Prefab。使用一個下拉列表來創(chuàng)建不同的敵人,或者根據(jù)敵人的位置、玩家的進度來計算。
18、在Prefab之間鏈接,而不要鏈接實例對象當(dāng)Prefab放置到場景中時,它們的鏈接關(guān)系是被維護的,而實例的鏈接關(guān)系不被維護。盡可能的使用Prefab之間的鏈接可以減少場景創(chuàng)建的操作,并且減少場景的修改。
19、如果可能,自動在實例對象之間產(chǎn)生鏈接關(guān)系如果你確實需要在實例之間鏈接,那么應(yīng)該在程序代碼中去創(chuàng)建。例如,Player對象在Start時需要把自己注冊到GameManager,或者GameManager可以在Start時去查找Player對象。
對于需要添加腳本的Prefab,不要用Mesh作為根節(jié)點。當(dāng)你需要從Mesh創(chuàng)建一個Prefab時,首先創(chuàng)建一個空的GameObject作為父對象,并用來做根節(jié)點。把腳本放到根節(jié)點上,而不要放到Mesh節(jié)點上。使用這種方法,當(dāng)你替換Mesh時,就不會丟失所有你在Inspector中設(shè)置的值了。
使用互相鏈接的Prefab來實現(xiàn)Prefab嵌套。Unity并不支持Prefab的嵌套,在團隊合作中第三方的實現(xiàn)方案可能是危險的,因為嵌套的Prefab之間的關(guān)系是不明確的。
20、使用安全的流程來處理Prefab分支我們用一個名為Player的Prefab來講解這個過程。
用下面這個流程來修改Player:

  • 復(fù)制Player Prefab;

  • 把復(fù)制出來的Prefab重命名為__Player_Backup;

  • 修改Player Prefab;

  • 測試一切工作正常,刪除__Player_Backup;

不要把新復(fù)制的命名為Player_New,然后修改它。
有些情況可能更復(fù)雜一些。例如,有些修改可能涉及到兩個人,上述過程有可能使得場景無法工作,而所有人必須停下來等他們修改完畢。如果修改能夠很快完成,那么還用上面這個流程就可以。如果修改需要花很長時間,則可以使用下面的流程:




    • 第一個人:

      • 復(fù)制Player Prefab;

      • 把它重命名為__Player_WithNewFeature或者__Player_ForPerson2;

      • 在復(fù)制的對象上做修改,然后提交給第二個人;

    • 第二個人:

      • 在新的Prefab上做修改;

      • 復(fù)制Player Prefab,并命名為__Player_Backup;

      • 把__Player_WithNewFeature拖放到場景中,創(chuàng)建它的實例;

      • 把這個實例拖放到原始的Player Prefab中;

      • 如果一切工作正常,則可使刪除__Player_Backup和__Player_WithNewFeature;




? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???
擴展和MonoBehaviourBase21、擴展一個自己的Mono Behaviour基類,然后自己的所有組件都從它派生這可以使你方便的實現(xiàn)一些通用函數(shù),例如類型安全的Invoke,或者是一些更復(fù)雜的調(diào)用(例如random等等)。
22、為Invoke, StartCoroutine and Instantiate 定義安全調(diào)用方法定義一個委托任務(wù)(delegate Task),用它來定義需要調(diào)用的方法,而不要使用字符串屬性方法名稱,例如:
public void Invoke(Task task, float time){? ?Invoke(task.Method.Name, time);}
23、為共享接口的組件擴展有些時候把獲得組件、查找對象實現(xiàn)在一個組件的接口中會很方便。
下面這種實現(xiàn)方案使用了typeof,而不是泛型版本的函數(shù)。泛型函數(shù)無法在接口上工作,而typeof可以。下面這種方法把泛型方法整潔的包裝起來。
//Defined in the common base class for all mono behaviourspublic 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中修改組件(即使他們有同樣的基類)。下面是一種替代方案,當(dāng)一個必要的組件沒有找到時,輸出一條錯誤信息。
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;}


? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?
風(fēng)格26、避免對同一件事使用不同的處理風(fēng)格在很多情況下,某件事并不只有一個慣用手法。在這種情況下,在項目中明確選擇其中的一個來使用。下面是原因:

  • 一些做法并不能很好的一起協(xié)作。使用一個,能強制統(tǒng)一設(shè)計方向,并明確指出不是其他做法所指的方向;

  • 團隊成員使用統(tǒng)一的風(fēng)格,可能方便大家互相的理解。他使得整體結(jié)構(gòu)和代碼都更容易理解。這也可以減少錯誤;

幾組風(fēng)格的例子:

  • 協(xié)程與狀態(tài)機(Coroutines vs. state machines);

  • 嵌套的Prefab、互相鏈接的Prefab、超級Prefab(Nested prefabs vs. linked prefabs vs. God prefabs);

  • 數(shù)據(jù)分離的策略;

  • 在2D游戲的使用Sprite的方法;

  • Prefab的結(jié)構(gòu);

  • 對象生成策略;

  • 定位對象的方法:使用類型、名稱、層、引用關(guān)系;

  • 對象分組的方法:使用類型、名稱、層、引用數(shù)組;

  • 找到一組對象,還是讓它們自己來注冊;

  • 控制執(zhí)行次序(使用Unity的執(zhí)行次序設(shè)置,還是使用Awake/Start/Update/LateUpdate,還是使用純手動的方法,或者是次序無關(guān)的架構(gòu));

  • 在游戲中使用鼠標(biāo)選擇對象/位置/目標(biāo):SelectionManager或者是對象自主管理;

  • 在場景變換時保存數(shù)據(jù):通過PlayerPrefs,或者是在新場景加載時不要銷毀的對象;

  • 組合動畫的方法:混合、疊加、分層;



? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???
時間27、維護一個自己的Time類,可以使游戲暫停更容易實現(xiàn)做一個“Time.DeltaTime”和""Time.TimeSinceLevelLoad"的包裝,用來實現(xiàn)暫停和游戲速度縮放。這使用起來略顯麻煩,但是當(dāng)對象運行在不同的時鐘速率下的時候就方便多了(例如界面動畫和游戲內(nèi)動畫)。

? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???
生成對象28、不要讓游戲運行時生成的對象搞亂場景層次結(jié)構(gòu)在游戲運行時,為動態(tài)生成的對象設(shè)置好它們的父對象,可以讓你更方便的查找。你可以使用一個空的對象,或者一個沒有行為的單件來簡化代碼中的訪問。可以給這個對象命名為“DynamicObjects”。


? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?
類設(shè)計29、使用單件(Singleton)模式
從下面這個類派生的所有類,將自動獲得單件功能:
public class Singleton<T> : MonoBehaviour where T : MonoBehaviour{? ?protected static T instance;? ? /**? ?? ?Returns the instance of this singleton.? ?*/? ?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)。不要為了堅持這條原則把類的層次關(guān)系復(fù)雜化,寧愿在你的GameManager(或其他合適的管理器中)中持有一個它們的引用。

  • 對于外部經(jīng)常使用的共有變量和方法定義為static,這樣你可以這樣簡便的書寫“GameManager.Player”,而不用寫成“GameManager.Instance.player”。


30、在組件中不要使用public成員變量,除非它需要在inspector中調(diào)節(jié)
除非需要設(shè)計師(策劃or美術(shù))去調(diào)節(jié)的變量,特別是它不能明確表明自己是做什么的變量,不要聲明為public。如果在這些特殊情況下,無法避免,則可使用兩個甚至四個下劃線來表明不要從外部調(diào)節(jié)它,例如:
public float __aVariable;
31、把界面和游戲邏輯分開這一條本質(zhì)上就是指的MVC模式。


所有的輸入控制器,只負(fù)責(zé)向相應(yīng)的組件發(fā)送命令,讓它們知道控制器被調(diào)用了。舉一個控制器邏輯的例子,一個控制器根據(jù)玩家的狀態(tài)來決定發(fā)送哪個命令。但是這樣并不好(例如,如果你添加了多個控制器,那將會導(dǎo)致邏輯重復(fù))。相反的,玩家對象應(yīng)該根據(jù)當(dāng)前狀態(tài)(例如減速、驚恐)來設(shè)置當(dāng)前的速度,并根據(jù)當(dāng)前的面朝向來計算如何向前移動。控制器只負(fù)責(zé)做他們自己狀態(tài)相關(guān)的事情,控制器不改變玩家的狀態(tài),因此控制前甚至可以根本不知道玩家的狀態(tài)。另外一個例子,切換武器。正確的方法是,玩家有一個函數(shù):“SwitchWeapon(Weapon newWeapon)”供GUI調(diào)用。GUI不應(yīng)該維護所有對象的Transform和他們之間的父子關(guān)系。


所有界面相關(guān)的組件,只負(fù)責(zé)維護和處理他們自己狀態(tài)相關(guān)的數(shù)據(jù)。例如,顯示一個地圖,GUI可以根據(jù)玩家的位移計算地圖的顯示。但是,這是游戲狀態(tài)數(shù)據(jù),它不屬于GUI。GUI只是顯示游戲狀態(tài)數(shù)據(jù),這些數(shù)據(jù)應(yīng)該在其他地方維護。地圖數(shù)據(jù)也應(yīng)該在其他地方維護(例如GameManager)。


游戲玩法對象不應(yīng)該關(guān)心GUI。有一個例外是處理游戲暫停(可能是通過控制Time.timeScale,其實這并不是個好主意)。游戲玩法對象應(yīng)該知道游戲是否暫停。但是,這就是全部了。另外,不要把GUI組件掛到游戲玩法對象上。


這么說吧,如果你把所有的GUI類都刪了,游戲應(yīng)該可以正確編譯。


你還應(yīng)該達到:在不需要重寫游戲邏輯的前提下,重寫GUI和輸入控制。



32、分離狀態(tài)控制和簿記變量簿記變量只是為了使用起來方便或者提高查找速度,并且可以根據(jù)狀態(tài)控制來覆蓋。將兩者分離可以簡化:

  • 保存游戲狀態(tài)

  • 調(diào)試游戲狀態(tài)

實現(xiàn)方法之一是為每個游戲邏輯定義一個”SaveData“類,例如:
[Serializable]PlayerSaveData{? ?public float health; //public for serialisation, not exposed in inspector}??Player{? ?//... bookkeeping variables? ? //Don’t expose state in inspector. State is not tweakable.? ?private PlayerSaveData playerSaveData; }
33、分離特殊的配置假設(shè)我們有兩個敵人,它們使用同一個Mesh,但是有不同的屬性設(shè)置(例如不同的力量、不同的速度等等)。有很多方法來分離數(shù)據(jù)。下面是我比較喜歡的一種,特別是對于對象生成或者游戲存檔時,會很好用。(屬性設(shè)置不是狀態(tài)數(shù)據(jù),而是配置數(shù)據(jù),所以我們不需要存檔他們。當(dāng)對象加載或者生成是,屬性設(shè)置會自動加載。)

  • 為每一個游戲邏輯類定義一個模板類。例如,對于敵人,我們來一個“EnemyTemplate”,所有的屬性設(shè)置變量都保存在這個類中。

  • 在游戲邏輯的類中,定義一個上述模板類型的變量。

  • 制作一個敵人的Prefab,以及兩個模板的Prefab:“WeakEnemyTemplate”和"StrongEnemyTemplate"。

  • 在加載或者生成對象是,把模板變量正確的復(fù)制。

這種方法可能有點復(fù)雜(在一些情況下,可能不需要這樣)。

舉個例子,最好使用泛型,我們可以這樣定義我們的類:
public class BaseTemplate{? ?...} public class ActorTemplate : BaseTemplate{? ?...} public class Entity<EntityTemplateType> where EntityTemplateType : BaseTemplate{? ?EntityTemplateType template;? ?...} public class Actor : Entity <ActorTemplate>{? ?...}
34、除了顯示用的文本,不要使用字符串特別是不要用字符串作為對象或者prefab等等的ID標(biāo)識。一個很遺憾的例外是動畫系統(tǒng),需要使用字符串來訪問相應(yīng)的動畫。
35、避免使用public的數(shù)組舉例說明,不要定義一個武器的數(shù)組,一個***的數(shù)組,一個粒子的數(shù)組,這樣你的代碼看起來像這樣:
public void SelectWeapon(int index){? ? currentWeaponIndex = index;? ?Player.SwitchWeapon(weapons[currentWeapon]);} public void Shoot(){? ?Fire(bullets[currentWeapon]);? ?FireParticles(particles[currentWeapon]);? ?}
這在代碼中還不是什么大問題,但是在Inspector中設(shè)置他們的值的時候,就很難不犯錯了。

我們可以定義一個類,來封裝這三個變量,然后使用一個它的實例數(shù)組:
[Serializable]public class Weapon{? ?public GameObject prefab;? ?public ParticleSystem particles;? ?public Bullet bullet;} 這樣代碼看起來很整潔,但是更重要的是,在Inspector中設(shè)置時就不容易犯錯了。

36、在結(jié)構(gòu)中避免使用數(shù)組舉個例子,一個玩家可以有三種***形式,每種使用當(dāng)前的武器,并發(fā)射不同的***、產(chǎn)生不同的行為。
你可以把三個***作為一個數(shù)組,并像下面這樣組織邏輯:
public void FireAttack(){? ?/// behaviour? ?Fire(bullets[0]);} public void IceAttack(){? ?/// behaviour? ?Fire(bullets[1]);} public void WindAttack(){? ?/// behaviour? ?Fire(bullets[2]);} 使用枚舉值可以讓代碼看起來更好一點:

public void WindAttack(){? ?/// behaviour? ?Fire(bullets[WeaponType.Wind]);}
但是這對Inspector一點也不好。

最好使用單獨的變量,并且起一個好的變量名,能夠代表他們的內(nèi)容的含義。使用下面這個類會更整潔。
[Serializable]public class Bullets{? ?public Bullet FireBullet;? ?public Bullet IceBullet;? ?public Bullet WindBullet;}這里假設(shè)沒有其他的Fire、Ice、Wind的數(shù)據(jù)。

37、把數(shù)據(jù)組織到可序列化的類中,可以讓inspector更整潔有些對象有一大堆可調(diào)節(jié)的變量,這種情況下在Inspector中找到某個變量簡直就成了噩夢。為了簡化這種情況,可以使用一下的步驟:

  • 把這些變量分組定義到不同的類中,并讓它們聲明為public和serializable;

  • 在一個主要的類中,把上述類的實例定義為public成員變量;

  • 不用在Awake或者Start中初始化這些變量,因為Unity會處理好它們;

  • 你可以定義它們的默認(rèn)值;

這可以把變量分組到Inspector的分組頁簽中,方便管理。

[Serializable]public class MovementProperties //Not a MonoBehaviour!{? ?public float movementSpeed;? ?public float turnSpeed = 1; //default provided} public class HealthProperties //Not a MonoBehaviour!{? ?public float maxHealth;? ?public float regenerationRate;} public class Player : MonoBehaviour{? ?public MovementProperties movementProeprties;? ?public HealthPorperties healthProeprties;}


? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???
文本38、如果你有很多的劇情文本,那么把他們放到一個文件里面。不要把他們放到Inspector的字段中去編輯。這些需要做到不打開Unity,也不用保存Scene就可以方便的修改。
39、如果你計劃實現(xiàn)本地化,那么把你的字符串分離到一個統(tǒng)一的位置。有很多種方法來實現(xiàn)這點。例如,定義一個文本Class,為每個字符串定義一個public的字符串字段,并把他們的默認(rèn)值設(shè)為英文。其他的語言定義為子類,然后重新初始化這些字段為相應(yīng)的語言的值。
另外一種更好的技術(shù)(適用于文本很大或者支持的語言數(shù)量眾多),可以讀取幾個單獨的表單,然后提供一些邏輯,根據(jù)所選擇的語言來選取正確的字符串。


? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???
測試與調(diào)試40、實現(xiàn)一個圖形化的Log用來調(diào)試物理、動畫和AI。這可以顯著的加速調(diào)試工作。詳見這里。
41、實現(xiàn)一個HTML的Log。在很多情況下,日志是非常有用的。擁有一個便于分析的Log(顏色編碼、有多個視圖、記錄屏幕截圖等)可以使基于Log的調(diào)試變動愉悅。詳見這里。
42、實現(xiàn)一個你自己的幀速率計算器。沒有人知道Unity的FPS計算器在做什么,但是肯定不是計算幀速率。實現(xiàn)一個你自己的,讓數(shù)字符合直覺并可視化。
43、實現(xiàn)一個截屏的快捷鍵。很多BUG是圖形化的,如果你有一個截圖,就很容易報告它。一個理想的系統(tǒng),應(yīng)該在PlayerPrefes中保存一個計數(shù),并根據(jù)這個計數(shù),使得所有成功保存的截屏文件都不被覆蓋掉。截屏文件應(yīng)該保存在工程文件夾之外,這可以防止人們不小心把它提交到版本庫中。
44、實現(xiàn)一個打印玩家坐標(biāo)的快捷鍵。這可以在匯報位置相關(guān)的BUG時明確它發(fā)生在世界中的什么位置,這可以讓Debug容易一些。
45、實現(xiàn)一些Debug選項,用來方便測試。一些例子:

  • 解鎖所有道具;

  • 關(guān)閉所有敵人;

  • 關(guān)閉GUI;

  • 讓玩家無敵;

  • 關(guān)閉所有游戲邏輯;

46、為每一個足夠小的團隊,創(chuàng)建一個適合他們的Debug選項的Prefab。
設(shè)置一個用戶標(biāo)識文件,單不要提交到版本庫,在游戲運行時讀取它。下面是原因:

  • 團隊的成員不會因為意外的提交了自己的Debug設(shè)置而影響到其他人。

  • 修改Debug設(shè)置不需要修改場景。

47、維護一個包含所有游戲元素的場景。
例如,一個場景,包括所有的敵人,所有可以交互的對象等等。這樣可以不用玩很久,而進行全面的功能測試。
48、定義一些Debug快捷鍵常量,并把他們保存在統(tǒng)一的地方。Debug鍵通常(方便起見)在一個地方來處理,就像其他的游戲輸入一樣。為了避免快捷鍵沖突,在一個中心位置定義所有常量。一種替代方案是,在一個地方處理所有按鍵輸入,不管他是否是Debug鍵。(負(fù)面作用是,這個類可能需要引用更多的其他對象)


? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???文檔49、為你的設(shè)置建立文檔。
代碼應(yīng)該擁有最多的文檔,但是一些代碼之外的東西也必須建立文檔。讓設(shè)計師們通過代碼去看如果進行設(shè)置是浪費時間。把設(shè)置寫入文檔,可以提高效率(如果文檔的版本能夠及時更新的話)。
用文檔記錄下面這些:

  • Layer的使用(碰撞、檢測、射線檢測——本質(zhì)上說,什么東西應(yīng)該在哪個Layer里);

  • Tag的使用;

  • GUI的depth層級(說什么應(yīng)該顯示在什么之上);

  • 慣用的處理方式;

  • Prefab結(jié)構(gòu);

  • 動畫Layer。



命名規(guī)則和目錄結(jié)構(gòu)50、遵從一個命名規(guī)范和目錄結(jié)構(gòu),并建立文檔命名和目錄結(jié)構(gòu)的一致性,可以方便查找,并明確指出什么東西在哪里。
你很有可能需要創(chuàng)建自己的命名規(guī)則和目錄結(jié)構(gòu),下面的例子僅供參考。


普遍的命名規(guī)則

  • 名字應(yīng)該代表它是什么,例如鳥就應(yīng)該叫做Bird。

  • 選擇可以發(fā)音、方便記憶的名字。如果你在制作一個瑪雅文化相關(guān)的游戲,不要把關(guān)卡命名為QuetzalcoatisReturn。

  • 保持唯一性。如果你選擇了一個名字,就堅持用它。

  • 使用Pascal風(fēng)格的大小寫,例如ComplicatedVerySpecificObject
    不要使用空格,下劃線,或者連字符,除了一個例外(詳見為同一事物的不同方面命名一節(jié))。

  • 不要使用版本數(shù)字,或者標(biāo)示他們進度的名詞(WIP、final)。

  • 不要使用縮寫:DVamp@W應(yīng)該寫成DarkVampire@Walk。

  • 使用設(shè)計文檔中的術(shù)語:如果文檔中稱呼一個動畫為Die,那么使用DarkVampire@Die,而不要用DarkVampire@Death。

  • 保持細(xì)節(jié)修飾詞在左側(cè):DarkVampire,而不是VampireDark;PauseButton,而不是ButtonPaused。舉例說明,在Inspector中查找PauseButton,比所有按鈕都以Button開頭方便。(很多人傾向于相反的次序,認(rèn)為那樣名字可以自然的分組。然而,名字不是用來分組的,目錄才是。名字是用來在同一類對象中可以快速辨識的。)

  • 為一個序列使用同一個名字,并在這些名字中使用數(shù)字。例如PathNode0, PathNode1。永遠(yuǎn)從0開始,而不是1。

  • 對于不是序列的情況,不要使用數(shù)字。例如 Bird0, Bird1, Bird2,本應(yīng)該是Flamingo, Eagle, Swallow。

  • 為臨時對象添加雙下劃線前綴,例如__Player_Backup。


為同一事物的不同方面命名在核心名稱后面添加下劃線,后面的部分代表哪個方面。例如

  • GUI中的按鈕狀態(tài):EnterButton_Active、EnterButton_Inactive

  • 貼圖: DarkVampire_Diffuse, DarkVampire_Normalmap

  • 天空盒:JungleSky_Top, JungleSky_North

  • LOD分組:DarkVampire_LOD0, DarkVampire_LOD1


結(jié)構(gòu)場景組織、工程目錄、腳本目錄應(yīng)該使用相似的模式。
目錄結(jié)構(gòu)MaterialsGUIEffectsMeshes? ?Actors? ?? ?DarkVampire? ?? ?LightVampire? ?? ?...? ?Structures? ?? ?Buildings? ?? ?...? ?Props? ?? ?Plants? ?? ?...? ?...PluginsPrefabs? ?Actors? ?Items? ?...Resources? ?Actors? ?Items? ?...Scenes? ?GUI? ?Levels? ?TestScenesScriptsTexturesGUIEffects...場景結(jié)構(gòu)[size=11.818181991577148px]CamerasDynamic ObjectsGameplay? ?Actors? ?Items? ?...GUI? ?HUD? ?PauseMenu? ?...ManagementLightsWorld? ?Ground? ?Props? ?Structure? ?...腳本目錄結(jié)構(gòu)ThirdParty? ?...MyGenericScripts? ?Debug? ?Extensions? ?Framework? ?Graphics? ?IO? ?Math? ?...MyGameScripts? ?Debug? ?Gameplay? ?? ?Actors? ?? ?Items? ?? ?...? ?Framework? ?Graphics? ?GUI? ?...


轉(zhuǎn)載于:https://blog.51cto.com/9575485/1718764

《新程序員》:云原生和全面數(shù)字化實踐50位技術(shù)專家共同創(chuàng)作,文字、視頻、音頻交互閱讀

總結(jié)

以上是生活随笔為你收集整理的用Unity3D的50个技巧:Unity3D最佳实践的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。