はじめに
最近やっとAddressable Asset System(Addressables?正式名称わからず)を使い始め、シーンのロードにも対応していることに驚きました。
UnityのAddressable Asset System、
— su10@ハイパーカジュアルゲーム開発 (@su10_dev) June 15, 2020
専用の参照を使ってアセットを非同期ロードできる
↓
シーンファイルにも対応(!)
├→AssetBundle化されるのでBuildSettingsに登録不要
↓
シーンの参照をインスペクタにD&Dで設定できる🤩🤩🤩
↓
読込にマジックストリング・シーン名定数が不要
神だわ🙏
自分は Extenject 使いなので今後シーンのロードは Addressables + ZenjectSceneLoader
でやろうと思ったのですが、ZenjectSceneLoader
は AssetReference
によるシーン読み込みに対応してなかったので、検証したり対応してみたりしました。
Extenject with Addressablesの状況
Unity Addressable Assets #8(Extenjectリポジトリのissue)
In terms of addressable assets, I'm not really sure. I haven't played with it yet. But we could certainly add it as an alternative to prefabs when it's ready
Addressablesからのプレハブのバインドに関するissueはあるっぽいのですが、シーンのロードに関してはなさそうでした。
そもそも対応しないとどうなる?
API | ProjectContext Bindings | Parent Bindings | Self Bindings |
---|---|---|---|
Addressables.LoadSceneAsync(.. Single) |
⭕ | - | ⭕ |
Addressables.LoadSceneAsync(.. Additive) |
⭕ | ❌ | ⭕ |
Addressables.LoadSceneAsync(.. Additive) + Contract Names |
⭕ | ⭕ | ⭕ |
ZenjectSceneLoader#LoadSceneAsync(.. Single) |
✅ | - | ✅ |
ZenjectSceneLoader#LoadSceneAsync(.. Additive, .. Child) |
✅ | ✅ | ✅ |
ZenjectSceneLoader#LoadSceneAsync(.. Additive) + Contract Names |
✅ | ✅ | ✅ |
- ⭕,❌:そのままの状態のときの挙動
- ✅:APIを足したことにより実現した挙動
検証してみたら Addressables.LoadSceneAsync(.. Additive)
+ Contract Names は普通に動いたので驚きましたが、 ZenjectSceneLoader
を用いて動的にペアレンティングしたい場合は対応する必要がありそうでした。
対応させてみる
環境
- macOS Catalina 10.15.5
- Unity 2018.4.21f1
- Extenject 8.0.1
- Addressables 1.10.0
- エディター上・スタンドアロンビルドで簡単な動作検証済み
動作検証用に簡単なプロジェクトを作ったので興味ある人はこちらのリポジトリをクローンして触ってみてください。
ExtenjectからAddressablesを参照できるようにする
それぞれAssembly DefinitionでDLLが区切られているので zenject.asmdef
にAddressables関連の asmdef
への参照を追加します。

AssetReferenceSceneAssetを実装する
AssetReference
をそのまま使ってもいいのですが、 SceneAsset
以外の参照を渡したときにコンパイルエラーにしたいので SceneAsset
専用の参照クラスを実装します。
参考:something like AssetReferenceT(Unityフォーラム)
using System;
using UnityEngine.AddressableAssets;
[Serializable]
public class AssetReferenceSceneAsset : AssetReferenceT<
# if UNITY_EDITOR
UnityEditor.SceneAsset
# else
UnityEngine.Object
# endif
>
{
public AssetReferenceSceneAsset(string guid) : base(guid)
{
}
}
このクラスをExtenjectから参照できるようにします。
-
Assets/Plugins/Zenject/Source
とかのzenject.asmdef
配下に置く - 自前の
asmdef
の配下に置いて先の手順のようにzenject.asmdef
に参照を足す
などプロジェクトに応じてよしなに設定します。
ZenjectSceneLoader
にメソッドを追加する
以下を追加します。
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;
using UnityEngine.ResourceManagement.ResourceProviders;
...
public AsyncOperationHandle<SceneInstance> LoadSceneAsync(AssetReferenceSceneAsset sceneReference)
{
return LoadSceneAsync(sceneReference, LoadSceneMode.Single);
}
public AsyncOperationHandle<SceneInstance> LoadSceneAsync(AssetReferenceSceneAsset sceneReference, LoadSceneMode loadMode)
{
return LoadSceneAsync(sceneReference, loadMode, null);
}
public AsyncOperationHandle<SceneInstance> LoadSceneAsync(
AssetReferenceSceneAsset sceneReference, LoadSceneMode loadMode, Action<DiContainer> extraBindings)
{
return LoadSceneAsync(sceneReference, loadMode, extraBindings, LoadSceneRelationship.None);
}
public AsyncOperationHandle<SceneInstance> LoadSceneAsync(
AssetReferenceSceneAsset sceneReference,
LoadSceneMode loadMode,
Action<DiContainer> extraBindings,
LoadSceneRelationship containerMode)
{
return LoadSceneAsync(
sceneReference, loadMode, extraBindings, containerMode, null);
}
public AsyncOperationHandle<SceneInstance> LoadSceneAsync(
AssetReferenceSceneAsset sceneReference,
LoadSceneMode loadMode,
Action<DiContainer> extraBindings,
LoadSceneRelationship containerMode,
Action<DiContainer> extraBindingsLate)
{
PrepareForLoadScene(loadMode, extraBindings, extraBindingsLate, containerMode);
return Addressables.LoadSceneAsync(sceneReference, loadMode);
}
ちなみにこれらはZenjectSceneLoader
の既存の LoadSceneAsync()
をコピペして
- 返り値
- 引数
SceneManager
をAddressables対応仕様に書き換えただけのものです。
これで ZenjectSceneLoader
にシーンの参照を渡してロードしつつ、シーン内の [Inject]
に適切にInjectされるようにできました。
感想
Addressablesを使ったバインディングのベストプラクティスが知りたい。