news 2026/10/13 12:18:01

NaughtyAttributes 装饰器属性(Decorator Attributes)完全指南:HorizontalLine 与 InfoBox 的 Inspector 定制实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
NaughtyAttributes 装饰器属性(Decorator Attributes)完全指南:HorizontalLine 与 InfoBox 的 Inspector 定制实战
  • 游戏开发
  • 开发工具

【免费下载链接】NaughtyAttributes

Attribute Extensions for Unity

项目地址:https://gitcode.com/gh_mirrors/na/NaughtyAttributes
点击查看免费下载

本指南深入讲解 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()); } }

关键实现细节:

  1. 高度计算:GetHeight()返回EditorGUIUtility.singleLineHeight + lineAttr.Height,即在标准单行高度基础上叠加线自身的厚度,为分割线及其上下留白预留足够空间;
  2. 缩进对齐:OnGUI()先通过EditorGUI.IndentedRect(position)获取与字段缩进对齐的矩形,再在垂直方向上偏移singleLineHeight / 3,让线与字段顶部保持合理间距;
  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); } }

关键实现细节:

  1. 高度自适应:GetHelpBoxHeight()以singleLineHeight * 2.0f为最小高度,再通过GUI.skin.box.CalcHeight根据提示文本与当前窗口宽度计算实际需要的文本高度,取二者最大值。这意味着提示文本较长时,提示框会自动增高换行,而不是截断;
  2. 缩进处理:OnGUI()利用NaughtyEditorGUI.GetIndentLength(rect)(内部通过EditorGUI.IndentedRect计算缩进差值,见 NaughtyEditorGUI.cs)得到当前层级缩进量,使提示框与字段保持同一缩进级别,而非贴住 Inspector 左边缘;
  3. 类型映射: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)。官方提供的三种安装方式:

  1. 通过 openupm-cli 安装(推荐):
openupm add com.dbrizov.naughtyattributes
  1. 通过 Git URL 安装:在项目的Packages/manifest.json中添加:
"com.dbrizov.naughtyattributes": "https://github.com/dbrizov/NaughtyAttributes.git#upm"
  1. 从 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

项目地址:https://gitcode.com/gh_mirrors/na/NaughtyAttributes
点击查看免费下载

相关推荐

上一篇:官方 MCP Registry API 全解:鉴权令牌交换、服务器过滤同步与状态管理端点实现
下一篇:解决照片色彩偏差:darktable ICC嵌入全流程指南

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/13 12:15:56

读懂GitHub Trending日榜:从刷榜到高效筛选开源项目的实战指南

每天到工位的第一件事,打开 GitHub Trending 扫一眼日榜,已经成了我保持了好几年的固定动作。这个习惯看起来很简单,其实等于让整个开源社区给我当情报雷达——看看过去 24 小时里,全球开发者都在为哪些项目激动、点赞、转发。202…

作者头像 李华
网站建设 2026/10/13 12:12:37

北京市SHP文件处理指南:坐标系校验、几何修复与属性标准化

简介:本资源为北京市行政区划矢量地理数据包,面向GIS初学者、城市规划从业者、地理信息专业学生及空间分析研究者,解决行政边界数据缺失、底图构建困难、区域统计分析缺乏空间基础等实际问题。压缩包共11个文件,含2个.shp&#xf…

作者头像 李华
网站建设 2026/10/13 12:10:47

基于小波分解与重构的短时交通流量预测:原理、Matlab实现与效果优化

1. 项目概述:短时交通流量预测为什么离不开小波分解年复一年跟数据打交道,最让人头疼的其实不是模型选得不够花哨,而是原始数据太“脏”。短时交通流量预测,说白了就是要在几秒钟到几十分钟的时间尺度内,把未来一段时间…

作者头像 李华
网站建设 2026/10/13 12:10:25

语法分析实现与实验报告:递归下降与FIRST/FOLLOW集实战指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/13 12:08:24

Windows韩语打字训练:构建初声-中声-终声肌肉记忆

简介:这是一款专为韩语初学者及输入法进阶用户设计的Windows平台打字练习工具,适用于希望提升韩文盲打准确率与速度的学习者,尤其适合备考TOPIK、日常办公或语言自学场景。资源包共14个文件,包含2个可执行程序(setup.e…

作者头像 李华
网站建设 2026/10/13 12:08:00

hping.win32:Windows原生网络协议深度诊断工具

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华