单例模式

🎯 单例实现

一、不继承 MonoBehaviour

1. 单例内部 没有跨场景物体

饿汉式(立即创建,推荐)

public class Manager
{
    private static Manager instance = new Manager();
    public static Manager Instance => instance;
    private Manager() {}
}

适用场景

  • 纯逻辑类:ConfigManager、SaveManager、EventBus
  • 不需要场景对象
  • 启动后总会用到(无资源浪费)

懒汉式(延迟创建)

public class Manager
{
    private static Manager instance;
    public static Manager Instance
    {
        get
        {
            if (instance == null)
                instance = new Manager();
            return instance;
        }
    }
    private Manager() {}
}

适用场景

  • 创建成本大
  • 不一定会被访问
  • 不在多线程环境中

2. 单例内部 有跨场景的物体(例如 Canvas)(推荐继承MonoBehaviour,将要跨场景的物体拖入Inspect)

方式一 — 依赖场景已有物体(不推荐)

public class Manager
{
    private static Manager instance = new Manager();
    public static Manager Instance => instance;
    private Manager() 
    {
      canvas = GameObject.Find("Canvas").transform;
      DontDestroyOnLoad(canvas);
    }
}

为什么不推荐?

  • 多场景出现多个 Canvas 会出错
  • 完全依赖场景资源,不稳定

方式二 — 动态加载 Prefab(推荐)

public class Manager
{
    private static Manager instance = new Manager();
    public static Manager Instance => instance;
    private Manager() 
    {
      canvas = Object.Instantiate(Resources.Load<GameObject>("UI/Canvas")).transform;
      DontDestroyOnLoad(canvas);
    }
}

推荐用于:

  • UIManager(如果你不用 MonoBehaviour)
  • 规避场景中重复对象
  • Prefab 已经配置完成(带 UI 参数、组件等)

二、继承 MonoBehaviour

Unity 不允许 new 出 MonoBehaviour,因此必须通过以下两种方式:

  • 场景挂载 + Awake 初始化(场景中已有该物体)
  • 自动创建(懒加载) + AddComponent

下面分两类说明:

1. 管理器物体 不跨场景

场景手动挂载 + Awake 设置

public class Manager : MonoBehaviour
{
    private static Manager instance;
    public static Manager Instance => instance;
    private void Awake();
    {
      instance = this
    }
}

适用场景

  • 跟运行场景强绑定
  • 不需要持久化
  • 例如:局部战斗管理器、场景相机控制器

自动挂载(懒加载)

public class Manager : MonoBehaviour
{
    private static Manager instance;
    public static Manager Instance
    {
        get
        {
            if (instance == null)
            {
                GameObject obj = new GameObject("Manager");
                obj.name = typeof(T).Name;
                instance = obj.AddComponent<Manager>();
            }
            return instance;
        }
    }
}

适用场景

  • 不需要 Inspector 配置
  • 完全逻辑类但需要 MonoBehaviour 功能(协程、Update)

2. 管理器物体 需要跨场景(DontDestroyOnLoad)

🟦 方式一(强烈推荐用于需要 prefab/配置的对象)

场景挂载 + Awake 检查重复(最安全、最通用)

public class Manager : MonoBehaviour
{
    private static Manager instance;
    public static Manager Instance => instance;
    private void Awake()
    {
        if (instance != null)
        {
            Destroy(gameObject);
            return;
        }
        instance = this;
        DontDestroyOnLoad(gameObject);
    }
}

👍 什么时候非常推荐使用它?

  1. 该对象在场景中已经设置好(UI、音效、参数较多等)
  2. 你需要 Inspector 配置(特别是 Canvas、AudioSource、相机)
  3. Prefab 已经有复杂结构,不可能用 new GameObject 替代
  4. 多人协作时避免出现多个管理器竞争

🟩 方式二(推荐用于纯逻辑管理器)

自动创建 + DontDestroyOnLoad(懒加载)

public class Manager : MonoBehaviour
{
    private static Manager instance;
    public static Manager Instance
    {
        get
        {
            if (instance == null)
            {
                GameObject obj = new GameObject("Manager");
                obj.name = typeof(T).Name;
                instance = obj.AddComponent<Manager>();
                DontDestroyOnLoad(obj);
            }
            return instance;
        }
    }
}

👍 什么时候非常推荐使用它?

  1. 管理器不需要 Inspector 配置
  2. 希望自动创建,使用即存在
  3. GameManager、TimerManager、PoolManager(纯逻辑)

posted @ 2025-12-09 16:21  高山仰止666  阅读(1)  评论(0)    收藏  举报