单例模式,正如它的名字一样,它的目标是确保自身的单一性,即在游戏运行时,始终只存在该类的一个实例。一旦出现第二个实例,则需要被立即摧毁。
单例模式的优点:
- 可以全局访问。用单例模式创建的资源可以被全局访问。
- 控制并发:该模式可用于限制对共享资源的并发访问。
单例模式的缺点:
- 难以进行单元测试。单例模式通常作为管理类来管理游戏中的其他组件,过度使用,会使各个管理类之间相互依赖,使得难以对某一模块进行单独测试。
- 会导致懒惰和不好的编程习惯。由于单例模式可以轻松的在任何地方访问所有内容,它提供的简单性可能会使我们不愿意使用更加复杂的方法和结构。
在选择设计模式时要考虑它是否可维护,是否可拓展,是否可测试。
在进行游戏开发过程中,最常见的使用单例模式的就是GameManager类。它可以具有初始化游戏设置,与后端通信,记录和保存玩家进度等功能。具体使用情况因人而异。一些人使用它来管理游戏状态或作为核心游戏系统的全局可访问的前端界面。GameManager类的生命周期应该贯穿整个游戏运行过程。
首先,我们在Unity中写一个单例模式的父类,它将具有单例模式的基本特点——单一性。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace Chapter.Singleton
{
public class Singleton<T> : MonoBehaviour where T : Component
{
private static T _instance;
public static T Instance
{
get
{
//调用时确保实例存在
if(_instance == null)
{
_instance = FindObjectOfType<T>();
if(_instance == null)
{
GameObject obj = new GameObject();
obj.name = typeof(T).Name;
_instance = obj.AddComponent<T>();
}
}
return _instance;
}
}
public virtual void Awake()
{
if(_instance == null)
{
_instance = this as T;
DontDestroyOnLoad(gameObject); //GameManager在场景切换时不会被销毁
}
else
{
Destroy(gameObject);
}
}
}
}
之后创建一个GameManager的类,继承刚才写的父类,为了方便观察GameManager类的生命周期,我们在该类中定义了两个变量来记录开始和结束的时间,并在Unity创建一个新的场景,用来切换场景。
在GameManager类中输入以下内容
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
using System;
namespace Chapter.Singleton
{
public class GamManager : Singleton<GamManager>
{
//记录GameManager生成时间
private DateTime _sessionStartTime;
//记录GameManager结束时间
private DateTime _sessionEndTime;
// Start is called before the first frame update
void Start()
{
_sessionStartTime = DateTime.Now;
Debug.Log("Game Session Start :" + _sessionStartTime);
}
private void OnApplicationQuit()
{
_sessionEndTime = DateTime.Now;
Debug.Log("Game Session End :" + _sessionEndTime);
}
private void OnGUI()
{
if(GUILayout.Button("Next Scene")
&& SceneManager.GetActiveScene().buildIndex < 2)
{
SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex + 1);
}
}
}
}
把脚本挂载到空物体上,运行游戏,点击NextScene按钮,场景切换,但GameManger类并没由被摧毁,一直到游戏退出,该类生命周期才结束。
之前我们提到过单例模式具有可以全局访问的优点,接下来我们来试验一下这个特性。删除之前添加的挂载GameManger的物体。新建一个名为Test的脚本,用来测试。
在GameManger中加入下面函数,
public void Test()
{
Debug.Log("11");
}
Test脚本中输入以下内容
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace Chapter.Singleton
{
public class Test : MonoBehaviour
{
private void OnGUI()
{
if(GUILayout.Button("TESE"))
{
GamManager.Instance.Test();
}
}
}
}
运行游戏点击TEST按钮,发现Hierarchy窗口自动创建了GameManager物体,并且控制台输出了“11”的字符。
(学术水平有限,只是想记录学习过的知识,如果有错请私信指正)