はじめに
Unityで他のスクリプトの変数を参照したいとき、通常はスクリプトがアタッチされているオブジェクトを取得して、GetComponentで変数を取得します。
オブジェクトを取得するために、インスペクターにドラッグ&ドロップすることが多い!
1個や2個ならまだしも、もっと多くの変数にアクセスしたいとき、毎回オブジェクトを取得するのは面倒ではないでしょうか?
「シングルトン」を使うことで、いちいちオブジェクトを取得せずにアクセスすることができます。
本記事では、シングルトンの実際の使い方について、できるだけ初心者にもわかりやすく解説します。
ゲームを開発するうえで、非常に便利な内容なのでぜひ理解して取り入れてみましょう。
シングルトンとは?
シングルトンとは、クラスのインスタンスが必ず1つしか生成されないようにするデザインパターンです。
Unity固有の機能ではありません。
一種のテクニックみたいなものだよ
基本的な使い方
以下のように、ボタンを押したら数字が1つ増える処理を作ってみます。
ScoreManagerというからのオブジェクトを作って、スコアの管理を行います。
2つのスクリプトを用意します。
- ScoreManager: 数字(スコア)を管理するスクリプト
- ScoreText: ボタンを押したら数字を増やしてスコアを表示するスクリプト
ScoreTextから、ScoreManagerの変数にアクセスします。
参照元のスクリプト
Awakeの中に、シングルトンの処理を書きます。インスタンスが1つのみ存在するように、複数存在したらDestroyで消すようにします。
using UnityEngine;
public class ScoreManager : MonoBehaviour
{
public static ScoreManager instance; // インスタンスの定義
public int number = 0; // この変数にアクセスしたい
private void Awake()
{
// シングルトンの呪文
if (instance == null)
{
// 自身をインスタンスとする
instance = this;
}
else
{
// インスタンスが複数存在しないように、既に存在していたら自身を消去する
Destroy(gameObject);
}
}
}
補足:シングルトンの呪文について
Awake内を以下のように書くだけでも動作はします。
private void Awake()
{
// シングルトンの呪文
// 自身をインスタンスとする
instance = this;
}
ただし、このように書くと、うっかりシングルトンであることを忘れて複数のオブジェクトにこのスクリプトをアタッチしてしまったときに、インスタンスが複数存在することになるので、正確に変数にアクセスできなくなってしまいます。
必ず1つだけ存在するのがシングルトンだよ!
シングルトンであることを忘れなければ良いのですが、忘れて複数生成しても必ず1つになるような処理(最初のコード)にしておくのがベターです。
変数にアクセスするスクリプトの例
今度は先ほど作成したスクリプトの変数にアクセスする例です。
ScoreManager.instance.numberで、ScoreManagerのnumberという変数にアクセスできます。インスタンスは1つしか存在しないので、ScoreManager.instanceだけでアクセスできるという仕組みです。
using UnityEngine;
using TMPro;
public class ScoreText : MonoBehaviour
{
[SerializeField] TextMeshProUGUI score = default;
// ボタンを押したら1増やして表示する関数
public void OnClickButton()
{
// ScoreManagerの数字を1つ上げる
ScoreManager.instance.number++;
// テキスト更新
score.text = ScoreManager.instance.number.ToString();
}
}
一行でアクセスできるからいいね!
中級者向け:シングルトンを多用したいなら
ここまでで、シングルトンが扱えるようになりましたが、シングルトンにしたいスクリプトには、毎回Awake内に「シングルトンの呪文」をコピペしないといけないのが手間ですね。
コピペが不要となる簡単な方法もあるので、その方法を紹介します。
Singletonという名前で新しくスクリプトを作成します。
using UnityEngine;
public class Singleton<T> : MonoBehaviour where T :MonoBehaviour
{
public static T instance;
protected virtual void Awake()
{
if (instance == null)
instance = (T)FindObjectOfType(typeof(T));
else
Destroy(gameObject);
}
}
FindObjectOfType(typeof(T)) は、シーン内でT型のコンポーネントがアタッチされている最初のオブジェクトを返します。ただ、返すのはオブジェクト型のため、T型に変換するために頭に(T)をつけています。
このスクリプトを用意しておくと、ScoreManagerが以下のように簡略化できるようになります。
using UnityEngine;
public class ScoreManager : Singleton<ScoreManager>
{
public int number = 0;
}
すごい簡単に書ける!
以降、他のスクリプトもシングルトンにしたいときは同様にできます。
シーンをまたいでも消去しないようにするには
音量設定など、シーンが切り替わっても消去させたくないものはDontDestroyOnLoad関数を入れましょう。
SingletonDontDestroyという名前のスクリプトを新しく作成します。
using UnityEngine;
public class SingletonDontDestroy<T> : MonoBehaviour where T : MonoBehaviour
{
public static T instance;
protected virtual void Awake()
{
if (instance == null)
{
instance = (T)FindObjectOfType(typeof(T));
DontDestroyOnLoad(gameObject); // 追加!!
}
else
Destroy(gameObject);
}
}
ScoreManagerを対応させるには、以下のようにします。
using UnityEngine;
public class ScoreManager : SingletonDontDestroy<ScoreManager>
{
public int number = 0;
}
おわりに
シングルトンの使い方と応用の紹介でした。
音量設定やゲームの全体を管理するスクリプトは、シングルトン化しておくことで、開発がしやすくなりますので、活用してみましょう!