【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,做知识归纳整理了.本来设计模式就是个坑,各种文章也写烂了.不过,不是自己写的东西,缺少点知识的存在感.目前还没做到光看即能记住,得写.所以准备跳入设计模式这个大坑. 开篇先贡 ...
随机推荐
- 自定义类StyleSheet跨浏览器操作样式表中的规则
这是群里网友地瓜提供的一个类,不熟悉样式表对象和样式规则对象的浏览器差异的可以看看 /** * Stylesheet.js: utility methods for scripting CSS sty ...
- known good die( KGD) /确认好裸芯片,合格芯片
英文:known good die( KGD) / 中文:确认好裸芯片,合格芯片 完工的晶圆上有许多晶片存在,其单一品质有好有坏,经过老化试验后,其确知电性能良好的晶片即称为己知好晶片.不过,已知好晶 ...
- QLabel设置行间距(使用html的语法,比较巧妙)
1.设置行间距 QLabel没有设置行间距的函数,所以这种办法是行不通的.只能采用其它类似的方法来实现,例如设置行高,使用样式代码如下: <p style='line-height:18px'& ...
- cocos2dx进阶学习之CCDirector
继承关系 CCDirecotor -> CCObject, TypeInfo 处理主窗口消息,管理何时.何种方式执行场景. 经常被翻译成导演,负责管理整个游戏的进程推动和周边支持. 成员 inl ...
- IT第八天 - 类的应用、debug、项目开发模式优化
IT第八天 上午 类的应用 1.对象在实例化时是非常耗费系统资源的,因此要尽量减少new字段的使用 2.类的初始值是null,在使用未实例化的类时,很容易导致报错:NullExceptionPoint ...
- 解题报告 HDU1944 S-Nim
S-Nim Time Limit: 5000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) Problem De ...
- Loadrunner 运行场景时:missing newline in XXX.dat 错误解决
脚本参数化类型为file,在controller里运行场景的时候,报了个missing newline的错误,查了一下,将参数化的dat文件中的最后一行补上一个空行就解决啦!! 如果遇到此错误,需检查 ...
- Java线程(十):CAS
前言 在Java并发包中有这样一个包,java.util.concurrent.atomic,该包是对Java部分数据类型的原子封装,在原有数据类型的基础上,提供了原子性的操作方法,保证了线程安全.以 ...
- Aqua Data Studio 查询结果中文乱码
菜单条里[文件]-[选项]会弹出个选项对话框 然后选[一般] 在右边的[外观]以下找到[网格结果字体] 将字体类型改成宋体即可了 watermark/2/text/aHR0cDovL2Jsb2cuY ...
- 简单仿京东导航下拉菜单 javascript
<html xmlns="http://www.w3.org/1999/xhtml"><head runat="server"> ...