シーンの読み込み
概要
このFusion Scene Loadingサンプルでは、開発者が必要に応じて操作できる、よりカスタマイズ性の高いFusionのシーン読み込み方法を探求し、紹介しています。このサンプルでは、単純なシングルシーンの読み込みではなく、加算読み込みにより最大4つのシーンを同時に読み込み、各クライアントがサーバーに読み込まれたシーンをローカルに読み込むかどうかを制御することができます。
ハイライト
- カスタマイズ可能なシーン読み込み。
- 最大4シーンの同時読み込み
- クライアントサイドのシーンインタレスト管理
ダウンロード
バージョン | リリース日 | ダウンロード | ||
---|---|---|---|---|
1.1.3 | Oct 21, 2022 | Fusion Scene Loading 1.1.3 Build 55 |
プロジェクト
デモを実行する前に、Photon Cloud 用の Fusion App Id を作成し、PhotonAppSettings
アセットにコピーペーストしておく必要があります。App IdはPhoton Dashboardから作成することができます。Realtime Id ではなく、Fusion App Id を作成するようにしてください。
Photon App Settings アセットは、Unity メニューの Fusion > Realtime Settings
から選択することができます。
生成された App Id を App Id Fusion
フィールドに貼り付けます。
フォルダの構成
サンプルのコードは、Scripts
フォルダにあり、以下のように細分化されています。
- Helpers: 便利なマイナークラスです。
- Sample: サンプル。このサンプルが提供する2つのシーンで使用されるロジックです。
- CustomSceneLoading: シーンのロードを処理する、サンプルのコアクラスです。
サンプルループ
MainScene
には、セッション名を入力するフィールドと、クライアントシーン管理を有効にするチェックボックスが表示され、利用可能な各モードを入力するための2つのボタンも表示されます。
クライアントシーン管理はホストのみが使用し、クライアントはセッションのホストからネットワークプロパティを介して更新を取得します。指定された名前のセッションがない場合、ホストモードでセッションが作成されます。
Buttonsサンプル には、4つのシーンが表示されます。ホストは、これらのボタンでどのシーンを読み込むかを切り換えることができます。
クライアントのシーン管理が有効な場合、クライアントはこれらのボタンを切り替えて、どのシーンの読み込みを許可するかを表示することができます。
Character サンプルでは、各プレイヤーがキャラクターを操作します。キャラクターをマップの隅に近づけると、そのエリアに隣接するシーンの読み込みが開始されます。そのエリアから離れると、アンロードが行われます。
クライアントのシーン管理が有効な場合、クライアントは許可されたシーンのみを読みこみます。
クライアントシーン管理
クライアントシーン管理ロジックは、クライアントが明示的に許可したシーンのみ読み込むように制限します。これらは InterestedInScenes
コレクションによって指定されます。これにより、各クライアントはアクティブなサーバーシーンの異なるサブセットを読み込むことができます。ホスト/サーバーは、セッションのステートオーソリティであるため、すべてのシーンを読み込む必要があります。
このサンプルでは、複数のシーンリストでこれを処理します。
CurrentScenes
: ホスト/サーバーに読み込まれた現在のシーンのネットワーク化されたリストです。InterestedInScenes
: ローカルクライアントが読み込むシーンが含まれています。サーバーのCurrentScenes
に追加されたシーンは、このコレクションに含まれている場合にのみ、クライアントに読み込まれます。LoadedScenes
: ローカルで現在読み込まれているシーンです。これはCurrentScenes
やInterestedInScenes
の変更が読み込み、アンロード、または何もしないかのトリガーになるかを決定するために使用されます。
ClientSceneManagement
が enabled の場合、クライアントは CurrentScenes
エントリのうち、クライアントが特に許可したもの (InterestedInScenes
) のみを読み込むようになります。
もし ClientSceneManagement
が disabled ならば、クライアントは CurrentScenes
コレクションにリストされている全てのシーンを読み込みます。
CustomSceneLoaderBase (カスタムシーンローダーベース)
このクラスは NetworkSceneManagerBase
のクローンで、いくつかの変更が加えられています。最も重要な変更点は、2つの抽象的なメソッドを宣言していることです。
bool IsScenesUpdated()
: 読み込む必要のあるシーンがある場合、true を返します。SceneRef GetDesiredSceneToLoad()
: 読み込むべきシーンのSceneRef
を返します。
CustomSceneLoader
このクラスは CustomSceneLoaderBase
を継承し、以下の抽象的なメソッドを実装します。
SwitchScene()
オリジナルの 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;
}
IsScenesUpdated()
新しいシーンを読み込む必要があるかどうかを決定する役割を果たします。この実装では、CustomNetworkSceneManager
上の関数を呼び出し、適切なロジックを実装して読み込みと読み込み解除が必要なシーンがあるかどうかをチェックします。
C#
protected override bool IsScenesUpdated() {
if (Runner.SceneManager() && Runner.SceneManager().Object) {
Runner.SceneManager().UnloadOutdatedScenes();
return Runner.SceneManager().IsSceneUpdated(out _desiredScene);
}
return true;
}
GetDesiredSceneToLoad()
Returns the scene to be loaded, _desiredScene
which is assigned on IsScenesUpdated()
as explained above.
C#
private SceneRef _desiredScene;
protected override SceneRef GetDesiredSceneToLoad() {
return _desiredScene;
}
CustomNetworkSceneManager
読み込むべきシーンのインデックスを持つリストを持つには、ネットワークコレクションを持つ 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;
//...
}
Extension method
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)
は、読み込むべきシーンがあれば true を返し、 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