Write in front
When using UnityEditor to customize the property panel, we mostly use the method of inheriting Editor directly. One script class writes one Editor class, but there are many non monobehavior classes that need to be used in different scripts. At this time, the demand comes. It's too troublesome to write each one repeatedly, You can use PropertyDrawer at this time.
Unity version I use: Unity 2020.3.10
summary
Two main functions of PropertyDrawer:
1. It is used to customize and draw serializable classes or structures
Classes or structures that need custom display and may be used in multiple scripts can be implemented using PropertyDrawer. PropertyDrawer, as its name is, is like a special property painter. After custom property drawing is performed on a class or structure, when the script displays the property on the property panel, it will call the custom drawing, We don't need to write Editor script to draw separately, so a lot of work is saved
2. Use PropertyAttribute to customize the Attribute
I believe that you have used attributes such as [Header] [ToolTip] in ordinary times, and you can customize your own attributes through PropertyAttribute+PropertyDrawer
Custom draw serializable classes or structures
Here we use the class of book and book list to illustrate this function
Book is the class we want to customize drawing
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); //FocusType.Passive will not be selected when switching with Tab, and FocusType.Keyboard will be selected when switching with Tab. Obviously, we don't need label to be selected for editing position = EditorGUI.PrefixLabel(position, GUIUtility.GetControlID(FocusType.Passive), label); //Do not let the indentLevel level affect the drawing of the same line, because PropertyDrawer may be used in many places, and nested use may occur 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(); } }
Custom before and after comparison
Later, we have a library class. At this time, we don't need to write the Editor class, which has a direct effect
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 custom Attribute
Custom Attribute allows us to define many very convenient attributes to use. We implement a Label Attribute here. We often complain that we can't understand the parameters on the script. With LabelAttribute, the localization work is too convenient
You can give full play to your creativity according to your own needs
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("Book name")] public string bookName; [LabelAttribute("Book type")] public BookType bookType; [LabelAttribute("Book price")] public float price; }
Not yet understood
PropertyDrawer also has a rewritable method CreatePropertyGUI, but I don't know how to use it. Where is it used? I haven't used it yet. I'll look at it later. I still don't understand it according to the official documents.
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; } }