Last active
December 9, 2015 09:23
-
-
Save bruce965/785bc506610347cdcc35 to your computer and use it in GitHub Desktop.
Unity Singleton
This file contains 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; | |
using System.Linq; | |
using System.Linq.Expressions; | |
using System.Reflection; | |
using UnityEngine; | |
namespace SandWar.Unity.Tools | |
{ | |
/// <summary> | |
/// Attributes to be used with <see cref="Singleton<T>" />s. | |
/// </summary> | |
public static class Singleton { | |
/// <summary>Forces an instance a <see cref="Singleton<T>" /> to be automatically added to every scene.</summary> | |
[AttributeUsage(AttributeTargets.Class, Inherited = true, AllowMultiple = false)] | |
public sealed class SelfInitializedAttribute : Attribute { } | |
/// <summary>Ensures this <see cref="Singleton<T>" /> to persist when changing scene.</summary> | |
[AttributeUsage(AttributeTargets.Class, Inherited = true, AllowMultiple = false)] | |
public sealed class PersistentAttribute : Attribute { } | |
/// <summary>Hides the instance of this <see cref="Singleton<T>" /> from the scene.</summary> | |
[AttributeUsage(AttributeTargets.Class, Inherited = true, AllowMultiple = false)] | |
public sealed class HiddenAttribute : Attribute { } | |
/// <summary>Loads the instance of this <see cref="Singleton<T>" /> from a resource.</summary> | |
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)] | |
public sealed class LoadFromResourceAttribute : Attribute { | |
public string Path; | |
public LoadFromResourceAttribute(string path) { | |
this.Path = path; | |
} | |
} | |
class FakeSingleton : Singleton<FakeSingleton> { } | |
[RuntimeInitializeOnLoadMethod] | |
static void initSingletons() { | |
// ensures singletons with SelfInitializedAttribute are initialized | |
var ensureInstantiatedMethod = getMethod(() => FakeSingleton.EnsureInstantiated()); | |
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies()) | |
foreach (var type in assembly.GetTypes().Where(type => isSelfInitialized(type))) | |
ensureInstantiatedMethod.getMethodDefinitionInType(type.BaseType).Invoke(null, new object[0]); | |
} | |
static MethodInfo getMethod(Expression<Action> method) { | |
return ((MethodCallExpression)method.Body).Method; | |
} | |
static MethodInfo getMethodDefinitionInType(this MethodInfo method, Type type) { | |
// HACK: MetadataToken seems to be shared between generic implementations, but this is undocumented! | |
var originalToken = method.MetadataToken; | |
return type.GetMethods().First(method2 => method2.MetadataToken == originalToken); | |
} | |
static bool isSelfInitialized(Type type) { | |
return type.IsDefined(typeof(Singleton.SelfInitializedAttribute), false); | |
} | |
} | |
/// <summary> | |
/// Provides a way to quickly instantiate singletons. | |
/// | |
/// Always seal the inheritor class. | |
/// </summary> | |
[AddComponentMenu("")] | |
public abstract class Singleton<T> : MonoBehaviour where T : Singleton<T> | |
{ | |
static object @lock = new object(); | |
static bool instantiated = false; | |
static bool quitting = false; | |
static T instance = null; | |
static bool persistent { | |
get { return typeof(T).IsDefined(typeof(Singleton.PersistentAttribute), true); } | |
} | |
static bool visible { | |
get { return !typeof(T).IsDefined(typeof(Singleton.HiddenAttribute), true); } | |
} | |
static string resource { | |
get { | |
var attrib = (Singleton.LoadFromResourceAttribute)typeof(T).GetCustomAttributes(typeof(Singleton.LoadFromResourceAttribute), false).FirstOrDefault(); | |
return attrib == null ? null : attrib.Path; | |
} | |
} | |
/// <summary>Holds a reference to the instance of this <see cref="Singleton<T>" />.</summary> | |
protected static T Instance { | |
get { | |
EnsureInstantiated(); | |
return instance; | |
} | |
set { | |
instantiated = value != null; | |
instance = value; | |
} | |
} | |
/// <summary>Intantiates a <see cref="Singleton<T>" /> of type <typeparamref>T</typeparamref> if not already in the scene.</summary> | |
public static void EnsureInstantiated() { | |
lock (@lock) { | |
if (!instantiated) { | |
if (quitting) | |
return; | |
instantiated = true; | |
instance = GameObject.FindObjectOfType<T>(); | |
if (instance == null) { | |
GameObject singleton; | |
if (resource != null) { | |
instance = GameObject.Instantiate(UnityEngine.Resources.Load<T>(resource)); | |
singleton = instance.gameObject; | |
} else { | |
singleton = new GameObject(typeof(T).FullName + "(Singleton)"); | |
instance = singleton.AddComponent<T>(); | |
} | |
if (!visible) | |
singleton.hideFlags = HideFlags.HideInHierarchy; | |
} | |
if (persistent) | |
GameObject.DontDestroyOnLoad(instance.gameObject); | |
} | |
} | |
} | |
/// <summary> | |
/// Used by Unity to initialize the <see cref="GameObject" />. | |
/// | |
/// Always call this method if overriding. | |
/// </summary> | |
protected virtual void Awake() { | |
lock (@lock) { | |
if (instance != null && instance != this) { | |
// Do not log anything, because switching scene triggers this code if there is an instance of the Singleton | |
//Debug.LogWarningFormat(this, "Multiple instances of Singleton<{0}> found on the scene.", typeof(T).FullName); | |
GameObject.Destroy(gameObject); | |
} | |
} | |
} | |
/// <summary> | |
/// Used by Unity to destroy the <see cref="GameObject" />. | |
/// | |
/// Always call this method if overriding. | |
/// </summary> | |
protected virtual void OnDestroy() { | |
lock (@lock) { | |
if (instance == this) { | |
instantiated = false; | |
instance = null; | |
} | |
} | |
} | |
/// <summary> | |
/// Called by Unity when the application is quitting. | |
/// | |
/// Always call this method if overriding. | |
/// </summary> | |
protected virtual void OnApplicationQuit() { | |
lock (@lock) | |
quitting = true; | |
} | |
} | |
} |
This file contains 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 UnityEngine; | |
using SandWar.Unity.Tools; | |
using System; | |
// Ensure there will only be no more than one instance at a time. | |
public sealed class PlayerController : Singleton<PlayerController> | |
{ | |
public Ship Ship; | |
void Update() { | |
Ship.MoveTowards(InputSettings.AxialInput); | |
if (InputSettings.IsBrakeDown) | |
Ship.Brake(); | |
if (InputSettings.IsScreenDown()) | |
Ship.MoveTo(InputSettings.GetScreenPoint(Ship.transform.position.z)); | |
if (InputSettings.IsTriggerDown) | |
Ship.Fire(); | |
} | |
} |
This file contains 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 UnityEngine; | |
using SandWar.Unity.Tools; | |
using System; | |
// Ensure this singleton is not unloaded between scenes. | |
[Singleton.Persistent] | |
// Load "ShipPrefabs.prefab" from "Resources" folder as the instance of this singleton. | |
[Singleton.LoadFromResource("ShipPrefabs")] | |
// Do not show the instance of this singleton in the inspector. | |
// [Singleton.Hidden] | |
public sealed class ShipPrefabs : Singleton<ShipPrefabs> | |
{ | |
public Transform Ship1Prefab; | |
public Transform Ship2Prefab; | |
} |
This file contains 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 UnityEngine; | |
using SandWar.Unity.Tools; | |
using System; | |
// Automatically add an instance of this singleton to every scene. | |
[Singleton.SelfInitialized] | |
public sealed class TestAutorun : Singleton<TestAutorun> | |
{ | |
void Start() { | |
Debug.Log("Hello, World!"); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment