Unity EditorStyles:3 个让扩展看起来像原生的写法
【免费下载链接】UnityCsReferenceUnity C# reference source code.项目地址: https://gitcode.com/gh_mirrors/un/UnityCsReference
给 Inspector 扩展加了一个工具栏按钮,被同事指出和原生工具栏“明显不是一家人”——边框、底色、hover 反馈全都不对。手写 GUIStyle 自定义样式更麻烦:深浅两套皮肤颜色各不相同,改好一边另一边又坏。问题其实就两个:Unity EditorStyles 的内置样式是怎么维护的,以及不同项目规模该选哪种落地模式。
EditorStyles 的真实维护方式:一次 GetStyle 加一层皮肤缓存
所有公开属性的源头在 EditorStyles.cs,600 来行、一百多种样式。挑 5 个代表性的字段看:
label对应 "ControlLabel",是 EditorGUI 各控件的 prefix label,最基础的文本样式;miniButton对应 "miniButton",配套的miniButtonLeft/miniButtonMid/miniButtonRight用于把一排按钮连成横向组;toolbar和toolbarButton分别对应 "toolbar" 与 "toolbarbutton",前者是窗口顶部工具栏的底色,后者是它上面的按钮;helpBox对应 "HelpBox",信息提示区块的背景,源码里额外开了imageIsTopAligned对齐 UIToolkit 版本;inspectorDefaultMargins不来自皮肤文件,是纯代码构造的,padding = RectOffset(18, 4, 4, 0),其中 18 来自kInspectorPaddingLeft = 8 + 10这个常量。
初始化逻辑在UpdateSkinCache。先看s_CachedStyles[skinIndex]两个槽位,为空时才刷新皮肤资源并实例化:
s_CachedStyles[skinIndex] = new EditorStyles(); s_CachedStyles[skinIndex].InitSharedStyles(); s_Current = s_CachedStyles[skinIndex];每个m_xxx字段都在InitSharedStyles里一次性填充,靠GetStyle(styleName)取值:先查当前GUI.skin,再回退到EditorGUIUtility.GetBuiltinSkin(EditorSkin.Inspector),仍找不到就打 "Missing built-in guistyle" 并返回GUISkin.error。
有两个细节值得记住。其一,并非全部来自皮肤文件:m_TextArea = new GUIStyle(m_TextField) { wordWrap = true },m_CenteredGreyMiniLabel是把 normal / hover / active / focused 四个状态全改成灰色。所谓派生样式,其实就是在基样式上改一两个参数,写 GUIStyle 自定义样式时照抄内置再调参,比从零开始稳定得多。其二,皮肤切换就是s_Current = s_CachedStyles[skinIndex]这一行,用户在编辑器里换 Pro / 个人版皮肤后,读到的每个属性都会自动换成新皮肤的一套,不需要任何额外处理 🎯
三种落地模式:按项目规模选对姿势
纯代码 GUIStyle:一次性 UI 的最快路径
一次性的 UI 小块,直接复制内置样式改参数:
static readonly GUIStyle SectionTitle = BuildSectionTitle(); static GUIStyle BuildSectionTitle() { var s = new GUIStyle(EditorStyles.boldLabel); s.fontSize += 2; s.margin = new RectOffset(8, 8, 4, 2); return s; }适用边界:一次性的 UI 小块,Inspector 扩展样式场景最常见,必须缓存成静态字段,不能在 OnGUI 里每次 new。
USS 样式表:多窗口项目的集中管理
一个项目里有多个窗口时,样式代码散落在各 C# 文件里很难维护,把规则挪进 .uss 文件,由窗口根节点引用。SceneView.cs 的写法是现成模板:
root.AddStyleSheetPath("StyleSheets/MyWindowCommon.uss"); root.AddStyleSheetPath(EditorGUIUtility.isProSkin ? "StyleSheets/MyWindowDark.uss" : "StyleSheets/MyWindowLight.uss");适用边界:基于 UIElements 的窗口、多窗口项目;公共规则放 Common 一份,Dark / Light 拆两份跟皮肤走。
isProSkin 分支:Editor 皮肤切换的标准写法
IMGUI 路径下,皮肤切换的判断点就是EditorGUIUtility.isProSkin,源码里即skinIndex == 1:
GUIStyle BuildWarnLabel() { var s = new GUIStyle(EditorStyles.label); s.normal.textColor = EditorGUIUtility.isProSkin ? new Color(0.9f, 0.85f, 0.4f) : new Color(0.6f, 0.45f, 0f); return s; }适用边界:颜色确实随皮肤变化时才分支,别给每个 label 都写一份;样式来自EditorStyles.*时框架已经处理了切换,不用手动分支。
踩坑与下一步
1. 切完深浅色皮肤,自定义样式颜色纹丝不动
- 现象:用户切换皮肤后,扩展里的手写样式颜色不变。
- 原因:启动时把样式从 EditorStyles 复制进了静态字段,皮肤切换只换
s_Current,旧拷贝不会动。 - 解法:在皮肤切换的合适时机重新取基样式重建;或者调用处直接用
EditorStyles.*,把切换交给框架。
2. 控制台出现 "Missing built-in guistyle"
- 现象:运行日志里报缺失内置样式。
- 原因:
GetStyle先查GUI.skin,在 play 模式或错误上下文里当前皮肤不是编辑器皮肤,内置样式名找不到。 - 解法:确认代码运行在编辑器上下文;确需取内置样式时用
EditorGUIUtility.GetBuiltinSkin(EditorSkin.Inspector)。
3. 自定义控件多的窗口偶发卡顿
- 现象:窗口里自定义控件一多,帧时间抖动。
- 原因:OnGUI 里每帧
new GUIStyle,复制样式、赋字段都是每帧分配。 - 解法:样式缓存进静态字段,只在皮肤切换后重建,
s_CachedStyles的双槽缓存就是这个思路。
下一步是把扩展用到的 Unity Editor Styles 属性列一张清单,标出哪些来自皮肤、哪些是代码构造的,再挑最头疼的几个先改成 USS 规则。
【免费下载链接】UnityCsReferenceUnity C# reference source code.项目地址: https://gitcode.com/gh_mirrors/un/UnityCsReference
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考