見出し画像

【Unity】簡単で使いやすいSingletonクラス



はじめに

 今回は私がいつもUnityで使ってるSingletonクラスの作り方を紹介します。関数名などは自由に変更していただいても構いませんが、他の関数名と名前の衝突(重複)を起こしてしまう場合があるのでご注意ください。そして、今回紹介するSingletonクラスはあくまで我流なので、もっと簡単で使いやすい方法があったら申し訳ありません。

Singletonクラス

早速コードを載せておきます。

using System;
using UnityEngine;

[DisallowMultipleComponent]
public abstract class Singleton<T> : MonoBehaviour
	where T : Singleton<T>
{
	private static T s_instance = default;

	protected virtual void Awake()
	{
		if (s_instance)
		{
			Destroy(this);
			Debug.LogWarning($"{typeof(T)}のインスタンスは既に生成されています");

			return;
		}

		s_instance = this as T;
	}
	protected virtual void OnDestroy()
	{
		if (ReferenceEquals(this, s_instance))
			s_instance = null;
	}

	public static void InvokeIfInstanceIsNotNull(Action<T> act)
	{
		if (s_instance)
			act?.Invoke(s_instance);
	}

	public static TResult InvokeIfInstanceIsNotNull<TResult>(Func<T, TResult> func)
	{
		if (!s_instance || func is null)
			return default;

		return func.Invoke(s_instance);
	}
}

注意点

 今回紹介したSingletonクラスを使う上で注意することが何点かあります。

 まず一つ目、Singletonクラスを継承したクラスA、これは1つのオブジェクトにしかアタッチしてはなりません。シングルトンですから、クラスAが複数あっては困るわけです。複数あってもエラーが出ないよう実装はしていますが、警告はでるようにしてます。

 二つ目、SingletonクラスにはDisallowMultipleComponentという属性が付いています。この属性が付いたコンポーネントは1つのオブジェクトに同じコンポーネントをアタッチすることが出来なくなってます。シングルトンは1つだけしかあってはならないので、事前策として属性をつけてます。

 三つ目、Singletonクラスをアタッチすることはできません。abstractが付いたクラスはそのままインスタンス化できません。なので必ず継承してから継承したコンポーネントをアタッチしなければなりません。

 四つ目、継承先でAwake関数またはOnDestroy関数を実装する際、親クラスのAwake関数またはOnDestroy関数は必ず呼び出すこと。これに関しては下のサンプルのTestクラスをご参照ください。

使い方(サンプル)

今回はサンプル用に2つのクラスを用意しました。
1つはSingletonクラスを継承したTestクラス。
もう1つはTestクラスを使うTest2クラスです。

using UnityEngine;

public class Test : Singleton<Test>
{
	private int m_int;

	protected override void Awake()
	{
		base.Awake();

		/* === SingletonのAwakeを先に呼び出す === */
	}

	protected override void OnDestroy()
	{
		/* === SingletonのOnDestroyを後に呼び出す === */

		base.OnDestroy();
	}

	public int GetInt()
	{
		return m_int;
	}
}
using UnityEngine;	

public class Test2 : MonoBehaviour
{
	private void Start()
	{
		var test = Test.InvokeIfInstanceIsNotNull(t => t);
		var integer = Test.InvokeIfInstanceIsNotNull(t => t.GetInt());
		Test.InvokeIfInstanceIsNotNull(t =>
		{
			/* === なんか処理 === */
		});
	}
}


この記事が気に入ったらサポートをしてみませんか?