Scene Loading
概述
這個Fusion場景載入範例探索及展示了在Fusion中載入場景的一個更客製化的方式,開發者可以操控這個方式所需要的東西。這個範例使用一個最大為四個的場景載入,而非簡單的單一場景載入,其可以使用累加的載入來同時載入四個場景,並且能夠讓各個客戶端來控制哪些在伺服器上的被載入場景應該在本機上載入。
聚焦點
- 可客製化的場景載入。
- 最多達4個同時存在的場景。
- 客戶端側的場景興趣管理。
下載
版本 | 發佈日期 | 下載 | ||
---|---|---|---|---|
1.1.3 | 2022年10月21日 | Fusion場景載入1.1.3組建55 |
專案
在運行示範版本之前,需要建立及複製貼上一個針對Photon Cloud的 Fusion 應用程式帳號到PhotonAppSettings
資產中。可以從Photon Dashboard建立應用程式帳號。請確保建立一個 Fusion 應用程式帳號,而並不是只是一個即時帳號。
可以從Unity選單Fusion > Realtime Settings
來選擇Photon應用程式設定資產。
簡單地貼上已生成的應用程式帳號到App Id Fusion
欄位之中。
資料夾架構
針對範例的程式碼位於Scripts
資料夾中,並且被次分為:
- 協助程式:為了方便性的小的類別。
- 範例:用於範例提供的2個場景的邏輯。
- 自訂場景載入:範例核心類別,其處理場景載入。
範例迴圈
MainScene
含有一個欄位,以輸入遊戲階段名稱,及一個勾選框,以啟用客戶端場景管理,以及兩個用於輸入各個可用的模式的按鈕。
客戶端場景管理只由主機端使用,而客戶端將從它們的遊戲階段的主機端,透過已連線屬性來獲得更新。如果沒有帶有已提供的名稱的遊戲階段,將在主機端模式下建立一個遊戲階段。
在「按鈕範例」中,顯示四個按鈕,其代表四個可用的場景。主機端可以使用這些按鈕來切換將被載入的場景。
如果啟用客戶端場景管理,客戶端可以切換這些按鈕,以指出被允許載入的場景。
在「角色範例」中,各個玩家控制一個角色。移動該角色到靠近地圖的任何一個角落,將觸發靠近該區域的場景的載入。離開該區域將觸發一個取消載入。
如果啟用了客戶端場景管理,客戶端將只載入被允許的場景。
客戶端場景管理
客戶端場景管理邏輯限制客戶端只能載入該客戶端明確允許的場景;這些由InterestedInScenes
集合來指定。這允許各個客戶端來載入不同的啟用的伺服器場景的子集。主機端/伺服器必須載入每個場景,因為它是遊戲階段的狀態授權。
這個範例以多個場景清單來處理這件事情:
CurrentScenes
:在主機端/伺服器上載入的目前場景的已連線清單。InterestedInScenes
:含有本機客戶端將載入的場景。當場景被新增到伺服器的CurrentScenes
上,如果該場景被包含在這個集合中,它們將只在客戶端上載入。LoadedScenes
:是本機目前已載入的場景。它用於確定在CurrentScenes
或InterestedInScenes
中的改變是否應該觸發一個載入、一個取消載入或不觸發任何事情。
如果ClientSceneManagement
被 啟用,客戶端將只載入CurrentScenes
輸入項目,其在客戶端上被特定地允許(InterestedInScenes
)。
如果ClientSceneManagement
被 停用,客戶端將載入列於CurrentScenes
集合中的所有場景。
自訂場景載入器基礎
這個類別是NetworkSceneManagerBase
的一個複製,並且附有一些改變。最重要的改變是,它宣告兩個抽象方法。
bool IsScenesUpdated()
:如果有任何將需要載入的場景,則傳回真。SceneRef GetDesiredSceneToLoad()
:傳回將需要載入的場景的SceneRef
。
自訂場景載入器
這個類別繼承於CustomSceneLoaderBase
並且執行以下的抽象方法:
切換場景()
在原始NetworkSceneManagerBase
類別中,定義一個協同程式以適當地處理在Unity中載入場景,並且傳回所有在已載入場景中找到的NetworkObjects
。與在這個範例中使用的這個變數的主要的區別在於,它現在是一個Task
,並且額外地載入場景。
C#
protected override async Task<IEnumerable<NetworkObject>> SwitchScene(SceneRef prevScene, SceneRef newScene) {
prevScene = SceneManager.GetSceneAt(SceneManager.sceneCount - 1).buildIndex;
Debug.Log($"Switching Scene from {prevScene} to {newScene}");
List<NetworkObject> sceneObjects = new List<NetworkObject>();
if (newScene >= 0) {
var loadedScene = await LoadSceneAsset(newScene, LoadSceneMode.Additive);
Debug.Log($"Loaded scene {newScene}: {loadedScene}");
sceneObjects = FindNetworkObjects(loadedScene, disable: false);
}
Debug.Log($"Switched Scene from {prevScene} to {newScene} - loaded {sceneObjects.Count} scene objects");
return sceneObjects;
}
場景被更新()
負責決定何時需要載入新的場景。在這個執行方式中,它調用一個在CustomNetworkSceneManager
上的功能,其執行適當的邏輯並且檢查需要被載入或取消載入的任何場景。
C#
protected override bool IsScenesUpdated() {
if (Runner.SceneManager() && Runner.SceneManager().Object) {
Runner.SceneManager().UnloadOutdatedScenes();
return Runner.SceneManager().IsSceneUpdated(out _desiredScene);
}
return true;
}
取得所需的場景以載入()
傳回將要載入的場景,_desiredScene
,其是在IsScenesUpdated()
上被指派,如上所述。
C#
private SceneRef _desiredScene;
protected override SceneRef GetDesiredSceneToLoad() {
return _desiredScene;
}
自訂網路場景管理器
為了有一個將載入的場景索引的清單,需要一個附有已連線集合的NetworkBehaviour
。這個NetworkBehaviour
將負責保有目前已載入及希望載入的場景。
C#
public class CustomNetworkSceneManager : NetworkBehaviour
{
private const int MAXSCENES = 4;
[Networked] public NetworkBool ClientSceneManagement { get; set; }
[Networked, Capacity(MAXSCENES)] public NetworkLinkedList<SceneRef> CurrentScenes => default;
public List<SceneRef> InterestedInScenes = new List<SceneRef>(MAXSCENES);
public List<SceneRef> LoadedScenes = new List<SceneRef>(MAXSCENES);
private SceneRef _sceneToUnload;
//...
}
擴展方法
在Spawned()
上,CustomNetworkSceneManager
在NetworkRunner.SetSceneManager()
自訂擴展方法的協助下,以NetworkRunner
註冊自己。
C#
//CustomNetworkSceneManager.cs
public override void Spawned()
{
if (Runner.SceneManager())
{
Runner.Despawn(Object);
return;
}
DontDestroyOnLoad(this);
Runner.SetSceneManager(this);
}
新增及移除場景
CustomNetworkSceneManager
有針對新增及移除場景的方法。這些方法負責處理主機端上的[Networked] CurrentScenes
已連結清單中的場景索引,及客戶端側上的InterestedInScenes
清單中的場景索引。
場景更新偵測及已過期的取消載入
如果有任何應該被載入的場景,則bool IsSceneUpdated(out SceneRef sceneRef)
傳回真,並且指派out
變數到需要被載入的場景的索引。
C#
public bool IsSceneUpdated( out SceneRef sceneRef )
{
for (int i = 0; i < CurrentScenes.Count; i++) {
if (CurrentScenes[i] == default) continue;
if (LoadedScenes.Contains(CurrentScenes[i])) continue;
if (ClientSceneManagement == false || InterestedInScenes.Contains(CurrentScenes[i])) {
sceneRef = CurrentScenes[i];
LoadedScenes.Add(CurrentScenes[i]);
return false;
}
}
sceneRef = SceneRef.None;
return true;
}
UnloadOutdatedScenes()
功能偵測是否有任何已載入場景應該被取消載入,並從_sceneToUnload
清單來移除它。
C#
public void UnloadOutdatedScenes(Action<AsyncOperation> removeOutdatedObjects)
{
_sceneToUnload = LoadedScenes.Except(ClientSceneManagement ? InterestedInScenes.Intersect(CurrentScenes) : CurrentScenes).FirstOrDefault();
// Reload scenes is priority
// Reloading scenes is basically done by having a stack of
// scenes to unload and if there's any scene left, pop out and
// set to _sceneToUnload.
if (_scenesToReload.Count > 0) {
_sceneToUnload = _scenesToReload.Pop();
// The host needs to say the clients to reload
// only when finished reloading all scenes.
if (_scenesToReload.Count == 0)
OnUpToDate += TriggerReloadByte;
}
if (_sceneToUnload) {
Debug.Log("Unloading scene - " + _sceneToUnload);
SceneManager.UnloadSceneAsync(_sceneToUnload);
LoadedScenes.Remove(_sceneToUnload);
}
}
Back to top