シーンローディング
概要
Fusionにはシーンの継承コンセプトがありません。代わりに、INetworkSceneManagerインターフェースを提供しており、これを使用するとデベロッパーがシーン変更のカスタムハンドリングを定義できます。 これをScene Managerと呼びます。
Scene Manager インスタンスは、StartGameArgsのSceneManagerフィールド経由でNetworkRunner.StartGame()NetworkSceneManagerDefaultのデフォルトインスタンスを作成して使用します。
Scene Manager を定義するオプション配下の通りです。
- INetworkSceneManager: このインターフェースを実装すると完全にカスタムの Scene Manager を定義できます。
- NetworkSceneManagerBase: 基本のハンドリングでの
INetworkSceneManagerの抽象実装。このクラスから派生して拡張することで、カスタムの Scene Managerを定義します。 - NetworkSceneManagerDefault:
NetworkSceneManagerBaseから派生する、完全で効果的なINetworkSceneManagerのプロトタイプ実装。このクラスはそのまま使用することも、必要に応じて拡張して上書きすることもできます。完全なカスタム実装を作成する方法の事例としても作用します。このクラスは、NetworkRunner.StartGame()に提供されるSceneManagerがない場合、デフォルトの Scene Manager となります。
INetworkSceneManager
クラスをNetworkRunnerに Scene Manager としてアサインするには、そのクラスに以下のメソッドでINetworkSceneManagerインターフェースが実装されている必要があります。
void Initialize(NetworkRunner runner):NetworkRunnerインスタンスを使用するあらゆる初期化に使用。NetworkRunner.Initialize()ハンドリングの一環としてFusionが呼び出しを行う。void Shutdown(NetworkRunner runner):NetworkRunner.Shutdown()プロセスの一環としてFusionが呼び出しを行う。bool IsReady(NetworkRunner runner): 現在のランナーシーンが完了したかどうかの確認を定義する。
INetworkSceneManagerObjectResolver
Fusionは、デフォルトで登録されている全てのネットワークシーンオブジェクトを管理しランナーからのリクエストに応じて解決します。
INetworkSceneManagerObjectResolverインターフェースを実装することで、このプロセスを手動で行うことも可能です。このインターフェースは、ネットワークシーンオブジェクトがパスされたsceneObjectGuidにマッチする方法を決定するTryResolveSceneObject()メソッドを提供します。(オブジェクトの検索と返答)
メソッドシグネチャ全体は bool TryResolveSceneObject(NetworkRunner runner, Guid sceneObjectGuid, out NetworkObject instance)となります。
NetworkSceneManagerDefault
StartGameArgsからNetworkRunner.StartGame()の一環として一何も実装がが提供されない場合、NetworkSceneManagerDefaultがデフォルトの Scene Manager となります。
NetworkSceneManagerDefaultは NetworkSceneManagerBaseから継承されます。抽象コルーチンを実装してUnityのローカルでシーンを切り替え、そのシーンにあるすべてのNetworkObjectをプールします。NetworkSceneManagerDefaultクラスはまた、オーバーライドを使用して確証し、カスタム Scene Manager 挙動を作成することもできます。
NetworkSceneManagerDefaultを使用している場合、 Runner.SetActiveScene()がデフォルトの実装にあるscene update detectionで使用されるシーンインデックスを設定し、すべてのクライアントで同期します。
NetworkSceneManagerBase
NetworkSceneManagerBaseは、INetworkSceneManagerインターフェースを実装する抽象クラスです。カスタムの Scene Manager を作成する際の出発点として使用することもできます。
新しいシーンのローディングが必要な場合、NetworkSceneManagerBaseが検知を行い、非同期のローディングプロセスを管理します。プールしていたシーンのNetworkObjectsをNetworkRunner.RegisterSceneObjects()へパスすることでシミュレーションオブジェクトも割り当てます。
NetworkSceneManagerBaseから継承したカスタムの Scene Manager を実装する場合、 IEnumerator SwitchScene()の抽象メソッドが必要です。このメソッドはシーンをローディングし、ローディングが終わるとそのシーンにある全てのNetworkObjectsを集め、NetworkObjectsのコレクションをfinished委譲にパスします。それ以外に、デベロッパーは合うと思う自由に機能を追加することができます。
または、INetworkSceneManagerを実装することで完全にカスタムのクラスを作成することもできます。
シーンアップデート検知
NetworkSceneManagerBaseはLateUpdate()機能を使用して最新のローディングシーンがNetworkRunner.CurrentSceneに提供されるものと異なるかどうか確認します。この確認は、ローディングすべき新しいシーンがあるかの検出に必要です。
C#
private SceneRef _currentScene;
private bool _currentSceneOutdated;
protected virtual void LateUpdate()
{
if (!Runner)
return;
if (Runner.CurrentScene != _currentScene)
_currentSceneOutdated = true;
}
NetworkRunner.CurrentScene は、クライアントが使用する1つの SceneRefのみをネットワークしてシーンの変更を検出するシンプルな実装です。複数のシーンを管理するため、NetworkRunner.CurrentSceneを無駄にせずにコレクションを保持しておくことも可能です。INetworkSceneManager.IsReady
NetworkSceneManagerBaseはINetworkSceneManagerを実装するので、IsReady()の実装を提供してNetworkRunnerが新しくローディングしたネットワークシーンオブジェクトを会った資するタイミングを知らせる必要があります。
C#
bool INetworkSceneManager.IsReady(NetworkRunner runner)
{
Assert.Check(Runner == runner);
if (_runningCoroutine != null)
return false;
if (_currentSceneOutdated)
return false;
if (runner.CurrentScene != _currentScene)
return false;
return true;
}
ローディングシーンコルーチンが実行中または現在のシーンの有効期限が切れている場合、メソッドのリターンはfalseになります。
SwitchSceneWrapper
SwitchSceneWrapperは実際のシーンローディングをハンドリングするコルーチンです。
C#
LateUpdate()
{
// All the previous checks
var prevScene = _currentScene;
_currentScene = Runner.CurrentScene;
_currentSceneOutdated = false;
_runningCoroutine = SwitchSceneWrapper(prevScene, _currentScene);
StartCoroutine(_runningCoroutine);
}
このコルーチンの適切な実行は以下の通りです。
新しくローディングされたシーンオブジェクトを保持するコントロール変数とディクショナリが作成され、NetworkSceneManagerDefaultに実装されたSwitchScene()抽象メソッドが呼び出され、結果として生成されるオブジェクトのリストが登録されNetworkRunner.RegisterSceneObjects()メソッドへパスされます。
C#
private IEnumerator SwitchSceneWrapper(SceneRef prevScene, SceneRef newScene)
{
bool finishCalled = false;
IEnumerable<NetworkObject> sceneObjects;
//...
//Call and execution of abstratc SwitchScene()
//...
if (error != null)
LogError($"Failed to switch scenes: {error}");
else if (!finishCalled)
LogError($"Failed to switch scenes: SwitchScene implementation did not invoke finished delegate");
else
{
Runner.RegisterUniqueObjects(sceneObjects.Values);
}
}
SwitchScene
SwitchScene()メソッドはNetworkSceneManagerDefaultによって実装され、以下のオペレーションを行います。
- 現在のシーンをアンロードする(現在のシーンが有効の場合)
- Unity
LoadSceneAsync()メソッドを呼び出す - 待機中が完了するまで実行する(シーンのローディング完了
- 待機中が完了してローディングしたシーンが有効になったら、エラーが出てコルーチンを実行する。
- 各ローディングしたシーンの
NetworkObjectを検索して保存する- 新しくローディングしたシーンにあるすべての
NetworkObjectを無効化する(後でアタッチする時に有効化する) PeerMode.Multipleの場合: 各NetworkObjectをRunner Visibilityハンドリングで登録する
- 新しくローディングしたシーンにあるすべての
Finished()委譲を起動して、引数としてシーン内にあるNetworkObjectのコレクションにパスする
PeerMode.Multipleで、特定のRunnerの Physics Sceneにオブジェクトを挿入したり、RunnerのVisibilityハンドリングに`NetworkObject`を追加したりするにはシーンのローディングに特別な対応が必要です。
C#
//The following code is a simplified overview.
//The coroutine itself.
//It selects the proper handling method based on the peer mode.
protected override IEnumerator SwitchScene(SceneRef prevScene, SceneRef newScene, FinishedLoadingDelegate finished) {
if (Runner.Config.PeerMode == NetworkProjectConfig.PeerModes.Single) {
return SwitchSceneSinglePeer(prevScene, newScene, finished);
} else {
return SwitchSceneMultiplePeer(prevScene, newScene, finished);
}
}
//Summary of the single peer implementation.
protected virtual IEnumerator SwitchSceneSinglePeer(SceneRef prevScene, SceneRef newScene, FinishedLoadingDelegate finished) {
// Creating and defining needed variables.
Scene loadedScene;
Scene activeScene = SceneManager.GetActiveScene();
LoadSceneParameters loadSceneParameters = new LoadSceneParameters(LoadSceneMode.Single);
loadedScene = default;
yield return LoadSceneAsync(newScene, loadSceneParameters, scene => loadedScene = scene);
if (!loadedScene.IsValid()) {
throw new InvalidOperationException($"Failed to load scene {newScene}: async op failed");
}
//Delay frames for safety
for (int i = PostLoadDelayFrames; i > 0; --i) {
yield return null;
}
var sceneObjects = FindNetworkObjects(loadedScene, disable: true);
finished(sceneObjects);
}
Back to top