一、内建特性
首先是喜闻乐见的特性(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
Scripting API:
·
PropertyAttribute
·
CustomPropertyDrawer
·
DecoratorDrawer
·
PropertyDrawer
·
Editor
·
HideFlags
Ps. 社区里关于修改编辑器的文章好像比较少,所以打算写一些关于扩展编辑器的内容。 叨逼叨的写了这么多😂,虽然梳理了一边后,我自己又明白了些。 但本人语言组织能力确是不好,作为给别人看的文章,似乎还是太琐碎了,请见谅。 之后我会整一些示例加注释的方式,简单粗暴的展开。
本文详细介绍Unity编辑器的扩展方法,包括内建特性、自定义特性、可序列化类型的自定义绘制、组件的自定义绘制及hideFlags的使用,助力开发者提升编辑器功能。
971

被折叠的 条评论
为什么被折叠?



