Unity場景問題,以及老程序DontDestory問題

Unity 5.3中新增加了多場景編輯功能,允許用戶將一個大場景以某種邏輯分割成多個小場景並方便的編輯和管理。這在某些情況下會比較有用,是對Unity編輯器對場景編輯能力的一個重要提升。

什麼是多場景編輯

多場景編輯就是允許用戶在Unity編輯器中同時打開多個場景,並對它們進行編輯。Unity提供了一系列的UI和Scripting API來管理這些場景。以下就是一個在Unity編輯器中進行多場景編輯的一個實例:



什麼是場景在進一步瞭解多場景編輯之前,我們先了解下什麼是場景(Scene)。簡而言之場景就是包含了遊戲對象的一個文件,比如Game Objects,Components等。不過有一些對象可能一些用戶並不會太在意,那就是Scene Game Managers。

Unity中有兩類Game Managers:
  • 一類是Global Game Managers。它們是全局的Game Managers,包括AudioManager、InputManager、PhysicsManager等。當在Unity5.3中發佈遊戲的時候,你會發現在輸出目錄或者發佈包裏面會有個globalgamemanagers文件,它包含了所有全局Game Managers。

  • 另一類是Scene Game Managers。如果你將Editor Settings中的Asset Serialization選擇爲“Force Text”模式,並打開一個已經保存的*.unity場景文件,你會發現前保存在文件最前面的就是SceneSettings、RenderSettings、LightMapSettings和NavMeshSettings,它們就是每個場景都會有的Game Managers。


爲什麼需要多場景編輯
  • 首先,將大場景分割成多個場景,可以更好的支持場景的流式加載(Scene streaming);

  • 其次,可以更好地支持協同合作,尤其是在有源代碼版本管理的時候可以允許多人同時編輯而不會產生衝突;

  • 再者,支持卸載場景(Scene Unloading),在5.3之前用戶可以通過Application.LoadLevelAddtive()和Application.LoadLevelAdditiveAsync()動態加載場景,但沒有對應的Application.UnloadScene()。而5.3中提供了場景卸載,讓用戶可以更靈活的管理多個場景


多場景編輯基本功能介紹接下來我們看看Unity在5.3中具體提供了哪些多場景編輯的功能。
Scene結構在5.3之前,Unity中只有概念上的場景,而到5.3中我們引入了真正的Scene結構。它包含了name、path、isLoaded等變量,同時也提供了IsValid()以及GetRootGameObjects()方法。
Active Scene在5.3中,我們引入了Active Scene(當前場景)的概念。引入它的目的在於:
  • 如果有多個場景同時打開,我們會選擇Active Scene的Scene Game Managers作爲當前的Scene Game Managers。比如在Bake Lightmapping的時候,我們會使用Active Scene的LightMapSettings來Bake當前打開的所有場景。

  • 在創建Game Object的時候,會默認加入到Active Scene。


SceneManagerSceneManager在UnityEngine.SceneManagement之下,它是Runtime中的Scene Manager,提供了以下方法:
LoadScene() /LoadSceneAsync()
它們允許用戶通過Name、Build Index來加載場景。用戶可以在Build Settings窗口查看Name和Build Index。通過這兩個方法加載的場景,要麼被加到了Build Settings,要麼存在於AssetBundle之中。如果是從AssetBundle中加載場景,則只能通過名字加載
要說明的是,如果有多個場景同名但位於不同的目錄之下,可以使用完整的路徑(不帶.unity後綴名)來加載不同的場景。
用戶可以通過LoadSceneMode來指定不同的加載模式。LoadSceneMode.Single在加載之前會卸載其它所有的場景,LoadSceneMode.Additive則是加載的時候不關閉之前的場景。
還有一點很重要,LoadScene()並不是完全同步的,它只能保證在下一幀開始之前加載完畢。所以在此推薦大家使用LoadSceneAsync()這個異步的加載方法
UnLoadScene()
目前5.3中用戶只能通過Name和Build Index來同步的卸載一個Scene。在後續的版本中我們會提供通過Scene結構來卸載一個Scene,並且提供異步卸載的方法。

GetActiveScene() /SetActiveScene()
獲取和設置Active scene。
GetSceneAt() / GetSceneByName() / GetSceneByPath()
查詢Scene的一組方法。
其它

EditorSceneManager


EditorSceneManager在UnityEditor.SceneManagement之下,它是Editor中的Scene Manager,提供了以下方法:
OpenScene()
它是一個同步的方法,用戶只能通過path來打開場景。不同於LoadScene() / LoadSceneAsync(),它可以直接打開一個存在於Assets目錄下的場景,不管它是否被添加到Build Settings
用戶可以通過OpenSceneMode來指定不同的打開模式,相比較LoadSceneMode,它多了一個AdditiveWithoutLoading模式,允許用戶增加一個場景但並不真正加載它
CloseScene()
顧名思義,它可以關閉一個場景,同時它提供了一個bool參數來指定關閉的時候是否將場景從Scene Manager中移除。
SaveScene() / SaveScenes()
通過它們可以保存一個或多個Scene。
MarkSceneDirty() / MarkAllScenesDirty()
通過它們可以將某個指定的場景或者所有場景標記爲Dirty。大部分情況Unity內部通過Undo系統來實現場景的Dirty跟蹤,但是有些模塊並沒有完全支持Undo,比如Terrain在設置某些參數的時候就不支持Undo。所以我們提供了這兩個方法支持直接將場景設置爲Dirty。
API的使用限制在Editor Mode下,UnityEngine.SceneManagement.SceneManager的某些方法是不能使用的:
  • LoadScene()

  • LoadSceneAsync()

  • CreateScene()

  • UnloadScene()


同樣在Play Mode下,UnityEditor.SceneManagement.EditorSceneManager的某些方法也不能使用:
  • OpenScene()

  • NewScene()

  • CloseScene()

  • SaveScene() / SaveScenes() / …

  • MarkSceneDirty() / MarkAllScenesDirty()/…


如果用戶在不同的模式下使用了錯誤的方法,我們會在Console輸出對應的錯誤信息引導用戶使用正確的方法。
DontDestroyOnLoad場景在Unity 5.3中,如果用戶通過Object.DontDestroyOnLoad()方法將某個Game Object標記成DontDestroyOnLoad,在進入Play Mode的時候會發現Hierarchy窗口中多出一個DontDestroyOnLoad場景,它包含了之前標記成DontDestroyOnLoad的Game Object。

爲什麼需要DontDestroyOnLoad場景在Unity 5.3中,所有的Game Object必須隸屬於某一個場景。這樣我們就必須有一個特別的場景來管理這些被標記爲DontDestroyOnLoad的Game Objects,否則在Unload這些Game Objects所屬的場景的時候,這些Game Objects也會被刪除掉。這顯然不是我們想要的結果。
因此我們引入了DontDestroyOnLoad場景,當進入Play Mode時候,我們會把所有標記爲DontDestroyOnLoad的Game Objects從所屬的場景移入到這個特別的場景之中。
DontDestroyOnLoad場景的特點DontDestroyOnLoad場景僅僅存在於Runtime,或者是Play Mode下。它不能從外部訪問,僅僅是Unity內部用於管理標記爲DontDestroyOnLoad的Game Objects。
事實上從Unity 5.3開始,我們並不推薦用戶使用DontDestroyOnLoad這一功能,它使得我們內部的代碼邏輯複雜度增加了不少。5.3之前因爲沒有多場景的支持,所以並沒有很好地辦法繞開它並實現相同的功能。而從5.3開始我們推薦用戶創建一個Manager場景,由它負責加載/卸載其它所有的遊戲場景。它從遊戲開始便存在一直到遊戲退出,這樣所有需要被標記爲DontDestroyOnLoad的Game objects都應該屬於這個場景。
多場景編輯的進階接下來我們介紹一些關於多場景編輯的進階以及一些小技巧。

1Scene Manager Setup

Scene Manager Setup可以用來保存並恢復當前的Scene Hierarchy。EditorSceneManager上提供了GetSceneManagerSetup() / RestoreSceneManagerSetup()來獲取和恢復Scene Hierarchy。


我們可以通過ScriptableObject來保存Scene Hierarchy,代碼如下所示:




以下的代碼展示瞭如何保存以及讀取Scene Manager Setup:

2Lightmap & NavMesh Baking

在Unity 5.3中,Lightmap和NavMesh的烘焙都同時支持多個場景,它們之間的不同之處在於如何管理和劃分烘焙的結果


對於Lightmap Baking,我們會根據Scenes劃分Lightmaps和Realtime GI數據。每個場景都只會加載和自己相關的那部分數據。
對於NavMesh Baking,因爲它烘焙的結果很小,所以我們將NavMesh的數據保存在一個asset當中,每個場景都會引用到這個asset並能夠找到自己所關聯的那部分數據。
另外用戶也可以通過腳本進行Baking,Lightmapping.BakeMultipleScenes()和NavMeshBuilder.BuildNavMeshForMultipleScenes()都支持一次烘焙多個場景。

3Scene Dirty Track

Unity內部大多通過Undo系統來實現Scene Dirty追蹤。Unity 5.3爲了支持多場景編輯,我們通過在Undo操作中保存Scene Handles來擴展Undo系統。


另外我們增加了Undo.MoveGameObjectToScene()方法來支持場景之間Game Object移動的Undo。同時Scene結構上面也有一個Scene.isDirty屬性用於查詢某個Scene是否被修改。

4“Ctrl + S”的行爲

在這裏有一個問題想跟大家討論的是“Ctrl + S”的行爲。在5.3以前,無論場景是否Dirty,只要用戶按下”Ctrl + S”,我們一定會保存該場景。而在5.3中,因爲多場景編輯的引入,我們改變了這一行爲。


從5.3開始,”Ctrl + S”只會保存Dirty的場景。試想如果用戶打開了上百個場景,只修改了其中某一個場景,如果“Ctrl + S”還是保存非Dirty的場景,保存速度會受到比較大的影響。
這個改動會影響到一些Editor的工具。比如某個Editor的工具創建了一個Game Object,由於沒有使用Undo系統(Undo.RegisterCreatedObjectUndo),使得場景未標記成Dirty。這樣當在保存的時候,Unity並不會去真正保存這個場景。
在此推薦大家使用Undo系統來註冊Undo操作,從而能夠正確的將受影響的場景標記爲Dirty。我們也樂於聽到大家的反饋,來看看我們是否有辦法更好的處理這個問題。

5Scene加載的延遲Awaking

在多場景的使用中,一個比較有意思的地方就是Scene加載過程中的Delay Awaking。在介紹它之前我們來看看Unity內部加載一個Scene所需的步驟。


6Scene加載的兩個步驟Unity內部場景的加載分爲兩步:
  • Loading。是指從文件、內存(主要是Streamed scene AssetBundle)中加載Scene的內容,創建並讀取所有相關的Game objects、Assets以及Scene game managers。所有的IO操作都在這一步完成,所以它是比較耗時的過程。當這一步完成的時候,我們內部會將加載進度標記爲90%。

  • Awaking。主要是一些輕量級的操作,比如在Transform的Awaking的時候,我們會將Game objects加入到它所屬於的Scene。我們這裏所說的Scene加載過程中的Delay awaking就是指第二步。


比如用戶有一個大場景劃分成了若干個子場景,在所有場景加載完畢我們纔會開始Game play。這時我們就可以推遲所有子場景的Awaking。當所有的加載第一步完成了,我們才進行所有場景的Awaking。

用戶可以通過將AsyncOperation.allowSceneActivation設置成false來阻止Scene的Awaking,示例如下:




當加載進度AsyncOperation.progress到達90%的時候,就可以將allowSceneActivation設置成true來允許Scene awaking。


原文鏈接
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章