Last active
July 13, 2024 12:49
-
-
Save sassembla/2de4e864bd0918a06761356398a0d02e to your computer and use it in GitHub Desktop.
automated singleton holder for Unity. auto-initialize on Player boot time and well isolated.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System.Collections.Generic; | |
using System.Linq; | |
using UnityEngine; | |
public class SingletonHolder : MonoBehaviour { | |
private List<Base> instances = new List<Base>(); | |
private static SingletonHolder holderInstance; | |
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)] private static void NewSingleton () { | |
GameObject o = new GameObject("SingletonHolder []"); | |
holderInstance = o.AddComponent<SingletonHolder>(); | |
GameObject.DontDestroyOnLoad(holderInstance); | |
} | |
public static T Get<T>() where T : Base, new() { | |
return holderInstance.GetInstance<T>(); | |
} | |
public static Base[] GetAll () { | |
return holderInstance.instances.ToArray(); | |
} | |
private T GetInstance<T>() where T : Base, new(){ | |
foreach (var instance in instances) { | |
if (instance is T) { | |
return (T)instance; | |
} | |
} | |
var t = new T(); | |
instances.Add(t); | |
NameUpdate(); | |
/* | |
本来のUnityのMonoBehaviour.Start はUnityがInstanceを生成したときだけ呼ばれるので、メインスレッドから呼ぶという制約があるが、 | |
このSingletonHolderにはその制約がない。 | |
が、その場合、この Start が呼ばれるのはサブスレッドになってしまうため、インスタンス内でエラーが出ると思う。 | |
そうなった場合、まずサブスレッドでこのメソッドを実行するのを見直してほしい。 | |
*/ | |
t.Awake(); | |
t.OnEnable(); | |
t.Start(); | |
return t; | |
} | |
public static bool Delete<T>() where T : Base, new() { | |
return holderInstance.DeleteInstance<T>(); | |
} | |
private bool DeleteInstance<T>() where T : Base, new() { | |
foreach (var instance in holderInstance.instances) { | |
if (instance is T) { | |
instances.Remove(instance); | |
NameUpdate(); | |
return true; | |
} | |
} | |
return false; | |
} | |
private void NameUpdate () { | |
// update this instance's name. | |
holderInstance.gameObject.name = "SingletonHolder [" + string.Join(", ", instances.Select(i => i.GetType().ToString()).ToArray()) + "]"; | |
} | |
/* | |
handler methods. | |
*/ | |
private void Start () { | |
foreach (var instance in holderInstance.instances) { | |
if (instance == null) continue; | |
instance.Start(); | |
} | |
} | |
private void OnGUI () { | |
foreach (var instance in holderInstance.instances) { | |
if (instance == null) continue; | |
instance.OnGUI(); | |
} | |
} | |
private void Update () { | |
foreach (var instance in holderInstance.instances) { | |
if (instance == null) continue; | |
instance.Update(); | |
} | |
} | |
private void LateUpdate () { | |
foreach (var instance in holderInstance.instances) { | |
if (instance == null) continue; | |
instance.LateUpdate(); | |
} | |
} | |
private void OnApplicationQuit () { | |
foreach (var instance in holderInstance.instances) { | |
if (instance == null) continue; | |
instance.OnApplicationQuit(); | |
} | |
} | |
private void OnDisable () { | |
foreach (var instance in holderInstance.instances) { | |
if (instance == null) continue; | |
instance.OnDisable(); | |
} | |
} | |
public class Base { | |
public virtual void Awake () {} | |
public virtual void OnEnable () {} | |
public virtual void Start () {} | |
public virtual void FixedUpdate () {} | |
public virtual void Update () {} | |
public virtual void LateUpdate () {} | |
public virtual void OnGUI () {} | |
public virtual void OnApplicationPause () {} | |
public virtual void OnDisable () {} | |
public virtual void OnApplicationQuit () {} | |
} | |
} |
Usage
APIは
T singletonOfMySingleton = SingletonHolder.Get<T>();
bool isDeleted = SingletonHolder.Delete<T>();
の2つしかない。 サンプルコードは以下。
/**
SingletonHolder.Baseクラスを拡張してシングルトンの元を作る。
特にconstructorを作ったりしないでも大丈夫。
また、MonoBehaviourを拡張してあるので、AwakeとかStartとかをorverride定義すると、MainThread上でそれらのメソッドが実行される。
*/
public class MySingleton : SingletonHolder.Base {
public override void Start () {
Debug.LogError("start!");
}
public override void Update () {
Debug.LogError("update!");
}
public override void OnApplicationQuit () {
Debug.LogError("quit!");
}
}
/**
Singletonを使ったり消したりするクラスのサンプル。
Get関数で特定の型のインスタンスをシングルトンにしてSingletonHolderに保存し、取得できる。
Delete関数で特定の型のシングルトンをSingletonHolderから削除できる。
*/
public class SampleSingletonUser {
public void Get () {
var singletonOfMySingleton = SingletonHolder.Get<MySingleton>();
}
public void Delete () {
SingletonHolder.Delete<MySingleton>();
}
}
Q1: MonoBehaviourのシングルトンが必須な機会ってあるの?
A1: ごめん、ないかも。
・MonoBehaviourのシングルトンが欲しい場合と、それ以外とを分割
・インスタンスに対してのInspectorを提供するのはとても面白そう。値の編集も可能。
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Motivation
Unityで使えるシングルトン管理のクラス。
適当なクラスをSingletonHolder.Baseを継承して自作し、適当なタイミングでSingletonとして登録することができる。
また、登録したクラスを適当なタイミングで削除することもできる。
Unity自体が勝手にこのクラスを初期化してくれるので、どのシーンから起動しても使用できる。
また、MonoBehaviourを継承してあるので、登録した適当なクラスにUpdateなどのメソッドをoverrideで実装すると、それらがMainThreadで実行される。
好きなインスタンスを好きなタイミングでsingletonにしたり、singleton解除したりすることができる。
あと、どの型のシングルトンが現在場に存在するかは、UnityのHierarchy上から見ることができる。