Unity开发疑问:单例是否必须销毁?无DontDestroyOnLoad能否使用单例
不依赖
DontDestroyOnLoad实现Unity单例的方案 当然可以不依赖DontDestroyOnLoad来实现Unity中的单例模式,核心是通过场景切换时的实例生命周期控制来替代强制跨场景存活的逻辑,以下是具体实现思路:
1. 场景内专属单例
适合仅在当前场景生效的管理器(比如场景专属UI管理器),实例随场景卸载自动销毁,切换场景后新场景重新初始化:
protected virtual void Init() { // 检查当前场景内是否已有实例 if (_instance != null && _instance != this) { Destroy(gameObject); return; } _instance = this; // 移除DontDestroyOnLoad调用,对象生命周期绑定当前场景 }
使用时需要确保每个目标场景中,要么预先挂载该单例脚本的对象,要么在场景加载完成后通过代码自动创建实例。
2. 全局按需单例(无跨场景强制存活)
如果需要类似全局单例的效果,但不想用DontDestroyOnLoad,可以通过工厂类跟踪实例状态,配合场景加载回调实现实例的复用与清理:
public static class SingletonFactory { private static readonly Dictionary<Type, object> _singletonCache = new(); public static T GetOrCreate<T>() where T : MonoBehaviour { // 优先从缓存取存活实例 if (_singletonCache.TryGetValue(typeof(T), out var cachedObj) && cachedObj is T cachedInstance && cachedInstance.gameObject.activeInHierarchy) { return cachedInstance; } // 查找当前场景中的实例 T sceneInstance = Object.FindObjectOfType<T>(); if (sceneInstance != null) { _singletonCache[typeof(T)] = sceneInstance; return sceneInstance; } // 无实例则创建新对象 var singletonObj = new GameObject(typeof(T).Name); T newInstance = singletonObj.AddComponent<T>(); _singletonCache[typeof(T)] = newInstance; return newInstance; } // 场景卸载时清理无效缓存 public static void ClearInvalidCache() { foreach (var pair in _singletonCache.ToList()) { if (pair.Value is MonoBehaviour mono && !mono.gameObject.scene.isLoaded) { _singletonCache.Remove(pair.Key); } } } }
使用时在场景加载回调中调用清理逻辑:
private void OnEnable() { SceneManager.sceneLoaded += (_, __) => SingletonFactory.ClearInvalidCache(); }
3. 关键注意事项
- 若不使用
DontDestroyOnLoad,必须保证每个需要单例的场景中存在实例或有创建逻辑,否则会出现空引用报错。 - 如需跨场景传递数据,可将数据抽离到非MonoBehaviour的静态类中,让管理器仅负责逻辑处理,数据独立于场景生命周期。
- 避免在
Awake中做复杂初始化,尽量将逻辑延迟到Start或场景加载完成回调,防止场景切换时的实例冲突。
内容的提问来源于stack exchange,提问作者khjtoy
相关产品推荐
相关产品推荐

