Unity中单例模式的使用

一.单例模式的基本实现:

单例模式(singleton pattern)大家都不陌生,我今天主要是和大家探讨一下单例模式在unity中的实现,比起一般的单例,unity中有些他的特点。

最普通的单例:(样式一)

public class Singleton
{
    static Singleton instance;
  
    public static Singleton Instance {
        get {
            if (instance == null) {
                instance = new Singleton ();
            }
            return instance;
        }
    }
}


unity单例模式二:
但是unity的所有脚本都必须挂在一个gameobject上,否则无法执行,你这个单例中若只是一些数据,那倒没关系,但我相信绝大多数单例模式都不会只包含数据,若只要实现包含数据的功能,用全局静态变量就行了,说到这里加一句,有些盆友喜欢用单例脚本当做全局脚本来用,那其实是违背单例模式的初衷的...

好,我们来实现以下挂到gameobject的单例(模式二):

public class UnitySingleton : MonoBehaviour
{
  static UnitySingleton instance;
  
  public static UnitySingleton Instance {
  get {
   if (instance == null) {
   instance = FindObjectOfType (typeof(UnitySingleton)) as UnitySingleton;
   if (instance == null) {
   GameObject obj = new GameObject ();  
   instance = obj.AddComponent (typeof(UnitySingleton));
   }
 }
 return instance;
 }
    }
}


unity单例模式三:

那如果我的游戏里有很多单例脚本,每个脚本都这么写岂不是很麻烦?岂不是很违背oo思想?^_^,那我们来设计一个泛型类:

public class UnitySingleton<T> : MonoBehaviour
    where T : Component
{
    private static T _instance;
    public static T Instance {
        get {
            if (_instance == null) {
                instance = FindObjectOfType (typeof(T)) as T;
                if (_instance == null) {
                    GameObject obj = new GameObject ();
                    obj.hideFlags = HideFlags.HideAndDontSave;//隐藏实例化的new game object,下同
                    _instance = obj.AddComponent (typeof(T));
                }
            }
            return _instance;
        }
    }
}
这样一来,场景中需要单例化的脚本只要简单的继承这个类就可以了

public class Manager : UnitySingleton<Manager>
{
public string testText = "hello Sinngleton!";
}

manager单例脚本将在第一次实例化时自动创建,当然,你是看不到他的呵呵,但是底部的debuglog出卖了他...:

public class SingletonTest : MonoBehaviour {
        // Use this for initialization
        void Start () {
                Debug.Log(Manager.Instance.testText);
        }
}


unity单例模式四:

最后一个问题就是,我希望我的所有单例脚本在场景变化后依然存在,那自然是用DontDestroyOnLoad,直接上脚本:

using UnityEngine;
public class UnitySingleton<T> : MonoBehaviour 
        where T:Component {
        private static T _instance;
        public static T Instance        {
                get{
                        if(_instance == null){
                                _instance = FindObjectOfType(typeof(T)) as T;
                                if(_instance == null){
                                        GameObject obj = new GameObject ();
                                        //obj.hideFlags = HideFlags.DontSave;
                                        obj.hideFlags = HideFlags.HideAndDontSave;
                                        _instance =(T) obj.AddComponent(typeof(T));
                                }
                        }
                        return _instance;
                }
        }
        public virtual void Awake()
        {
                DontDestroyOnLoad(this.gameObject);
                if(_instance == null){
                        _instance = this as T;
                }
                else{
                        Destroy(gameObject);
                }
        }
}

至此,所有单例模式已经全部探讨完了,单例模式是最简单的设计模式,概念上没什么难懂的,只不过在unity中实现起来需要注意一下,
本文抛砖引玉,希望和各位探讨


转载:http://www.manew.com/thread-16916-1-1.html


二.单例模式的高级使用

此种方式更加具有健壮性和灵活性,更加适合面向对象的基本思想“多组合,少继承”,更容易实现代码的维护和扩展。例如可以继续扩展对多个不同单例对象组件的管理

一位国外的程序猿在论坛上问:“在unity3D中,有木有什么萌萌哒方法可以创建一个类似全局类的静态实例一样的单例管理类啊!”
然后又说:“我该怎么做呢?需要把它挂在物体上吗?我能让它萌萌哒躺在文件夹里而不需要放在场景中吗”
下面就有位技术大大看到这个萌萌哒问题,心里顿时就软了,
然后很细心的告诉他了,这要看情况了,一般我用两种单例类:
1、作为组件挂在物体上
2、作为一个单独的类并且不继承字MonoBehaviour类
下面就是这个类的示例代码:

这个类主要由两部分组成:一个是用于单例管理的静态方法CreateInstance(),一个用于挂载组件的GameObject类型Main。

public class MainComponentManger {
    private static MainComponentManger instance;
    public static void CreateInstance () {
        if (instance == null) {
            instance = new MainComponentManger ();
            GameObject go = GameObject.Find ("Main");
            if (go == null) {
                go = new GameObject ("Main");
                instance.main = go;
                // important: make game object persistent:
                Object.DontDestroyOnLoad (go);
            }
            // trigger instantiation of other singletons
            Component c = MenuManager.SharedInstance;
            // ...
        }
    }
 
    GameObject main;
 
    public static MainComponentManger SharedInstance {
        get {
            if (instance == null) {
                CreateInstance ();
            }
            return instance;
        }
    }
 
    public static T AddMainComponent <T> () where T : UnityEngine.Component {
        T t = SharedInstance.main.GetComponent<T> ();
        if (t != null) {
            return t;
        }
        return SharedInstance.main.AddComponent <T> ();
    }
?
然后呢,这个技术大大又说这个类是这样用的,

示例代码如下:

public class AudioManager : MonoBehaviour {
    private static AudioManager instance = null;
    public static AudioManager SharedInstance {
        get {
            if (instance == null) {
                instance = MainComponentManger.AddMainComponent<AudioManager> ();
            }
            return instance;
        }
    }

原文链接:http://stackoverflow.com/questions/13730112/unity3d-singleton-manager-classes

转载:http://www.manew.com/forum.php?mod=viewthread&tid=21956


三.多个单例对象的管理


庆幸的是,单例模式的代码非常简单。下面是Singleton.cs的内容:
[csharp]  view plain copy print ?
  1. using System;  
  2. using System.Collections;  
  3. using System.Collections.Generic;  
  4.    
  5.    
  6. public class Singleton : MonoBehaviour  
  7. {  
  8.     private static GameObject m_Container = null;  
  9.     private static string m_Name = "Singleton";  
  10.     private static Dictionary<stringobject> m_SingletonMap = new Dictionary<stringobject>();  
  11.     private static bool m_IsDestroying = false;  
  12.        
  13.     public static bool IsDestroying  
  14.     {  
  15.         get { return m_IsDestroying; }  
  16.     }  
  17.        
  18.     public static bool IsCreatedInstance(string Name)  
  19.     {  
  20.         if(m_Container == null)  
  21.         {  
  22.             return false;  
  23.         }  
  24.         if (m_SingletonMap!=null && m_SingletonMap.ContainsKey(Name))   
  25.         {  
  26.             return true;  
  27.         }  
  28.         return false;  
  29.            
  30.     }  
  31.     public static object getInstance (string Name)  
  32.     {  
  33.         if(m_Container == null)  
  34.         {  
  35.             Debug.Log("Create Singleton.");  
  36.             m_Container = new GameObject ();  
  37.             m_Container.name = m_Name;      
  38.             m_Container.AddComponent (typeof(Singleton));  
  39.         }  
  40.         if (!m_SingletonMap.ContainsKey(Name)) {  
  41.             if(System.Type.GetType(Name) != null)  
  42.             {  
  43.                 m_SingletonMap.Add(Name, m_Container.AddComponent (System.Type.GetType(Name)));  
  44.             }  
  45.             else  
  46.             {  
  47.                 Debug.LogWarning("Singleton Type ERROR! (" + Name + ")");  
  48.             }  
  49.         }  
  50.         return m_SingletonMap[Name];  
  51.     }     
  52.        
  53.     public static void RemoveInstance(string Name)  
  54.     {  
  55.         if (m_Container != null && m_SingletonMap.ContainsKey(Name))  
  56.         {  
  57.             UnityEngine.Object.Destroy((UnityEngine.Object)(m_SingletonMap[Name]));  
  58.             m_SingletonMap.Remove(Name);  
  59.               
  60.             Debug.LogWarning("Singleton REMOVE! (" + Name + ")");  
  61.         }  
  62.     }  
  63.    
  64.     void Awake ()  
  65.     {  
  66.         Debug.Log("Awake Singleton.");  
  67.         DontDestroyOnLoad (gameObject);  
  68.     }  
  69.        
  70.     void Start()  
  71.     {  
  72.         Debug.Log("Start Singleton.");  
  73.     }     
  74.        
  75.     void Update()  
  76.     {  
  77.     }  
  78.        
  79.     void OnApplicationQuit()  
  80.     {  
  81.         Debug.Log("Destroy Singleton");  
  82.         if(m_Container != null)  
  83.         {  
  84.             GameObject.Destroy(m_Container);  
  85.             m_Container = null;  
  86.             m_IsDestroying = true;  
  87.         }             
  88.     }  
  89.        
  90. }  

代码大部分都比较容易看懂,下面介绍几点注意的地方:
  • 当我们在其他代码里需要访问某个单例时,只需调用getInstance函数即可,参数是需要访问的脚本的名字。我们来看一下这个函数。它首先判断所有单例所在的容器m_Container是否为空(实际上就是场景中是否存在一个Gameobject,上面捆绑了一个Singleton脚本),如果为空,它将自动创建一个对象,然后以“Singleton”命名,再捆绑Singleton脚本。m_SingletonMap是负责维护所有单例的映射。当第一次访问某个单例时,它会自动向m_Container上添加一个该单例类型的Component,并保存在单例映射中,再返回这个单例。因此,我们可以看出,单例的创建完全都是自动的,你完全不需要考虑在哪里、在什么时候捆绑脚本,这是多么令人高兴得事情!
  • Awake函数中,有一句代码DontDestroyOnLoad (gameObject);,这是非常重要的,这句话意味着,当我们的场景发生变化时,单例模式将不受任何影响。除此之外,我们还要注意到,这句话也必须放到Awake函数,而不能放到Start函数中,这是由两个函数的执行顺序决定的,如果反过来,便可能会造成访问单例不成功,下面的例子里会更详细的介绍;
  • OnApplicationQuit函数中,我们将销毁单例模式。
  • 最后一点很重要:一定不要在OnDestroy函数中直接访问单例模式!这样很有可能会造成单例无法销毁。这是因为,当程序退出准备销毁单例模式时,我们在其他脚本的OnDestroy函数中再次请求访问它,这样将重新构造一个新的单例而不会被销毁(因为之前已经销毁过一次了)。如果一定要访问的话,一定要先调用IsCreatedInstance,判断该单例是否存在。
转载:http://blog.csdn.net/candycat1992/article/details/10960731

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值