设计模式?
在软件工程中,设计模式(design pattern)是对软件设计中普遍存在(反复出现)的各种问题,所提出的解决方案。(点击查看百科介绍)
简单地说,设计模式就是前人在用生命写代码时总结下来的一些人生经验,因为实用,用得人多了,慢慢地就变成一种约定俗成的模式。
今天就说说应该时模式中最简单的模式——单例模式
单例模式(Singleton)
在我们制作游戏的过程中,经常需要保持一个唯一的实例,例如:记录着玩家各种状态和游戏进程的游戏状态管理器、管理整体游戏声音的声音管理器、存放游戏资源的缓存池等。
以上举的例子都对整个游戏周期进行着影响,如果创造了多个实例,就会造成很多问题,比如不一致的角色数据、浪费资源。
这个时候就需要使用单例模式了,因为总不能,也没必要有两个声音管理器去接管整个游戏的声音播放。
单例模式的几个特点:
- 单例类只能有一个实例
- 单例类必须自己创建自己的唯一实例
- 单例类必须给所有其他对象提供这一实例
顺带介绍一下Unity项目常用的架构方式
关于Unity的架构有如下几种常用的方式。
EmptyGO:
在Hierarchy上创建一个空的GameObject,然后挂上所有逻辑控制的脚本。使用GameObject.Find()访问对象数据。 缺点:逻辑代码散落在各处,不适合大型项目。Simple GameManager: 所有与GameObject无关的逻辑都放在一个单例中。
缺点:单一文件过于庞大。Manager Of Managers:将不同的功能单独管理,适用于中大型项目。如下:
MainManager: 主管理器、作为入口管理器
EventManager: 消息管理
AudioManager: 声音管理器
PoolManager: GameObject管理(减少动态开辟内存消耗,减少GC)。
LevelManager:关卡管理器
XXXXManager:XXXX管理器
可以发现,单例模式最适合适用于第三种,即Manager Of Managers(MoM)的架构方式。所以在之后的文章当中,我会使用这种方式来实现我的毕业设计项目。
穿插了那么多的介绍,现在可以来试一下单例模式了。
开个脚本试试
1 | using System.Collections; |
上面这种是最简单的单例模式实现,用一个static修饰符声明来记录当前的类。如果为null则表示还没被赋值,此时就将当前的类赋值给静态变量_instance。如果结果不为null,则表示目前已经创建了。
注意多线程
要注意的是:在多线程下,可能会并发执行,同时实例化了对象,就会产生单例不唯一性,这个时候就要为单例加上锁。
虽然会损失一定的性能,但是安全第一。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Test_Singleton : MonoBehaviour {
private static Test_Singleton _instance;
private static readonly Test_Singleton locker = new Test_Singleton();
public static Test_Singleton Instance
{
get
{
lock(locker)
{
if (_instance == null)
{
_instance = new Test_Singleton();
}
}
return _instance;
}
}
}
泛型版:一份代码 多次使用
如果要在每一个需要单例类里都写这么一段脚本,那实在是太累了,而且代码重复也并不是什么好事情,当你要改进某个功能的时候,你不得不要每个脚本文件都要修改一次,也够受的了。
所以这个时候,就需要泛型来救场了。
1 | using System.Collections; |
上面这个就是继承着MonoBehaviour的(传闻频繁继承MonoBehaviour会大大减慢游戏速度)简单单例类模板了,上面用<>括着的T,就是你将要使用单例的类。
使用的时候:1
2
3
4
5
6
7
8
9
10
11
12using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class XXXManager : Singleton<XXXManager>
{
void Start()
{
XXXManager.Instance();
}
}
以上就是单例类模板的使用方式。放在C#里也是通用的。
单例什么时候用,什么时候别用
- 什么时候用:有关资源对象的声音管理器、Bundle管理器,与Scene无关的全局的状态管理,无状态的通用函数、辅助函数。
- 什么时候别用:单例之间往往存在依赖(好比说主管理器必须在别的管理器之前先创建),如果不能保证单例的创建/销毁顺序能够好好维护,就别用。
(大项目能够精确维护生命周期的话,就不会使用单例,如果是Unity做手游的话,现在这个趋势一年不到就过气了,单例怎么舒服怎么来)