【转载】关于对方法实例化的相关感悟以及unity的50个技巧
關于實例化問題的感悟(筆者自悟,大神勿噴)
在之前的程序編寫過程中,雖然對相關的方法進行了實例化,但是在運行的時候總是會出現“未將對象引用設置到對象的實例”,出現該種問題的原因是由于在實例化后,沒有對實例化進行引用賦值,所以導致相關變量無法在其他方法中進行讀取,以后需對此謹記。
?
同時之前瀏覽過一片大神寫過的關于unity相關技巧的文章,筆者覺得受益匪淺,現將鏈接與原文轉載于下,希望可以幫助大家。
?
使用Unity3D的50個技巧:Unity3D最佳實踐
作者:房燕良
?
關于這些技巧
這些技巧不可能適用于每個項目。- 這些是基于我的一些項目經驗,項目團隊的規模從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、考慮使用外部的關卡編輯工具
Unity不是一個完美的關卡編輯器。例如,我們使用TuDee來創建3D Tile-Based的游戲,這使我們可以獲得對Tile友好的工具的益處(網格約束,90度倍數的旋轉,2D視圖,快速Tile選擇等)。從一個XML文件來實例化Prefab也很簡單。詳見Guerrilla Tool Development。4、考慮把關卡保存為XML,而非scene
這是一種很奇妙的技術:- 它可以讓你不必每個場景都設置一遍;
- 他可以加載的更快(如果大多數對象都是在場景之間共享的)。
- 它讓場景的版本合并變的簡單(就算是Unity的新的文本格式的Scene,也由于數據太多,而讓版本合并變的不切實際)。
- 它可以使得在關卡之間保持數據更簡便。
5、考慮編寫通用的自定義Inspector代碼
實現自定義的Inspector是很直截了當的,但是Unity的系統有很多的缺點:- 它不支持從繼承中獲益;
- 它不允許定義字段級別的Inspector組件,而只能是class類型級別。舉個例子,如果沒有游戲對象都有一個ScomeCoolType字段,而你想在Inspector中使用不同的渲染,那么你必須為你的所有class寫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啟動
這將大大的降低測試的時間。為了達到所有場景可運行,你需要做兩件事: 首先,如果需要前面場景運行產生的一些數據,那么要模擬出它們。 其次,生成在場景切換時必要保存的對象,可以是這樣: [csharp]?view plain?copy ?美術
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,然后鏈接他們。這可以:- 在同一個地方修改所有類型
- 在不動用場景的情況下進行修改
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;
- 把它重命名為__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),用它來定義需要調用的方法,而不要使用字符串屬性方法名稱,例如: [csharp]?view plain?copy ?23、為共享接口的組件擴展
有些時候把獲得組件、查找對象實現在一個組件的接口中會很方便。 下面這種實現方案使用了typeof,而不是泛型版本的函數。泛型函數無法在接口上工作,而typeof可以。下面這種方法把泛型方法整潔的包裝起來。 [csharp]?view plain?copy ?24、使用擴展來讓代碼書寫更便捷
例如: [csharp]?view plain?copy ?25、使用防御性的GetComponent()
有些時候強制性組件依賴(通過RequiredComponent)會讓人蛋疼。例如,很難在Inspector中修改組件(即使他們有同樣的基類)。下面是一種替代方案,當一個必要的組件沒有找到時,輸出一條錯誤信息。 [csharp]?view plain?copy ?風格
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)模式
從下面這個類派生的所有類,將自動獲得單件功能: [csharp]?view plain?copy ?- 對于那些非唯一的prefab實例使用單件管理器(例如Player)。不要為了堅持這條原則把類的層次關系復雜化,寧愿在你的GameManager(或其他合適的管理器中)中持有一個它們的引用。
- 對于外部經常使用的共有變量和方法定義為static,這樣你可以這樣簡便的書寫“GameManager.Player”,而不用寫成“GameManager.Instance.player”。
30、在組件中不要使用public成員變量,除非它需要在inspector中調節
除非需要設計師(策劃or美術)去調節的變量,特別是它不能明確表明自己是做什么的變量,不要聲明為public。如果在這些特殊情況下,無法避免,則可使用兩個甚至四個下劃線來表明不要從外部調節它,例如: [csharp]?view plain?copy ?31、把界面和游戲邏輯分開
這一條本質上就是指的MVC模式。 所有的輸入控制器,只負責向相應的組件發送命令,讓它們知道控制器被調用了。舉一個控制器邏輯的例子,一個控制器根據玩家的狀態來決定發送哪個命令。但是這樣并不好(例如,如果你添加了多個控制器,那將會導致邏輯重復)。相反的,玩家對象應該根據當前狀態(例如減速、驚恐)來設置當前的速度,并根據當前的面朝向來計算如何向前移動。控制器只負責做他們自己狀態相關的事情,控制器不改變玩家的狀態,因此控制前甚至可以根本不知道玩家的狀態。另外一個例子,切換武器。正確的方法是,玩家有一個函數:“SwitchWeapon(Weapon newWeapon)”供GUI調用。GUI不應該維護所有對象的Transform和他們之間的父子關系。 所有界面相關的組件,只負責維護和處理他們自己狀態相關的數據。例如,顯示一個地圖,GUI可以根據玩家的位移計算地圖的顯示。但是,這是游戲狀態數據,它不屬于GUI。GUI只是顯示游戲狀態數據,這些數據應該在其他地方維護。地圖數據也應該在其他地方維護(例如GameManager)。 游戲玩法對象不應該關心GUI。有一個例外是處理游戲暫停(可能是通過控制Time.timeScale,其實這并不是個好主意)。游戲玩法對象應該知道游戲是否暫停。但是,這就是全部了。另外,不要把GUI組件掛到游戲玩法對象上。 這么說吧,如果你把所有的GUI類都刪了,游戲應該可以正確編譯。 你還應該達到:在不需要重寫游戲邏輯的前提下,重寫GUI和輸入控制。32、分離狀態控制和簿記變量
簿記變量只是為了使用起來方便或者提高查找速度,并且可以根據狀態控制來覆蓋。將兩者分離可以簡化:- 保存游戲狀態
- 調試游戲狀態
33、分離特殊的配置
假設我們有兩個敵人,它們使用同一個Mesh,但是有不同的屬性設置(例如不同的力量、不同的速度等等)。有很多方法來分離數據。下面是我比較喜歡的一種,特別是對于對象生成或者游戲存檔時,會很好用。(屬性設置不是狀態數據,而是配置數據,所以我們不需要存檔他們。當對象加載或者生成是,屬性設置會自動加載。)- 為每一個游戲邏輯類定義一個模板類。例如,對于敵人,我們來一個“EnemyTemplate”,所有的屬性設置變量都保存在這個類中。
- 在游戲邏輯的類中,定義一個上述模板類型的變量。
- 制作一個敵人的Prefab,以及兩個模板的Prefab:“WeakEnemyTemplate”和"StrongEnemyTemplate"。
- 在加載或者生成對象是,把模板變量正確的復制。
34、除了顯示用的文本,不要使用字符串
特別是不要用字符串作為對象或者prefab等等的ID標識。一個很遺憾的例外是動畫系統,需要使用字符串來訪問相應的動畫。35、避免使用public的數組
舉例說明,不要定義一個武器的數組,一個子彈的數組,一個粒子的數組,這樣你的代碼看起來像這樣: [csharp]?view plain?copy ?36、在結構中避免使用數組
舉個例子,一個玩家可以有三種攻擊形式,每種使用當前的武器,并發射不同的子彈、產生不同的行為。 你可以把三個子彈作為一個數組,并像下面這樣組織邏輯: [csharp]?view plain?copy ?37、把數據組織到可序列化的類中,可以讓inspector更整潔
有些對象有一大堆可調節的變量,這種情況下在Inspector中找到某個變量簡直就成了噩夢。為了簡化這種情況,可以使用一下的步驟:- 把這些變量分組定義到不同的類中,并讓它們聲明為public和serializable;
- 在一個主要的類中,把上述類的實例定義為public成員變量;
- 不用在Awake或者Start中初始化這些變量,因為Unity會處理好它們;
- 你可以定義它們的默認值;
文本
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、遵從一個命名規范和目錄結構,并建立文檔
命名和目錄結構的一致性,可以方便查找,并明確指出什么東西在哪里。 你很有可能需要創建自己的命名規則和目錄結構,下面的例子僅供參考。普遍的命名規則
為同一事物的不同方面命名
在核心名稱后面添加下劃線,后面的部分代表哪個方面。例如- GUI中的按鈕狀態:EnterButton_Active、EnterButton_Inactive
- 貼圖:?DarkVampire_Diffuse, DarkVampire_Normalmap
- 天空盒:JungleSky_Top, JungleSky_North
- LOD分組:DarkVampire_LOD0, DarkVampire_LOD1
結構
場景組織、工程目錄、腳本目錄應該使用相似的模式。目錄結構
[plain]?view plain?copy ?場景結構
[plain]?view plain?copy ?腳本目錄結構
[plain]?view plain?copy ??
?
轉載于:https://www.cnblogs.com/cokefenta/p/7910912.html
總結
以上是生活随笔為你收集整理的【转载】关于对方法实例化的相关感悟以及unity的50个技巧的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: linux操作系统网络内核优化
- 下一篇: Lync与Exchange 2013 U