708 字
4 分钟
游戏设计模式01——单例模式

最直白最不绕弯子的理解#

单例模式(Singleton)保证某个类在程序运行时只有一个实例,并且任何地方都能方便地访问到它。

为什么游戏开发里很常用#

游戏里有一些系统天然就应该只有一个:

  • GameManager:管理游戏状态(开始、暂停、结束)
  • AudioManager:统一播放音乐和音效
  • UIManager:打开和关闭界面
  • SaveManager:存档和读档
  • InputManager:统一处理输入

以上管理器的共同特点:

  • 全局唯一:如果有两个 AudioManager,背景音乐可能会同时播两遍。
  • 到处要用:玩家、敌人、UI 都可能要播放音效。
  • 生命周期长:通常从游戏启动一直存在到退出。

单例解决的就是“唯一”和“方便访问”这两件事,不用到处拖拽引用,也不用反复调用 FindObjectOfType 去找。

三个核心思想#

  1. 唯一实例:类自己保存“我那唯一的一个实例”。
  2. 全局访问点:对外提供一个统一入口,通常叫 Instance。
  3. 防止重复:如果出现第二个实例,要阻止或销毁它。

最简单例写法#

using UnityEngine;
public class GameManager : MonoBehaviour
{
public static GameManager Instance;
void Awake()
{
Instance = this;
}
}

使用前提#

  • 场景里只放一个挂了这个脚本的物体。
  • 在其他脚本的 Start 里访问,不要在 Awake 里访问。

未解决的点#

  • 不防止重复实例:如果有两个,后执行 Awake 的那个会覆盖前一个。
  • 不跨场景:切换场景后,这个实例会和场景一起被销毁。
  • Instance 是公开字段,其他脚本可以改它。

这种写法只适合单个场景里用的小管理器,或是小型项目使用。

懒汉单例#

第一次访问 Instance 时才去寻找或创建实例。

using UnityEngine;
public class GameManager : MonoBehaviour
{
private static GameManager _instance;
public static GameManager Instance
{
get
{
if (_instance == null)
{
// 1. 先在场景里找有没有现成的
_instance = FindObjectOfType<GameManager>();
// 2. 场景里没有,就自己创建一个
if (_instance == null)
{
GameObject go = new GameObject("GameManager");
_instance = go.AddComponent<GameManager>();
}
}
return _instance;
}
}
private void Awake()
{
// 防止场景里出现重复实例
if (_instance != null && _instance != this)
{
Destroy(gameObject);
return;
}
_instance = this;
DontDestroyOnLoad(gameObject); // 跨场景保留
}
// 测试用方法
public void Hello()
{
Debug.Log("GameManager 单例被调用");
}
}

饿汉单例#

using UnityEngine;
public class GameManager : MonoBehaviour
{
// 全局访问点:外部只能读,不能改
public static GameManager Instance { get; private set; }
private void Awake()
{
// 已经有实例了,并且不是自己 → 销毁多余的
if (Instance != null && Instance != this)
{
Destroy(gameObject);
return;
}
Instance = this; // 场景加载时就登记自己
DontDestroyOnLoad(gameObject); // 跨场景保留(不需要可以删掉)
}
// 测试用方法
public void Hello()
{
Debug.Log("GameManager 单例被调用");
}
}