當要加載一個較大的場景,長時間等待勢必會影響用戶體驗,並且一個較大場景全部加載到場景中也會影響操作流暢度。不可避免的需要用一個技術就是在Unity中進行動態的加載場景中的資源。
當然本文的動態加載場景資源,是以玩家為中心,玩家的視野為半徑進行加載。首先讓美術將整個場景以一定的格式寫入XML文件中,然后在程序開始運行時讀取美術給我們提供的XML文件,然后遍歷這個xml文件中的所有節點找到所有處於玩家周圍一定范圍內的節點並加載即可。
准備工作:
首先我們要定義一個對象GameObjectInfo,其中包括這個資源的名稱(string)、位置(Vector3)、旋轉(Vector3)、縮放(Vector3)、所有子節點(List<T>)、Prefab路徑(string)、包圍盒(Bounds)等。
1,將場景資源寫入到XML文件中
當美術做好了我們要的模型之后,我們使用我們寫的編輯器擴展程序將模型的屬性寫入到XML中。
首先這個方法對於美術制作場景有一定的要求:
1,場景中的節點分為兩類,分組節點和模型節點
2,分組節點上不能掛有任何組件,其作用僅在於將其子節點作為一個整體分組。
3,模型節點是從資源里加載的節點,可以從Prefab中加載,也可以從網絡上下載。
首先,我們在編輯器中添加菜單( [MenuItem("Helper/SaveToXml")]),彈出SaveToXmlEdit,將模型的根節點拖到ObjectField中,點擊SaveToXml按鈕將獲取根節點並將其與其子節點的屬性以及對應關系保存到xml文件中。而點擊LoadFromXml則是會彈出一個選擇要導入到場景中的模型信息的xml文件.


那下面呢就講下他是怎么實現的吧
SaveToXml
在編輯器中獲取選擇對象,再選擇要保存的路徑。
將獲取到的根節點下的所有子節點,實例化成GameObjectInfo對象,並將其序列化成xml文件。
LoadFromXml
選擇文件,將獲取到的xml文件進行反序列化GameObjectInfo對象。
首先創建一個空的Transform t,然后判斷當前GameObjectInfo是否是Prefab,如果是則獲取使用其PrefabPath加載其Prefab(”AssetDatabase.LoadAssetAtPath<Transform>(info.prefabPath)“),如果不是,則Tranform t=new GameObject().transform;
然后將GameObjectInfo中的屬性(名稱/位置/旋轉/縮放/等)將t賦值。
最后獲取他的子對象,並使用遞歸的方式將其子對象進行創建和賦值。
(注:如果當前對象是Prefab時,那么當前對象就不用再獲取他的子對象了)
2,玩家移動時加載
在Start中加載xml文件,反序列化GameObjectInfo。
在Update中根據玩家當前的位置(P)遍歷所XML節點:
1。如果P在當前節點的包圍盒之內或和包圍盒的距離小於viewDistance:
a 如果當前節點尚未加載,那么加載當前節點;如果當前節點是分組節點,那么遞歸遍歷其子節點。
b 如果當前節點已經加載,那么將其顯示出來。
2。否則如果當前節點已經加載那么將其隱藏。
在實現上,使用一個鍵值對字典用來判斷某個節點是否加載,用一個枚舉NodeBehaviour用來表示對當前節點作出什么處理(Load/Hide/None)
在Update中獲取玩家的位置/GameObjectInfo/viewDistance根據GameObject中記錄的包圍盒進行計算對象是否需要被加載,在首次加載中如果GameObjectInfo需要被加載就將NodeBehaviour設置為Load,並將其添加到鍵值對中,以供下次加載時不用再次創建對象。然后既然前句判斷玩家不在當前GameObjectInfo的包圍盒內那么則將其NodeBehaviour設置為Hide。
最后,根據NodeBehaviour狀態是否是將當前的GameObjectInfo是加載還是隱藏。
Load狀態:
首先判斷在保存GameObjectInfo和對象應創建的GameObject的鍵值對中是否存在,如果不存在,判斷是否是Prefab,如果是prefab就獲取其Prefab,如果不是則(new GameObject()).transform。然后將其名稱/座標/旋轉/縮放賦值,並將其保存於鍵值對中。
如果存在,將判斷些對象的activeSelf是否為false,如果為false,則將其設置為true.
最后,判斷其是否有子對象,如果有子對象,遞歸加載。
Hide狀態:
判斷在鍵值對中是否存在當前GameObjectInfo,如果存在就將此對象的狀態設置為隱藏。
3,需特殊處理的物體
如果有一些物體需要特殊處理,比如燈光/地形等需要在場景中始終顯示,並不需要根據與玩家的距離進行動態的加載。那么我們就需要將他作為一個特殊的物體進行處理了。
首先我們將新建一個腳本LoadFlag掛在我們需要特殊處理的物體上,這個腳本設置了一個枚舉值Flag,分別是Dynamic/Allways/Ignore這三個狀態,然后我們就可以根據自己的需要來選擇其所需要的加載狀態了。
我們還需要將GameObjectInfo中增加一個加載標識LoadFlag.Flag flag。
比如說燈光,我們需要他在場景運行時就一直加載到場景中無需隱藏,那么我們將這個腳本掛在這個燈光上,並設置其狀態為Allways(一直顯示),那么我們在給美術將模型寫入xml的腳本中如果我們的模型中有LoadFlag這個腳本時,我們就將這個這個腳本中的枚舉值賦值為GameObjectInfo中的flag屬性中。
在程序使用的腳本中,當在場景運行時,我們在讀取xml文檔的時候實例化GameObjectInfo時,在解讀加載到場景中時如我們首先判斷GameObjectInfo中的flag屬性,然后flag值為Allways那么我們就將NodeBehaviour設置為Load,如果flag值為Dynamic那么參考如上第二點玩家移動時加載,flag值為Ignore那么我們就將NodeBehaviour設置為None。
