スポンサーリンク
設計・アーキテクチャ

Unityの設計パターンを徹底比較!シングルトン・サービスロケーター・イベント駆動の違いと使い方

設計・アーキテクチャ

はじめに

Unityでゲーム開発をしていると、かなり早い段階で「設計ってこれで合ってるのかな?」という壁にぶつかります。

とりあえず動かしたいからシングルトンを使う。 気づいたら ○○Manager が増えていて、どこから何が呼ばれているのか分からない。 あとからイベント駆動やService Locatorを見かけて、「なんとなく良さそうだけど、正直違いが分からない」。

もし今、こんなモヤっと感を抱えているなら、それはかなり健全な成長サインです 🙂

Unityの設計で大事なのは、「正解のパターンを1つ覚えること」ではありません。 プロジェクトの規模・目的・将来の拡張に合わせて、判断できる軸を持つことです。

この記事では、Unity開発で特によく登場する

  • シングルトン(Singleton)
  • サービスロケーター(Service Locator)
  • イベント駆動(Event-driven)

この3つの設計パターンについて、

  • それぞれ何が役割なのか
  • 何が違って、どこで破綻しやすいのか
  • 小規模・中規模・チーム開発ではどう選ぶべきか

といった視点で、できるだけ実務寄りに整理していきます。

「これが最強の設計!」という話はしません。 代わりに、自分で「今回はこれを選ぶ」と判断できるようになることをゴールにしています。

設計に少し不安を感じ始めた今だからこそ、ぜひ一度立ち止まって読んでみてください。




結論: 「どれが優れているか」ではなく「どの責務を任せるか」で選ぶ

先に結論からお伝えします。

Unityにおけるシングルトン・サービスロケーター・イベント駆動は、 「どれが優れているか」ではなく「どの責務を任せるか」で選ぶものです。

設計が破綻する多くの原因は、パターン選択そのものではなく、

  • 本来の責務を超えて使ってしまう
  • 依存関係がコード上から見えなくなる
  • 将来の拡張を考えずに便利さだけで採用する

といった 使い方のズレ にあります。

ざっくり整理すると、考え方の軸は次のとおりです。

  • シングルトン → ログや設定管理など、ほぼ全体で共有される「環境的な依存」
  • サービスロケーター → 実装を差し替えたい依存関係を、ある程度まとめて管理したい場合
  • イベント駆動 → システム同士を直接つなげず、「起きた事実」だけを伝えたい場合

小規模な個人開発であれば、多少シングルトンが多くても致命的にはなりません。 ただし、規模が大きくなったり、後から機能追加・チーム開発を想定するなら、 「なぜこの設計を選んだのか」を説明できない状態はかなり危険です。

この記事ではここから、

  • それぞれの設計パターンが本来持つ責務
  • Unityで使うときに起こりやすい勘違い
  • どんな場面で選ぶと破綻しにくいのか

を一つずつ分解して解説していきます。

読み終わる頃には、 「今回はシングルトンでいい」「ここはイベントに逃がすべき」 と、自分で判断できる状態になっているはずです。




各設計パターンの特徴と責務整理

シングルトン(Singleton)とは何か

Unityで開発していると、最初に出会う設計パターンがこのシングルトンだと思います。

シングルトンの本来の責務は、とてもシンプルです。

  • クラスのインスタンスを必ず1つだけに制限する
  • そのインスタンスに対するグローバルなアクセスポイントを提供する

つまり、「どこからでも同じものにアクセスできる」状態を作るための仕組みですね。

Unityではこの性質がかなり便利に見えるため、

  • AudioManager
  • GameManager
  • UIManager

のようなクラスを、ついシングルトンで作りがちです。

確かに、小規模なプロジェクトやプロトタイプ段階では、 書くコードが少なく、考えることも減るというメリットがあります。

ただし問題はここからです。

シングルトンは「便利すぎる」ため、本来の責務を超えて使われやすいという大きな落とし穴を持っています。

たとえば、

  • ゲーム全体の状態管理
  • スコア計算
  • 敵の生成管理
  • UI更新

といった、まったく性質の違う責務が、 「とりあえずManagerだから」という理由だけで、1つのシングルトンに集まり始めます。

こうなると、そのクラスは変更に弱く、テストしづらく、壊れやすい存在になります。

また、シングルトンはどこからでも Instance 経由で呼べるため、

  • どのクラスが依存しているのか分かりにくい
  • 修正の影響範囲が予測できない

という「密結合」の問題も発生します。

このあたりは実装面の話とセットで理解すると一気に腑に落ちるので、 シングルトンの具体的な落とし穴や改善方法については、こちらの記事で詳しく解説しています。

ここで覚えておいてほしいのは、 「シングルトン=悪」ではないということです。

問題になるのは、

  • 何でもシングルトンにしてしまう
  • 依存関係を隠したまま増やし続ける

この状態です。

次は、「シングルトンよりは柔軟そう」に見える サービスロケーターについて整理していきましょう。




サービスロケーター(Service Locator)とは何か

サービスロケーターは、シングルトンに少し違和感を覚え始めた人が、次に出会いやすい設計パターンです。

一言でいうと、サービスロケーターの責務は次のとおりです。

  • アプリケーションが必要とするサービス(依存先)を取得する窓口になる
  • 利用側は具体的な実装クラスを知らずにサービスを使える

シングルトンとの大きな違いは、 「何を使うか」をロケーター側に集約できる点にあります。

たとえば、

  • 実機用の入力処理
  • テスト用のダミー実装
  • プラットフォームごとの処理差分

こうした実装の切り替えを、利用側のコードをほとんど触らずに行えるのは、 サービスロケーターの大きなメリットです。

そのため、

  • シングルトンだらけの設計から抜け出したい
  • でもDIはまだ難しそう

という段階では、現実的な選択肢になることも多いです。

ただし、サービスロケーターにも明確な欠点があります。

それが、依存関係がコード上から見えなくなるという問題です。

クラスの中で

ServiceLocator.Get<IScoreService>()

のような呼び出しをしていると、一見すると 「このクラスが何に依存しているのか」が分かりません。

結果として、

  • 必要なサービスが登録されていないまま実行時エラーになる
  • 後からコードを読んだ人が依存関係を把握しづらい

といったトラブルが起きやすくなります。

この点で、サービスロケーターは シングルトンよりは柔軟だが、設計としては中途半端とも言われがちです。

実務では、サービスロケーターを

  • DI(依存性注入)へ移行する前段階
  • 小〜中規模プロジェクトの妥協点

として使うケースが多い印象です。

DIとどう違うのか、どこまで許容できるのかについては、 こちらの記事で詳しく整理しています。

次は、ここまでとは発想がかなり違う イベント駆動(Event-driven)の設計について見ていきましょう。




イベント駆動(Event-driven)とは何か

イベント駆動は、ここまで紹介してきたシングルトンやサービスロケーターとは、 設計の発想そのものがかなり違うアプローチです。

イベント駆動の基本的な責務は、とてもシンプルです。

  • 「何かが起きた」という事実を通知する
  • それを誰が受け取るかは気にしない

つまり、送信側(発行者)は、

  • 誰が反応するのか
  • 何個の処理がぶら下がっているのか

を一切知らなくてOKです。

たとえば「プレイヤーがダメージを受けた」というイベントが発行されたとき、

  • UIがHPバーを更新する
  • 効果音が鳴る
  • 敵AIが行動を変える

といった処理が、それぞれ独立して反応できます。

この疎結合こそが、イベント駆動最大の強みです。

Unityでは特に、

  • C#のevent / Action
  • ScriptableObjectを使ったイベントアセット

といった形でイベント駆動を実装することが多いです。

中でもScriptableObjectを使ったイベント設計は、

  • シーンを跨いでも参照できる
  • 依存関係をアセットとして可視化できる

という点で、Unityとの相性がとても良いです。

この考え方は、いわゆる「データ駆動設計」にも近く、 ハードコーディングから脱却したいタイミングで一気に効いてきます。

ただし、イベント駆動にも弱点はあります。

処理の流れがイベントで分断されるため、

  • どの順番で何が起きているのか追いづらい
  • イベントが増えすぎると全体像を把握しにくい

といった問題が起きやすくなります。

特に、

  • 責務が曖昧なイベント名
  • 何でもイベントで解決しようとする設計

は、あとから見返したときにかなり辛くなります。

イベント駆動は万能ではありませんが、 「システム同士を直接つなぎたくない場面」では、 非常に強力な選択肢になります。

次は、ここまで解説した3つの設計パターンを、 横並びで比較して整理していきましょう。




3つの設計パターンを横並びで比較する

ここまでで、シングルトン・サービスロケーター・イベント駆動それぞれの特徴を見てきました。

ただ、個別に理解できても、

  • 結局どこが一番違うのか
  • 自分のプロジェクトではどれを選ぶべきか

が分からないままだと、実務では判断に使えません。

そこでここでは、よく迷いがちなポイントを中心に、3つを横並びで整理します。

項目シングルトンサービスロケーターイベント駆動
主な役割唯一の共有インスタンスを提供依存先の取得を集約事実の通知
依存関係の見えやすさ低い(内部で呼ばれる)低い(取得するまで不明)高い(イベント定義のみ)
拡張性低〜中高い
テストのしやすさ低い高い
向いている規模小規模・試作小〜中規模中〜大規模

この表から見えてくる一番大きな違いは、 「依存関係をどう扱っているか」です。

シングルトンとサービスロケーターは、

  • 便利だが依存が隠れやすい
  • コードを読まないと関係性が分からない

という共通の弱点を持っています。

一方、イベント駆動は、

  • 送信側と受信側が直接つながらない
  • 「何が起きたか」だけが明示される

ため、拡張性と保守性が高くなりやすいです。

ただし、ここで誤解しないでほしいのは、

「イベント駆動が常に正解」というわけではない

という点です。

イベント駆動は、

  • 処理の流れを直感的に追いにくい
  • 小さな処理でもイベント化すると逆に複雑

といったコストも持っています。

つまり、

  • 即座に結果が欲しい処理
  • 明確な1対1の依存関係

では、シングルトンや直接参照の方が分かりやすいこともあります。

次の章では、こうした違いを踏まえたうえで、 「どこで破綻しやすいのか」を具体的に見ていきましょう。




実践的な判断基準:「結局どれを選べばいい?」

ここまで読むと、「結局どれを使えばいいの?」という気持ちが一番強くなっていると思います。

そこでこの章では、設計パターンを感覚ではなく判断できるように、 実務で使いやすい基準に落とし込みます。

① それは「環境的な依存」か?

まず考えてほしいのは、その機能が ほぼすべてのクラスから共通して使われるものかどうかです。

たとえば、

  • ログ出力
  • 設定値の取得
  • 時刻・乱数の取得

のようなものは、アプリケーションの「環境」に近い存在です。

こうしたケースでは、シングルトン(または静的クラス)を使っても、 設計が破綻しにくいことが多いです。

逆に、ゲームロジックや状態管理までシングルトンに寄せ始めたら、 黄色信号だと思ってください。

② 実装を差し替える可能性はあるか?

次に考えるのは、 将来その実装を別のものに置き換える可能性があるかです。

たとえば、

  • プラットフォームごとの処理差分
  • テスト用のダミー実装

が想定される場合、直接シングルトンを参照する設計は相性がよくありません。

この場合は、

  • サービスロケーター
  • DI(依存性注入)

といった選択肢が見えてきます。

サービスロケーターは妥協点として使われがちですが、 最終的にはDIに移行する前提で選ぶと失敗しにくいです。

③ 「事実」を複数のシステムに伝えたいか?

もし、

  • 1つの出来事に対して
  • 複数のシステムが反応する

という構造なら、イベント駆動が非常に強力です。

イベント駆動は、

  • UI
  • 演出
  • AI

といった異なる責務を、直接つなげずに連携させられます。

特にUnityでは、ScriptableObjectを使って イベントをアセットとして管理すると、 依存関係がかなり整理しやすくなります。

④ 将来チーム開発・規模拡張を想定しているか?

最後に重要なのが、プロジェクトの未来です。

個人開発・短期制作なら多少荒れても何とかなりますが、

  • メンバーが増える
  • 仕様が増える
  • 運用が長期化する

場合は、依存関係が明示されている設計ほど強くなります。

その意味では、

  • DI
  • イベント駆動

をベースに、必要なところだけシングルトンを使う、 という組み合わせ前提の設計が現実的です。

次の章では、シングルトンだらけの設計から抜け出すための 具体的な改善ステップを紹介します。




改善ステップ例:Singletonだらけの設計から抜け出す

ここまで読んで、「理屈は分かったけど、今のコードをどう直せばいいの?」と感じている人も多いと思います。

この章では、すでにシングルトンが増えてしまったプロジェクトを前提に、 現実的に進めやすい改善ステップを紹介します。

① まずは「責務」を疑う

最初にやるべきことは、シングルトンクラスそのものを消すことではありません。

それよりも、

  • このクラスは、本当は何をする責務なのか
  • やってはいけない仕事まで抱えていないか

を一つずつ見直します。

たとえば、

  • 状態管理
  • 計算ロジック
  • UI更新

が1つのManagerに混ざっていたら、それは分離できるサインです。

② 直接参照をやめて「渡す」設計に変える

次に、クラスの中で

SomeManager.Instance.DoSomething();

のような呼び出しをしている箇所を見つけます。

これを、

  • 初期化時に引数として渡す
  • 外部から設定してもらう

形に変えるだけでも、依存関係はかなり見えやすくなります。

「Instanceを呼ばない」というだけで、 設計の自由度は一気に上がります。

③ 共通データ・イベントをアセットに切り出す

ここで非常に相性がいいのが、ScriptableObjectの活用です。

データやイベントをクラスではなくアセットとして管理すると、

  • シーンを跨いでも安全に共有できる
  • 依存関係がInspector上で可視化される

というメリットがあります。

特に、

  • 設定値
  • 状態データ
  • ゲーム内イベント

は、シングルトンから切り離しやすい代表例です。

このあたりを自前で作るのが大変な場合は、 ScriptableObjectを前提に設計されたツールを使うのも現実的な選択です。

ScriptableObject Database
✅アセットストアでチェックする

データ管理や参照関係を整理したい場合はこちらも便利です。

ScriptableObjects Tools
✅アセットストアでチェックする

これらは「設計を理解したうえで使う」と、 シングルトン依存から抜け出す強力な助けになります。

④ いきなり完璧を目指さない

最後にとても大事なことです。

既存プロジェクトを、

  • 一気にイベント駆動にする
  • 全部DIに置き換える

必要はありません。

まずは、

  • 新しく書くコードだけ改善する
  • 触る予定のある部分から直す

くらいの温度感で十分です。

設計は「一度決めて終わり」ではなく、 プロジェクトと一緒に育てていくものだという意識を持つと、かなり楽になります。

次は、初心者が特につまずきやすい よくある誤解・注意点をまとめていきます。




よくある誤解・注意点まとめ

設計パターンについて学び始めると、どうしても 「正しい・間違っている」で考えてしまいがちです。

ここでは、Unity開発で特に多い誤解と、気をつけてほしいポイントを整理します。

「この設計が最強」という答えは存在しない

シングルトン・サービスロケーター・イベント駆動のどれにも、

  • 向いている場面
  • 向いていない場面

があります。

ネットで見かける

  • 「シングルトンは悪」
  • 「イベント駆動こそ正義」

といった極端な意見は、 前提条件が省略されていることがほとんどです。

大切なのは、

  • なぜその設計を選んだのか
  • 別の選択肢と比べて何を捨てたのか

を説明できることです。

最初から完璧な設計を目指さなくていい

初心者〜中級者の段階で、 最初から美しい設計を組むのは正直かなり難しいです。

それよりも、

  • 破綻し始めたタイミングに気づける
  • 直す選択肢を知っている

ことの方が、はるかに重要です。

設計は、コードを書きながら 「育てていくもの」だと考えてください。

イベントにすれば全部解決するわけではない

イベント駆動はとても強力ですが、万能ではありません。

特に、

  • 即時に結果が欲しい処理
  • 明確な1対1のやり取り

では、イベントにすると逆に分かりづらくなります。

「疎結合にしたいからイベント」という思考停止ではなく、

  • 本当に複数の購読者が必要か
  • 処理の流れが追えるか

を一度考えるだけで、設計の質はかなり変わります。




まとめ

今回は、Unity開発でよく使われる

  • シングルトン(Singleton)
  • サービスロケーター(Service Locator)
  • イベント駆動(Event-driven)

この3つの設計パターンについて、違いと使い分けの考え方を整理しました。

改めてポイントを振り返ると、

  • 設計パターンに「絶対の正解」はない
  • 重要なのは、責務と依存関係を意識できているか
  • 規模や将来像によって最適解は変わる

という点に尽きます。

シングルトンは便利ですが、使いすぎると依存関係が見えなくなりがちです。 サービスロケーターは柔軟ですが、依存を隠してしまうリスクがあります。 イベント駆動は疎結合で強力ですが、設計を間違えると全体像を把握しにくくなります。

だからこそ、

  • 「これは環境的な依存か?」
  • 「将来差し替える可能性はあるか?」
  • 「複数のシステムに通知したい事実か?」

といった判断軸を持つことが、設計で迷わなくなる一番の近道です。

私自身も、最初からうまく設計できていたわけではありません。 シングルトンだらけで後悔した経験も何度もあります。

でも、設計に悩んだ経験があるからこそ、 「今のうちに気づけてよかった」と後から思えるようになります。

この記事が、あなた自身のプロジェクトで 「なぜこの設計を選ぶのか」を言葉にできるきっかけになれば嬉しいです。


参考文献・参考リンク


よくある質問(FAQ)

Q
小規模なゲームでもイベント駆動は使うべきですか?
A

必ずしも使う必要はありません。

小規模なゲームや短期間で作るプロトタイプでは、

  • 直接参照
  • シングルトン

の方が分かりやすく、実装コストも低いことが多いです。

ただし、

  • UI・演出・ロジックが増え始めた
  • 「この処理、他にも影響してそう」と感じる場面が増えた

タイミングでは、イベント駆動を部分的に取り入れる価値があります。

最初から全面採用するのではなく、 「複数のシステムに通知したい箇所だけ」使うのがおすすめです。

Q
サービスロケーターは結局アンチパターンなのでしょうか?
A

文脈次第です。

サービスロケーターは、

  • 依存関係が見えにくい
  • 実行時エラーが起きやすい

という欠点があるため、 設計原則の観点ではアンチパターンとされることが多いです。

ただしUnityでは、

  • 小〜中規模プロジェクト
  • DIを導入するほどでもない段階

で、現実的な妥協案として使われるケースもあります。

大切なのは、

  • なぜ今それを選んでいるのか
  • 将来どう移行する想定なのか

を意識して使うことです。

Q
シングルトンはもう使わない方がいいですか?
A

いいえ、完全に避ける必要はありません。

シングルトンが向いているケースは、今でも確実に存在します。

  • ログ出力
  • 設定管理
  • グローバルに一意であるべきサービス

こうした「環境的な依存」では、 シングルトンはシンプルで分かりやすい選択です。

問題になるのは、

  • ゲームロジック
  • 状態管理
  • 振る舞いの中心

までシングルトンに寄せてしまうことです。

「使うか・使わないか」ではなく、「どこまで任せるか」 この視点を持つだけで、設計はかなり安定します。

※当サイトはアフィリエイト広告を利用しています。リンクを経由して商品を購入された場合、当サイトに報酬が発生することがあります。

※本記事に記載しているAmazon商品情報(価格、在庫状況、割引、配送条件など)は、執筆時点のAmazon.co.jp上の情報に基づいています。
最新の価格・在庫・配送条件などの詳細は、Amazonの商品ページをご確認ください。

スポンサーリンク