大家好!我是 [数擎 AI],一位热爱探索新技术的前端开发者,在这里分享前端和 Web3D、AI 技术的干货与实战经验。如果你对技术有热情,欢迎关注我的文章,我们一起成长、进步!
开发领域 :前端开发 | AI 应用 | Web3D | 元宇宙
技术栈 :JavaScript、React、ThreeJs、WebGL、Go
经验经验 :6 年+ 前端开发经验,专注于图形渲染和 AI 技术
开源项目 :AI 智简未来、晓智元宇宙、数字孪生引擎
一、什么是单例模式?
单例模式(Singleton Pattern) 是一种常用设计模式,核心思想是:
一个类只有一个实例,并提供一个全局访问点。
适用场景:
在游戏开发中,通常用于:
- 🎵 AudioManager(音频管理)
- 🎮 GameManager(游戏控制)
- 🧠 DataManager(数据持久化)
- 🖼 UIManager(界面逻辑)
- 🗺 SceneManager(场景切换)
二、Unity 与单例的"冲突"
Unity 的 MonoBehaviour
无法用传统 C# 单例写法(new
关键字)创建实例:
限制 | 说明 |
---|---|
不能使用 new |
MonoBehaviour 由 Unity 引擎托管 |
必须挂在 GameObject 上 | 无法脱离场景生命周期 |
多场景可能重复实例 | 场景加载可能重复创建组件实例 |
三、MonoBehaviour 单例实现方案
✅ 推荐写法(最通用)
csharp
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); // 场景切换不销毁
}
}
### 使用方式:
```csharp
GameManager.Instance.DoSomething();
核心要点:
- 使用 Awake() 初始化,确保运行时唯一
- 使用 DontDestroyOnLoad 保持全局可访问性
- 在有旧实例时销毁当前 GameObject(或反之)
四、泛型单例基类(进阶封装)
csharp
public class Singleton<T> : MonoBehaviour where T : MonoBehaviour
{
private static T _instance;
private static readonly object _lock = new object();
private static bool _applicationIsQuitting = false;
public static T Instance
{
get
{
if (_applicationIsQuitting)
{
Debug.LogWarning("[Singleton] 已销毁,将不再创建实例:" + typeof(T));
return null;
}
lock (_lock)
{
if (_instance == null)
{
_instance = FindObjectOfType<T>();
if (_instance == null)
{
GameObject singletonObject = new GameObject(typeof(T).Name);
_instance = singletonObject.AddComponent<T>();
DontDestroyOnLoad(singletonObject);
}
}
return _instance;
}
}
}
protected virtual void OnDestroy()
{
_applicationIsQuitting = true;
}
}
示例继承:
csharp
public class AudioManager : Singleton<AudioManager>
{
public void PlaySound(string clipName)
{
// 实现播放逻辑
}
}
五、常见陷阱 ⚠️
陷阱 | 说明 |
---|---|
❌ 多个场景创建多个实例 | 应用 DontDestroyOnLoad 并检测重复 |
❌ 在 OnDestroy 后再次访问 | 应避免退出时访问 Instance |
❌ 静态实例未初始化 | Unity 生命周期复杂,可能在未执行 Awake() 时访问 Instance |
六、非 MonoBehaviour 单例(纯逻辑类)
适用于工具类、算法类等无需挂载到场景的:
csharp
public class ConfigManager
{
private static ConfigManager _instance;
public static ConfigManager Instance => _instance ?? (_instance = new ConfigManager());
private ConfigManager()
{
// 私有构造函数
}
}
七、总结
类型 | 是否 MonoBehaviour | 是否跨场景切换 | 是否自动创建 |
---|---|---|---|
简单单例 | ✅ 是 | ✅ 是 | ❌ 否(需手动挂载) |
泛型单例 | ✅ 是 | ✅ 是 | ✅ 是(自动创建) |
纯逻辑单例 | ❌ 否 | ✅ 是 | ✅ 是(自动创建) |
✅ 建议:
- 管理 UI、音频、数据等组件时使用 MonoBehaviour 单例;
- 工具类使用普通 C# 单例;
- 多场景中必须使用 DontDestroyOnLoad 配合检测重复。
八、附加建议与思考 💡
Service Locator 模式 有时比单例更灵活,适合大项目。
避免过度使用单例,否则可能造成依赖过强、测试困难。
Unity 2021+ 支持 ScriptableObject + Addressable 实现配置数据全局共享,也是替代方案之一。
结语
在 Unity 中掌握单例模式,不仅是对设计模式的理解,更是构建 可维护、可扩展、高内聚系统架构 的关键一步。