美文网首页unitycsharpunity3d
[Unity3D] 通用 MonoSingleton<T&

[Unity3D] 通用 MonoSingleton<T&

作者: 雨落随风 | 来源:发表于2019-01-28 03:12 被阅读1次

    开发中都会遇到类型单例的需求,在本文,笔者将为大家整理一个通用的MonoBehavour单例类:

    写在前面:

    很多时候,笔者都是图简单,使用下面这个方式做的单例:

    public static DemoClass Get{get;private set;}
    void Awake()
    {
      Get = this;
    }
    

    对,你没看错,就是这么简单粗暴!但是,单例需求量上来了,这么写也不是个办法,也学习别人整理一个通用的单例类来好了。

    MonoSingleton:

    using System;
    using UnityEngine;
    
    [AutoSingleton(true)]
    public class MonoSingleton<T> : MonoBehaviour where T : MonoBehaviour
    {
        private static T _instance;
        private static object _lock = new object();
        public static T Instance
        {
            get
            {
                Type _type = typeof(T);
                if (_destroyed)
                {
                    Debug.LogWarningFormat("[Singleton]【{0}】已被标记为销毁,返 Null!", _type.Name);
                    return (T)((object)null);
                }
                lock (_lock)
                {
                    if (_instance == null)
                    {
                        _instance = (T)FindObjectOfType(_type);
                        if (FindObjectsOfType(_type).Length > 1)
                        {
                            Debug.LogErrorFormat("[Singleton]类型【{0}】存在多个实例.", _type.Name);
                            return _instance;
                        }
                        if (_instance == null)
                        {
                            object[] customAttributes = _type.GetCustomAttributes(typeof(AutoSingletonAttribute), true);
                            AutoSingletonAttribute autoAttribute = (customAttributes.Length > 0) ? (AutoSingletonAttribute)customAttributes[0] : null;
                            if (null == autoAttribute || !autoAttribute.autoCreate)
                            {
                                Debug.LogWarningFormat("[Singleton]欲访问单例【{0}】不存在且设置了非自动创建~", _type.Name);
                                return (T)((object)null);
                            }
                            GameObject go = null;
                            if (string.IsNullOrEmpty(autoAttribute.resPath))
                            {
                                go = new GameObject(_type.Name);
                                _instance = go.AddComponent<T>();
                            }
                            else
                            {
                                go = Resources.Load<GameObject>(autoAttribute.resPath);
                                if (null != go)
                                {
                                    go = GameObject.Instantiate(go);
                                }
                                else
                                {
                                    Debug.LogErrorFormat("[Singleton]类型【{0}】ResPath设置了错误的路径【{1}】", _type.Name, autoAttribute.resPath);
                                    return (T)((object)null);
                                }
                                _instance = go.GetComponent<T>();
                                if (null == _instance)
                                {
                                    Debug.LogErrorFormat("[Singleton]指定预制体未挂载该脚本【{0}】,ResPath【{1}】", _type.Name, autoAttribute.resPath);
                                }
                            }
                        }
                    }
                    return _instance;
                }
            }
        }
    
        protected virtual void Awake()
        {
            if (_instance != null && _instance.gameObject != gameObject)
            {
                Debug.Log("创造了新的克隆体!");
                if (Application.isPlaying)
                {
                    GameObject.Destroy(gameObject);
                }
                else
                {
                    GameObject.DestroyImmediate(gameObject);
                }
            }
            else
            {
                _instance = GetComponent<T>();
                DontDestroyOnLoad(gameObject);
                OnInit();
            }
        }
    
        public static void DestroyInstance()
        {
            if (_instance != null)
            {
                GameObject.Destroy(_instance.gameObject);
            }
            _destroyed = true;
            _instance = (T)((object)null);
        }
    
        /// <summary>
        /// 清除 _destroyed 锁
        /// </summary>
        public static void ClearDestroy()
        {
            DestroyInstance();
            _destroyed = false;
        }
    
        private static bool _destroyed = false;
        /// <summary>
        /// 当播放停止时,Unity 会以随机顺序销毁对象
        /// 若单例 gameObject 先于其他对象销毁,不排除这个单例再次被调用的可能性。
        /// 故而在编辑器模式下,即便播放停止了,也可能会生成一个 gameObject 对象残留在编辑器场景中。
        /// 所以,此方法中加把锁,避免不必要的单例调用
        /// </summary>
        public void OnDestroy()
        {
            if (_instance != null && _instance.gameObject == base.gameObject)
            {
                _instance = (T)((object)null);
                _destroyed = true;
            }
        }
    
        /// <summary>Awake 初始化完成之后 </summary>
        public virtual void OnInit()
        {
            Debug.Log("OnInit");
        }
    
    }
    
    [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
    public class AutoSingletonAttribute : Attribute
    {
        public bool autoCreate; //是否自动创建单例
        public string resPath;  //从指定的预制体路径生成单例
    
        public AutoSingletonAttribute(bool _autoCreate, string _resPath = "")
        {
            this.autoCreate = _autoCreate;
            this.resPath = _resPath;
        }
    }
    

    Tips:
    这个单例能够解决以下痛点:

    • 使用了泛型 T 实现逻辑复用,避免重复撰写单例逻辑。
    • 使用了自定义属性,加入单例的实例化规则:
      1. 当单例获取失败,用户可决定是否动态创建。
      2. 当单例获取失败,用户可决定是否从Resource目录下获取预制体动态创建,或者创建空游戏对象。
    • 使用了 _destoryed 锁,避免了编辑器模式下停止播放时生成单例游戏对象的bug。
    • Awake 中新增 “gamaObject 实例清除” 逻辑, 确保了来回跳场景也不会反序列化出多余的单例对象。

    使用示例

    using UnityEngine;
    [AutoSingleton(true,"Player")]  //第一个参数表示如获取不到则自动创建单例,第二个代表希望加载的预制体路径
    public class Player : MonoSingleton<Player> 
    {
        void Start () 
        {
            Debug.Log("Player 闪亮登场!!");
        }
    }
    
    

    扩展阅读:

    1. QFrameWork中的单例实现
    2. Graphy 中的单例(链接整个拷贝哈)
      https://github.com/Tayx94/graphy/blob/master/Assets/Tayx/Graphy - Ultimate Stats Monitor/Scripts/Util/G_Singleton.cs
      Graphy-唯美的Runtime 性能查看器

    相关文章

      网友评论

        本文标题:[Unity3D] 通用 MonoSingleton<T&

        本文链接:https://www.haomeiwen.com/subject/mosojqtx.html