【Unity3D基础教程】给初学者看的Unity教程(七):在Unity中构建健壮的单例模式(Singleton)
作者:王选易,出处:http://www.cnblogs.com/neverdie/ 欢迎转载,也请保留这段声明。如果你喜欢这篇文章,请点推荐。谢谢!

该博客中的代码均出自我的开源项目 : 迷你微信
为什么需要单例模式
游戏中需要单例有以下几个原因:
- 我们需要在游戏开始前和结束前做一些操作,比如网络的链接和断开,资源的加载和卸载,我们一般会把这部分逻辑放在单例里。
- 单例可以控制初始化和销毁顺序,而静态变量和场景中的GameObject都无法控制自己的创建和销毁顺序,这样就会造成很多潜在的问题。
- Unity3D的GameObject需要动态创建。而不是固定在场景里,我们需要使用单例来创建GameObject。
- Unity3D的场景中的各个GameObject需要从单例中存取数据。
单例的设计原则
在设计单例的时候,我并不建议采取延迟初始化的方案,正如云风所说:
对于单件的处理,采用静态对象和惰性初始化的方案,简直就是 C++ 程序员的陋习。Double Checked Locking is broken,相信很多人都读过了。过于依赖语法糖,通常就会造成这种结果。其实让程序有明显的初始化和退出阶段,是很容易被规划出来的。把单件(singleton) 的处理放在正确的时机,以正确的次序来处理并非难事。
我们应该在程序某处明确定义单例是否被初始化,在初始化执行完毕后再执行正常的游戏逻辑
- 尽量避免多线程创建单例带来的复杂性
- 在某处定义了一定的初始化顺序后,可以在游戏结束的时候按照相反的顺序销毁这些单例
设计单例的基类
在Unity中,我们需要一个基类来为所有单例的操作提供统一的接口,同时,我们还要让所有单例继承MonoBehaviour,只有这样才能让单例自由使用协程这一特性。
基类设计如下,代码链接
using System;
using UnityEngine;
namespace MiniWeChat
{
[RequireComponent(typeof(GameRoot))]
public class Singleton<T> : MonoBehaviour where T : Singleton<T>
{
private static T _instance;
public static T GetInstance()
{
return _instance;
}
public void SetInstance(T t)
{
if (_instance == null)
{
_instance = t;
}
}
public virtual void Init()
{
return;
}
public virtual void Release()
{
return;
}
}
}
设计单例的管理类
除了设计基类之外, 还需要设计一个让所有基类初始化和销毁的类,我们把这个类叫做GameRoot,并且把它绑定在一个名为GameRoot的GameObject上,并且把这个GameObject放在游戏进入的Main场景中。
GameRoot类设计如下,代码链接
namespace MiniWeChat
{
public class GameRoot : MonoBehaviour
{
private static GameObject _rootObj;
private static List<Action> _singletonReleaseList = new List<Action>();
public void Awake()
{
_rootObj = gameObject;
GameObject.DontDestroyOnLoad(_rootObj);
StartCoroutine(InitSingletons());
}
/// <summary>
/// 在这里进行所有单例的销毁
/// </summary>
public void OnApplicationQuit()
{
for (int i = _singletonReleaseList.Count - 1; i >= 0; i--)
{
_singletonReleaseList[i]();
}
}
/// <summary>
/// 在这里进行所有单例的初始化
/// </summary>
/// <returns></returns>
private IEnumerator InitSingletons()
{
yield return null;
// Init Singletons
}
private static void AddSingleton<T>() where T : Singleton<T>
{
if (_rootObj.GetComponent<T>() == null)
{
T t = _rootObj.AddComponent<T>();
t.SetInstance(t);
t.Init();
_singletonReleaseList.Add(delegate()
{
t.Release();
});
}
}
public static T GetSingleton<T>() where T : Singleton<T>
{
T t = _rootObj.GetComponent<T>();
if (t == null)
{
AddSingleton<T>();
}
return t;
}
}
}
如何拓展新的单例
有了以上两个类之后,当我们需要新创建一个类的时候,就可以继承Singleton<T>来创建新的单例,重写Init和Release方法,同时在GameRoot的InitSingleton方法的适当顺序执行AddSingleton<T>方法即可。具体的使用可以参考该类代码链接
【Unity3D基础教程】给初学者看的Unity教程(七):在Unity中构建健壮的单例模式(Singleton)的更多相关文章
- 【Unity3D基础教程】给初学者看的Unity教程(四):通过制作Flappy Bird了解Native 2D中的RigidBody2D和Collider2D
作者:王选易,出处:http://www.cnblogs.com/neverdie/ 欢迎转载,也请保留这段声明.如果你喜欢这篇文章,请点[推荐].谢谢! 引子 在第一篇文章[Unity3D基础教程] ...
- 【Unity3D基础教程】给初学者看的Unity教程(一):GameObject,Compoent,Time,Input,Physics
作者:王选易,出处:http://www.cnblogs.com/neverdie/ 欢迎转载,也请保留这段声明.如果你喜欢这篇文章,请点推荐.谢谢! Unity3D重要模块的类图 最近刚刚完成了一 ...
- Unity3D基础知识梳理
这段时间在做Unity的项目,这差不多是我的第一次实战啊~然后公司来了实习的童鞋要学Unity,但是我一向不靠谱啊,所以只能帮他们稍微梳理下基础的东西了啊,唉~学长只能帮你们到这里了~顺便就把自己这两 ...
- Unity教程之再谈Unity中的优化技术
这是从 Unity教程之再谈Unity中的优化技术 这篇文章里提取出来的一部分,这篇文章让我学到了挺多可能我应该知道却还没知道的知识,写的挺好的 优化几何体 这一步主要是为了针对性能瓶颈中的”顶点 ...
- i3D的一篇Unity教程中的笔记
原地址:http://blog.sina.com.cn/s/blog_72b936d80100wwej.html 以下是i3D的一篇Unity教程中的笔记. i3D的这篇教程是[i3D.Next-Ge ...
- Nullsoft教程 NSIS初学者图文教程一
Nullsoft教程 NSIS初学者图文教程一 来源:互联网 作者:佚名 时间:03-29 00:34:33 [大 中 小] Nullsoft Installation System(nsis) 是一 ...
- ios游戏开发 Sprite Kit教程:初学者 1
注:本文译自Sprite Kit Tutorial for Beginners 目录 Sprite Kit的优点和缺点 Sprite Kit vs Cocos2D-iPhone vs Cocos2D- ...
- Sprite Kit教程:初学者
作者:Ray Wenderlich 原文出处:点击打开链接 http://www.raywenderlich.com/42699/spritekit-tutorial-for-beginners 转自 ...
- 专门针对初学者的Node.js教程
转载原文:http://www.csdn.net/article/2013-08-28/2816731-absolute-beginners-guide-to-nodejs Node.js的教程并不缺 ...
随机推荐
- RabbitMQ、Redis
进程QUEUE,可以是父进程与子进程间进行交互,也可以是同属于一个父进程的子进程间的交互:如果要实现进程A与进程B之间的通信,就需要借助一个中间进程了,我们习惯称为消息队列. QQ无法直接与WORD通 ...
- GSM Hacking:使用BladeRF、树莓派、YatesBTS搭建便携式GSM基站
每次看到黑客在网上发布的那些GSM技术相关文章我都十分惊讶.然而在没有Software Defined Radios (SDRs)之前,玩GSM并不便宜,除此之外想要好好玩你得下大功夫. 拓展阅读 G ...
- php大力力 [017节]来来来,庆祝一下🎁大力力第一个数据库录入程序完成!
庆祝一下
- Logger日志打印普通方法
using System; using System.IO; using System.Text; namespace Core { public class LogHelper { private ...
- Inno Setup脚本语法大全
Inno Setup脚本语法大全 ResourceShare Bruce 11个月前 (10-28) 6136浏览 0评论 Inno Setup 是什么?Inno Setup 是一个免费的 Win ...
- 重学STM32---(六)DAC+DMA+TIM
这两天复习了DAC,DMA再加上把基本定时器TIM6和TIM7看了一下,打算写一个综合点的程序,,,就在网上找了一些关于DAC,DMA和定时器相关的程序,最终打算写了输出正弦波的程序... 由于没有示 ...
- 怎么在手机浏览器上访问电脑本地的文件,局域网内,自建WiFi也可以
首先,电脑要有Mysql+Apache+PHP环境,我直接用Wampsever,开启环境后手机和电脑要再同一个局域网内,然后电脑上打开win+R,输入cmd,再输入ipconfig,就可以看着这台的电 ...
- iptables导致数据包过多时连接失败
前几天做服务器压力测试,本地10个线程不停的去向服务器建立连接,然后请求数据,然后连接再关闭,程序每运行几万次之后就会发现客户端程序没办法connect服务器,connect超时. 一开始怀疑是自己服 ...
- android studio 中怎么使用adb无线调试
之前再eclipse下进行安卓开发,但谷歌却抛弃了eclipse而力挺android studio开发环境,没办法只好跟着走. 在eclipse下开发时调试用adb无线方式特别方便,但是在androi ...
- debug进入线程中
今天debug调试程序时,怎么也进入不了线程中,f5直接进源码,f6直接跳过了,后来把断点打在线程的Run()方法里面,按f8(可能要多按几次)就可以了.