Fusion シーンの読み込み

Level Advanced

概要

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

このFusion Scene Loadingサンプルでは、開発者が必要に応じて操作できる、よりカスタマイズ性の高いFusionのシーン読み込み方法を探求し、紹介しています。このサンプルでは、単純なシングルシーンの読み込みではなく、加算読み込みにより最大4つのシーンを同時に読み込み、各クライアントがサーバーに読み込まれたシーンをローカルに読み込むかどうかを制御することができます。

トップに戻る

ハイライト

  • カスタマイズ可能なシーン読み込み。
  • 最大4シーンの同時読み込み
  • クライアントサイドのシーンインタレスト管理

トップに戻る

ダウンロード

バージョン リリース日 ダウンロード
1.1.1 Jun 14, 2022 Fusion Scene Loading 1.1.1 Build 4

トップに戻る

プロジェクト

デモを実行する前に、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になり、シーンを追加的に読み込むようになったことです。

  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上の関数を呼び出し、適切なロジックを実装して読み込みと読み込み解除が必要なシーンがあるかどうかをチェックします。

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.

private SceneRef _desiredScene;

protected override SceneRef GetDesiredSceneToLoad() {
  return _desiredScene;
}

トップに戻る

CustomNetworkSceneManager

読み込むべきシーンのインデックスを持つリストを持つには、ネットワークコレクションを持つ NetworkBehaviour が必要です。この NetworkBehaviour は、現在読み込まれているシーンと、これから読み込みたいシーンを保持する役割を果たします。

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 に自分自身を登録します。

//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 変数に読み込む必要のあるシーンのインデックスを代入します。

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 リストから削除します。

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);
    }
}


ドキュメントのトップへ戻る