高级 Unity Singleton
此示例将 Internet 上的 MonoBehaviour 单例的多个变体组合成一个,并允许你根据全局静态字段更改其行为。
此示例使用 Unity 5 进行测试。要使用此单例,你需要做的就是按如下方式扩展它:public class MySingleton : Singleton<MySingleton> {}
。你可能还需要覆盖 AwakeSingleton
来使用它而不是通常的 Awake
。要进一步调整,请更改静态字段的默认值,如下所述。
- 此实现使用 DisallowMultipleComponent 属性为每个 GameObject 保留一个实例。
- 这个类是抽象的,只能扩展。它还包含一个需要重写的虚拟方法
AwakeSingleton
,而不是实现普通的Awake
。 - 此实现是线程安全的。
- 这个单例已经过优化。通过使用
instantiated
标志而不是实例 null 检查,我们避免了 Unity 实现==
运算符所带来的开销。 ( 了解更多 ) - 当它即将被 Unity 销毁时,此实现不允许对单例实例的任何调用。
- 这个单例有以下选择:
FindInactive
:是否查找附加到非活动 GameObject 的相同类型组件的其他实例。Persist
:是否在场景之间保持组件存活。DestroyOthers
:是否要销毁同类型的任何其他组件并且只保留一个。Lazy
:是否动态设置单例实例(在Awake
中)或仅按需设置(当调用 getter 时)。
using UnityEngine;
[DisallowMultipleComponent]
public abstract class Singleton<T> : MonoBehaviour where T : Singleton<T>
{
private static volatile T instance;
// thread safety
private static object _lock = new object();
public static bool FindInactive = true;
// Whether or not this object should persist when loading new scenes. Should be set in Init().
public static bool Persist;
// Whether or not destory other singleton instances if any. Should be set in Init().
public static bool DestroyOthers = true;
// instead of heavy comparision (instance != null)
// http://blogs.unity3d.com/2014/05/16/custom-operator-should-we-keep-it/
private static bool instantiated;
private static bool applicationIsQuitting;
public static bool Lazy;
public static T Instance
{
get
{
if (applicationIsQuitting)
{
Debug.LogWarningFormat("[Singleton] Instance '{0}' already destroyed on application quit. Won't create again - returning null.", typeof(T));
return null;
}
lock (_lock)
{
if (!instantiated)
{
Object[] objects;
if (FindInactive) { objects = Resources.FindObjectsOfTypeAll(typeof(T)); }
else { objects = FindObjectsOfType(typeof(T)); }
if (objects == null || objects.Length < 1)
{
GameObject singleton = new GameObject();
singleton.name = string.Format("{0} [Singleton]", typeof(T));
Instance = singleton.AddComponent<T>();
Debug.LogWarningFormat("[Singleton] An Instance of '{0}' is needed in the scene, so '{1}' was created{2}", typeof(T), singleton.name, Persist ? " with DontDestoryOnLoad." : ".");
}
else if (objects.Length >= 1)
{
Instance = objects[0] as T;
if (objects.Length > 1)
{
Debug.LogWarningFormat("[Singleton] {0} instances of '{1}'!", objects.Length, typeof(T));
if (DestroyOthers)
{
for (int i = 1; i < objects.Length; i++)
{
Debug.LogWarningFormat("[Singleton] Deleting extra '{0}' instance attached to '{1}'", typeof(T), objects[i].name);
Destroy(objects[i]);
}
}
}
return instance;
}
}
return instance;
}
}
protected set
{
instance = value;
instantiated = true;
instance.AwakeSingleton();
if (Persist) { DontDestroyOnLoad(instance.gameObject); }
}
}
// if Lazy = false and gameObject is active this will set instance
// unless instance was called by another Awake method
private void Awake()
{
if (Lazy) { return; }
lock (_lock)
{
if (!instantiated)
{
Instance = this as T;
}
else if (DestroyOthers && Instance.GetInstanceID() != GetInstanceID())
{
Debug.LogWarningFormat("[Singleton] Deleting extra '{0}' instance attached to '{1}'", typeof(T), name);
Destroy(this);
}
}
}
// this might be called for inactive singletons before Awake if FindInactive = true
protected virtual void AwakeSingleton() {}
protected virtual void OnDestroy()
{
applicationIsQuitting = true;
instantiated = false;
}
}