シーンローディング
概要
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