📦
【もぉん流】Unity × VContainer : 手っ取り早く使おう
この記事では、手っ取り早く下記の目的を達成するための手順を紹介します。
- VContainerに触れてみたい
- シーン間でデータを引き継ぎたい
- 手動で設定する参照関係を最小限したい
補足
丁寧な使い方については、公式サイトや他の記事などを参照してください!
1. インストール
公式サイトからどうぞ!
Git URLが手軽でおすすめです
2. OnePlaySaveData.csを実装
シーンを跨いで引き継ぐデータを実装します。
public class OnePlaySaveData
{
public int Score;
}
3. RootLifetimeScope.csを実装
RootLifetimeScopeは、どのシーンから再生しても最初にただ1つ生成される、すべての親となるLifetimeScopeです。(LifetimeScopeは、参照関係を管理/解決する機構のこと)
using VContainer;
using VContainer.Unity;
public sealed class RootLifetimeScope : LifetimeScope
{
protected override void Configure(IContainerBuilder builder)
{
// OnePlaySaveDataを登録
builder.Register<OnePlaySaveData>(Lifetime.Singleton);
// 参照関係が構築された後に、
// 子に存在する全てのオブジェクトにInject(注入)する
builder.RegisterBuildCallback(resolver =>
{
foreach (Transform child in transform)
{
resolver.InjectGameObject(child.gameObject);
}
});
}
}
4. RootLifetimeScopeを設定する
- 適当な
Prefabを作成し、RootLifetimeScope.csをアタッチする
-
Projectウィンドウで右クリックし、Create/VContainer/VContainer Settingsで設定ファイルを作成 - 作成した
VContainerSettings.assetのRootLifetimeScopeに、作成したPrefabをアタッチする
5. CommonLifetimeScope.csを実装
各シーンに配置するLifetimeScopeです。各シーン読み込み時に、そのシーン内のオブジェクトの依存関係を解決する役割です。
using VContainer;
using VContainer.Unity;
public sealed class CommonLifetimeScope : LifetimeScope
{
protected override void Configure(IContainerBuilder builder)
{
// 参照関係が構築された後に、
// シーンに存在する全てのオブジェクトにInject(注入)する
builder.RegisterBuildCallback(resolver =>
{
foreach (var rootGameObject in gameObject.scene.GetRootGameObjects())
{
resolver.InjectGameObject(rootGameObject);
}
});
}
}
6. Test.csを実装
インスタンスがInjectされ、値が引き継がれていることを確認する為のテストクラスです。
using UnityEngine;
using UnityEngine.SceneManagement;
using VContainer;
public class Test : MonoBehaviour
{
// 注入して欲しい!と宣言するAttribute
[Inject] private OnePlaySaveData _onePlaySaveData;
private void Update()
{
_onePlaySaveData.Score++;
Debug.Log($"Score: {_onePlaySaveData.Score}");
if (Input.GetKeyDown(KeyCode.R))
{
// シーンを跨いでも値が引き継がれることを確認する
SceneManager.LoadScene(gameObject.scene.name);
}
}
}
7. 動作確認する
- 適当なシーンを作成する
-
CommonLifetimeScope.csをアタッチする -
Test.csをアタッチする
8. (番外編)PureC# 以外のインスタンスを注入したい
例ではPureC#なOnePlaySaveDataのインスタンスを注入しましたが、MonoBehaviourや生成済みのインスタンスも注入することができます。
// 画面を制御する仮クラス
public class DisplayFader : MonoBehaviour
{
[SerializeField] private CanvasGroup _canvasGroup;
public void Fill()
{
_canvasGroup.alpha = 1f;
}
public void Clear()
{
_canvasGroup.alpha = 0f;
}
}
~ 省略 ~
public sealed class RootLifetimeScope : LifetimeScope
{
// ここへインスタンスを手動で設定
[SerializeField] private DisplayFader _displayFader;
protected override void Configure(IContainerBuilder builder)
{
// DisplayFaderを、RegisterInstance関数で登録
builder.RegisterInstance(_displayFader);
// PureC#について、自分で作成したインスタンスを登録することも可能です
// builder.Register<OnePlaySaveData>(Lifetime.Singleton);
var onePlaySaveData = new OnePlaySaveData();
builder.RegisterInstance(onePlaySaveData);
~ 省略 ~
}
}
RootLifetimeScopeプレハブの子オブジェクトにDisplayFaderを用意し、SerializeFieldに手動で設定しています

9. TIPS
動的に生成したオブジェクトの[Inject]が注入されない!
通常のInstantiate関数ではなく、IObjectResolverのInstantiate関数を使って生成しましょう
[Inject] private IObjectResolver _resolver;
[Inject] private GameObject _prefab;
private void Generate()
{
// var instance = Instantiate(_prefab); // 従来
_resolver.Instantiate(_prefab); // 置き換え
}
登録するInstanceのTypeを限定したい!
RegisterInstance<T>関数を使いましょう
public interface IHoge{
}
public class Hoge : IHoge{
}
protected override void Configure(IContainerBuilder builder)
{
var hoge = new Hoge();
// IHogeとして登録
builder.RegisterInstance<IHoge>(hoge);
}
// 注入される側
[Inject] Hoge hoge; // 注入されない
[Inject] IHoge hoge; // 注入される
シーン別にInjectするクラスを変えたい
今回はCommonLifetimeScopeで代用しましたが、シーン別に〜LifetimeScopeを作成し、そこで必要最低限の依存関係を解決する運用をオススメします
Discussion