写在前面
我们在使用UnityEditor自定义属性面板时,大多的时候都是直接使用继承Editor的方式实现,一个脚本类写一个Editor类,但是有很多非MonoBehaviour的类,会需要在不同的脚本上使用,这个时候需求就来了,每一个都要重复写那也太麻烦了,这个时候就可以用PropertyDrawer了。
我使用的Unity版本:Unity 2020.3.10
总结
PropertyDrawer的两大主要作用: 1. 用于自定义绘制可序列化的类或结构体 有需要自定义显示的又会可能在多个脚本中使用到的类或结构体都可以使用PropertyDrawer来实现,PropertyDrawer如其名,就像一个专门的属性绘制器一样,对一个类或结构体进行自定义属性绘制后,当脚本在属性面板上显示该属性时,就会调用到该自定义的绘制,不用我们再写Editor脚本去单独绘制,所以省去了大量的工作 2. 搭配PropertyAttribute可自定义Attribute 相信大家平常都有用过诸如【Header】【ToolTip】之类的Attribute,而通过PropertyAttribute+PropertyDrawer就可以自定义自己的Attribute
自定义绘制可序列化的类或结构体
这里我们使用书,书单的类来示例这个功能 Book就是我们要进行自定义绘制的类
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public enum BookType
{
Novel,
Science,
Life,
Tool,
}
[System.Serializable]
public class Book
{
public string name;
public BookType type;
public string overview;
}
public class BookList : MonoBehaviour
{
public Book currentRead;
public Book[] readList;
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
[CustomPropertyDrawer(typeof(Book))]
public class BookDrawer : PropertyDrawer
{
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
EditorGUI.BeginProperty(position, label, property);
position = EditorGUI.PrefixLabel(position, GUIUtility.GetControlID(FocusType.Passive), label);
var indent = EditorGUI.indentLevel;
EditorGUI.indentLevel = 0;
var nameRect = new Rect(position.x, position.y, 50, position.height);
var typeRect = new Rect(position.x + 55, position.y, 60, position.height);
var overviewRect = new Rect(position.x + 120, position.y, position.width - 90, position.height);
EditorGUI.PropertyField(nameRect, property.FindPropertyRelative("name"), GUIContent.none);
EditorGUI.PropertyField(typeRect, property.FindPropertyRelative("type"), GUIContent.none);
EditorGUI.PropertyField(overviewRect, property.FindPropertyRelative("overview"), GUIContent.none);
EditorGUI.indentLevel = indent;
EditorGUI.EndProperty();
}
}
自定义前后对比 后来我们又有一个书库的类,这个时候就不用再写Editor类了,直接就有效果
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[System.Serializable]
public class BookTypeList
{
public string name;
public Book[] bookList;
}
public class BookLibrary : MonoBehaviour
{
[SerializeField]
public List<BookTypeList> library;
}
PropertyAttribute+PropertyDrawer自定义Attribute
自定义Attribute可以让我们定义很多非常方便的Attribute去使用,我们这里实现一个Label的Attribute,经常策划会抱怨看不懂脚本上的参数,有了LabelAttribute,汉化工作就方便太多了 大家可以根据自己的需要大胆发挥创作
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class LabelAttribute : PropertyAttribute
{
public string name;
public LabelAttribute(string name)
{
this.name = name;
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
[CustomPropertyDrawer(typeof(LabelAttribute))]
public class LabelAttributeDrawer : PropertyDrawer
{
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
LabelAttribute labelAttribute = this.attribute as LabelAttribute;
EditorGUI.PropertyField(position, property, new GUIContent(labelAttribute.name));
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class LabelAttributeTest : MonoBehaviour
{
[LabelAttribute("书籍名称")]
public string bookName;
[LabelAttribute("书籍类型")]
public BookType bookType;
[LabelAttribute("书籍价格")]
public float price;
}
还未搞明白的
PropertyDrawer还有一个可重写的方法 CreatePropertyGUI,但不知如何用,具体用在哪里?目前还没有使用到,后续碰到再看,目前根据官方文档还是搞不明白。
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEditor.UIElements;
using UnityEngine.UIElements;
[CustomPropertyDrawer(typeof(Book))]
public class BookDrawerUIE : PropertyDrawer
{
public override VisualElement CreatePropertyGUI(SerializedProperty property)
{
var container = new VisualElement();
var nameField = new PropertyField(property.FindPropertyRelative("name"));
var typeField = new PropertyField(property.FindPropertyRelative("type"));
var overviewField = new PropertyField(property.FindPropertyRelative("overview"));
container.Add(nameField);
container.Add(typeField);
container.Add(overviewField);
return container;
}
}
|