啊啊啊啊啊又到了设计模式时间,前面讲delegate和event时就提到了observer pattern观察者模式,这回要讲的是Singletons单例模式
Singleton Design Pattern
- The singleton is a software engineer concept where u have global access to a class, and that class only exists once. [Example: Manager Classes, like UI Manager, Spawn Manager]
Use singleton to avoid the use ofGetComponent
, because you’re already going to have a reference to the Manager and a direct communication path.
在前面,我们通常是先通过查找gameManger在的实体然后获取他的脚本,从而使用gameManger的东西。但是gameManger在整个游戏中只会有一个,我们可以用单例来做这件事情。
下面是单例的代码示例
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace ChapterSingleton{
public class GameManager : MonoBehaviour
{
// a private variable to declare the instance of the class -- has to be static
private static GameManager _instance;
// a static, public property to be able to access and get the private variable _instance
public static GameManager Instance
{
get{
// return the instance
// handle error
if(_instance == null){
Debug.LogError("game manager is null");
}
return _instance;
}
// only get because we don't want anyone to be able to change it
} // for other classes to communicate with
//assign the instance
private void Awake() {
_instance = this;
}
public void DisplayName(){
Debug.Log("mzk");
}
}
}
其他物体这样获取单例中的信息
namespace ChapterSingleton{
public class Player : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
GameManager.Instance.DisplayName();
}
// Update is called once per frame
void Update()
{
}
}
}
(说真的 单例我看得晕乎乎的 可能前面把property相关看得太简单了?(是需要反复观看的地方
然后这边有涉及到函数Awake()
,这个是在Start()
之前运行的
Singleton UI Manager
这节是创一个UI Manager, 模仿前面,创一个private变量再创一个public static property
注意:单例之间是可以互相调用的,但是不能调用非单例
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace ChapterSingleton{
public class UIManager : MonoBehaviour
{
private static UIManager _instance;
public static UIManager Instance
{
get{
if(_instance == null){
Debug.LogError("ui manager is null");
}
return _instance;
}
}
private void Awake() {
_instance = this;
}
public void UpdateScore(int score){
Debug.Log("score: " + score);
Debug.Log("Notifying the game manager");
// manager class can talk to each other
// but the manager class should never talk to an obj that's not singleton
GameManager.Instance.DisplayName();
}
}
}
Lazy Instantiation(延迟初始化)
在前面我们知道,我们必须在程序运行前先声明好这些manager。像在前面我们会先做个判断(if(_instance == null)
)这些实例是否存在然后才能返回结果。在前面我们就只是扔个报错提示说这个实例不存在啊,我们也可以如果不存在的话就创造一个实例。
比如前面的那个UI Manager我们可以改成这样,当判断为true时就生成一个装这个脚本的gameobj,然后当我们后面比如说按个空格啊获取这个UIManager的信息时才初始化这个UIManager,这个就是简单的延迟初始化
public static UIManager Instance
{
get{
if(_instance == null){
// Debug.LogError("ui manager is null");
GameObject go = new GameObject("UI Manager");
go.AddComponent<UIManager>();
}
return _instance;
}
}
详情的话还是要看链接的文档,配合线程相关食用更香
Downfall of Lazy Instantiation
这边讲了一下lazy instantiation的弊端吧。就假设我们的SpawnManager要进行怪物的生成,那就需要接入怪物的预制件。在一般情况下我们就是在inspector上面直接把预制件拖进去什么的。但是由于我们的SpawnManager是在程序运行过程中生成的,它不会自动给你接入这些预制件,就那个地方会显示None。这就需要我们写个脚本让它能够动态的引入这个预制件。这是一个需要注意的坑。
MonoSingleton
- MonoSingleton: a ability to turn any manager class easily into a singleton and use it to act as a singleton in initialized values.
从前面的部分我们可以发现,这些Manager Class的格式都是一样的,都是先弄一个private variable去声明这个实例,然后做一个public static property,最后在Awake()函数里面分配这个实例。Manager Class一多的话,就可以意识到我们一直在写一个重复的部分,一个两个还好,但万一我们要做十几个二十几个Manager Class呢。这时候我们就要采用MonoSingleton的方法,它就像是一个模板,或者我们叫它所有单例的通用模板(generic template)。MonoSingleton是一个抽象类,忘记抽象类是什么的话看这里。
看看代码(啊好头疼怎么那么难啊
这里用了通用类型T,在这边T是继承MonoSingleton的类。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace ChapterSingleton{
public class MonoSingleton<T> : MonoBehaviour where T : MonoSingleton<T>
{
private static T _instance;
public static T Instance
{
get{
if(_instance == null){
Debug.LogError(typeof(T).ToString() + " is NULL");
}
return _instance;
}
}
private void Awake() {
_instance = this as T;
}
}
}
那怎么把一个类变成单例呢 嗯就这样
public class ShopManager : MonoSingleton<ShopManager>
{
}
我们可以再拓展一下 比如说有些通用函数我们需要在子类重写的 我们也可以先在MonoSingleton种弄个虚函数 像这样
namespace ChapterSingleton{
public class MonoSingleton<T> : MonoBehaviour where T : MonoSingleton<T>
{
private static T _instance;
public static T Instance
{
get{
if(_instance == null){
Debug.LogError(typeof(T).ToString() + " is NULL");
}
return _instance;
}
}
private void Awake() {
_instance = this as T;
Init();
}
public virtual void Init()
{
//optional to override
}
}
}
我们在创建单例时就可以直接重写了
namespace ChapterSingleton
{
public class ShopManager : MonoSingleton<ShopManager>
{
public override void Init()
{
base.Init();
Debug.Log("Shop sys initialized");
}
}
}
这章是需要反复观看的