【Unity笔记】Unity编辑器扩展:打造一个可切换Config.assets的顶部菜单插件
引言
在Unity项目中,Config.assets
文件通常存储游戏配置(如难度参数、关卡数据、UI设置),通过编辑器扩展可以快速切换不同环境(开发/测试/生产)的配置,提升开发效率。顶部菜单插件使用MenuItem
创建主菜单项,结合EditorWindow
提供可视化界面,支持一键切换Config文件、自动保存/加载和版本管理。本教程基于Unity 2022.3 LTS+,使用SerializedObject
和AssetDatabase
实现完整功能,支持多Config文件管理和环境预设。
核心功能:
- 顶部菜单:Tools > Config Manager 快速访问
- 可视化切换:按钮/下拉选择不同Config.assets
- 自动保存:修改后一键Apply/Save
- 环境预设:Dev/Test/Prod配置模板
- 版本控制:Config历史记录和回滚
适用场景:多人协作项目、不同平台配置、多环境部署测试。
完整实现代码
1. ConfigManager主菜单(顶部菜单入口)
using UnityEngine;
using UnityEditor;
public static class ConfigManagerMenu
{
[MenuItem("Tools/Config Manager/打开配置管理器", false, 10)]
static void OpenConfigManager()
{
ConfigManagerWindow.ShowWindow();
}
[MenuItem("Tools/Config Manager/切换到开发配置", false, 11)]
static void SwitchToDevConfig()
{
ConfigManager.Instance.SwitchConfig("Assets/Configs/DevConfig.asset");
}
[MenuItem("Tools/Config Manager/切换到测试配置", false, 12)]
static void SwitchToTestConfig()
{
ConfigManager.Instance.SwitchConfig("Assets/Configs/TestConfig.asset");
}
[MenuItem("Tools/Config Manager/切换到生产配置", false, 13)]
static void SwitchToProdConfig()
{
ConfigManager.Instance.SwitchConfig("Assets/Configs/ProdConfig.asset");
}
[MenuItem("Tools/Config Manager/保存当前配置", false, 20)]
static void SaveCurrentConfig()
{
ConfigManager.Instance.SaveCurrentConfig();
}
[MenuItem("Tools/Config Manager/创建新配置", false, 30)]
static void CreateNewConfig()
{
ConfigManager.Instance.CreateNewConfig();
}
}
2. Config数据结构定义
首先定义Config的ScriptableObject基类,支持序列化字段。
using UnityEngine;
using System;
[CreateAssetMenu(fileName = "NewConfig", menuName = "Config/Base Config")]
public class BaseConfig : ScriptableObject
{
[Header("基础设置")]
public string configName = "Default Config";
public string version = "1.0.0";
public ConfigEnvironment environment = ConfigEnvironment.Development;
[Header("游戏参数")]
public float gameSpeed = 1f;
public int maxPlayers = 4;
public bool enableDebugMode = true;
[Header("UI设置")]
public Color primaryColor = Color.white;
public float uiScale = 1f;
[Header("性能设置")]
public int targetFrameRate = 60;
public bool useHighQualityGraphics = true;
public enum ConfigEnvironment
{
Development,
Testing,
Production
}
// 标记配置已修改
[HideInInspector] public bool isDirty;
public virtual void OnConfigChanged()
{
isDirty = true;
Debug.Log($"配置[{configName}]已修改,环境:{environment}");
}
// 环境特定逻辑
public virtual void ApplyEnvironmentSettings()
{
switch (environment)
{
case ConfigEnvironment.Development:
enableDebugMode = true;
targetFrameRate = 30; // 开发时降低帧率
break;
case ConfigEnvironment.Production:
enableDebugMode = false;
targetFrameRate = 60;
useHighQualityGraphics = true;
break;
}
}
}
3. Config管理单例(核心控制器)
using UnityEngine;
using UnityEditor;
using System.Collections.Generic;
using System.IO;
public class ConfigManager : ScriptableObject
{
private static ConfigManager _instance;
public static ConfigManager Instance
{
get
{
if (_instance == null)
{
string[] guids = AssetDatabase.FindAssets("t:ConfigManager");
if (guids.Length > 0)
{
_instance = AssetDatabase.LoadAssetAtPath<BaseConfig>(AssetDatabase.GUIDToAssetPath(guids[0])) as ConfigManager;
}
else
{
_instance = CreateInstance<ConfigManager>();
}
}
return _instance;
}
}
[Header("配置管理")]
public BaseConfig currentConfig;
public List<BaseConfig> availableConfigs = new List<BaseConfig>();
public string configPath = "Assets/Configs/";
[Header("备份设置")]
public bool autoBackup = true;
public int maxBackupCount = 5;
void OnEnable()
{
RefreshAvailableConfigs();
}
public void RefreshAvailableConfigs()
{
availableConfigs.Clear();
string[] guids = AssetDatabase.FindAssets("t:BaseConfig");
foreach (string guid in guids)
{
string path = AssetDatabase.GUIDToAssetPath(guid);
BaseConfig config = AssetDatabase.LoadAssetAtPath<BaseConfig>(path);
if (config != null)
{
availableConfigs.Add(config);
}
}
}
public void SwitchConfig(string configPath)
{
if (currentConfig != null)
{
SaveCurrentConfig(); // 保存当前配置
}
currentConfig = AssetDatabase.LoadAssetAtPath<BaseConfig>(configPath);
if (currentConfig != null)
{
currentConfig.ApplyEnvironmentSettings();
EditorUtility.SetDirty(currentConfig);
AssetDatabase.SaveAssets();
// 通知场景中依赖配置的对象
NotifyConfigChanged();
Debug.Log($"已切换到配置:{currentConfig.configName}");
}
else
{
Debug.LogError($"配置文件不存在:{configPath}");
}
}
public void SaveCurrentConfig()
{
if (currentConfig != null)
{
if (autoBackup)
{
CreateBackup(currentConfig);
}
EditorUtility.SetDirty(currentConfig);
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
Debug.Log($"已保存配置:{currentConfig.configName}");
}
}
private void CreateBackup(BaseConfig config)
{
string backupDir = "Assets/Configs/Backups/";
if (!Directory.Exists(backupDir))
{
Directory.CreateDirectory(backupDir);
}
string timestamp = System.DateTime.Now.ToString("yyyyMMdd_HHmmss");
string backupPath = $"{backupDir}{config.name}_backup_{timestamp}.asset";
BaseConfig backup = Instantiate(config);
backup.name = $"{config.name}_backup_{timestamp}";
AssetDatabase.CreateAsset(backup, backupPath);
// 清理旧备份
CleanupOldBackups(backupDir);
}
private void CleanupOldBackups(string backupDir)
{
string[] backups = Directory.GetFiles(backupDir, "*.asset");
if (backups.Length > maxBackupCount)
{
System.Array.Sort(backups, (a, b) => File.GetCreationTime(b).CompareTo(File.GetCreationTime(a)));
for (int i = maxBackupCount; i < backups.Length; i++)
{
AssetDatabase.DeleteAsset(backups[i]);
}
}
}
public void CreateNewConfig()
{
string newPath = EditorUtility.SaveFilePanelInProject(
"创建新配置", "NewConfig", "asset", "选择保存位置");
if (!string.IsNullOrEmpty(newPath))
{
BaseConfig newConfig = CreateInstance<BaseConfig>();
newConfig.name = Path.GetFileNameWithoutExtension(newPath);
newConfig.ApplyEnvironmentSettings();
AssetDatabase.CreateAsset(newConfig, newPath);
AssetDatabase.SaveAssets();
RefreshAvailableConfigs();
SwitchConfig(newPath);
}
}
private void NotifyConfigChanged()
{
// 通知所有监听配置的对象
var allConfigs = FindObjectsOfType<MonoBehaviour>();
foreach (var obj in allConfigs)
{
if (obj is IConfigListener listener)
{
listener.OnConfigChanged(currentConfig);
}
}
}
}
4. 可视化编辑器窗口
using UnityEngine;
using UnityEditor;
using System.Linq;
public class ConfigManagerWindow : EditorWindow
{
private BaseConfig selectedConfig;
private Vector2 scrollPos;
private bool showAdvanced = false;
[MenuItem("Tools/Config Manager/打开配置管理器")]
public static void ShowWindow()
{
GetWindow<ConfigManagerWindow>("Config Manager");
}
void OnEnable()
{
selectedConfig = ConfigManager.Instance?.currentConfig;
RefreshConfigs();
}
void OnGUI()
{
GUILayout.Label("配置管理器", EditorStyles.boldLabel);
// 配置列表
EditorGUILayout.BeginVertical("box");
GUILayout.Label("可用配置", EditorStyles.miniBoldLabel);
ConfigManager.Instance.RefreshAvailableConfigs();
foreach (var config in ConfigManager.Instance.availableConfigs)
{
bool isSelected = config == selectedConfig;
EditorGUILayout.BeginHorizontal();
if (GUILayout.Toggle(isSelected, config.configName, EditorStyles.radioButton))
{
if (!isSelected)
{
SelectConfig(config);
}
}
if (GUILayout.Button("切换", GUILayout.Width(60)))
{
SelectConfig(config);
}
if (GUILayout.Button("备份", GUILayout.Width(60)))
{
CreateBackup(config);
}
EditorGUILayout.EndHorizontal();
}
EditorGUILayout.EndVertical();
// 当前配置编辑
if (selectedConfig != null)
{
EditorGUILayout.Space();
EditorGUILayout.LabelField("当前配置", EditorStyles.boldLabel);
SerializedObject serializedConfig = new SerializedObject(selectedConfig);
SerializedProperty iterator = serializedConfig.GetIterator();
bool enterChildren = true;
scrollPos = EditorGUILayout.BeginScrollView(scrollPos);
while (iterator.NextVisible(enterChildren))
{
if (iterator.propertyType == SerializedPropertyType.ManagedReference)
{
EditorGUILayout.PropertyField(iterator, true);
}
else
{
EditorGUILayout.PropertyField(iterator, true);
}
}
EditorGUILayout.EndScrollView();
serializedConfig.ApplyModifiedProperties();
EditorGUILayout.Space();
EditorGUILayout.BeginHorizontal();
if (GUILayout.Button("应用更改"))
{
serializedConfig.ApplyModifiedProperties();
EditorUtility.SetDirty(selectedConfig);
AssetDatabase.SaveAssets();
}
if (GUILayout.Button("保存配置"))
{
ConfigManager.Instance.SaveCurrentConfig();
}
EditorGUILayout.EndHorizontal();
}
// 高级选项
showAdvanced = EditorGUILayout.Foldout(showAdvanced, "高级选项");
if (showAdvanced)
{
ConfigManager.Instance.autoBackup = EditorGUILayout.Toggle("自动备份", ConfigManager.Instance.autoBackup);
ConfigManager.Instance.maxBackupCount = EditorGUILayout.IntSlider("最大备份数", ConfigManager.Instance.maxBackupCount, 1, 10);
}
}
void SelectConfig(BaseConfig config)
{
ConfigManager.Instance.SwitchConfig(AssetDatabase.GetAssetPath(config));
selectedConfig = config;
Repaint();
}
void CreateBackup(BaseConfig config)
{
ConfigManager.Instance.CreateBackup(config);
RefreshConfigs();
}
void RefreshConfigs()
{
ConfigManager.Instance.RefreshAvailableConfigs();
}
}
5. 配置监听接口(游戏逻辑集成)
public interface IConfigListener
{
void OnConfigChanged(BaseConfig newConfig);
}
public class GameSettings : MonoBehaviour, IConfigListener
{
private BaseConfig currentConfig;
void Start()
{
ApplyConfig(ConfigManager.Instance.currentConfig);
}
public void OnConfigChanged(BaseConfig newConfig)
{
ApplyConfig(newConfig);
}
void ApplyConfig(BaseConfig config)
{
if (config == null) return;
currentConfig = config;
Application.targetFrameRate = config.targetFrameRate;
Time.timeScale = config.gameSpeed;
Debug.unityLogger.logEnabled = config.enableDebugMode;
Debug.Log($"应用配置:{config.configName}");
}
}
6. 自定义编辑器样式(可选增强)
#if UNITY_EDITOR
using UnityEditor;
[CustomEditor(typeof(BaseConfig))]
public class BaseConfigEditor : Editor
{
public override void OnInspectorGUI()
{
BaseConfig config = (BaseConfig)target;
// 自定义GUI样式
GUILayout.Label("配置编辑器", EditorStyles.boldLabel);
EditorGUILayout.HelpBox($"环境: {config.environment}\n版本: {config.version}", MessageType.Info);
DrawDefaultInspector();
EditorGUILayout.Space();
EditorGUILayout.BeginHorizontal();
if (GUILayout.Button("应用环境设置"))
{
config.ApplyEnvironmentSettings();
EditorUtility.SetDirty(config);
}
if (GUILayout.Button("保存配置"))
{
EditorUtility.SetDirty(config);
AssetDatabase.SaveAssets();
}
EditorGUILayout.EndHorizontal();
}
}
#endif
使用流程
1. 安装与初始化
- 创建文件夹:
Assets/Editor/
(编辑器脚本)和Assets/Configs/
(配置存储)。 - 导入脚本:将所有代码放入对应文件夹。
- 创建ConfigManager:右键 > Create > Config > Config Manager(单例)。
- 生成默认配置:
- Tools > Config Manager > 创建新配置
- 生成 DevConfig/TestConfig/ProdConfig
2. 日常使用
- 切换配置:Tools > Config Manager > [环境名称]
- 可视化编辑:Tools > Config Manager > 打开配置管理器
- 游戏集成:在MonoBehaviour实现
IConfigListener
监听配置变更 - 备份管理:自动备份到
Assets/Configs/Backups/
3. 高级功能
- 环境预设:不同Config设置不同参数(如开发模式开启Debug)
- 版本控制:自动备份 + 手动回滚
- 团队协作:Config文件可提交到版本控制,快速同步
扩展与优化
- 多项目支持:通过GUID管理不同项目的配置
- 加密配置:敏感数据加密存储
- 云同步:集成Unity Cloud Build自动部署不同环境配置
- 验证系统:配置参数合法性检查和自动修复
通过此插件,可实现高效的配置管理,大幅提升开发和测试效率!