- 游戏开发
- 开发工具
【免费下载链接】NaughtyAttributes
Attribute Extensions for Unity
本指南深入讲解 NaughtyAttributes 中"装饰器属性"(Decorator Attributes)这一核心类别,围绕官方文档(装饰器属性索引)定义的概念模型,结合HorizontalLine与InfoBox两个官方属性的源码实现、测试用例与 Inspector 实际效果,说明如何在不替换字段原有绘制方式的前提下,为字段周围叠加分割线与提示信息框,从而提升 Unity Inspector 的可读性与可用性。读完本文,你将掌握装饰器属性的定位、两个内置装饰器的全部参数与默认值、它们与 Drawer 属性的组合规则,以及其底层 GUI 绘制原理。
什么是 Decorator Attributes:装饰在字段周围,而非替换字段
在 NaughtyAttributes 的属性体系中,官方文档对 Decorator Attributes 给出了一句精确定义:
Decorator attributes draw additional GUI around a field, instead of replacing the way the field itself is drawn.
即:装饰器属性在字段周围绘制额外的 GUI 元素,而不是替换字段本身的绘制方式。这与"抽屉属性"(Drawer Attributes)形成鲜明对比——Drawer 属性(如Dropdown、ProgressBar、MinMaxSlider)会接管字段自身的 Inspector 绘制逻辑,改变字段的呈现形态;而 Decorator 属性只是在字段的上方或周围"附加"视觉元素(分割线、提示框等),字段本身依然按照 Unity 默认方式渲染。
与 Drawer Attributes 的关键区别
官方文档特别用 note 强调了二者在使用规则上的差异(见 drawer_attributes/index.rst):
- Drawer 属性:一个
field、property或function只能挂一个 Drawer 属性; - Decorator 属性:一个
field可以同时挂多个Decorator 属性,并且可以与一个 Drawer 属性叠加使用。
这一规则在源码层面同样成立:从属性基类看,DrawerAttribute与DecoratorDrawer是两套独立的 Unity 扩展体系。其中 Decorator 属性继承自DrawerAttribute(见 DrawerAttribute.cs),它本身又是PropertyAttribute与INaughtyAttribute的组合,供NaughtyInspector(NaughtyInspector.cs)识别并进入自定义绘制流程。
官方文档中,Decorator Attributes 目录下目前包含两个属性:
| 属性 | 作用 | 核心参数 |
|---|---|---|
HorizontalLine | 在字段上方绘制一条水平分割线 | height(线高/厚度)、color(颜色) |
InfoBox | 在字段上方绘制一个提示信息框 | text(文本)、type(提示类型) |
下文分别展开。
HorizontalLine:用一条可定制颜色的水平线分隔 Inspector 字段
基本用法与官方示例
HorizontalLine的定位与 Unity 自带的Space属性类似——都是用于在 Inspector 中制造视觉间距——但它的表现不是空白,而是一条水平线。官方文档(horizontal_line.rst)给出的示例:
public class NaughtyComponent : MonoBehaviour { [HorizontalLine(color: EColor.Red)] public int red; [HorizontalLine(color: EColor.Green)] public int green; [HorizontalLine(color: EColor.Blue)] public int blue; }运行后在 Inspector 中即可看到:每个字段上方出现一条对应颜色的水平分割线,字段本身的int绘制不受影响(效果见上文第一张截图)。
参数说明:高度与颜色
HorizontalLine共有两个可选参数,均带有默认值。其完整定义见源码 HorizontalLineAttribute.cs:
[AttributeUsage(AttributeTargets.Field, AllowMultiple = true, Inherited = true)] public class HorizontalLineAttribute : DrawerAttribute { public const float DefaultHeight = 2.0f; public const EColor DefaultColor = EColor.Gray; public float Height { get; private set; } public EColor Color { get; private set; } public HorizontalLineAttribute(float height = DefaultHeight, EColor color = DefaultColor) { Height = height; Color = color; } }参数细节:
height:线的厚度(像素),默认值2.0f。不传时使用默认值;传入更大数值可绘制更粗的分割线,例如测试文件中使用的[HorizontalLine(10.0f)](见 HorizontalLineTest.cs)。color:线的颜色,类型为EColor,默认值EColor.Gray。注意:这里用的是 NaughtyAttributes 自定义的EColor枚举,而不是UnityEngine.Color。
EColor枚举共定义了 12 种常用颜色,定义在 EColor.cs:
| 枚举值 | 实际 RGBA 值(Color32) | 视觉呈现 |
|---|---|---|
Clear | (0, 0, 0, 0) | 全透明 |
White | (255, 255, 255, 255) | 白色 |
Black | (0, 0, 0, 255) | 黑色 |
Gray | (128, 128, 128, 255) | 灰色(默认) |
Red | (255, 0, 63, 255) | 红色 |
Pink | (255, 152, 203, 255) | 粉色 |
Orange | (255, 128, 0, 255) | 橙色 |
Yellow | (255, 211, 0, 255) | 黄色 |
Green | (98, 200, 79, 255) | 绿色 |
Blue | (0, 135, 189, 255) | 蓝色 |
Indigo | (75, 0, 130, 255) | 靛蓝 |
Violet | (128, 0, 255, 255) | 紫色 |
这些 RGB 值并非 Unity 标准调色板,而是项目自定义的视觉配色。枚举通过扩展方法EColorExtensions.GetColor()转换为UnityEngine.Color32(同一文件内实现),供绘制时使用。
源码级剖析:HorizontalLine 是如何被绘制出来的
HorizontalLine的编辑器端实现位于 HorizontalLineDecoratorDrawer.cs,它继承自 Unity 的DecoratorDrawer,并通过[CustomPropertyDrawer(typeof(HorizontalLineAttribute))]完成绑定:
[CustomPropertyDrawer(typeof(HorizontalLineAttribute))] public class HorizontalLineDecoratorDrawer : DecoratorDrawer { public override float GetHeight() { HorizontalLineAttribute lineAttr = (HorizontalLineAttribute)attribute; return EditorGUIUtility.singleLineHeight + lineAttr.Height; } public override void OnGUI(Rect position) { Rect rect = EditorGUI.IndentedRect(position); rect.y += EditorGUIUtility.singleLineHeight / 3.0f; HorizontalLineAttribute lineAttr = (HorizontalLineAttribute)attribute; NaughtyEditorGUI.HorizontalLine(rect, lineAttr.Height, lineAttr.Color.GetColor()); } }关键实现细节:
- 高度计算:
GetHeight()返回EditorGUIUtility.singleLineHeight + lineAttr.Height,即在标准单行高度基础上叠加线自身的厚度,为分割线及其上下留白预留足够空间; - 缩进对齐:
OnGUI()先通过EditorGUI.IndentedRect(position)获取与字段缩进对齐的矩形,再在垂直方向上偏移singleLineHeight / 3,让线与字段顶部保持合理间距; - 实际绘制:最终调用
NaughtyEditorGUI.HorizontalLine(rect, height, color)。该方法(见 NaughtyEditorGUI.cs 中的HorizontalLine方法)实现极其轻量——将矩形高度设为线厚,然后调用EditorGUI.DrawRect(rect, color)直接填充一个纯色矩形。从实现可以推断,HorizontalLine 本质就是一段带缩进的纯色填充矩形,因此不支持渐变、圆角等效果。
有意思的是,NaughtyInspector在绘制 "Native Properties"、"Non-Serialized Fields"、"Buttons" 三类分节标题时,内部也复用了NaughtyEditorGUI.HorizontalLine来绘制分节分割线(见 NaughtyInspector.cs),使用默认的DefaultHeight与DefaultColor。这说明 HorizontalLine 的底层绘制函数是整个 NaughtyAttributes 内部通用的基础能力。
实战组合:多行分隔 + 不同颜色 + 粗线
从测试文件 HorizontalLineTest.cs 可以看到官方测试脚本对全部 12 种颜色和自定义厚度的覆盖用法——它将每种颜色配一个[Header]标签进行对照展示,同时演示了嵌套可序列化类(HorizontalLineNest1、HorizontalLineNest2)中装饰器属性同样生效。以下是一个可直接套用的综合示例:
public class NaughtyComponent : MonoBehaviour { [Header("Black")] [HorizontalLine(color: EColor.Black)] public int black; [Header("Thick")] [HorizontalLine(10.0f)] // 位置参数传入高度:10 像素的粗线 public int thick; [HorizontalLine] // 全部使用默认值:2.0f 高、Gray 色 public int defaultLine; }InfoBox:把 Normal / Warning / Error 提示框直接内嵌进 Inspector
基本用法与官方示例
InfoBox用于在字段上方提供额外信息提示,官方文档(info_box.rst)给出的示例展示了三种提示类型:
public class NaughtyComponent : MonoBehaviour { [InfoBox("This is my int", EInfoBoxType.Normal)] public int myInt; [InfoBox("This is my float", EInfoBoxType.Warning)] public float myFloat; [InfoBox("This is my vector", EInfoBoxType.Error)] public Vector3 myVector; }运行效果见上文第二张截图:Normal类型显示为蓝色信息框,Warning显示为黄色警告框,Error显示为红色错误框,与 Unity 内置的HelpBox视觉风格一致。
参数说明:文本与类型
InfoBox的定义位于 InfoBoxAttribute.cs。注意,EInfoBoxType枚举与该属性类定义在同一个文件中:
public enum EInfoBoxType { Normal, Warning, Error } [AttributeUsage(AttributeTargets.Field, AllowMultiple = true, Inherited = true)] public class InfoBoxAttribute : DrawerAttribute { public string Text { get; private set; } public EInfoBoxType Type { get; private set; } public InfoBoxAttribute(string text, EInfoBoxType type = EInfoBoxType.Normal) { Text = text; Type = type; } }参数细节:
text:必填参数,即提示框内展示的字符串;type:可选参数,EInfoBoxType枚举,默认值Normal。三种类型分别映射到 Unity 的MessageType.Info / Warning / Error。
源码级剖析:InfoBox 的绘制与高度自适应
编辑器端实现位于 InfoBoxDecoratorDrawer.cs:
[CustomPropertyDrawer(typeof(InfoBoxAttribute))] public class InfoBoxDecoratorDrawer : DecoratorDrawer { public override float GetHeight() { return GetHelpBoxHeight(); } public override void OnGUI(Rect rect) { InfoBoxAttribute infoBoxAttribute = (InfoBoxAttribute)attribute; float indentLength = NaughtyEditorGUI.GetIndentLength(rect); Rect infoBoxRect = new Rect( rect.x + indentLength, rect.y, rect.width - indentLength, GetHelpBoxHeight()); DrawInfoBox(infoBoxRect, infoBoxAttribute.Text, infoBoxAttribute.Type); } private float GetHelpBoxHeight() { InfoBoxAttribute infoBoxAttribute = (InfoBoxAttribute)attribute; float minHeight = EditorGUIUtility.singleLineHeight * 2.0f; float desiredHeight = GUI.skin.box.CalcHeight(new GUIContent(infoBoxAttribute.Text), EditorGUIUtility.currentViewWidth); float height = Mathf.Max(minHeight, desiredHeight); return height; } private void DrawInfoBox(Rect rect, string infoText, EInfoBoxType infoBoxType) { MessageType messageType = MessageType.None; switch (infoBoxType) { case EInfoBoxType.Normal: messageType = MessageType.Info; break; case EInfoBoxType.Warning: messageType = MessageType.Warning; break; case EInfoBoxType.Error: messageType = MessageType.Error; break; } NaughtyEditorGUI.HelpBox(rect, infoText, messageType); } }关键实现细节:
- 高度自适应:
GetHelpBoxHeight()以singleLineHeight * 2.0f为最小高度,再通过GUI.skin.box.CalcHeight根据提示文本与当前窗口宽度计算实际需要的文本高度,取二者最大值。这意味着提示文本较长时,提示框会自动增高换行,而不是截断; - 缩进处理:
OnGUI()利用NaughtyEditorGUI.GetIndentLength(rect)(内部通过EditorGUI.IndentedRect计算缩进差值,见 NaughtyEditorGUI.cs)得到当前层级缩进量,使提示框与字段保持同一缩进级别,而非贴住 Inspector 左边缘; - 类型映射:
EInfoBoxType被转换为 Unity 的MessageType(Normal → Info,Warning → Warning,Error → Error),最终调用NaughtyEditorGUI.HelpBox(rect, infoText, messageType),即EditorGUI.HelpBox,与 Unity 原生帮助框渲染完全一致。
从实现可以推断:InfoBox 本质上就是对 UnityEditorGUI.HelpBox的封装,因此它天然支持多行文本换行,并具备 Unity 标准帮助框的全部视觉与交互行为。
实战组合:在嵌套结构中使用三种类型
官方测试脚本 InfoBoxTest.cs 演示了在嵌套可序列化类中逐层使用三种类型:
public class InfoBoxTest : MonoBehaviour { [InfoBox("Normal", EInfoBoxType.Normal)] public int normal; public InfoBoxNest1 nest1; } [System.Serializable] public class InfoBoxNest1 { [InfoBox("Warning", EInfoBoxType.Warning)] public int warning; public InfoBoxNest2 nest2; } [System.Serializable] public class InfoBoxNest2 { [InfoBox("Error", EInfoBoxType.Error)] public int error; }在实际开发中,InfoBox 常被用于:
- 在复杂字段(如曲线、预制体引用、需要特定格式的输入)上方说明填写规范;
- 与
EnableIf/DisableIf等元属性搭配,提示当前字段被禁用或隐藏的原因; - 在序列化配置类中标注"此参数仅在 XX 模式生效"等警告信息。
组合用法:多个装饰器叠加 + 与 Drawer 属性共存
装饰器属性最独特的价值在于可叠加性。官方文档明确指出:一个字段可以同时拥有多个装饰器属性,也可以与一个 Drawer 属性组合。这意味着你可以写出这样的字段:
public class NaughtyComponent : MonoBehaviour { [HorizontalLine(color: EColor.Orange)] [InfoBox("该字段控制角色移动速度,仅能在编辑器中调试", EInfoBoxType.Warning)] [ProgressBar("Move Speed", 0f, 100f, EColor.Green)] public float moveSpeed; }在这个例子中:
HorizontalLine在字段上方绘制橙色分隔线;InfoBox提供黄色警告说明;ProgressBar(Drawer 属性)则替换字段本身的绘制,将其渲染为进度条。
三者职责互不冲突:装饰器负责"周围",Drawer 负责"本体",叠加后完整呈现"装饰 + 重绘"的效果。这正是装饰器属性与 Drawer 属性在设计上刻意区分的结果——前者绝不干预字段自身的绘制。
快速上手:安装与使用前提
本文所有属性均属于 NaughtyAttributes 包体,使用前需先安装(当前仓库要求Unity 2022.3 或更高版本,见 installation.rst)。官方提供的三种安装方式:
- 通过 openupm-cli 安装(推荐):
openupm add com.dbrizov.naughtyattributes- 通过 Git URL 安装:在项目的
Packages/manifest.json中添加:
"com.dbrizov.naughtyattributes": "https://github.com/dbrizov/NaughtyAttributes.git#upm"- 从 Asset Store 下载安装包。
安装完成后,在脚本顶部引入NaughtyAttributes命名空间,即可使用[HorizontalLine]、[InfoBox]等全部属性。仓库自带的示例场景位于 Assets/NaughtyAttributes/Samples/DemoScene,其中挂载了各属性测试组件的DemoScene.unity可以直接打开查看真实效果;对应的测试组件脚本集中在 Assets/NaughtyAttributes/Scripts/Test,是学习每个属性参数的最佳参照。
小结
- Decorator Attributes 的定位:在字段周围绘制附加 GUI,而不替换字段自身绘制,因此一个字段可以挂多个装饰器,且能与 Drawer 属性共存;
HorizontalLine:本质是带缩进的纯色矩形(EditorGUI.DrawRect),参数为厚度height(默认 2.0f)与EColor颜色(默认 Gray),共 12 种预设色;InfoBox:本质是对EditorGUI.HelpBox的封装,参数为text与EInfoBoxType(Normal / Warning / Error),支持文本换行与缩进对齐,高度自动适配内容;- 编辑器实现路径:两个属性的绘制逻辑分别位于 HorizontalLineDecoratorDrawer.cs 与 InfoBoxDecoratorDrawer.cs,底层复用 NaughtyEditorGUI.cs 的通用绘制方法,核心属性定义位于 Core/DrawerAttributes。
在序列化字段繁多的 MonoBehaviour 或 ScriptableObject 中,善用HorizontalLine分组、用InfoBox标注注意事项,可以让团队协作中的 Inspector 界面从"字段堆叠"变成"带注释的分区面板",这是 NaughtyAttributes 装饰器属性最直接的工程价值。
- 游戏开发
- 开发工具
【免费下载链接】NaughtyAttributes
Attribute Extensions for Unity
相关推荐
NaughtyAttributes 元属性(Meta Attributes)完整指南:用 C 特性高效定制 Unity Inspector
NaughtyAttributes 元属性(Meta Attributes)完整指南:用 C 特性高效定制 Unity Inspector Meta Attri
游戏开发开发工具Midway 3.0 自定义装饰器完全指南:类装饰器、属性装饰器、方法装饰器与参数装饰器实战
Midway 3.0 自定义装饰器完全指南:类装饰器、属性装饰器、方法装饰器与参数装饰器实战 导读 Midway 3.0 提供了一套由框架托管的自定义装饰器能力
后端微服务云原生NaughtyAttributes 的 InfoBox 特性详解:在 Unity Inspector 中嵌入提示信息框
NaughtyAttributes 的 InfoBox 特性详解:在 Unity Inspector 中嵌入提示信息框 导读 InfoBox 是 Unity 属
游戏开发开发工具
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考