自定义Inspector面板(一):改变面板的常用方法

本文详细介绍Unity编辑器的扩展方法,包括内建特性、自定义特性、可序列化类型的自定义绘制、组件的自定义绘制及hideFlags的使用,助力开发者提升编辑器功能。

一、内建特性

首先是喜闻乐见的特性(Attribute),Unity内建了许多方便的特性来改变和扩展编辑器。 内建的特性有许多人整理过,社区内就有一篇 超级汽水整理的 - 2019.x 常用 属性(Attribute) 解析
此外关于更多Unity内建的属性,可以在官方API文档中查看。 但文档并没有单独整理用于 Inspector面板 的特性,所以我在这做了一些简单的展开。
总所周知,Unity 提供了 “PropertyAttribute” 类,使我们可以方便的定义一个控制变量在Inspector中样式的特性。 于是,我们可以通过一个简单的反射,来获取Unity内建的这些属性:
    
using System . Reflection ; [ UnityEditor . MenuItem ( "Test/打印PropertyAttribute派生类" ) ] static public void GetPropertyAttributeDerivedClass ( ) { foreach ( var item in Assembly . Load ( "UnityEngine.CoreModule" ) . GetTypes ( ) ) if ( item . IsSubclassOf ( typeof ( PropertyAttribute ) ) ) Debug . Log ( item ) ; }
使用上面方法,我们得到了12个特性,没错只有12个。是不是总觉得少了很多。 所以我又打印了一下从 “Attribute” 派生的所有类,这回一共有90个。
于是简单的整理了下面表格(仅与Inspector面板有关): | 特性 | 说明 | | ------------------------------------------------------------ | -------------------------------------- | | “PropertyAttribute” 派生的所有特性 | 改变一个字段的样式 | | [HideInInspector] | 隐藏一个字段 | | [HelpURL]、[ContextMenu] | 作用在组件头部的两个特性 | | [RequireComponent]、[AddComponentMenu] | 功能性的两个特性 | | [SerializeField]、[SerializeReference]、[FormerlySerializedAsAttribute] | 序列化相关 | | [Serializable]、[NonSerialized] | 序列化相关,但属于 "System" 命名空间下 |
从上可以看出,除了 “PropertyAttribute” 派生的所有特性 和 [HideInInspector] 外,其他特性主要是功能性的。
至于 [HideInInspector] 为什么不用继承 “PropertyAttribute” 类来实现。 目前我所能理解的,由于 “PropertyDrawer” 的实现方式,面板在绘制数组或List时,其根部并不在 “PropertyDrawer” 的处理范围内,所以无法全部隐藏,于是单独定义了一个 [HideInInspector] 特性。(个人猜测)

二、自定义特性

扯完内建特性,接下来当然是自定义特性。 本着“要致富,先撸树”的原则。我们自然需要先定义一个继承自 “PropertyAttribute” 的类。
    
using UnityEngine ; [ System . AttributeUsage ( AttributeTargets . Field ) ] //限制属性只能使用在Field上 public class CustomAttribute : PropertyAttribute { } //定义特性时建议在命名后加上“Attribute”以方便区分,使用时则可以省略
属性定义完,接着就是画UI了。 绘制UI的方式有则有两种, 一是继承 “DecoratorDrawer” 类,这可以在原有的基础上添加一些内容,但不提供修改变量的方法,如 [Space] [Header]。 二是继承 “PropertyDrawer” 类,这就需要自己全部重头绘制了,但可以通过方法中的 “property” 参数来修改变量。
    
using UnityEngine ; using UnityEditor ; /*************** DecoratorDrawer ***************/ [ CustomPropertyDrawer ( typeof ( CustomAttribute ) ) ] //关联特性 public class CustomAttributeDrawer : DecoratorDrawer { public override void OnGUI ( Rect position ) { EditorGUI . DrawRect ( position , Color . green ) ; GUI . Label ( position , "DecoratorDrawer" ) ; } } /*************** PropertyDrawer ***************/ [ CustomPropertyDrawer ( typeof ( CustomAttribute ) ) ] //关联特性 public class CustomAttributeDrawer : PropertyDrawer { public override void OnGUI ( Rect position , SerializedProperty property , GUIContent label ) { EditorGUI . DrawRect ( position , Color . green ) ; GUI . Label ( position , "PropertyDrawer" ) ; } //PropertyDrawer 除了用重载 OnGUI 的方式来绘制基于 IMGUI 的 GUI 外, //还可以选择用重载 CreatePropertyGUI 的方式来绘制基于 UIElements 的 GUI。 }
之后定义个变量测试一下。

三、可序列化类型的自定义绘制

上面使用了 “DecoratorDrawer” 和 “PropertyDrawer” 两个类,来关联特性。 实际上我们也可以直接用来关联可序列化的类型。 只需要把 [CustomPropertyDrawer(typeof(...))] 中的特性类换掉就可以了。 [CustomPropertyDrawer(typeof(可序列化类型))]
像上面示例中,变量 test 是 int 类型的。 所以,将 “typeof(CustomAttribute)” 换成 “typeof(int)” 也能做到同样的效果。 不同的是,之后所有 int 类型的变量,默认样式都会改变。
当然,用在自定义的可序列化类型上也是可以的。
    
//==================== Test.cs public class Test : MonoBehaviour { //定义个类,记得加上可序列化特性 [ System . Serializable ] public class MyClass { .. . } //之后定义变量就不用加特性啦 public MyClass c ; } //==================== MyClassDrawer.cs [ CustomPropertyDrawer ( typeof ( Test . MyClass ) ) ] //关联可序列化类型 public class MyClassDrawer : DecoratorDrawer //或者继承PropertyDrawer { .. . }

四、组件的自定义绘制

编写组件的时候,免不了整了一些有关联的变量,或是数组套数组,看着特闹心, 又或是想加个功能按钮之类的,上面的方法就不好用了, 所以这时候,一不做二不休,直接继承 Editor,重画整个组件吧。
    
using UnityEngine ; using UnityEditor ; [ CustomEditor ( typeof ( Test ) ) ] //关联类型 public class TestEditor : Editor { public override void OnInspectorGUI ( ) { Rect position = GUILayoutUtility . GetRect ( 0 , 20 , GUILayout . ExpandWidth ( true ) ) ; EditorGUI . DrawRect ( position , Color . green ) ; GUI . Label ( position , "TestEditor" ) ; } //也可以选择用重载 CreateInspectorGUI 的方式来绘制基于 UIElements 的 GUI。 }
当然,改变Unity自带组件的样式也是可以的。 甚至,GameObject 本身也算个组件。所以,需要的话改变 GameObject 的显示也不是问题。

五、hideFlags

hideFlags 主要用于控制对象销毁,保存以及在检查器中的可见性,一般不会动他。 但我们的游戏物体和组件一般默认是 None 的状态, 而 HideInHierarchy、HideInInspector、NotEditable 也就改变了对象在 Hierarchy 和 Inspector 中的显示, 所以有需要的大可放心用。需要注意的是剩下带 “Dont” 的状态,都需要手动销毁。
    
using UnityEngine ; public class Test : MonoBehaviour { private void Start ( ) { gameObject . hideFlags = HideFlags . NotEditable ; //使gameObject不可编辑 } }

附、参考文档

Unity Manual: · Property Drawers · Custom Editors
Ps. 社区里关于修改编辑器的文章好像比较少,所以打算写一些关于扩展编辑器的内容。 叨逼叨的写了这么多😂,虽然梳理了一边后,我自己又明白了些。 但本人语言组织能力确是不好,作为给别人看的文章,似乎还是太琐碎了,请见谅。 之后我会整一些示例加注释的方式,简单粗暴的展开。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值