「Unity3D」(10)自定义属性面板Inspector详解
通常情况是不需要扩展属性面板的,也就是Inspector,但如果需要开发定制的工具和插件,那么就需要扩展属性面板来完成需求。本文会对自定义扩展Inspector进行全面的介绍。工具类和概念的介绍
自定义Inspector有两个层面,第一个是扩展已有属性的功能,第二个是按照意图增加新的面板功能。扩展已有的功能,包括替换显示文字,动态控制可见性,增加动画,错误提示,数据的校验和限制等等。增加新的功能就是按照需求定制更为复杂的功能。
Unity的内置工具类提供了必要的实现基础,主要有EditorGUILayout和EditorGUI两大类。两者有重叠的部分,也有特有的部分。总体上来说,EditorGUILayout是带有自动布局的,EditorGUI需要传入Rect来对控件进行定位。另外,这两者和GUILayout与GUI概念非常类似,只不过GUI的主要绘制是在场景中(运行时,游戏中可见),EditorGUI上在Inspector中(仅在编辑器中)。
自定义流程
一共分为三步:
[*]第一步,继承Editor父类。
[*]第二步,添加注解,告诉编辑器这个类是扩展哪个组件的Inspector。
[*]第三步,覆写OnInspectorGUI方法,实现自定义的扩展。
public class CustomEditorTest : MonoBehaviour
{
public int intValue;
public bool boolValue;
public Vector2 v2;
public float[] floatArray = new float[] {1.0f, 2.0f, 3.0f};
}
public class CustomEditorTestEditor : Editor
{
public override void OnInspectorGUI()
{
// 自定义绘制Inspector
}
}
自定义属性详细介绍
[*]绘制原有属性
public override void OnInspectorGUI()
{
// 绘制全部原有属性
base.DrawDefaultInspector()
// 后面可以扩展自己功能
}
[*]自定义绘制
有两个重要的内置对象,target和serializedObject。target代表的是CustomEditorTest本身,而serializedObject代表的是当前Inspector的可绘制对象。
public override void OnInspectorGUI()
{
// 更新显示
this.serializedObject.Update();
// 自定义绘制
// 应用属性修改
this.serializedObject.ApplyModifiedProperties();
}
[*]绘制已经有的属性
有两个接口用来查找已有的属性,FindProperty和FindPropertyRelative。FindProperty用来查找当前属性名对应的属性对象,FindPropertyRelative查找相对于属性对象的属性。
// 显示intValue属性
EditorGUILayout.PropertyField(this.serializedObject.FindProperty("intValue"));
// 显示boolValue并替换属性标签为GUIContent
EditorGUILayout.PropertyField(this.serializedObject.FindProperty("boolValue"), this.boolValueContent);
var v2Property = this.serializedObject.FindProperty("v2");
// 现实v2属性
EditorGUILayout.PropertyField(v2Property);
// 设置v2属性的x属性值
v2Property.FindPropertyRelative("x").floatValue = 999.0f;
// 分隔符
EditorGUILayout.Separator();
EditorGUILayout.PropertyField能够在Inspector上绘制属性控件,如果返回false表示属性控件不可用,或者处在不可见的状态。
[*]绘制数组属性
// 查找floatArray属性
var elements = this.serializedObject.FindProperty("floatArray");
// 属性元素可见,控件展开状态
if (EditorGUILayout.PropertyField(elements))
{
// 缩进一级
EditorGUI.indentLevel++;
// 设置元素个数
elements.arraySize = EditorGUILayout.DelayedIntField("Size", elements.arraySize);
// 绘制元素
for (int i = 0, size = elements.arraySize; i < size; i++)
{
// 检索属性数组元素
var element = elements.GetArrayElementAtIndex(i);
EditorGUILayout.PropertyField(element);
}
// 重置缩进
EditorGUI.indentLevel--;
}
// 空格
EditorGUILayout.Space();
对elements.arraySize赋值就会自动设置数组容量。DelayedIntField在回车的时候,控件设置的数值才会返回到arraySize。
[*]绘制默认数组和对象
public CustomData data;
public CustomData[] datas;
public class CustomData
{
public int a;
public int b;
public int c;
}
// 第二个参数为true,则会默认绘制所有子元素
EditorGUILayout.PropertyField(this.serializedObject.FindProperty(&#34;data&#34;),true);
EditorGUILayout.PropertyField(this.serializedObject.FindProperty(&#34;datas&#34;), true);
[*]绘制折叠动画
// 每个动画都需要一个AnimBool
private AnimBool fadeGroup;
private void OnEnable()
{
this.fadeGroup = new AnimBool(true);
// 注册动画监听
this.fadeGroup.valueChanged.AddListener(this.Repaint);
}
private void OnDisable()
{
// 移除动画监听
this.fadeGroup.valueChanged.RemoveListener(this.Repaint);
}
// target控制动画开始播放
this.fadeGroup.target = EditorGUILayout.Foldout(this.fadeGroup.target, &#34;BeginFadeGroup&#34;, true);
// 系统使用tween渐变faded数值
if (EditorGUILayout.BeginFadeGroup(this.fadeGroup.faded))
{
EditorGUILayout.BoundsField(&#34;BoundsField&#34;, new Bounds());
EditorGUILayout.BoundsIntField(&#34;BoundsIntField&#34;, new BoundsInt());
}
// begin - end 之间元素会进行动画
EditorGUILayout.EndFadeGroup();
// 又一种风格的空格
GUILayout.Space(10);
Editor中依然可以使用GUILayout的功能。
[*]绘制水平布局
// 水平布局,并使用box皮肤
EditorGUILayout.BeginHorizontal(GUI.skin.box);
// 使用了GUILayout去覆盖自动布局的设置
EditorGUILayout.LabelField(&#34;This is BeginHorizontal&#34;,
GUILayout.MaxWidth(150.0f));
EditorGUILayout.DelayedDoubleField(11.1f);
EditorGUILayout.DelayedTextField(&#34;DelayedTextField&#34;);
EditorGUILayout.DropdownButton(GUIContent.none, FocusType.Passive);
EditorGUILayout.EndHorizontal();
EditorGUILayout.Separator();
[*]绘制垂直布局
EditorGUILayout.BeginVertical(GUI.skin.box);
// 依然可以使用GUILayout来显示控件
GUILayout.Box(&#34;This is BeginVertical&#34;);
EditorGUILayout.ColorField(&#34;ColorField&#34;, Color.yellow);
EditorGUILayout.CurveField(&#34;CurveField&#34;, new AnimationCurve(),
GUILayout.MaxWidth(400.0f));
EditorGUILayout.HelpBox(&#34;HelpBox&#34;, MessageType.Info);
EditorGUILayout.EnumFlagsField(&#34;EnumFlagsField&#34;, this.types);
EditorGUILayout.EnumPopup(&#34;EnumPopup&#34;, this.types);
this.selectOption = EditorGUILayout.IntPopup(&#34;IntPopup&#34;, (int) this.selectOption, new string[] {&#34;0&#34;, &#34;1&#34;, &#34;2&#34;}, new int[] {0, 1, 2});
this.selectOption = EditorGUILayout.IntSlider(&#34;IntSlider&#34;, (int) this.selectOption, 0, 2);
this.selectOption = EditorGUILayout.MaskField(&#34;MaskField&#34;, (int) this.selectOption, new string[] {&#34;mask1&#34;, &#34;mask2&#34;, &#34;mask3&#34;});
this.selectOption = EditorGUILayout.Popup(&#34;Popup&#34;, (int) this.selectOption, new string[] {&#34;s1&#34;, &#34;s2&#34;, &#34;s3&#34;});
EditorGUILayout.EndVertical();
EditorGUILayout.Separator();
[*]绘制滚动区域
// 超出内容会出现滚动条
this.scrollPos = EditorGUILayout.BeginScrollView(this.scrollPos, GUI.skin.box);
GUILayout.Box(&#34;this is BeginScrollView&#34;);
EditorGUILayout.TextArea(&#34;this is TextArea&#34;);
EditorGUILayout.RectField(&#34;RectField&#34;, new Rect());
this.toggle = EditorGUILayout.Toggle(&#34;Toggle&#34;, this.toggle);
// 在toggleGroup之间的会被整体设置可用性
this.toggleGroup = EditorGUILayout.BeginToggleGroup(&#34;BeginToggleGroup&#34;, this.toggleGroup);
GUILayout.Button(&#34;Btn1&#34;);
GUILayout.Button(&#34;Btn2&#34;);
GUILayout.Button(&#34;Btn3&#34;);
EditorGUILayout.EndToggleGroup();
EditorGUILayout.EndScrollView();
总结
[*]PropertyField方法是用来显示已有属性控件的,第二个参数为true则绘制所有子元素。
[*]Delayed开头的Field方法,是延迟控件。回车确定后,数值才会返回。
[*]其它Field方法是用来绘制自定义控件的。
[*]Begin - End 是区域绘制控件,在之间的绘制会被整体控制。
[*]可以混合使用GUI和GUILayout以及EditorGUI进行绘制。
[*]GUIContent 提供了重写控件名字和添加提示的机会。
[*]GUILayoutOption 是覆盖自动布局的设置,属性来自于GUILayout类。
推荐一个Unity编辑器插件:
MojoUnity-Editor:一个简单通用的Unity属性面板(Inspector)和窗口(Window)的扩展
<hr/> mark感谢楼主!
页:
[1]