Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
automated singleton holder for Unity. auto-initialize on Player boot time and well isolated.
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 () {}
}
}
@sassembla

This comment has been minimized.

Copy link
Owner Author

sassembla commented Dec 16, 2016

Motivation

Unityで使えるシングルトン管理のクラス。
適当なクラスをSingletonHolder.Baseを継承して自作し、適当なタイミングでSingletonとして登録することができる。
また、登録したクラスを適当なタイミングで削除することもできる。

Unity自体が勝手にこのクラスを初期化してくれるので、どのシーンから起動しても使用できる。

また、MonoBehaviourを継承してあるので、登録した適当なクラスにUpdateなどのメソッドをoverrideで実装すると、それらがMainThreadで実行される。

好きなインスタンスを好きなタイミングでsingletonにしたり、singleton解除したりすることができる。

あと、どの型のシングルトンが現在場に存在するかは、UnityのHierarchy上から見ることができる。

@sassembla

This comment has been minimized.

Copy link
Owner Author

sassembla commented Dec 16, 2016

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>();
    }
}
@sassembla

This comment has been minimized.

Copy link
Owner Author

sassembla commented Dec 18, 2016

Q1: MonoBehaviourのシングルトンが必須な機会ってあるの?

A1: ごめん、ないかも。

@sassembla

This comment has been minimized.

Copy link
Owner Author

sassembla commented Dec 25, 2016

・MonoBehaviourのシングルトンが欲しい場合と、それ以外とを分割
・インスタンスに対してのInspectorを提供するのはとても面白そう。値の編集も可能。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.