1. 程式人生 > >關於對方法例項化的相關感悟以及unity的50個技巧

關於對方法例項化的相關感悟以及unity的50個技巧

關於例項化問題的感悟(筆者自悟,大神勿噴)

在之前的程式編寫過程中,雖然對相關的方法進行了例項化,但是在執行的時候總是會出現“未將物件引用設定到物件的例項”,出現該種問題的原因是由於在例項化後,沒有對例項化進行引用賦值,所以導致相關變數無法在其他方法中進行讀取,以後需對此謹記。

同時之前瀏覽過一片大神寫過的關於unity相關技巧的文章,筆者覺得受益匪淺,現將連結與原文轉載於下,希望可以幫助大家。

關於這些技巧

這些技巧不可能適用於每個專案。
  • 這些是基於我的一些專案經驗,專案團隊的規模從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,也由於資料太多,而讓版本合併變的不切實際)。
  • 它可以使得在關卡之間保持資料更簡便。
你仍就可以使用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啟動

這將大大的降低測試的時間。為了達到所有場景可執行,你需要做兩件事: 首先,如果需要前面場景執行產生的一些資料,那麼要模擬出它們。 其次,生成在場景切換時必要儲存的物件,可以是這樣:
  1. myObject = FindMyObjectInScene();  
  2. if (myObjet == null)  
  3. {  
  4.    myObject = SpawnMyObject();  
  5. }  

美術

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:
  1. 複製Player Prefab;
  2. 把複製出來的Prefab重新命名為__Player_Backup;
  3. 修改Player Prefab;
  4. 測試一切工作正常,刪除__Player_Backup;
不要把新複製的命名為Player_New,然後修改它。 有些情況可能更復雜一些。例如,有些修改可能涉及到兩個人,上述過程有可能使得場景無法工作,而所有人必須停下來等他們修改完畢。如果修改能夠很快完成,那麼還用上面這個流程就可以。如果修改需要花很長時間,則可以使用下面的流程:
  • 第一個人:
    1. 複製Player Prefab;
    2. 把它重新命名為__Player_WithNewFeature或者__Player_ForPerson2;
    3. 在複製的物件上做修改,然後提交給第二個人;
  • 第二個人:
    1. 在新的Prefab上做修改;
    2. 複製Player Prefab,並命名為__Player_Backup;
    3. 把__Player_WithNewFeature拖放到場景中,建立它的例項;
    4. 把這個例項拖放到原始的Player Prefab中;
    5. 如果一切工作正常,則可使刪除__Player_Backup和__Player_WithNewFeature;

擴充套件和MonoBehaviourBase

21、擴充套件一個自己的Mono Behaviour基類,然後自己的所有元件都從它派生

這可以使你方便的實現一些通用函式,例如型別安全的Invoke,或者是一些更復雜的呼叫(例如random等等)。

22、為Invoke, StartCoroutine and Instantiate 定義安全呼叫方法

定義一個委託任務(delegate Task),用它來定義需要呼叫的方法,而不要使用字串屬性方法名稱,例如:
  1. publicvoid Invoke(Task task, float time)  
  2. {  
  3.    Invoke(task.Method.Name, time);  
  4. }  

23、為共享介面的元件擴充套件

有些時候把獲得元件、查詢物件實現在一個元件的介面中會很方便。 下面這種實現方案使用了typeof,而不是泛型版本的函式。泛型函式無法在介面上工作,而typeof可以。下面這種方法把泛型方法整潔的包裝起來。
  1. //Defined in the common base class for all mono behaviours
  2. public I GetInterfaceComponent<I>() where I : class
  3. {  
  4.    return GetComponent(typeof(I)) as I;  
  5. }  
  6. publicstatic List<I> FindObjectsOfInterface<I>() where I : class
  7. {  
  8.    MonoBehaviour[] monoBehaviours = FindObjectsOfType<MonoBehaviour>();  
  9.    List<I> list = new List<I>();  
  10.    foreach(MonoBehaviour behaviour in monoBehaviours)  
  11.    {  
  12.       I component = behaviour.GetComponent(typeof(I)) as I;  
  13.       if(component != null)  
  14.       {  
  15.          list.Add(component);  
  16.       }  
  17.    }  
  18.    return list;  
  19. }  

24、使用擴充套件來讓程式碼書寫更便捷

例如:
  1. publicstaticclass CSTransform   
  2. {  
  3.    publicstaticvoid SetX(this Transform transform, float x)  
  4.    {  
  5.       Vector3 newPosition =   
  6.          new Vector3(x, transform.position.y, transform.position.z);  
  7.       transform.position = newPosition;  
  8.    }  
  9.    ...  
  10. }   

25、使用防禦性的GetComponent()

有些時候強制性元件依賴(通過RequiredComponent)會讓人蛋疼。例如,很難在Inspector中修改元件(即使他們有同樣的基類)。下面是一種替代方案,當一個必要的元件沒有找到時,輸出一條錯誤資訊。
  1. publicstatic T GetSafeComponent<T>(this GameObject obj) where T : MonoBehaviour  
  2. {  
  3.    T component = obj.GetComponent<T>();  
  4.    if(component == null)  
  5.    {  
  6.       Debug.LogError("Expected to find component of type "
  7.          + typeof(T) + " but found none", obj);  
  8.    }  
  9.    return component;  
  10. }  


風格

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)模式

從下面這個類派生的所有類,將自動獲得單件功能:
  1. publicclass Singleton<T> : MonoBehaviour where T : MonoBehaviour  
  2. {  
  3.    protectedstatic T instance;  
  4.    /** 
  5.       Returns the instance of this singleton. 
  6.    */
  7.    publicstatic T Instance  
  8.    {  
  9.       get
  10.       {  
  11. 相關推薦

    關於方法例項相關感悟以及unity的50技巧

    關於例項化問題的感悟(筆者自悟,大神勿噴) 在之前的程式編寫過程中,雖然對相關的方法進行了例項化,但是在執行的時候總是會出現“未將物件引用設定到物件的例項”,出現該種問題的原因是由於在例項化後,沒有對例項化進行引用賦值,所以導致相關變數無法在其他方法中進行讀取,以後需對

    python中類的定義、例項、封裝以及私有變數/方法

    1.  定義類 python中定義一個類的格式如下: class MyClass(object): def __init__(self,data1,data2): sel

    spring bean 構造方法例項

    spring框架例項化bean有3中方式,即構造方法例項化、靜態工廠例項化、例項工廠例項化(其中,最常用的是構造方法例項化) 構造方法例項化 spring容器可以呼叫bean對應類中的無引數構造方法來例項化bean,這種方式稱為構造方法例項化 1.建立web應用,並匯入依賴的jar包

    Python內建函式類方法靜態方法例項方法學習

    靜態方法 class C(object): @staticmethod def f(): print('runoob'); C.f(); # 靜態方法無需例項化 cobj = C() cobj.f() # 也可以例項化後

    通過java反射技術獲取泛型的真實型別並例項

    public DaoBaseInterfaceImpl() { // 反射得到T的真實型別 ParameterizedType ptype = (ParameterizedType) thi

    Sping+Struts2+Hibernate中如何用main方法例項dao或service

    demo1 public static void main(String[] args) {     ApplicationContext context = new ClassPathXmlApplicationContext(               new Str

    Calendar例項的方式以及處理時間的工具類

    1.Calendar例項化的方式  首先Calendar是一個抽象類,不能直接例項化,Java提供了靜態工廠方法getInstance()來例項化Calendar, 或者通過它的子類來例項化 Calendar c=new GregorianCalendar();//直接建

    學習php反射(2)——不用new方法例項

    上一篇簡單介紹了 php 反射的幾個常見類的使用方法,但是用反射能做些什麼,你可能還是想象不到, 下面我稍微應用反射類來做點東西,大家知道例項化一個類需要用new 關鍵字,不用 new 可以嗎?答案是可以的,用反射就能實現: 首先建立一個檔案 student.php

    Android中利用newInstance()方法例項fragment

    Android是在Android 3.0 (API level 11)開始引入Fragment的。Fragment可以使你能夠將activity分離成多個可重用的元件,每個都有它自己的生命週期和UI。那我們應該怎麼去建立fragment呢?Google已經考慮到

    java通過反射,只需要傳了類名和引數,就可以根據不同引數的構造方法例項物件

    轉載自:http://www.jianshu.com/p/69ca44916ebf 程式碼塊 @requires_authorization private Object reflateInstance(String className, Object[] args)th

    Java 抽象方法例項

    通過查詢Java的API發現:抽象類有構造方法,而介面沒有構造方法。 抽象類不可以直接建立物件,表示抽象類本身不能被例項化,即 抽象類 a = new 抽象類();這樣寫是錯誤的,但是抽象類可以宣告物件,因為,抽象類的子類必須複寫抽象類中的所有抽象方法,即抽象類的子類是可以

    Spring 使用註解完成bean例項、依賴注入的相關配置以及注意事項

    一、 相關配置例項化註解介紹 首先使用註解完成spring容器例項的配置,主要用到以下幾個: 1、@Repository(“name”):配置持久層例項bean,通常用在DAO; 這裡配置的name屬性相當於在 2、@Servic

    擴展方法json序列及反序列

    runt mes get int esp () exceptio new ask this+類型名+變量名,.NET 3.0 之後新增的一種特性,叫“擴展方法”。 int類型變量都能調用toString()方法,將int類型變量轉換成string

    Tomcat和myeclipse的相關操作以及myeclipse的激活方法

    java部署WEB應用:1、創建應用目錄2、創建WEB-INF3、Classes、lib、web、xml(examples)4、創建Servlet:A、創建類 implements ServletB、Service(request,response)C、實現代碼5、編譯ServletJavac -d . h

    三種例項bean方式——Springbean的管理(一)

    三種例項化bean方式——Spring對bean的管理(一) Spring容器提供了三種對bean的例項化方式: 1)構造器例項化 2)靜態工廠方法例項化 3)例項工廠方法是例項化 構造方法例項化 先建一個Demo實體類 public class Demo { p

    tensorflow 中資料經過網路傳輸後的embedding視覺方法例項

    最近在GitHub上看程式碼偶然發現了使輸入經過網路傳輸後的輸出,即“embedding”視覺化的小細節,在此寫下來加深記憶: Git原連結:https://github.com/ywpkwon/siamese_tf_mnist 首先是建立網路(Siamese 網路): import t

    Python視覺中Matplotlib(4.三種設定樣式方法、設定座標刻度以及標籤、設定顯示出特殊字元)

    1.三種設定方式   (1)向方法傳入關鍵字引數 上一節已經總結過,一直在使用   (2)對例項使用一系列的setter方法           具體的方法直接看程式碼  import matplotli

    tomcat載入spring過程以及例項bean

    參考:https://blog.csdn.net/yztezhl/article/details/52300663 tomcat啟動載入web.xml配置的ContextLoaderListener,呼叫方法contextInitialized開始初始化,   在init

    面向物件—的__new__()方法詳解 [Python] Python 之 __new__() 方法例項

    [Python] Python 之 __new__() 方法與例項化   __new__() 是在新式類中新出現的方法,它作用在構造方法建造例項之前,可以這麼理解,在 Python 中存在於類裡面的構造方法 __init__() 負責將類的例項化,而在 __init__()

    bean的4種例項方法

      具體檔案格式 application.xml <?xml version="1.0" encoding="UTF-8" ?> <beans xmlns = "http://www.springframework.org/schema/