0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Unity】シングルトンを久々に使って感じたこと

Last updated at Posted at 2025-12-16

はじめに

この記事はHamster Output Advent Calendar 2025の1日目の記事です。

今回はゲーム開発中にシングルトンを使って感じたことを記事にまとめました!

参考リンク

使用経緯

プロトタイプを作っている時に、プレイヤーがもつ情報(体力とか)をエネミーやUIにどのように参照させるかということを考えていました。

UIに関してはSerializeFieldで解決しますが、エネミーに関しては都度生成される都合から他の解決方法が必要でした。そこで、エネミーとUIが使うプレイヤーの情報をシングルトン化して参照させようと考えました。

シングルトンの大まかな内容

・どこからでもアクセスできる
・同じ内容の物が存在できない(2つあったら後から生成された片方が削除される)
大体こんな感じの内容です。

実装方法を調べる

参考記事の内容を元に、実装しました。

1.継承型の実装
Unity公式が公開しているデザインパターンデモのシングルトンの実装方法も確かこれだった記憶があります。継承したクラスがMonoBehaviourかつシングルトンになる実装方法です。

参考記事の内容とほぼ変わりませんが、Unity6からFindObjectsOfTypeを使うとFindObjectsByTypeを使ってね!という注意書きが出るのでそこだけ変えています。

ソースコード
using UnityEngine;

public class SingletonMonoBehaviour<T> : MonoBehaviour where T : Component
{
    private static T _instance;
    public static T Instance
    {
        get
        {
            if (_instance == null)
            {
                var findObject = FindAnyObjectByType<T>();

                if (_instance == null)
                {
                    SetupInstance();
                }
                else
                {
                    string typeName = typeof(T).Name;

                    Debug.Log("[Singleton] " + typeName + " instance already created: " +
                        _instance.gameObject.name);
                }
            }

            return _instance;
        }
    }

    public virtual void Awake()
    {
        RemoveDuplicates();
    }

    private static void SetupInstance()
    {
        _instance = FindAnyObjectByType<T>();

        if (_instance == null)
        {
            GameObject gameObj = new GameObject();
            gameObj.name = typeof(T).Name;

            _instance = gameObj.AddComponent<T>();
            DontDestroyOnLoad(gameObj);
        }
    }

    private void RemoveDuplicates()
    {
        if (_instance == null)
        {
            _instance = this as T;
            DontDestroyOnLoad(gameObject);
        }
        else
        {
            Destroy(gameObject);
        }
    }
}

2.変数型のシングルトン
こちらの方法の場合、継承しなくてもシングルトンにすることができるので、他のクラスを継承した状態でシングルトン化することができます。ただ、この方法だと重複チェックを行っていないため、シングルトン化した同じオブジェクトが複数存在できてしまいます。

こちらも上記の内容と同様に、Unity6からFindObjectsOfTypeの部分だけ変えています。

ソースコード
using UnityEngine;
using UnityEngine.Assertions;

public static class SingletonAttacher<T> where T : Object
{
    static T _Entity;

    public static T Entity
    {
        get
        {
            if (_Entity == null)
            {
                _Entity = Object.FindAnyObjectByType<T>();
                Assert.IsNotNull(_Entity, $"Singleton取得エラーです。{typeof(T).Name}が存在していません。");
            }
            return _Entity;
        }
    }
}

シングルトンを使って感じたデメリット

・どこからでもアクセスできるが故に参照する対象が増えやすい
・参照元の変数やメソッドが変更された時に参照先でエラーが起こりやすい
・複数人開発の場合、自分以外のソースコードでエラーが起こる可能性大+修正時が大変

やはり、どこからでもアクセスできるということが便利な反面デメリットに感じてしまいました。開発中の人数が多い場合はエラーを起こす要因になりそうで怖いです。

実際に個人でプロトを作っている際でも、参照箇所が割と多くて修正が面倒になることが起きました...

シングルトンの使いどころを考えた

プロトタイプ、ゲームジャムといった短期開発なら使い時だと思ってます(時間が短いと思うので)逆に、長期開発の場合は急な仕様変更とかに対応しづらくなるので使うべきではないと思いました。

ただ、これが汎用系とかの場合だとまた話が変わってくると考えていて、単に音を再生するだけのシングルトンとかだったら長期開発でも使い時だと思ってます。

まとめ

・短期開発には向いてるけど長期開発だと危険
・複数人開発だと危険性がより上がるので使いどころの見極めが必要
・シングルトン化する対象は汎用系(音再生、エフェクト再生)などの処理が変更されにくい単純な物だと使いやすい

汎用系との組み合わせならかなり使いやすい印象を感じましたが、プレイヤーに関する情報など後から変更されそうな内容の場合は、あまり相性は良くない考えています。

これらのことを考えると、DIコンテナ+インターフェースを使うこともありですね...

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?