尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
NaughtyAttributes 的 Foldout 折叠分组特性:Inspector 字段分组与折叠的完整实战指南
游戏开发开发工具【免费下载链接】NaughtyAttributesAttribute Extensions for Unity项目地址https://gitcode.com/gh_mirrors/na/NaughtyAttributes点击查看免费下载Foldout 是 NaughtyAttributes 提供的**元属性Meta Attribute**之一用于在 Unity Inspector 中把多个序列化字段折叠进可展开/收起的分组从而大幅精简复杂组件的 Inspector 界面。本文以 NaughtyAttributes 官方文档docs/sphinx/src/attributes/meta_attributes/foldout.rst为主线结合本仓库的源码与测试用例完整讲解[Foldout]的用法、叠加规则、状态持久化机制及其与BoxGroup、ShowIf/HideIf等特性的协作方式。读完本文你将能独立用[Foldout]组织自己的 MonoBehaviour / ScriptableObject 字段并理解其底层实现原理。一、Foldout 是什么让 Inspector 不再臃肿Unity 默认的 Inspector 会把一个组件上所有可序列化字段自上而下平铺展示。当字段数量变多时例如几十个配置项Inspector 会变得非常长寻找某个字段需要反复滚动。NaughtyAttributes 通过[Foldout(分组名)]元属性把共享同一分组名的字段收纳进一个可展开、可收起的折叠组实现了默认收起、按需展开的整洁界面。与直接改m_Script或编写自定义 Editor 脚本不同[Foldout]是声明式的——你只需要在字段上加一行特性剩下的分组与折叠逻辑全部由 NaughtyAttributes 的编辑器扩展NaughtyInspector自动完成无需编写任何 Editor 代码。从源码结构看FoldoutAttribute位于Assets/NaughtyAttributes/Scripts/Core/MetaAttributes/FoldoutAttribute.cs它继承自MetaAttribute并实现IGroupAttribute接口using System; namespace NaughtyAttributes { [AttributeUsage(AttributeTargets.Field, AllowMultiple false, Inherited true)] public class FoldoutAttribute : MetaAttribute, IGroupAttribute { public string Name { get; private set; } public FoldoutAttribute(string name) { Name name; } } }关键点有三Name分组名即折叠组在 Inspector 中显示的文字AttributeUsage该特性只能用于字段AttributeTargets.Field且不允许重复叠加AllowMultiple false一个字段只能属于一个 Foldout 分组Inherited true支持继承链上的派生类继续生效。二、基本用法同一组名即同一折叠组官方文档给出的最小示例是public class NaughtyComponent : MonoBehaviour { [Foldout(Integers)] public int firstInt; [Foldout(Integers)] public int secondInt; }两个字段都标记了[Foldout(Integers)]因此在 Inspector 中它们会被收纳进同一个名为Integers的折叠组。分组依据是Name字符串的相等性与字段声明顺序、字段类型无关——只要Name相同就会合并到同一组。仓库自带的测试脚本Assets/NaughtyAttributes/Scripts/Test/FoldoutTest.cs展示了更丰富、更贴近实际项目的用法——多个分组、不同字段类型、以及与 Drawer 属性MinMaxSlider的组合using UnityEngine; namespace NaughtyAttributes.Test { public class FoldoutTest : MonoBehaviour { [Foldout(Integers)] public int int0; [Foldout(Integers)] public int int1; [Foldout(Floats)] public float float0; [Foldout(Floats)] public float float1; [Foldout(Sliders)] [MinMaxSlider(0, 1)] public Vector2 slider0; [Foldout(Sliders)] [MinMaxSlider(0, 1)] public Vector2 slider1; public string str0; public string str1; [Foldout(Transforms)] public Transform trans0; [Foldout(Transforms)] public Transform trans1; } }这个测试用例透露了几个重要信息分组粒度是字段级[Foldout]标记的是字段而不是类同一 MonoBehaviour 里可以有多个不同名的折叠组Integers、Floats、Sliders、Transforms未标记字段保持平铺str0、str1没有[Foldout]它们不会进入任何折叠组仍按默认方式直接展示可与 Drawer 属性叠加[Foldout(Sliders)]与[MinMaxSlider(0, 1)]同时作用于同一Vector2字段说明元属性与抽屉属性并不冲突——字段仍然由MinMaxSlider绘制只是被收纳进 Foldout 分组。实际使用建议折叠组适合开箱即用、极少改动的配置如音效参数、物理参数、渲染参数把经常需要调整的字段留在组外或单独的组中可以显著提升开发效率。三、组内字段的折叠跟随特性Foldout 的一个独特行为是组内字段的可见性跟随折叠状态当折叠组处于收起状态时组内字段会整体隐藏展开后则全部显示。这正是它与BoxGroup的本质区别——BoxGroup用带边框的盒子始终展示所有组内字段参见 BoxGroupAttribute.cs 与官方文档 box_group.rst而Foldout则通过折叠实现默认隐藏、点击展开。组内字段依然遵循 ShowIf / HideIf 条件从NaughtyInspector.cs的绘制逻辑Assets/NaughtyAttributes/Scripts/Editor/NaughtyInspector.cs#L109-L135可以看到Foldout 分组绘制前会先做一次可见性过滤// Draw foldout serialized properties foreach (var group in GetFoldoutProperties(_serializedProperties)) { IEnumerableSerializedProperty visibleProperties group.Where(p PropertyUtility.IsVisible(p)); if (!visibleProperties.Any()) { continue; } if (!_foldouts.ContainsKey(group.Key)) { #if UNITY_6000_4_OR_NEWER _foldouts[group.Key] new SavedBool(${EntityId.ToULong(target.GetEntityId())}.{group.Key}, false); #else _foldouts[group.Key] new SavedBool(${target.GetInstanceID()}.{group.Key}, false); #endif } _foldouts[group.Key].Value EditorGUILayout.Foldout(_foldouts[group.Key].Value, group.Key, true); if (_foldouts[group.Key].Value) { foreach (var property in visibleProperties) { NaughtyEditorGUI.PropertyField_Layout(property, true); } } }这里PropertyUtility.IsVisible(p)实现在Assets/NaughtyAttributes/Scripts/Editor/Utility/PropertyUtility.cs#L153-L196会检查字段上的ShowIfAttributeBase条件。因此你可以组合出这样的行为public class NaughtyComponent : MonoBehaviour { public bool showExtra; [Foldout(Advanced)] [ShowIf(showExtra)] public float extraValue; [Foldout(Advanced)] public int alwaysShown; }当showExtra为false时extraValue会被过滤掉若组内只剩alwaysShown则整个 Advanced 折叠组依然渲染如果组内所有字段都不可见visibleProperties.Any()为假折叠组本身也会被跳过不显示。另外若组内字段还带有ReadOnly、EnableIf/DisableIf等元属性这些字段的禁用/只读状态在展开后同样生效因为它们最终都经由NaughtyEditorGUI.PropertyField_Layout绘制Assets/NaughtyAttributes/Scripts/Editor/Utility/NaughtyEditorGUI.cs#L42-L80该函数内部会统一执行可见性检查、校验器验证与启用状态判断。四、编辑器实现原理折叠状态持久化到 EditorPrefs理解[Foldout]的底层实现有助于你预判它的行为边界。折叠组的分组与绘制完全由 NaughtyAttributes 的编辑器扩展完成核心在Assets/NaughtyAttributes/Scripts/Editor/NaughtyInspector.csGetFoldoutProperties按FoldoutAttribute.Name分组NaughtyInspector.cs#L209-L214从全部序列化属性中筛选出带有FoldoutAttribute的属性再用GroupBy(p ...Name)按键分组折叠状态用SavedBool持久化每个折叠组对应一个SavedBool其内部通过EditorPrefs保存开关状态Assets/NaughtyAttributes/Scripts/Editor/Utility/SavedBool.cspublic class SavedBool { private bool _value; private string _name; public bool Value { get { return _value; } set { if (_value value) { return; } _value value; EditorPrefs.SetBool(_name, value); } } public SavedBool(string name, bool value) { _name name; _value EditorPrefs.GetBool(name, value); } }持久化键由目标对象 ID 分组名构成确保不同对象上的同名折叠组互不干扰#if UNITY_6000_4_OR_NEWER _foldouts[group.Key] new SavedBool(${EntityId.ToULong(target.GetEntityId())}.{group.Key}, false); #else _foldouts[group.Key] new SavedBool(${target.GetInstanceID()}.{group.Key}, false); #endif值得注意的实现细节折叠组默认是收起状态构造时value: false展开/收起状态存入EditorPrefs跨编辑会话、跨场景保持——你展开过一次的分组下次打开编辑器时依然保持展开折叠状态以target.GetInstanceID()为粒度同一个 Prefab 的不同实例、不同场景对象即使分组名相同折叠状态也彼此独立源码中针对 Unity 6000.4Unity 6 后续版本使用了新的EntityId替代已弃用的GetInstanceID()说明该实现同时兼容当前仓库使用的 Unity 2022.3.62f3见ProjectSettings/ProjectVersion.txt与更新的 Unity 6 版本。这种EditorPrefs 持久化的设计对日常开发非常友好你不会因为折叠了某个分组下次打开工程时又被强制展开也不会因为切换对象导致折叠状态串组。五、分组与绘制流程NaughtyInspector 中的完整顺序要理解[Foldout]在 Inspector 中的最终呈现需要看NaughtyInspector.DrawSerializedProperties()的完整绘制流程NaughtyInspector.cs#L71-L138非分组字段先按声明顺序平铺绘制GetNonGroupedProperties即没有任何IGroupAttribute的字段BoxGroup 分组字段以带边框的盒子形式整体绘制GetGroupedPropertiesNaughtyEditorGUI.BeginBoxGroup_Layout/EndBoxGroup_Layout见 NaughtyEditorGUI.csFoldout 分组字段最后绘制先渲染折叠头展开后再逐个绘制组内字段。因此一个同时混用BoxGroup与Foldout的组件其 Inspector 呈现顺序是平铺字段 → 盒子分组 → 折叠分组。m_Script字段会被特殊处理为始终禁用的只读显示NaughtyInspector.cs#L78-L84。另外值得注意的是NaughtyInspector通过[CustomEditor(typeof(UnityEngine.Object), true)]全局接管了所有对象的 Inspector 绘制NaughtyInspector.cs#L9-L10但仅当对象存在至少一个 Naughty 属性时才走自定义绘制路径否则回退到DrawDefaultInspector()NaughtyInspector.cs#L40-L48因此[Foldout]不会影响未使用 NaughtyAttributes 的普通组件。六、局限性为什么嵌套字段上不生效官方警告官方文档为[Foldout]明确标注了一条WarningDoesnt work on serialized fields that are nested inside serialized structs of classes.对嵌套在可序列化结构体/类内部的序列化字段不生效。这是由实现机制决定的NaughtyInspector只对顶层序列化属性serializedObject.GetIterator()迭代出的可见属性执行GetFoldoutProperties分组NaughtyInspector.cs#L55-L69。嵌套在结构体/类内部的字段其SerializedProperty.propertyPath形如myStruct.integerInspector 只会把整个myStruct作为一个顶层属性绘制[Foldout]无法触及嵌套层级的字段。因此下面这种写法不会得到预期的折叠效果[System.Serializable] public struct MyStruct { [Foldout(Integers)] // ❌ 无效嵌套字段上的 Foldout 不生效 public int innerInt; } public class NaughtyComponent : MonoBehaviour { public MyStruct myStruct; }对应的解决思路把[Foldout]放到顶层字段上myStruct本身可以进折叠组或者直接使用[BoxGroup]对嵌套字段做视觉分组。顺带一提BoxGroup文档同样声明了这一限制见 box_group.rst说明这是分组类元属性的共性约束。补充如果你需要在嵌套字段上使用EnableIf/DisableIf、ShowIf/HideIf等条件型元属性官方文档要求配合AllowNesting特性参见 allow_nesting.rst因为这类元属性本身不是 PropertyDrawer默认不支持嵌套。Foldout由于同样属于元属性阵营在嵌套场景下也遵循同一套约束逻辑。七、在自定义 Editor 中支持 Foldout进阶[Foldout]的折叠逻辑位于NaughtyInspectorEditor 层因此它不会像CustomPropertyDrawer实现的抽屉属性那样自动作用于自定义 Editor。如果你编写了自己的CustomEditor想继续享受[Foldout]分组能力官方推荐的做法是让你的自定义 Editor 继承自NaughtyInspector用NaughtyEditorGUI.PropertyField_Layout代替EditorGUILayout.PropertyField绘制属性。这样OnInspectorGUI中DrawSerializedProperties()包含 Foldout 分组逻辑就会在你的自定义 Editor 中生效。这一用法与 README 中meta attributes 在自定义 Editor 中需要继承NaughtyInspector才能工作的说明一致见 README.md 开头的 Overview 说明。八、完整示例与总结把官方文档示例、测试用例与实现细节整合起来一个完整的[Foldout]使用模板如下using UnityEngine; public class NaughtyComponent : MonoBehaviour { // 无分组平铺显示 public string sceneName; // 折叠分组Integers [Foldout(Integers)] public int firstInt; [Foldout(Integers)] public int secondInt; // 折叠分组Sliders与抽屉属性叠加 [Foldout(Sliders)] [MinMaxSlider(0, 1)] public Vector2 slider0; [Foldout(Sliders)] [MinMaxSlider(0, 1)] public Vector2 slider1; // 折叠分组 条件显示 public bool showAdvanced; [Foldout(Advanced)] [ShowIf(showAdvanced)] public float advancedValue; }使用要点回顾[Foldout(分组名)]只作用于字段同一分组名即可合并字段默认收起、点击展开分组名仅作为分组标识显示在折叠头上不要求与字段名有任何关联折叠状态经EditorPrefs持久化按对象实例 分组名隔离跨会话保持与[BoxGroup]的区别BoxGroup 始终展开显示所有组内字段Foldout 默认收起与[ShowIf]等条件元属性可自由组合组内字段遵循可见性过滤不支持嵌套在可序列化结构体/类内部的字段官方警告自定义 Editor 中如需生效需继承NaughtyInspector并使用NaughtyEditorGUI.PropertyField_Layout。如果你想在真实工程中快速验证可以运行仓库自带的示例场景与测试Assets/NaughtyAttributes/Scripts/Test/FoldoutTest.cs直接挂在任意 GameObject 上即可观察四个折叠组的展开/收起效果更多相关文档可查阅docs/sphinx/src/attributes/meta_attributes/index.rst中 Meta Attributes 目录以及相邻的 BoxGroup、ShowIf / HideIf 等文档。赞分享游戏开发开发工具【免费下载链接】NaughtyAttributesAttribute Extensions for Unity项目地址https://gitcode.com/gh_mirrors/na/NaughtyAttributes点击查看免费下载相关推荐WeChatMsg 完整指南免费导出微信聊天记录为 HTML、Word、CSV还能生成年度报告WeChatMsg 完整指南免费导出微信聊天记录为 HTML、Word、CSV还能生成年度报告 换手机之前你往聊天窗口里往上翻两年前的开头几段已经不见了Formily ArrayCollapse 折叠面板组件完全指南Schema 数组表单的折叠交互与联动方案Formily ArrayCollapse 折叠面板组件完全指南Schema 数组表单的折叠交互与联动方案 折叠面板ArrayCollapse是 Form前端UI组件RailsAdmin 字段分组Field Groups配置指南edit/create/update 视图的组、可见性、标签与折叠开关RailsAdmin 字段分组Field Groups配置指南edit/create/update 视图的组、可见性、标签与折叠开关 RailsAdmin后端上一篇openPangu-2.0-Pro 深度解析昇腾原生 505B 混合专家大模型的架构、训练与性能下一篇vLLM Semantic Router Multi-Objective 多目标路由配方用一套共享模型池同时服务平衡、速度、成本、精度与隐私五类业务创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
RELATED

相关推荐

AI Infra面试高频考点:集群网络拓扑、无损传输与AllReduce

AI Infra面试高频考点:集群网络拓扑、无损传输与AllReduce

这两年看AI Infra方向的面试,网络基础真的是绕不开的一道门槛。翻了不下三百份简历,也面过上百个候选人,一个特别明显的趋势是:框架层、算法层的知识大家普遍准备得很充分,什么Transformer结构、显存优化、流水线并行都…

📅 2026/10/13 3:12:11
多Agent协作框架实战:Agent管理Agent的架构设计与部署指南

多Agent协作框架实战:Agent管理Agent的架构设计与部署指南

1. 从单兵作战到团队协作:Agent管理Agent的底层逻辑第一次看到“让Agent学会管理Agent”这个说法,我脑子里蹦出来的画面是一群刚入行的新人程序员,每个人都能写点代码,但没人知道怎么分工、怎么排期、怎么互相Review。单个Agent的…

📅 2026/10/13 3:12:11
spine-runtimes 跨运行时快照测试:Haxe 序列化器生成器的实现与应用指南

spine-runtimes 跨运行时快照测试:Haxe 序列化器生成器的实现与应用指南

游戏开发图形学 【免费下载链接】spine-runtimes 2D skeletal animation runtimes for Spine. 项目地址: https://gitcode.com/gh_mirrors/sp/spine-runtimes 点击查看 免费下载 本指南以 todos/work/2025-07-26-18-21-26-serializer-generator-haxe/task.md 为核心…

📅 2026/10/13 3:07:10
MORE NEWS

更多资讯

📰

go-jose Safe JSON 深度解析:cortex 依赖链中 fork 版 encoding/json 的大小写敏感匹配与重复键拒绝机制

可观测性时序数据库后端指标监控 【免费下载链接】cortex A horizontally scalable, highly available, multi-tenant, long term Prometheus. 项目地址: https://gitcode.com/gh_mirrors/cortex6/cortex 点击查看 免费下载 导读 JOSE(JSON Object Sig…

📰

PSR开关电源EMC设计实战:PCB布局、变压器绕法与滤波整改指南

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

📰

2026年工业控制系统上位机:Qt高效工程搭建与避坑指南

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

📰

点云连续卷积:原生建模的几何不变性实现

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

📰

AI编程代理Codex生产环境实战:多场景自动化落地与避坑指南

1. 项目全貌与核心价值拆解先说结论:这个项目不是又一个"抄一段代码跑通就完事"的教程,而是把Codex这类AI编程代理真正丢进生产环境里,去碰那些文档里不会写的脏活累活——多仓库并行、流水线触发、测试自动修复、发布前卡点。项目…

📰

Faster RCNN小目标交通标志检测实战优化指南

1. 项目概述:为什么小目标交通标志检测是个“硬骨头”交通标志识别不是新鲜事,但真正落地到实际道路场景里,尤其是高速、长距离、复杂光照下的小目标检测,至今仍是不少团队卡壳的环节。我接触过不下十个类似项目,从某高…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬