在Unity游戏开发中,我们总会编写各种各样的管理器,比如UI管理器、音频管理器、资源管理器、状态机管理器等。传统开发中,绝大多数开发者会为每个Manager单独实现一套单例模式,这会导致多个核心痛点:
- 代码冗余严重:每个管理器都要重复写单例判断、实例创建、销毁逻辑,大量重复样板代码;
- 管控混乱:全局单例管理器泛滥,模块初始化、卸载、资源释放无统一入口。代码混乱,出现到处都是单例调用的混乱感,无法统一各个管理器
- 扩展性极差:新增业务模块需要重复编写单例逻辑,模块依赖、生命周期无法统一管理。
一、架构整体设计思路
这套架构采用静态类统一管理和暴露接口实例 + 统一模块生命周期 + 模块管理器管理所有模块接口的设计思想,核心分为四层结构,职责完全解耦:
- IModule 核心接口:定义所有业务模块的统一生命周期规范,强制模块实现初始化、销毁方法;
- ModuleBase 模块基类:实现接口默认逻辑,所有业务管理器统一继承该基类,无需重复实现生命周期;
- ModuleManager 全局模块管理器:架构核心,线程安全单例,负责所有模块的注册、查找、卸载、全局清空;
- CoreUtils 静态工具入口:封装所有模块的全局访问入口,开箱即用,无需手动创建模块。
整体架构运行流程:通过各模块接口实现,框架管理所有模块接口的注册,缓存和卸载,所有模块生命周期统一、实例唯一、自动管理。外部只需要通过一个统一的静态工具类调用所有的模块接口。
二、核心源码逐层解析
下面逐层拆解整套架构的源码实现,附带设计思路和关键细节讲解,全部源码可直接落地使用。
1. 生命周期规范接口 IModule
首先定义统一的模块生命周期接口,规范所有业务模块的行为,保证架构统一性,避免自定义模块逻辑混乱。
/// <summary>
/// 所有业务模块统一生命周期接口
/// </summary>
public interface IModule
{
/// <summary>
/// 模块初始化
/// </summary>
void OnInit();
/// <summary>
/// 模块销毁
/// </summary>
void OnDestroy();
}
设计亮点:通过接口强制约束,所有接入架构的模块必须实现初始化和销毁逻辑,从根源避免资源遗忘释放的问题。
2. 模块通用基类 ModuleBase
实现接口默认空逻辑,作为所有业务管理器的父类,子类可按需重写生命周期方法,简化开发。
using Managers.Interface;
/// <summary>
/// 所有业务模块基类
/// </summary>
public class ModuleBase : IModule
{
/// <summary>
/// 模块初始化,子类重写实现自定义初始化逻辑
/// </summary>
public virtual void OnInit()
{
}
/// <summary>
/// 模块销毁,子类重写实现资源释放逻辑
/// </summary>
public virtual void OnDestroy()
{
}
}
3. 核心管控中心 ModuleManager
这是整套架构的核心,采用线程安全懒加载单例设计,通过字典统一缓存所有模块,提供注册、查找、卸载、清空全套能力。
using System.Collections.Generic;
using Managers.Interface;
/// <summary>
/// 全局模块统一管理器
/// 负责所有业务模块的注册、实例化、卸载、资源释放
/// </summary>
public class ModuleManager
{
// 模块缓存字典:以接口名为Key,缓存对应模块实例
private readonly Dictionary<string, ModuleBase> _moduleMap = new();
// 单例锁对象,保证多线程安全
private static readonly object LockObj = new object();
private static ModuleManager _instance;
private static ModuleManager Instance
{
get
{
if (_instance == null)
{
lock (LockObj)
{
_instance ??= new ModuleManager();
}
}
return _instance;
}
}
/// <summary>
/// 注册模块,不存在则创建,存在则直接返回
/// </summary>
/// <typeparam name="T">模块接口类型</typeparam>
/// <typeparam name="K">模块实现类型</typeparam>
/// <returns>模块实例</returns>
private ModuleBase RegisterModule<T,K>() where T:IModule where K:ModuleBase,T,new()
{
var moduleName = typeof(T).Name;
// 避免重复注册模块
if (_moduleMap.TryGetValue(moduleName, out var existing))
return existing;
// 实例化模块并执行初始化
var module = new K();
module.OnInit();
_moduleMap[moduleName] = module;
return module;
}
/// <summary>
/// 对外统一获取模块接口,无模块则自动创建
/// </summary>
public static T GetOrCreateModule<T,K>() where T:IModule where K:ModuleBase,T,new()
{
var moduleName = typeof(T).Name;
var module = Instance.FindModule(moduleName);
return module == null ? (T)Instance.RegisterModule<T, K>() : (T)module;
}
/// <summary>
/// 卸载指定模块
/// </summary>
public static void UnRegister<T>() where T : IModule
{
Instance.UnRegisterModule<T>();
}
/// <summary>
/// 清空所有模块,全局释放资源
/// </summary>
public static void ClearAll()
{
Instance.ClearAllModules();
}
/// <summary>
/// 查找已注册模块
/// </summary>
private IModule FindModule(string moduleName)
{
_moduleMap.TryGetValue(moduleName, out var module);
return module;
}
/// <summary>
/// 内部卸载指定模块,执行销毁逻辑并移除缓存
/// </summary>
public void UnRegisterModule<T>() where T : IModule
{
string key = typeof(T).Name;
if (_moduleMap.TryGetValue(key, out var module))
{
module.OnDestroy();
_moduleMap.Remove(key);
}
}
/// <summary>
/// 清空所有模块,批量释放资源
/// </summary>
public void ClearAllModules()
{
foreach (var module in _moduleMap.Values)
{
module.OnDestroy();
}
_moduleMap.Clear();
}
}
核心关键设计解析:
- 线程安全单例:双重校验锁实现懒加载,兼顾性能与多线程安全,适配Unity多线程加载场景;
- 泛型约束解耦:通过接口T、实现类K双泛型约束,实现接口调用、实现隔离,符合面向接口编程思想;
- 自动去重注册:注册模块前优先判断缓存,保证全局模块唯一,彻底杜绝多实例问题;
- 生命周期闭环:模块创建自动执行OnInit,卸载/清空自动执行OnDestroy,资源管控闭环。
4. 全局统一入口 CoreUtils
封装静态工具类,为所有业务模块提供全局快捷访问入口,项目中任意位置直接调用,简洁优雅。
using Managers.Audio;
using Managers.FSM;
using Managers.Interface;
using Managers.Mono;
using Managers.Resources;
using Managers.UIM;
/// <summary>
/// 全局管理器快捷访问工具
/// 所有业务模块统一从此处调用
/// </summary>
public static class CoreUtils
{
public static IUIManager UIManager => GetManager<IUIManager, UIManager>();
public static IMonoManager MonoManager => GetManager<IMonoManager, MonoManager>();
public static IResourcesManager ResourcesManager => GetManager<IResourcesManager, ResourcesManager>();
public static IFsmManager FsmManager => GetManager<IFsmManager, FsmManager>();
public static IAudioManager AudioManager => GetManager<IAudioManager, AudioManager>();
/// <summary>
/// 泛型获取模块实例,自动创建
/// </summary>
private static T GetManager<T, K>() where T : class, IModule where K : ModuleBase, T, new()
{
return ModuleManager.GetOrCreateModule<T, K>();
}
}
三、架构使用教程
整套架构搭建完成后,新增任意业务模块只需三步,无需写任何单例、缓存逻辑,极简高效。
步骤1:定义模块接口
新建模块接口,继承 IModule,定义模块对外暴露的功能方法
public interface IAudioManager : IModule
{
void PlayBgMusic(string audioName);
void PlaySound(string audioName);
}
步骤2:实现模块逻辑
新建管理器类,继承 ModuleBase + 实现自定义接口,重写生命周期方法
using Managers.Interface;
public class AudioManager : ModuleBase, IAudioManager
{
// 初始化音频池、配置等
public override void OnInit()
{
base.OnInit();
Debug.Log("音频管理器初始化完成");
}
// 释放音频资源、清空缓存
public override void OnDestroy()
{
base.OnDestroy();
Debug.Log("音频管理器资源释放完成");
}
public void PlayBgMusic(string audioName)
{
// 背景音乐播放逻辑
}
public void PlaySound(string audioName)
{
// 音效播放逻辑
}
}
步骤3:注册全局入口
在 CoreUtils 中添加一行属性,即可全局访问
public static IAudioManager AudioManager => GetManager<IAudioManager, AudioManager>();
步骤4:项目中全局调用
任意脚本中直接调用,模块首次访问自动初始化,无需手动实例化
// 播放背景音乐
CoreUtils.AudioManager.PlayBgMusic("MainBg");
// 场景切换卸载音频模块
ModuleManager.UnRegister<IAudioManager>();
// 游戏退出清空所有模块资源
ModuleManager.ClearAll();
四、架构核心优势对比
1. 传统单例架构痛点
- 单例管理器泛滥,无法统一进行管理,代码极其混乱
- 每个Manager重复编写单例代码,冗余度极高;
- 单例分散,无统一销毁入口,极易内存泄漏;
- 多线程场景下单例可能出现多实例、空指针报错;
- 模块生命周期不统一,维护成本极高。
2. 本模块化架构优势
- 零冗余代码:无需重复写单例,新增模块仅需实现业务逻辑;
- 生命周期统一管控:全局统一初始化、卸载、资源释放,彻底解决内存泄漏;
- 线程安全:核心管理器双重锁单例,适配多线程资源加载;
- 接口解耦:面向接口编程,业务层只依赖接口,降低耦合,便于迭代重构;
- 懒加载优化:模块按需创建,不占用多余内存,提升项目启动速度;
- 极简调用:静态工具类统一入口,全局随处调用,开发效率拉满。
- 扩展性强:新增模块只需要按照流程实现,然后接入框架中
五、总结
这套模块化统一管理器架构是一套轻量化、高复用、低耦合的Unity通用基础架构,彻底解决了传统单例泛滥的行业痛点。架构设计简洁无冗余,不依赖第三方插件,适配2D/3D、手游/端游各类Unity项目。
核心本质就是:用中心化管理替代分散单例,用标准化和统一化生命周期规范模块行为,用泛型接口实现解耦复用,极大提升项目规范性、可维护性和扩展性。