当前位置: 首页 > news >正文

【Unity笔记】Unity 编辑器扩展:打造一个可切换 Config.assets 的顶部菜单插件


Unity 编辑器扩展:打造一个可切换 Config(ScriptableObject)的顶部菜单插件

关键词:Unity 编辑器扩展、ScriptableObject、EditorWindow、自定义菜单、配置文件管理

在这里插入图片描述

文章目录

  • Unity 编辑器扩展:打造一个可切换 Config(ScriptableObject)的顶部菜单插件
    • 1、前言:从需求到想法的萌芽
    • 2、技术选型:Unity 编辑器扩展的武器库
    • 3、原理解析:从输入到持久化
    • 4、代码实现:完整流程
    • 5、使用体验:效果演示
    • 6、总结与拓展

1、前言:从需求到想法的萌芽

在 Unity 的项目开发中,我们经常会遇到各种「配置文件」:

  • 游戏参数配置
  • 战斗数值配置
  • UI 样式配置
  • 网络服务端地址配置

这些配置往往存放在 ScriptableObject.asset 文件中,以方便可视化编辑。

但是,随着项目的迭代,我们会发现一个问题:

👉 配置文件数量变多,每次要手动在 Project 窗口搜索、点击、打开,非常低效。

特别是团队协作时,测试、策划、程序员都可能需要修改配置。光是「找到文件」这一步,就要多点几次。

于是,萌生了一个想法:

能不能在 Unity 编辑器顶部菜单栏添加一个「配置管理」菜单?点击它就能打开一个面板,里面有下拉列表,直接切换不同的 Config 文件,并在面板中修改它的内容。

进一步优化:

  • 第一次可以通过文件管理器选择一个配置文件。
  • 之后自动保存「上一次选择的配置」,下次打开时就能直接使用。

这就是本文要实现的功能。


2、技术选型:Unity 编辑器扩展的武器库

需求流程:
需求流程

为了实现上述需求,我们需要掌握以下 Unity 编辑器扩展相关技术

  1. EditorWindow

    • Unity 提供的编辑器自定义窗口基类,可以在菜单栏打开一个专属面板。
  2. MenuItem

    • 可以在 Unity 顶部菜单栏注册自定义菜单。
  3. ScriptableObject

    • 作为配置文件的载体,可以序列化保存为 .asset 文件,天然适合做 Config。
  4. EditorGUILayout

    • 用于在编辑器面板中绘制 UI,例如下拉框、按钮、对象选择器等。
  5. EditorPrefs

    • Unity 提供的本地偏好设置存储,可以保存简单的 key-value 数据(比如用户选择的上次 Config 文件路径)。
  6. AssetDatabase

    • 用于加载、查找 .asset 文件资源。

类图:
在这里插入图片描述


3、原理解析:从输入到持久化

在动手写代码前,我们先理一下「实现原理」:

  1. 入口

    • 在 Unity 顶部菜单栏添加一个菜单项,例如:Tools/Config Manager
  2. 面板 UI

    • 使用 EditorWindow 打开一个窗口。

    • 窗口中有:

      • 一个 下拉框,显示所有已知 Config 文件的名字。
      • 一个 按钮,点击后可通过文件管理器选择新的 Config 文件。
      • 一个 配置内容编辑区,直接显示并可修改 Config 的字段。
  3. 配置文件识别

    • Config 文件是 ScriptableObject,我们通过 AssetDatabase.FindAssets("t:Config") 找到所有同类型的 .asset 文件。
  4. 默认配置记忆

    • 使用 EditorPrefs.SetString("LastConfigPath", path) 保存上次选择的路径。
    • 下次打开窗口时,从 EditorPrefs 读取该路径,并尝试加载对应的配置文件。
  5. 编辑内容保存

    • Unity 的 SerializedObject + EditorGUILayout.PropertyField 可以动态绘制 ScriptableObject 的字段,并保证修改后能保存。

4、代码实现:完整流程

下面给出一个完整的实现案例。

假设我们的配置类是这样的:

using UnityEngine;[CreateAssetMenu(fileName = "GameConfig", menuName = "Config/GameConfig")]
public class GameConfig : ScriptableObject
{public string gameName;public int maxPlayerCount;public float gravityScale;
}

然后我们写一个编辑器插件 ConfigManagerWindow.cs

using UnityEngine;
using UnityEditor;
using System.Collections.Generic;
using System.IO;public class ConfigManagerWindow : EditorWindow
{private const string PREF_KEY = "LastConfigPath";private List<GameConfig> configs = new List<GameConfig>();private string[] configNames;private int selectedIndex = -1;private SerializedObject serializedConfig;private Vector2 scrollPos;[MenuItem("Tools/Config Manager")]public static void ShowWindow(){GetWindow<ConfigManagerWindow>("Config Manager");}private void OnEnable(){LoadConfigs();// 尝试读取上一次选择的配置string lastPath = EditorPrefs.GetString(PREF_KEY, "");if (!string.IsNullOrEmpty(lastPath)){GameConfig lastConfig = AssetDatabase.LoadAssetAtPath<GameConfig>(lastPath);if (lastConfig != null){selectedIndex = configs.IndexOf(lastConfig);if (selectedIndex >= 0){SetCurrentConfig(configs[selectedIndex]);}}}}private void OnGUI(){if (configs.Count == 0){EditorGUILayout.HelpBox("未找到任何 Config 文件,请先创建 ScriptableObject。", MessageType.Info);if (GUILayout.Button("选择 Config 文件")){SelectConfigFromFile();}return;}EditorGUILayout.LabelField("选择配置文件:", EditorStyles.boldLabel);int newIndex = EditorGUILayout.Popup(selectedIndex, configNames);if (newIndex != selectedIndex){selectedIndex = newIndex;SetCurrentConfig(configs[selectedIndex]);}if (GUILayout.Button("通过文件管理器选择 Config")){SelectConfigFromFile();}if (serializedConfig != null){EditorGUILayout.Space();EditorGUILayout.LabelField("配置内容:", EditorStyles.boldLabel);scrollPos = EditorGUILayout.BeginScrollView(scrollPos);serializedConfig.Update();SerializedProperty prop = serializedConfig.GetIterator();prop.NextVisible(true);while (prop.NextVisible(false)){EditorGUILayout.PropertyField(prop, true);}serializedConfig.ApplyModifiedProperties();EditorGUILayout.EndScrollView();}}private void LoadConfigs(){configs.Clear();string[] guids = AssetDatabase.FindAssets("t:GameConfig");foreach (string guid in guids){string path = AssetDatabase.GUIDToAssetPath(guid);GameConfig config = AssetDatabase.LoadAssetAtPath<GameConfig>(path);if (config != null){configs.Add(config);}}configNames = new string[configs.Count];for (int i = 0; i < configs.Count; i++){configNames[i] = configs[i].name;}}private void SetCurrentConfig(GameConfig config){serializedConfig = new SerializedObject(config);string path = AssetDatabase.GetAssetPath(config);EditorPrefs.SetString(PREF_KEY, path);}private void SelectConfigFromFile(){string path = EditorUtility.OpenFilePanel("选择 Config 文件", Application.dataPath, "asset");if (!string.IsNullOrEmpty(path)){path = "Assets" + path.Substring(Application.dataPath.Length);GameConfig config = AssetDatabase.LoadAssetAtPath<GameConfig>(path);if (config != null){if (!configs.Contains(config)){configs.Add(config);List<string> names = new List<string>(configNames);names.Add(config.name);configNames = names.ToArray();}selectedIndex = configs.IndexOf(config);SetCurrentConfig(config);}}}
}

5、使用体验:效果演示

交互流程:
在这里插入图片描述

  1. 在菜单栏点击 Tools/Config Manager

  2. 弹出一个面板:
    在这里插入图片描述

    • 上方下拉框显示已有的配置文件
    • 点击可切换不同 Config
    • 右侧按钮可打开文件管理器,手动选择新的 Config
  3. 下方面板显示所选 Config 的所有字段,可以直接修改
    运行截图

  4. 修改后 Unity 自动保存到对应的 .asset 文件


6、总结与拓展

通过这次实战,我们实现了一个 Unity 编辑器扩展插件,它解决了以下问题:

  • 配置文件散落在 Project 中 → 统一入口,集中管理
  • 每次要手动搜索配置 → 一键下拉切换
  • 修改不方便 → 在面板中直接可视化编辑

核心技术包括:

  • EditorWindow 自定义窗口
  • MenuItem 注册菜单
  • SerializedObject 保证 Unity 序列化
  • EditorPrefs 保存用户默认配置
  • AssetDatabase 搜索和加载 .asset 文件

可能的扩展方向

  1. 支持多类型 Config(不同的 ScriptableObject 类型)
  2. 添加「搜索框」快速过滤 Config
  3. 添加「收藏夹」功能,常用 Config 放到置顶位置
  4. 结合 EditorGUILayout.Toolbar 做更美观的 UI


文章转载自:

http://g7QvPPBF.Ljtwp.cn
http://mtOKRfQZ.Ljtwp.cn
http://biv1Drji.Ljtwp.cn
http://vh3xURBw.Ljtwp.cn
http://L01b4AQD.Ljtwp.cn
http://bmhkDDoS.Ljtwp.cn
http://U77HPAix.Ljtwp.cn
http://oVwslnyO.Ljtwp.cn
http://cjcEn0SE.Ljtwp.cn
http://1jP7cblI.Ljtwp.cn
http://eGWjl1hE.Ljtwp.cn
http://19SwDsij.Ljtwp.cn
http://dHWtL3N3.Ljtwp.cn
http://4KFDz9Bn.Ljtwp.cn
http://48eIp868.Ljtwp.cn
http://F5mS4VJb.Ljtwp.cn
http://tpKCrL05.Ljtwp.cn
http://eu9CEUgj.Ljtwp.cn
http://abFH7084.Ljtwp.cn
http://nkQTE7sZ.Ljtwp.cn
http://MM8THt9n.Ljtwp.cn
http://Xdi16lyd.Ljtwp.cn
http://MCkDuXG4.Ljtwp.cn
http://RHQAGnFl.Ljtwp.cn
http://piLNJQU9.Ljtwp.cn
http://gjFgDZSi.Ljtwp.cn
http://mEUaI5QB.Ljtwp.cn
http://AK4KhdYQ.Ljtwp.cn
http://eUqAeEQl.Ljtwp.cn
http://bGOmu9ZJ.Ljtwp.cn
http://www.dtcms.com/a/372901.html

相关文章:

  • Visual Studio Code设置个性化背景教程
  • AI内容标识新规实施后,大厂AI用户协议有何变化?(二)百度系
  • 【大模型应用开发 5.LlamaIndex知识管理与信息检索】
  • 打开SOLIDWORKS非常缓慢的测试排查方法
  • 《Redis Cluster 去中心化实战指南:从集群搭建、故障转移到扩缩容全流程》
  • 大厂的服务器自动扩缩容
  • 02OpenCV基本操作
  • 在Word和WPS文字中将手机中间4位替换为星号****
  • Chrome的“无处不在”与推动Web平台演进的使命
  • 开源PSS解析器1
  • 软件OS研发行业人机料法环应用总结:基于鱼骨图的分析框架
  • PyTorch 中nn.Embedding
  • Linux之环境变量(内容由浅入深,层层递进)
  • Linux control group笔记
  • 【Nginx】性能优化与实战(上)
  • LangChain RetrievalQA
  • MybatisPlus开启多租户三步快速集成
  • 现代Web应用前后端架构设计与Python实战
  • YOLO介绍(1)
  • 【javaSE】String类
  • 9.渗透-.Linux基础命令(一)(有vi编辑器)
  • LeetCode - LCR 179. 查找总价格为目标值的两个商品
  • ArcGIS Pro 遇到严重的应用程序错误而无法启动
  • 轻松Linux-9.进程间通信
  • 20250908的学习笔记
  • Golang 与 gRPC
  • shareId 的产生与传递链路
  • Go语言实战案例-开发一个JSON格式校验工具
  • AI技术架构与GEO算法原理如何重塑搜索引擎可见性
  • 【AI测试前沿】谷歌Fuzzing安全测试Go语言指南