スポンサーリンク
UnityUnityメモ

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

Unity

はじめに

Unityでゲームを作っていると、「コードがゴチャゴチャしてきたな…」「どこから手をつければいいかわからない」という場面に出会うことはありませんか?😅 そんなときに役立つのが設計パターンです。設計パターンを知っていると、プロジェクトの見通しが良くなり、保守や拡張もスムーズになります。

本記事では、Unity開発でよく使われるシングルトン(Singleton)サービスロケーター(Service Locator)イベント駆動(Event Driven)という3つの設計パターンを取り上げ、それぞれの特徴や使いどころをわかりやすく解説します。 「どのパターンを選べばいいの?」「実際の現場ではどう使われるの?」といった疑問をスッキリ解消できるはずです。

これから紹介する内容は、初心者はもちろん、中級者・上級者の方が「設計の引き出しを増やす」きっかけにもなります。 では、さっそくシングルトン・サービスロケーター・イベント駆動を比較していきましょう!




シングルトン(Singleton)

まず最初に紹介するのはシングルトンパターンです。Unityでもっともよく使われる設計パターンのひとつで、ゲーム開発の現場でも「とりあえずシングルトンでまとめておこう」という場面はとても多いです。

シングルトンとは?

シングルトンは「あるクラスのインスタンスをひとつだけに限定し、どこからでもアクセスできるようにする」仕組みです。 例えば、ゲーム全体のスコア管理やBGMの制御など、共通して使われるシステムをまとめるのに適しています。

実装例(GameManager)

Unityでシングルトンを作るときの定番は、GameManagerクラスです。次のように書くことで、シーンをまたいでも1つのインスタンスが維持されます。


using UnityEngine;

public class GameManager : MonoBehaviour
{
    public static GameManager Instance { get; private set; }

    public int Score { get; set; }

    private void Awake()
    {
        if (Instance != null && Instance != this)
        {
            Destroy(gameObject);
            return;
        }
        Instance = this;
        DontDestroyOnLoad(gameObject);
    }
}

このコードをアタッチしたオブジェクトをシーンに置いておけば、GameManager.Instance.Scoreでどこからでもスコアにアクセスできます。

メリット

  • グローバルにアクセスできるので実装がシンプル
  • どのシーンからでも同じインスタンスを利用可能
  • 「ゲーム全体でひとつだけ存在すべきもの」にぴったり

デメリット

  • 依存関係が増えやすく、コードが密結合になりやすい
  • テストが難しくなる(モック差し替えがしにくい)
  • プロジェクトが大きくなると「なんでもシングルトン」に陥りがち

小さなプロジェクトや個人制作ではとても便利ですが、商用や大規模プロジェクトでは乱用すると保守性が落ちるので注意しましょう。

さらに学びたい方へ

シングルトンの使い方やリファクタリングの考え方を学ぶなら、こちらの書籍がおすすめです📚


サービスロケーター(Service Locator)

次に紹介するのはサービスロケーターパターンです。 シングルトンが「ひとつのクラスをどこからでも使えるようにする」仕組みだったのに対し、サービスロケーターはインターフェースと実装を分離し、必要なときに適切なインスタンスを取得できる仕組みを提供します。

サービスロケーターとは?

サービスロケーターは「依存性を一元的に管理する箱」のようなものです。 クラス側は具体的な実装に依存せず、インターフェースを介してサービスにアクセスできます。

Unityでの活用イメージ

例えばセーブデータを扱う場合、通常ならコードの中で「本番用セーブ」「デバッグ用セーブ」を条件分岐で切り替えがちです。 しかしサービスロケーターを使えば、利用側は常に同じインターフェースを呼び出すだけで、裏側の実装を差し替えられるようになります。


public interface ISaveSystem
{
    void Save(string data);
    string Load();
}

public class FileSaveSystem : ISaveSystem
{
    public void Save(string data) { /* ファイルに保存 */ }
    public string Load() { return "file data"; }
}

public class DebugSaveSystem : ISaveSystem
{
    public void Save(string data) { Debug.Log("Save:" + data); }
    public string Load() { return "debug data"; }
}

// Locatorに登録して解決
public static class ServiceLocator
{
    private static Dictionary<Type, object> services = new();

    public static void Register<T>(T service) => services[typeof(T)] = service;
    public static T Resolve<T>() => (T)services[typeof(T)];
}

この仕組みにより、テスト時は DebugSaveSystem、本番は FileSaveSystem を登録する、といった柔軟な切り替えが可能になります。

メリット

  • 具体的な実装からコードを切り離せる(疎結合になる)
  • 本番環境とデバッグ環境を簡単に切り替え可能
  • モックを使った単体テストがやりやすい

デメリット

  • サービスロケーター自体に依存するコードが増える
  • クラス間の依存関係が見えにくくなる
  • DIコンテナ(Zenjectなど)に比べるとスケールしにくい

実務での活用例

  • 通信処理の本番版とモック版の切り替え
  • アセットバンドルをサーバーからロードするか、ローカルファイルから読み込むかの切り替え
  • プラットフォームごとに異なる機能(iOS / Android)の実装を共通化

さらに効率化するなら

サービスロケーターはシンプルですが、大規模開発では依存性注入(DI)フレームワークを使った方がスッキリします。 特にUnityでは Zenject が有名で、DIパターンを簡単に取り入れることができます。

👉 次は、柔軟で疎結合な仕組みを実現するイベント駆動(Event Driven)について解説していきます!




イベント駆動(Event Driven)

最後に紹介するのはイベント駆動パターンです。 これは「ある出来事(イベント)が発生したときに、それに応じた処理を呼び出す」仕組みで、C#やUnityが標準でサポートしている考え方でもあります。

イベント駆動とは?

イベント駆動の基本は「発行(発火)」と「購読」です。 あるクラスがイベントを発行し、他のクラスがそのイベントを購読して処理を実行します。 これにより、発行者と受け取り側のコードを直接結びつけずにやり取りできるのが大きな特徴です。

Unityでの実装例


using System;
using UnityEngine;

public class Player : MonoBehaviour
{
    public static event Action OnPlayerDead;

    public void Die()
    {
        Debug.Log("Player Dead!");
        OnPlayerDead?.Invoke();
    }
}

public class GameUI : MonoBehaviour
{
    private void OnEnable()
    {
        Player.OnPlayerDead += ShowGameOver;
    }

    private void OnDisable()
    {
        Player.OnPlayerDead -= ShowGameOver;
    }

    private void ShowGameOver()
    {
        Debug.Log("Game Over 画面を表示");
    }
}

このように、Playerが死亡したときにイベントを発行し、GameUIがそのイベントを購読してゲームオーバー画面を表示する、といった流れを簡単に実装できます。

メリット

  • コードが疎結合になるため、再利用性・テスト性が高い
  • 機能追加のときに既存コードをほとんど変更せずに済む
  • 責務が分離され、コードの見通しが良くなる

デメリット

  • イベントが増えすぎると「どこで何が起きているか」を追いづらい
  • 購読解除(OnDisableなど)が漏れるとメモリリークや意図しない動作につながる

Unity開発での活用例

  • プレイヤーの状態変化(死亡、ダメージ、アイテム取得)を通知
  • UI更新(スコア・HPゲージなど)をイベントで自動反映
  • ゲーム進行管理(ステージクリア、敵全滅などのトリガー)

イベント管理をもっとラクにしたいなら

イベント駆動は便利ですが、大規模プロジェクトになると「イベントがどこで発火しているのか」「誰が購読しているのか」を追いかけるのが大変になることも…。 そんなときにおすすめなのがEventBusアーキテクチャです。

👉 Unity向けに最適化されたイベント管理アセット EventBus for Unity を導入すれば、複雑になりがちなイベント処理をすっきりまとめられます。 「購読と発行の管理を統一したい」「メンテナンス性を上げたい」という方にぴったりです。

これで、シングルトン・サービスロケーター・イベント駆動の3つの設計パターンをひととおり学べました。 次は、それぞれの違いや特徴を整理した比較表を見ていきましょう!


設計パターンの比較表

ここまで紹介したシングルトン・サービスロケーター・イベント駆動は、それぞれに長所と短所があります。 どのパターンも「万能」ではないので、プロジェクトの規模や目的に応じて使い分けることが大切です。 以下に、3つのパターンの特徴を表で整理しました。

パターン特徴メリットデメリット向いているケース
シングルトンインスタンスを1つに制限し、どこからでもアクセス可能・実装が簡単
・共通データを一元管理できる
・密結合になりやすい
・テストが難しい
・ゲーム全体の管理(GameManager)
・サウンド/BGM管理
・スコアなど共有データ
サービスロケーターインターフェース経由で依存性を解決・複数実装を簡単に切り替え可能
・テストがしやすい
・依存関係が見えにくい
・Locatorに依存しすぎると複雑化
・セーブ/ロード処理
・通信の本番版とモック切り替え
・プラットフォーム依存処理の共通化
イベント駆動イベント発行と購読による疎結合な処理・疎結合で柔軟性が高い
・機能追加が容易
・イベントの流れが追いにくい
・購読解除を忘れると不具合
・UI更新(スコアやHP)
・状態変化通知(死亡/クリア)
・複数システムの連動

このように、シングルトンは「小規模で手軽に」、サービスロケーターは「実装の切り替えをしたいとき」、イベント駆動は「柔軟な拡張性が欲しいとき」に活躍します。 どのパターンも一長一短なので、状況に合わせて組み合わせて使うのがおすすめです✨


まとめ

今回はUnityでよく使われるシングルトン・サービスロケーター・イベント駆動の3つの設計パターンを比較しました。 それぞれの特徴を簡単に振り返ってみましょう。

  • シングルトン:シンプルに実装でき、ゲーム全体を管理する仕組みに向いている
  • サービスロケーター:インターフェースを通じて実装を切り替えやすく、テストやデバッグに強い
  • イベント駆動:疎結合で拡張性が高く、UIやゲーム進行など複数システムの連動に便利

大切なのは「どれが正解か」ではなく状況に応じて最適なパターンを選ぶことです。 小さな個人開発ならシングルトンで十分な場合もあれば、チーム開発ではサービスロケーターやイベント駆動を組み合わせた方が効率的になることもあります。

もし「もっと設計パターンを深く学びたい」「リファクタリングの具体例を知りたい」と思ったら、次の書籍がおすすめです📚

Unityでの設計は「コードを短く書く」ことよりも保守しやすく、拡張しやすい仕組みを作ることが大切です。 ぜひ今回学んだパターンを自分のプロジェクトに取り入れて、より快適な開発環境を作ってみてくださいね✨


あわせて読みたい

今回紹介したシングルトン・サービスロケーター・イベント駆動の理解をさらに深めたい方におすすめの記事をまとめました。 設計やコード品質に関する知識を広げたいときにぜひチェックしてみてください✨

これらの記事とあわせて読むことで、設計の選択肢が増え、より柔軟で保守性の高いUnityプロジェクトを作れるようになりますよ💡


よくある質問(FAQ)

Q
シングルトンとサービスロケーターはどう使い分ければいい?
A

シングルトンは「ゲーム全体でひとつしか存在しないもの(例:GameManagerやBGM管理)」に使うとシンプルです。 一方、サービスロケーターは「複数の実装を切り替えたいとき(例:デバッグ版と本番版のセーブシステム)」に向いています。 要するに、切り替えが必要ならサービスロケーター、ひとつで十分ならシングルトンが基本の目安です。

Q
イベント駆動を使いすぎると管理が大変になりませんか?
A

確かにイベントが増えすぎると「どこで発火して、誰が購読しているのか」が分かりづらくなります。 これを防ぐには命名ルールを統一したり、購読と解除を必ずセットで書くことが大事です。 さらに便利にしたいなら、専用アセット EventBus for Unity を導入するのもおすすめです。

Q
初心者はどの設計パターンから学ぶのがいい?
A

最初の一歩としてはシングルトンが理解しやすくておすすめです。 「どこからでも使える仕組み」を体験することで、設計パターンの便利さを実感できます。 そのあとでサービスロケーターやイベント駆動を学べば、「なぜ疎結合が必要なのか」も理解しやすくなりますよ✨

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

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

スポンサーリンク