【Unity技巧】使用单例模式Singleton
这几天想把在实习里碰到的一些好的技巧写在这里,也算是对实习的一个总结。好啦,今天要讲的是在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 () { }
}
OnDestroy函数里访问单例模式,我们把SingletonSample.cs脚本里注释掉得OnDestroy函数解开注释,然后再次运行。结果如下:
void OnDestroy() {
if (Singleton.IsCreatedInstance("LitJsonSample")) {
LitJsonSample litjson = Singleton.getInstance("LitJsonSample") as LitJsonSample;
litjson.DisplayFamilyList();
}
}结束语
Awake函数里调用了
ReloadFamilyData来初始化数据,细心的童鞋可以发现,在上一篇博文里,初始化数据是在Start函数里完成的。之所以要把它挪到Awake函数里,是为了在我们访问单例时,可以保证数据一定已经被初始化了,因此把初始化函数放到Awake函数里,访问单例的代码放在Start函数里。同样的原因,在Singleton.cs的脚本里
DontDestroyOnLoad (gameObject);需要放在Awake函数,而不是Start函数里。
【Unity技巧】使用单例模式Singleton的更多相关文章
- 设计模式之——单例模式(Singleton)的常见应用场景
单例模式(Singleton)也叫单态模式,是设计模式中最为简单的一种模式,甚至有些模式大师都不称其为模式,称其为一种实现技巧,因为设计模式讲究对象之间的关系的抽象,而单例模式只有自己一个对象,也因此 ...
- 设计模式之——单例模式(Singleton)的常见应用场景(转):
单例模式(Singleton)也叫单态模式,是设计模式中最为简单的一种模式,甚至有些模式大师都不称其为模式,称其为一种实现技巧,因为设计模式讲究对象之间的关系的抽象,而单例模式只有自己一个对象,也因此 ...
- Java设计模式之单例模式 - Singleton
用来创建独一无二的,是能有一个实例的对象的入场券.告诉你一个好消息,单例模式的类图可以说是所有模式的类图中最简单的,事实上,它的类图上只有一个类!但是,可不要兴奋过头,尽管从类设计的视角来说很简单,但 ...
- (转)单例模式(Singleton)的常见应用场景
转自:http://blog.csdn.net/likika2012/article/details/11483167 单例模式(Singleton)也叫单态模式,是设计模式中最为简单的一种模式,甚至 ...
- 设计模式之单例模式——Singleton
设计模式之单例模式--Singleton 设计意图: 保证类仅有一个实例,并且可以供应用程序全局使用.为了保证这一点,就需要这个类自己创建自己的对象,并且对外有 ...
- 【白话设计模式四】单例模式(Singleton)
转自:https://my.oschina.net/xianggao/blog/616385 0 系列目录 白话设计模式 工厂模式 单例模式 [白话设计模式一]简单工厂模式(Simple Factor ...
- ooad单例模式-Singleton
单例模式Singleton 主要作用是保证在Java应用程序中,一个类Class只有一个实例存在. 比如建立目录 ...
- iOS单例模式(Singleton)写法简析
单例模式的意思就是只有一个实例.单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例.这个类称为单例类. 1.单例模式的要点: 显然单例模式的要点有三个:一是某个类只能有一个实例: ...
- 浅谈设计模式--单例模式(Singleton Pattern)
题外话:好久没写blog,做知识归纳整理了.本来设计模式就是个坑,各种文章也写烂了.不过,不是自己写的东西,缺少点知识的存在感.目前还没做到光看即能记住,得写.所以准备跳入设计模式这个大坑. 开篇先贡 ...
随机推荐
- QT基本数据类型(以前没见过qintptr和qlonglong)
QT的基本数据类型 qint8:signed char 有符号8比特数据 qint16:signed short 16位数据类型 qint32:signed int. 32位有符号数据类型 qint6 ...
- 转: c++继承中的内存布局
英文原文: http://www.openrce.org/articles/files/jangrayhood.pdf 翻译: http://blog.csdn.net/jiangyi711/arti ...
- MFC自绘控件学习总结第二贴---转
首先感谢大家对第一帖的支持,应一些网友烈要求下面我在关于上一贴的一些补充和说明(老鸟可以无视)这一贴是实战+理论不知道第一帖的先看第一帖:http://topic.csdn.net/u/2011071 ...
- Linux下which、whereis、locate、find 区别
我们经常在linux要查找某个文件或命令,但不知道放在哪里了,可以使用下面的一些命令来搜索.which 查看可执行文件的位置 whereis 查看文件的位置 locate 配合 ...
- datetime方法
DateTime dt = DateTime.Now; dt.ToString();//2005-11-5 13:21:25 dt.ToFileTime().ToString(); dt.ToFile ...
- linux 修改IP, DNS 命令
linux 修改IP, DNS 命令 http://www.cnblogs.com/fighter/archive/2010/03/04/1678007.html 修改DNS [root@localh ...
- linux 工具: Top
linux TOP命令各参数详解[转载] http://www.cnblogs.com/sbaicl/articles/2752068.html
- c++,虚函数
1.在声明函数时,在最前加上virtual,则该函数就是函虚数,基类的虚函数被派生类继承后仍是虚函数.2.派生类中可以重写基类的虚函数.3.用指针访问重写的虚函数时,被访问的虚函数是指针指向的对象所属 ...
- Android 中 ListView Adapter getView 被多次调用问题 解决方法
执行多次原因是因为每显示一个VIew,它都去测量view的高度,执行measure方法,导致getView执行多次. 解决方法是将 ListView 的 layout_width 设置为 fill_p ...
- Linux下安装jekyll
折腾了大半天,终于搞定了,这可得记下来了. 我的Linux版本:CentOS 6.5 主要的安装顺序还是官网上的说明:http://jekyllrb.com/docs/installation/,所以 ...