Unityで開発を続けていると、「GameObject.Findが増えて管理しづらい」「Singletonがどんどん肥大化してきた」と感じることはありませんか?そんなときに耳にするのが依存性注入(DI)ですが、「本当に必要なの?」「初心者には難しそう」と迷ってしまう方も多いはずです。
この記事では、UnityでDIを導入するべきタイミングや、従来の参照方法との違い、そしてUnity向けDIライブラリとして人気のVContainerの基本的な使い方まで、順番にわかりやすく解説します。DIを使うべきかどうか判断できるようになりたい方は、ぜひ最後まで読んでみてください。
UnityでDIは必要?結論は「規模で判断」が最適
UnityでDIを使うべきか迷ったら、まずはプロジェクトの規模と参照管理の複雑さで判断するのがおすすめです。
たとえば、1画面だけのミニゲームや、数本のスクリプトで完結する試作なら、無理にDIを導入しなくても問題ありません。[SerializeField]で必要なオブジェクトをInspectorから設定するだけで、十分に見通しよく作れることが多いです。
一方で、次のような状態になってきたら、DIを検討するタイミングです。
GameObject.FindやFindObjectOfTypeが複数のスクリプトに散らばっているGameManagerやSoundManagerなどのManagerクラスが大きくなりすぎている- 処理を差し替えたいのに、クラス同士が強く結びついていて変更しづらい
- テスト用の処理や仮のデータに入れ替えにくい
DIは、すべてのUnityプロジェクトに必須の仕組みではありません。むしろ、小さなゲームでいきなり導入すると、登録処理や設計ルールのほうが重く感じることもあります。

大事なのは、「DIを使うかどうか」ではなく、今の参照管理で困っているかを見ることです。今の作り方で迷わず修正できるなら、そのまま進めても大丈夫です。参照先が増えてコードの見通しが悪くなってきたときに、DIは力を発揮しやすくなります。
UnityでDIが必要になる3つの状況
DIを導入するかどうかは、「有名だから使う」ではなく、今のコードで何に困っているかを見て判断すると失敗しにくいです。ここでは、Unity開発でDIが役立ちやすい代表的な状況を3つに絞って見ていきましょう。
GameObject.Findが増えて管理しづらい
GameObject.Findは手軽ですが、いろいろなスクリプトから何度も呼ぶようになると、参照先がどこで使われているのか追いにくくなります。オブジェクト名を変更しただけで動かなくなることもあるので、後から直すときに原因探しで時間を使いがちです。
たとえば、プレイヤー、UI、サウンド、スコア管理をそれぞれ別の場所から探しているなら、依存関係を整理するタイミングかもしれません。
Singletonが神クラスになっている
Singletonは便利ですが、何でも入れすぎるとGameManager.Instanceに処理が集中してしまいます。こうなると、一部だけ直したいのに他の機能まで影響しやすくなります。
Singleton自体が悪いわけではありません。ただ、ゲーム進行、音量、セーブ、UI更新、敵管理まで1つのManagerに集まっているなら、責務を分ける設計を考えたほうが安全です。
Singletonの使いどころに迷う場合は、先にコチラの記事も確認しておくと、DIとの違いがつかみやすくなります。
差し替えやテストが難しい
DIが特に役立つのは、処理を差し替えたい場面です。たとえば、実際のセーブ処理の代わりにテスト用の仮データを使いたい、課金処理の代わりにダミー処理で動作確認したい、といったケースですね。
クラスの中で直接newしたり、特定のManagerを直接呼んだりしていると、差し替えが難しくなります。DIを使うと、必要な処理を外から渡せるため、実装の入れ替えがしやすくなります。

目安として、Findが増えた、Managerが大きくなった、差し替えたい処理が出てきたのうち2つ以上当てはまるなら、DIを検討する価値があります。
DIを使うと何が変わる?従来手法との違い
DIを理解するときは、いきなり仕組みを覚えるよりも、Unityでよく使う参照方法と比べるとイメージしやすいです。ここでは、GameObject.Find、[SerializeField]、Singleton、VContainerを比べながら見ていきます。
| 方法 | 向いている場面 | 注意点 |
|---|---|---|
| GameObject.Find | 試作や一時的な確認 | 名前変更に弱く、使う場所が増えると追いにくい |
| [SerializeField] | シーン上の参照をInspectorで設定したいとき | 動的生成やPure C#クラスの依存管理には向きにくい |
| Singleton | ゲーム全体で1つだけ使う管理クラス | 依存関係が見えにくく、処理が集中しやすい |
| VContainer | 依存関係を整理し、差し替えやすくしたいとき | 小規模では設定の手間が大きく感じることがある |
GameObject.Findとの違い
GameObject.Findは、名前を指定してシーン内のオブジェクトを探す方法です。すぐ書けるので便利ですが、使う場所が増えるほど「どこで何を探しているのか」が分かりにくくなります。
DIでは、必要な相手を自分で探しに行くのではなく、外から渡してもらいます。そのため、クラスが何に依存しているのかがコード上で見えやすくなります。
SerializeFieldとの違い
[SerializeField]はUnityらしい、とても使いやすい参照方法です。シーン上のボタン、カメラ、UI、AudioSourceなどをInspectorで設定する場面では、今でも十分に有効です。
ただし、シーンに置かない通常のC#クラスや、実行中に生成されるオブジェクトが増えてくると、Inspectorだけでは管理しづらくなることがあります。DIは、そうした「コード側の依存関係」を整理したいときに向いています。
Singletonとの違い
Singletonは、どこからでもアクセスできる便利な仕組みです。ですが、便利なぶん、いろいろなクラスが同じManagerに直接依存しやすくなります。
DIでは、必要な機能だけを外から受け取る形にできます。たとえば、サウンド再生だけが必要なクラスにはサウンド用の処理だけを渡す、という分け方がしやすくなります。

つまり、DIはSingletonの代わりというより、依存関係を見える形にして、必要なものだけ受け取るための考え方です。SerializeFieldやSingletonをすべて捨てる必要はなく、プロジェクトの規模や目的に合わせて使い分けるのが現実的です。
VContainerとは?Unityで選ばれる理由
VContainerは、Unity向けに作られたDIコンテナです。DIコンテナとは、必要なクラスを登録しておくことで、使う側へ自動で渡してくれる仕組みのことです。
UnityではMonoBehaviourをシーン上のGameObjectにアタッチして使うことが多いですよね。そのため、通常のC#のようにコンストラクタで依存先を受け取る設計が少しやりにくい場面があります。VContainerは、そうしたUnity特有の作り方に合わせて、依存関係を整理しやすくしてくれます。
VContainerの特徴
VContainerの大きな特徴は、Unityで使いやすい形でDIを導入できることです。公式ドキュメントでも、コンストラクタ注入、メソッド注入、プロパティ・フィールド注入などに対応していることが説明されています。
たとえば、通常のC#クラスにはコンストラクタ注入を使い、シーン上のMonoBehaviourには[Inject]属性を使って必要な参照を渡す、といった使い分けができます。
- Pure C#クラスをゲームロジックとして扱いやすい
LifetimeScopeで依存関係の範囲を管理できる- 必要な依存先を登録して、自動で注入できる
- 診断機能やSource Generatorなど、開発を助ける機能も用意されている
Zenjectとの違い
UnityのDIライブラリとしては、Zenjectもよく知られています。Zenjectは歴史が長く、機能も豊富です。一方で、VContainerはシンプルなAPIや軽量さを重視しているため、これから新しくDIを試したい場合に選びやすい候補になります。
ただし、「どちらが絶対に正解」とは言い切れません。既存プロジェクトですでにZenjectを使っているなら、そのまま運用したほうが安全なこともあります。新規プロジェクトで、できるだけシンプルに始めたいなら、VContainerを検討しやすいです。
VContainerが向いているプロジェクト
VContainerは、スクリプト同士の依存関係が増えてきたプロジェクトに向いています。特に、ゲームロジックをMonoBehaviourから分けたいときや、テストしやすい形にしたいときに効果を感じやすいです。

反対に、数本のスクリプトで完結する小さなゲームなら、最初から導入しなくても大丈夫です。まずは[SerializeField]やシンプルなクラス分割で進めて、参照管理がつらくなってきた段階でVContainerを試すくらいでも十分です。
VContainerの基本実装を5ステップで解説
ここでは、VContainerで「サービスクラスを登録して、別のクラスで受け取る」最小構成を作ってみます。いきなり大きな設計にせず、まずは小さく動かすところから始めると理解しやすいです。
STEP1 インストール
VContainerは、主にPackage Managerから導入します。プロジェクトによって導入方法は変わるため、実際に使うときは公式ドキュメントの手順に合わせてください。
導入後、スクリプト内で次の名前空間を使えるようになれば準備は進められます。
using VContainer;
using VContainer.Unity;
STEP2 Serviceクラスを作る
まずは、依存される側のクラスを作ります。ここでは、メッセージを返すだけのシンプルなサービスにしておきます。
public class HelloWorldService
{
public string GetMessage()
{
return "Hello VContainer!";
}
}
このクラスはMonoBehaviourを継承していません。こうした普通のC#クラスをゲームロジックとして分けておくと、シーンに依存しにくくなります。
STEP3 LifetimeScopeを作る
次に、依存関係を登録するためのLifetimeScopeを作ります。これはVContainerのコンテナを作る場所だと思うとイメージしやすいです。
using VContainer;
using VContainer.Unity;
public class GameLifetimeScope : LifetimeScope
{
protected override void Configure(IContainerBuilder builder)
{
builder.Register<HelloWorldService>(Lifetime.Scoped);
}
}
作成したGameLifetimeScopeは、シーン上の空のGameObjectなどにアタッチします。ここに登録したクラスを、VContainerが必要な場所へ渡してくれます。
STEP4 Injectされるクラスを作る
次に、HelloWorldServiceを受け取って使うクラスを作ります。今回はUnityの開始処理で動かしたいので、IStartableを使います。
using UnityEngine;
using VContainer.Unity;
public class HelloWorldPresenter : IStartable
{
private readonly HelloWorldService helloWorldService;
public HelloWorldPresenter(HelloWorldService helloWorldService)
{
this.helloWorldService = helloWorldService;
}
public void Start()
{
Debug.Log(helloWorldService.GetMessage());
}
}
コンストラクタの引数にHelloWorldServiceを書いていますが、自分でnew HelloWorldService()とは書いていません。必要なものを外から受け取る形になっているのが、DIらしい部分です。
STEP5 EntryPointとして登録する
HelloWorldPresenterを動かすには、先ほどのGameLifetimeScopeに追加で登録します。
using VContainer;
using VContainer.Unity;
public class GameLifetimeScope : LifetimeScope
{
protected override void Configure(IContainerBuilder builder)
{
builder.Register<HelloWorldService>(Lifetime.Scoped);
builder.RegisterEntryPoint<HelloWorldPresenter>();
}
}
これで再生すると、ConsoleにHello VContainer!と表示されます。まずはこの形で、「登録する」「受け取る」「使う」の流れを確認してみてください。

なお、シーン上にあるMonoBehaviourへ注入したい場合は、RegisterComponentや[Inject]を使う場面もあります。ただ、最初から全部覚える必要はありません。まずはPure C#クラスを登録して受け取る流れを押さえるだけでも、DIの考え方はかなり見えやすくなります。
UnityでDIを使うときの注意点
DIは参照関係を整理しやすくする便利な考え方ですが、入れれば必ず開発が楽になるわけではありません。UnityではMonoBehaviourやPrefab、シーン管理との相性も考える必要があります。
小規模ゲームでは過剰設計になることがある
数本のスクリプトで完結するゲームなら、DIコンテナを使わないほうがシンプルに作れることがあります。たとえば、ボタンを押したら音を鳴らす、プレイヤーのHPをUIに表示する、といった処理なら[SerializeField]で参照を入れるだけでも十分です。
DIを導入すると、クラス登録やLifetimeScopeの管理が必要になります。参照関係がまだ少ない段階では、その設定のほうが負担に感じるかもしれません。
まずは「今のコードで困っているか」を基準にしましょう。困っていないなら、無理にDIへ置き換えなくても大丈夫です。
MonoBehaviourはコンストラクタ注入できない
通常のC#クラスでは、コンストラクタの引数で必要な依存先を受け取れます。しかし、UnityのMonoBehaviourはUnity側が生成を管理しているため、ゲーム開発者が自由に引数付きコンストラクタで生成する形には向いていません。
そのため、VContainerでMonoBehaviourに依存先を渡したい場合は、[Inject]を付けたメソッドやフィールドを使うことがあります。
using UnityEngine;
using VContainer;
public class PlayerView : MonoBehaviour
{
private HelloWorldService helloWorldService;
[Inject]
public void Construct(HelloWorldService helloWorldService)
{
this.helloWorldService = helloWorldService;
}
}
ただし、最初から何でもMonoBehaviourへ注入しようとすると、かえって構造が分かりにくくなることもあります。ゲームロジックはPure C#クラスへ分け、表示や入力などUnity側と近い処理だけMonoBehaviourに残すと整理しやすいです。
動的生成PrefabはFactoryなどの工夫が必要
敵キャラ、弾、エフェクトのように実行中にPrefabを生成する場合は、少し注意が必要です。普通にInstantiateで生成しただけでは、VContainerの管理外で作られるため、依存注入が期待通りに行われないことがあります。
このような場面では、VContainerの生成機能やFactoryパターンを使って、「生成するときに依存関係も渡す」形にすると安全です。
特に、敵や弾を大量に生成するゲームでは、生成処理そのものが負荷になる場合もあります。Unity 6系では非同期生成に関するAPIも用意されていますが、DIと組み合わせる場合は、プロジェクトのUnityバージョンやVContainer側の対応方法を確認しながら使うのが安心です。

DIを使うときは、シーンに置くもの、コードで生成するもの、差し替えたい処理を分けて考えると迷いにくくなります。
UnityでDIを導入するべきか迷ったときの判断基準
DIを導入するか迷ったときは、「便利そうだから」ではなく、今のプロジェクトで起きている問題から判断すると失敗しにくいです。次の項目に当てはまるほど、DIを検討する価値が高くなります。
- 同じManagerをいろいろなスクリプトから直接呼んでいる
GameObject.FindやFindObjectOfTypeを後から探すのが大変になっている- セーブ、サウンド、UI、ゲーム進行などの処理を差し替えたい場面がある
- Pure C#クラスにロジックを分けたい
- テスト用の仮データやモック処理を使いたい
このうち1つだけなら、まずは[SerializeField]やクラス分割で整理するだけでも十分かもしれません。2〜3個以上当てはまるなら、VContainerのようなDIコンテナを試すタイミングです。
特に目安にしやすいのは、変更したい処理が他のクラスに広く影響してしまうかです。たとえば、サウンド再生の仕様を変えたいだけなのに、プレイヤー、敵、UI、アイテムのコードまで直す必要があるなら、依存関係が広がりすぎている可能性があります。
DIを使うと、必要な機能を外から渡す形にできるため、処理の差し替えや責務の分離がしやすくなります。設計の考え方をもう少し整理したい場合は、コチラの記事も合わせて読むと、DIを使う理由がよりつかみやすくなります。

迷ったときは、いきなり全体へ導入しなくて大丈夫です。まずはサウンド管理やセーブ処理のように、差し替えやすさの効果が分かりやすい小さな機能から試してみましょう。
まとめ
UnityのDIは、すべてのプロジェクトで必ず使うものではありません。小さなゲームなら[SerializeField]で十分なことも多いですし、無理に導入すると設定の手間が増えてしまう場合もあります。
一方で、GameObject.Findが増えてきた、Managerが大きくなってきた、処理を差し替えにくいと感じてきたなら、DIを試すよいタイミングです。VContainerを使えば、Unityでも依存関係を整理しながら開発しやすくなります。
まずはプロジェクト全体ではなく、サウンド管理やセーブ処理など、小さな機能からDI化してみてください。少しずつ使うことで、自分のゲームに合う設計が見つけやすくなります。
よくある質問(FAQ)
- QUnity初心者でもDIを学ぶべきですか?
- A
最初から無理に学ばなくても大丈夫です。まずは
[SerializeField]、クラス分割、interfaceの基本を押さえてからDIに進むと理解しやすくなります。ただし、スクリプト同士の参照が増えて「どこから呼ばれているのか分からない」と感じ始めたら、DIを学ぶタイミングです。小さなServiceクラスを1つ作り、VContainerで受け取る練習から始めると取り入れやすいですよ。
- QDIを使わないと保守しにくいゲームになりますか?
- A
必ずしもそうではありません。小規模なゲームなら、
[SerializeField]やシンプルなクラス分割だけでも十分に保守しやすく作れます。大切なのは、DIを使うかどうかよりも、責務が分かれているか、参照関係が追いやすいかです。修正のたびに複数のManagerやスクリプトを直す状態になっているなら、DIを含めた設計改善を検討してみましょう。









※当サイトはアフィリエイト広告を利用しています。リンクを経由して商品を購入された場合、当サイトに報酬が発生することがあります。
※本記事に記載しているAmazon商品情報(価格、在庫状況、割引、配送条件など)は、執筆時点のAmazon.co.jp上の情報に基づいています。
最新の価格・在庫・配送条件などの詳細は、Amazonの商品ページをご確認ください。