Unity單例模式(普通單例和繼承MonoBehaviour的單例)
阿新 • • 發佈:2018-12-23
單例類是開發過程中必不可少的東西。介紹完兩個常用單例型別後會介紹一個簡單卻很實用的MonoEvent。
關於單例的寫法網上有很多,這篇文章主要想介紹的是MonoEvent以及為之後的計時器做準備。
1.普通單例
最普通的寫法,加了個執行緒鎖,通過屬性訪問,不光是Unity,所有的c#程式都能用。
using UnityEngine; /// <summary> /// 不繼承mono的單例基類,如果需要Update,可以將方法註冊進MonoEvent的事件中 /// </summary> /// <typeparam name="T"></typeparam> public class Singleton<T> where T : new() { private static T _instance; private static readonly object objlock = new object(); public static T Instance { get { if (_instance == null) { lock (objlock) { if (_instance == null) { _instance = new T(); } } } return _instance; } } }
普通單例類的構造有兩種方式,看習慣,個人比較喜歡第一種,呼叫的時候可以少寫一個單詞一對括號
1.通過繼承單例基類
構造: public class MyClass : Singleton<MyClass>{ }
呼叫: MyClass.Instance.MethodName
2.不繼承單例基類
構造: public class MyClass { }
呼叫: Singleton<MyClass>.Instance.MethodName
2.繼承MonoBehaviour的單例
不建議手動在Scene中的某個GameObject上掛載繼承Mono的單例類,呼叫的時候程式碼會自動建立
using UnityEngine; /// <summary> /// 此單例繼承於Mono,絕大多情況下,都不需要使用此單例型別。請使用Singleton /// 不需要手動掛載 /// </summary> public class MonoSingleton<T> : MonoBehaviour where T : MonoBehaviour { private static T _instance; /// <summary> /// 執行緒鎖 /// </summary> private static readonly object _lock = new object(); /// <summary> /// 程式是否正在退出 /// </summary> protected static bool ApplicationIsQuitting { get; private set; } /// <summary> /// 是否為全域性單例 /// </summary> protected static bool isGolbal = true; static MonoSingleton() { ApplicationIsQuitting = false; } public static T Instance { get { if (ApplicationIsQuitting) { if (Debug.isDebugBuild) { Debug.LogWarning("[Singleton] " + typeof(T) + " already destroyed on application quit." + " Won't create again - returning null."); } return null; } lock (_lock) { if (_instance == null) { // 先在場景中找尋 _instance = (T)FindObjectOfType(typeof(T)); if (FindObjectsOfType(typeof(T)).Length > 1) { if (Debug.isDebugBuild) { Debug.LogWarning("[Singleton] " + typeof(T).Name +" should never be more than 1 in scene!"); } return _instance; } // 場景中找不到就建立新物體掛載 if (_instance == null) { GameObject singletonObj = new GameObject(); _instance = singletonObj.AddComponent<T>(); singletonObj.name = "(singleton) " + typeof(T); if (isGolbal && Application.isPlaying) { DontDestroyOnLoad(singletonObj); } return _instance; } } return _instance; } } } /// <summary> /// 當工程執行結束,在退出時,不允許訪問單例 /// </summary> public void OnApplicationQuit() { ApplicationIsQuitting = true; } }
3.MonoEvent
很多時候,我們寫的類並不像繼承MonoBehaviour,但是我們又想這個類能夠在Mono的生命週期裡做一些事。比如Update,那麼我們可以這樣寫
MonoEvent.Instance.UPDATE += MethodName
using System;
/// <summary>
/// Mono生命週期事件
/// 一些不繼承Mono的類如果想在Mono生命週期做一些事,可以往這裡新增
/// </summary>
public class MonoEvent : MonoSingleton<MonoEvent>
{
public event Action UPDATE;
public event Action FIXEDUPDATE;
public event Action ONGUI;
public event Action LATEUPDATE;
private void Update()
{
UPDATE?.Invoke();
}
private void FixedUpdate()
{
FIXEDUPDATE?.Invoke();
}
private void OnGUI()
{
ONGUI?.Invoke();
}
private void LateUpdate()
{
LATEUPDATE?.Invoke();
}
}