This document is about: FUSION 1
SWITCH TO

シーンの読み込み

Level 4

概要

Fusion Scene Loading サンプルは現在、シングル ピア シーンの読み込みのみを展示しています。

この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: ローカルで現在読み込まれているシーンです。これは CurrentScenesInterestedInScenes の変更が読み込み、アンロード、または何もしないかのトリガーになるかを決定するために使用されます。

ClientSceneManagementenabled の場合、クライアントは CurrentScenes エントリのうち、クライアントが特に許可したもの (InterestedInScenes) のみを読み込むようになります。

もし ClientSceneManagementdisabled ならば、クライアントは 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() において、CustomNetworkSceneManagerNetworkRunner.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