这几天想把在实习里碰到的一些好的技巧写在这里,也算是对实习的一个总结。好啦,今天要讲的是在Unity里应用一种非常有名的设计模式——单例模式。
- using System;
- using System.Collections;
- using System.Collections.Generic;
- public class Singleton : MonoBehaviour
- {
- private static GameObject m_Container = null;
- private static string m_Name = "Singleton";
- private static Dictionary<string, object> m_SingletonMap = new Dictionary<string, object>();
- private static bool m_IsDestroying = false;
- public static bool IsDestroying
- {
- get { return m_IsDestroying; }
- }
- public static bool IsCreatedInstance(string Name)
- {
- if(m_Container == null)
- {
- return false;
- }
- if (m_SingletonMap!=null && m_SingletonMap.ContainsKey(Name))
- {
- return true;
- }
- return false;
- }
- public static object getInstance (string Name)
- {
- if(m_Container == null)
- {
- Debug.Log("Create Singleton.");
- m_Container = new GameObject ();
- m_Container.name = m_Name;
- m_Container.AddComponent (typeof(Singleton));
- }
- if (!m_SingletonMap.ContainsKey(Name)) {
- if(System.Type.GetType(Name) != null)
- {
- m_SingletonMap.Add(Name, m_Container.AddComponent (System.Type.GetType(Name)));
- }
- else
- {
- Debug.LogWarning("Singleton Type ERROR! (" + Name + ")");
- }
- }
- return m_SingletonMap[Name];
- }
- public void RemoveInstance(string Name)
- {
- if (m_Container != null && m_SingletonMap.ContainsKey(Name))
- {
- UnityEngine.Object.Destroy((UnityEngine.Object)(m_SingletonMap[Name]));
- m_SingletonMap.Remove(Name);
- Debug.LogWarning("Singleton REMOVE! (" + Name + ")");
- }
- }
- void Awake ()
- {
- Debug.Log("Awake Singleton.");
- DontDestroyOnLoad (gameObject);
- }
- void Start()
- {
- Debug.Log("Start Singleton.");
- }
- void Update()
- {
- }
- void OnApplicationQuit()
- {
- Debug.Log("Destroy Singleton");
- if(m_Container != null)
- {
- GameObject.Destroy(m_Container);
- m_Container = null;
- m_IsDestroying = true;
- }
- }
- }
代码大部分都比较容易看懂,下面介绍几点注意的地方:
- 当我们在其他代码里需要访问某个单例时,只需调用getInstance函数即可,参数是需要访问的脚本的名字。我们来看一下这个函数。它首先判断所有单例所在的容器m_Container是否为空(实际上就是场景中是否存在一个Gameobject,上面捆绑了一个Singleton脚本),如果为空,它将自动创建一个对象,然后以“Singleton”命名,再捆绑Singleton脚本。m_SingletonMap是负责维护所有单例的映射。当第一次访问某个单例时,它会自动向m_Container上添加一个该单例类型的Component,并保存在单例映射中,再返回这个单例。因此,我们可以看出,单例的创建完全都是自动的,你完全不需要考虑在哪里、在什么时候捆绑脚本,这是多么令人高兴得事情!
- 在Awake函数中,有一句代码DontDestroyOnLoad (gameObject);,这是非常重要的,这句话意味着,当我们的场景发生变化时,单例模式将不受任何影响。除此之外,我们还要注意到,这句话也必须放到Awake函数,而不能放到Start函数中,这是由两个函数的执行顺序决定的,如果反过来,便可能会造成访问单例不成功,下面的例子里会更详细的介绍;
- 在OnApplicationQuit函数中,我们将销毁单例模式。
- 最后一点很重要:一定不要在OnDestroy函数中直接访问单例模式!这样很有可能会造成单例无法销毁。这是因为,当程序退出准备销毁单例模式时,我们在其他脚本的OnDestroy函数中再次请求访问它,这样将重新构造一个新的单例而不会被销毁(因为之前已经销毁过一次了)。如果一定要访问的话,一定要先调用IsCreatedInstance,判断该单例是否存在。
- using UnityEngine;
- using System.Collections;
- public class SingletonSample : MonoBehaviour {
- // Use this for initialization
- void Start () {
- TestSingleton();
- }
- // Update is called once per frame
- void Update () {
- }
- private void TestSingleton() {
- LitJsonSample litjson = Singleton.getInstance("LitJsonSample") as LitJsonSample;
- litjson.DisplayFamilyList();
- }
- // void OnDestroy() {
- // LitJsonSample litjson = Singleton.getInstance("LitJsonSample") as LitJsonSample;
- //
- // litjson.DisplayFamilyList();
- // }
- }
注意,为了方便,我使用了上一篇博文里使用的Litjson的代码,并做了少许修改。下面是修改后的LitJsonSample.cs:
- using UnityEngine;
- using UnityEditor;
- using System.Collections;
- using System.Collections.Generic;
- using LitJson;
- public class FamilyInfo {
- public string name;
- public int age;
- public string tellphone;
- public string address;
- }
- public class FamilyList {
- public List<FamilyInfo> family_list;
- }
- public class LitJsonSample : MonoBehaviour {
- public FamilyList m_FamilyList = null;
- // Use this for initialization
- void Awake () {
- ReloadFamilyData();
- }
- private void ReloadFamilyData()
- {
- //AssetDatabase.ImportAsset("Localize/family.txt");
- UnityEngine.TextAsset s = Resources.Load("Localize/family") as TextAsset;
- string tmp = s.text;
- m_FamilyList = JsonMapper.ToObject<FamilyList>( tmp );
- if ( JsonMapper.HasInterpretError() )
- {
- Debug.LogWarning( JsonMapper.GetInterpretError() );
- }
- }
- public void DisplayFamilyList() {
- if (m_FamilyList == null) return;
- foreach (FamilyInfo info in m_FamilyList.family_list) {
- Debug.Log("Name:" + info.name + " Age:" + info.age + " Tel:" + info.tellphone + " Addr:" + info.address);
- }
- }
- // Update is called once per frame
- void Update () {
- }
- }
- void OnDestroy() {
- if (Singleton.IsCreatedInstance("LitJsonSample")) {
- LitJsonSample litjson = Singleton.getInstance("LitJsonSample") as LitJsonSample;
- litjson.DisplayFamilyList();
- }
- }