アドレッサブルシーン

Addressables カタログから加算シーンをロード/アンロードする。キーごとの参照カウント付き。

現在のレベルの上に追加のシーンをストリーミングして出し入れ — 参照カウント付きなので、共有コンテンツが早すぎるタイミングでアンロードされることはありません。

インターフェースIAddressableSceneService
オフスイッチNullAddressableSceneService
アセンブリCommonGameSystem.AddressableScene(オプション — Addressables パッケージを削除するとコンパイルから外れる)
起動起動時に自動登録(23 サービスの 1 つ)— セットアップ不要

概要

このサービスは、すでに開いているシーンの上に Addressables カタログからシーンをロードし(加算ロード — 新しいシーンは現在のシーンを置き換えるのではなく、そこに加わります)、用が済んだらアンロードします。Addressables は Unity のアセット配信システムです:コンテンツはカタログにパブリッシュされ、実行時に文字列キーでロードされます。

各シーンキーは参照カウントされます:すでにライブなキーをロードすると既存のシーンを共有してカウントが上がり、シーンが本当にアンロードされるのはカウントが 0 に戻ったときだけです。実行中のロードも共有されます — 同じキーの重なった 2 つのロードは、1 つの操作を await します。

ランタイム障害は例外を投げません。キーが見つからない、またはロードが失敗した場合は、警告をログして Succeeded = false の結果を返すので、ゲームは動き続けます。

このサービスは一度に 1 操作だけ実行し、その IsLoading ゲートはシーンフローサービス(ISceneService)から独立しています — 両者が互いをブロックすることはありません。

オプションの CommonGameSystem.AddressableScene アセンブリで出荷されます。Addressables パッケージを削除すると、このアセンブリは自分自身をオフにし、フレームワークの残りはコンパイルされ続けます。

クイックスタート

using CommonGameSystem.Core;
using UnityEngine;

public class MySceneStreamer : MonoBehaviour
{
    private IAddressableSceneService _scenes;

    private void Awake() => _scenes = ServiceLocator.Resolve<IAddressableSceneService>();

    public async void EnterArea()
    {
        SceneLoadResult result = await _scenes.LoadAdditiveAsync("DlcArea_01");
        if (result.Succeeded) { /* scene is live */ }
    }

    // later, when you're done with it:
    public async void LeaveArea()
    {
        await _scenes.UnloadAdditiveAsync("DlcArea_01");   // count goes down; unloads at 0
    }
}

Bootstrap は起動時にこのサービスを自動登録します。上のように、Awake() で一度解決してキャッシュしてください。

API リファレンス

IAddressableSceneService

メンバー説明
Task<SceneLoadResult> LoadAdditiveAsync(string key, CancellationToken ct = default)カタログのシーンを加算ロードします。すでにライブなキーをロードすると既存のシーンを共有します:カウントが上がり、共有された結果を受け取ります。別の操作の実行中にロードを開始すると、メッセージが [AddrScene] で始まる InvalidOperationException を投げます。キーが見つからない、またはロードが失敗した場合は決して例外を投げません — 警告をログして Succeeded = false を返します。例外を投げるのはプログラミングエラーだけです:null や空白のキー、破棄済みのサービス、ワーカースレッドからの呼び出し。
Task UnloadAdditiveAsync(string key, CancellationToken ct = default)ロード済みキーの参照カウントを下げます。シーンがアンロードされるのはカウントが 0 に達したときだけで、自動追い出しはありません。ロードされていないキーのアンロードは決して例外を投げません — 警告をログして完了済みタスクを返します。
IReadOnlyList<string> ActiveSceneList { get; }現在ロードされている異なるシーンキーを、ロード順で。読み取り専用のスナップショットで、参照カウントは公開されません。サービスがオフのときは空です。
bool IsLoading { get; }このサービスに実行中の操作があるかどうか。ISceneService.IsLoading とは別物です — 「何かシーン処理が走っているか?」を知りたければ両方を確認してください。

SceneLoadResult

シーンフローサービスが使うものと同じ結果型です:

メンバー説明
string SceneNameこの結果が属するシーンキー。
bool Succeeded成功とログ済みの失敗を見分けるには、これで分岐します。
bool Canceledここでは未使用 — 常に false
Exception Errorあれば、根底にある失敗。
TimeSpan TotalDuration操作にかかった時間。

イベント

イベントバス上でパブリッシュされます — ペアリング保証については下の落とし穴を参照:

イベント発火タイミング
AddrSceneLoadStarted(string Key)実際のロードが始まった(キーが未ロードからロード中になった)。
AddrSceneLoadCompleted(string Key, SceneLoadResult Result)ロードがキャンセルされずに終わった(成功またはログ済みの失敗)。
AddrSceneLoadCanceled(string Key, Exception Cause)実行中のロードが、その CancellationToken によりキャンセルされた。

AddressableSceneOptions — コンストラクタ設定

メンバー説明
bool WarnOnMissingKey / bool WarnOnUnloadOfUnloaded警告の饒舌さ。Default プリセットは警告し(診断しやすい)、Release プリセットは沈黙します。
int InitialKeyCapacity内部シーンテーブルの初期容量。0–1024 にクランプ。

使用例

using CommonGameSystem.Core;
using UnityEngine;

public class DlcLoader : MonoBehaviour
{
    private IAddressableSceneService _scenes;
    private const string DlcKey = "DlcArea_01";

    private void Awake() => _scenes = ServiceLocator.Resolve<IAddressableSceneService>();

    public async void EnterDlc()
    {
        // Loading twice is safe: a second call while live just raises the count.
        SceneLoadResult result = await _scenes.LoadAdditiveAsync(DlcKey);
        if (!result.Succeeded)
        {
            Debug.LogWarning($"DLC scene didn't load: {result.Error}");
            return;
        }
        Debug.Log($"Loaded in {result.TotalDuration.TotalMilliseconds:F0} ms. " +
                  $"Active: {string.Join(", ", _scenes.ActiveSceneList)}");
    }

    public async void LeaveDlc() => await _scenes.UnloadAdditiveAsync(DlcKey); // unloads at count 0
}

無効化する

ServiceLocator.Replace<IAddressableSceneService>(new NullAddressableSceneService());

これですべての加算ロードが抑止されます。LoadAdditiveAsync は呼び出し側が動き続けるよう Succeeded = true の完了済み結果を返し、UnloadAdditiveAsync は何もせず、ActiveSceneList は空で、イベントは発火しません。ヘッドレステストや、カタログコンテンツなしで出荷するビルドに便利です。置き換えの構築時に 1 回警告がログされるので、差し替えが無音で起きることはありません。プログラミングエラーは引き続き例外を投げ — null キーはやはり ArgumentNullException を投げます — 要求されたキャンセルも引き続き尊重されます。

よくある落とし穴

  • 参照カウントは呼び出しの対を意味します。 2 回の LoadAdditiveAsync("X") はカウントを 2 にします。シーンは最初の UnloadAdditiveAsync("X") を生き延び、2 回目でようやくアンロードされます。すべてのロードにちょうど 1 回のアンロードを対にしないと、シーンを生かしたまま残すことになります。
  • ゲートはシーンフローから独立しています。 ここでの IsLoading はこのサービス自身のロックです。シーンフローの操作をブロックすることも、ブロックされることもありません。[AddrScene]InvalidOperationException を投げるのは、このサービス上の 2 つの操作が重なったときだけです。
  • すべての開始は、ちょうど 1 回クローズされます。 共有されたロードと拒否された呼び出しはイベントをパブリッシュしません。したがって各 AddrSceneLoadStarted には、ちょうど 1 つの AddrSceneLoadCompleted またはちょうど 1 つの AddrSceneLoadCanceled が対応します — 両方でも、どちらもなしでもありません。
  • 通常のシーン遷移は加算シーンを破壊します。 ゲームがフル(非加算)のシーン切り替えを行うと、Unity はすべての加算シーンをアンロードします — このサービスがまだ追跡しているものも含めてです。そうした遷移の後は、必要なものをロードし直してください。

関連ページ

  • シーンフロー — ベースのシーンローダー(ISceneService)。Unity のビルドリストからの独自の加算ロードも含む
  • アセットプロバイダー — 兄弟サービス:同じ参照カウント方式による Addressables のアセットロード
  • イベントバスAddrSceneLoad* イベントの購読