Search Unity

When to choose a custom editor vs. property drawer

Discussion in 'Immediate Mode GUI (IMGUI)' started by liortal, Jun 13, 2015.

  1. liortal

    liortal

    Joined:
    Oct 17, 2012
    Posts:
    3,562
    Hey,

    In order for a particular custom type of mine to be displayed in a custom way, there's the option to use either a full custom inspector, or to define a new property drawer for this type.

    I'd like to know - what are the differences between the two, and how should i decide on what to create for my types ?
     
    Kokowolo likes this.
  2. mambo_2

    mambo_2

    Joined:
    Aug 19, 2013
    Posts:
    19
    I think this should be obvious enough but for sake of clarification I will try to explain it simply,

    custom inspectors draw how a class fields should look.
    property drawers draw how a property should look.

    so if you have a property you want to customize and only that property then just go with property drawers, less work since you only have to tag that property with your custom attribute, without having to create a new editor class every time, for sake of simplicity property drawers are generic meaning they deal with the type, so if you create a property drawer for a string type you can choose to implement it for every string field you define or not using attributes, custom inspectors will draw your class no matter and you will have a hard time referencing a property everytime.


    imagine this:


    Code (CSharp):
    1. Class A : Monobehaviour
    2. {
    3. [SerializedField]
    4. string myString;
    5. }
    6.  
    7. Class AClassEditor : Editor
    8. {
    9. A myAClass;
    10.  
    11. void OnEnable()
    12. {
    13. A = target as A;
    14. void override OnInspectorGUI()
    15. {
    16. A.myString = EditorGUILayout.PropertyDrawer(etc...);
    17. }
    18. }
    19. }
    this would be done for every class that contains a string value and that value needs to be drawn the same why, while you can do this :

    Code (CSharp):
    1. Class A : MonoBehaviour
    2. {
    3. [customStringDrawerAttribute]
    4. string myCustomString;
    5. }
    6.  
    7.  
    8. Class A : MonoBehaviour
    9. {
    10. [customStringDrawerAttribute]
    11. string myCustomString;
    12. }
    13.  
    14.  
    15. Class B : MonoBehaviour
    16. {
    17. [customStringDrawerAttribute]
    18. string myCustomString;
    19. }
    20.  
    21. Class C : MonoBehaviour
    22. {
    23. [customStringDrawerAttribute]
    24. string myCustomString;
    25. }
    26.  
    27.  
    28. Class D : MonoBehaviour
    29. {
    30. [customStringDrawerAttribute]
    31. string myCustomString;
    32. }
     
  3. TonyLi

    TonyLi

    Joined:
    Apr 10, 2012
    Posts:
    12,694
    In practical terms, opt for property drawers whenever you can, which is along the same lines @mambo_2 suggested. Even if you have a class with several different variables with their own property drawers, it's usually better than a custom editor, especially when you add in other attribute tags such as [Space], [Header], etc. You'll almost certainly end up using at least some of those property drawers elsewhere. This avoids duplicate code. And, if you add a new variable, you don't need to update a custom editor to be able to see it in the inspector.
     
  4. Sharlatan

    Sharlatan

    Joined:
    Jul 23, 2013
    Posts:
    111
    I'm a bit late to the party but hopefully someone still reads this =)

    I get mambo_2's example but he's only talking about using property drawers to create new porperty attributes. But you can also rewrite the appearance of a whole script/class, like they do in the first example on this page: http://docs.unity3d.com/ScriptReference/PropertyDrawer.html

    They even state
    And the first one looks to me like it achieves more or less the same a custom editor would (please correct me if I'm wrong).
    If I want to define how a whole class is displayed in the inspector, in which case should I be using a custom editor and in which case a property drawer? Is it a matter of taste or is there a good reason to chose one over the other in certain cases?

    Thanks a lot!
     
  5. TonyLi

    TonyLi

    Joined:
    Apr 10, 2012
    Posts:
    12,694
    Someone else might have some clever distinction to offer, but to me it just seems like personal preference.
     
  6. BMayne

    BMayne

    Joined:
    Aug 4, 2014
    Posts:
    186
    Hey there,

    Here is the quick difference between them.

    Custom Editor: Used for drawing a UnityEngine.Object class in full.
    Property Drawer: Used from drawing a nested class, struct, or Attribute inside of a UnityEngine.Object

    *You can also mix and match Editors and Property Drawers if you are using SerializedProperties.

    Here is a few other comparisons

    Property Drawers work only with Serialized Properties ⇒ Editor Windows can get the instance.
    If you have a custom Property Drawer for a class that is not a MonoBehavior or ScriptableObject there is not an easy way to get the instance of the class you are currently editing. With SerializedProperties you can only get the objectReferenceValue back as a UnityEngine.Object which your class will not be.

    *They also expose the FieldInfo where you can get the instance but it's not pretty and not everyone likes using reflection.


    Editor Windows can't easily be reused ⇒ Property Drawers can
    As people were saying property drawers can be reused all over you code. Editor windows can't easily be reused since they have to target a class type that inherits from UnityEngine.Object. Property Drawers can target a Attribute or any class.

    Property Drawers can't use the GUILayout system ⇒ Editor Windows can
    For performance reasons the Property Drawer can not use the GUILayout system. You are sent a Rect and you have to layout the editor yourself. An Editor Window can take full advantage of this system (which can save time).

    You can't really save local variables with Property Drawers ⇒ Custom Editor you can
    When you see your Custom Editor you are looking at an instance that Unity creates for you. You can modify this and save variables but once you look away from the Editor it's disposed.

    Property Drawers work a little bit differently. Lets say you have four public variables that have a custom Property Drawer. Unity creates one instance to draw all of them, it just reuses the same instance (this is why you are sent the serialized property with both it's functions). This means if you set a variable it's shared with all of the property drawers. This can be a very big limitation if you are trying to be fancy.

    I hope that helps clear up some details.

    Cheers,
     
    Last edited: Sep 19, 2015
  7. Meatloaf4

    Meatloaf4

    Joined:
    Jul 30, 2013
    Posts:
    183
    This thread was extremely helpful!

    I myself was a bit confused on the difference and all the answers above were excellent. Especially @BMayne breakdown.

    Thanks!
     
  8. BinaryCats

    BinaryCats

    Joined:
    Feb 8, 2016
    Posts:
    317
    Bleh, I try to use attributes when I can. I think its just neater.

    But I use custom editor when I need to use the .FindPropertyRelative(...) from the sterilized property. (in my case I have a custom drawer, which data links multiple objects), something that isn't possible with attributes
     
  9. liortal

    liortal

    Joined:
    Oct 17, 2012
    Posts:
    3,562
    @BMayne your answer is great; reading it again now, i find the term "editor windows" confusing, since you can also create editor windows in Unity (e.g: derive from EditorWindow), not to be confused with custom editors (e.g: inspectors).
     
    CheekySparrow78 and BMayne like this.
  10. skalev

    skalev

    Joined:
    Feb 16, 2012
    Posts:
    264
    Another thing to remember is that you can't use property drawers to draw an entire collection (List, array). In the case of a collection, OnGUI() in your property drawer will be called once per element in the collection, and the standard GUI will be drawn around it.

    @BinaryCats - you can access other properties from a drawer, SerializedProperty has a field for serializedObject, that will return the object that contains the property, and you can use that reference to access sibling SerializedProperties.
     
  11. BinaryCats

    BinaryCats

    Joined:
    Feb 8, 2016
    Posts:
    317
    @skalev

    correct me if I am wrong
    You can access the fields in the attribute. Not of the class using the attribute, iirc it errors saying it doesn't know the instance it is being used in. i.e.

    Code (csharp):
    1.  
    2. RangeAttribute range = attribute as RangeAttribute;
    3. range.max....
    4.  
    But you cant manipulate varibles in the thing using (in this case the) Range attribute.

    Custom editors cant directly do it either, but atleast with a custom editor you create an object, which you can manipulate.

    ----

    Holy Smokes, I take that back, I misread. I was trying to get the relatives from the property, I didn't know about the object thanks.

    For any that might come across this:

    Code (csharp):
    1.  
    2. range.m_bool = EditorGUI.Toggle(boolrect, range.m_bool);//Store the tickbox value
    3.  
    4. if (range.m_bool != oldValue)//Check if it has changed from the last draw
    5. {
    6.  
    7. property.serializedObject.FindProperty(range.m_text).boolValue = !property.serializedObject.FindProperty(range.m_text).boolValue;// Change the relative's value
    8.  
    9. oldValue = range.m_bool;
    10.  //store this so we can check if it has changed,
    11. }
    12.  
    13.  
    14.  
    Thanks a lot. This has opened up many possibilities for me now.
     
    Last edited: Feb 19, 2016
    mondzi likes this.
  12. DSivtsov

    DSivtsov

    Joined:
    Feb 20, 2019
    Posts:
    151
    I want to add my five cents, because I didn't find this information upper.
    If we compare the Drawer solution for a specific field (type) in an Inspector based on a "CustomEditor or PropertyDrawer", the main differences (and different uses) will be:
    • In case the "PropertyDrawer" the can write Drawer for Type (Class) only which not derived from MonoBehaviour or ScriptableObject you must create "Clean C# Data Class" (or use a standard Types that is possible, but doesn't make a lot of sense)
    • In case the "CustomEditor" we can also write Drawer to any Custom Class (include typical MonoBehaviour scripts etc.)
    Also the "PropertyDrawer" only give possibility to change appearance & acting in Inspector (OnGUI / CreatePropertyGUI), the "CustomEditor" also can possibility to change appearance & acting in Scene View (OnInspectorGUI and OnSceneGUI and many others).

    The simple Example use both variants together:
    Exist Custom Type ("Class") CameraMode and "MonoBehaviour Class" LookAtPoint. In Scene One GameObject with script LookAtPoint (the both "drawers" in any directory named the Editor)
    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. public enum CameraMode : byte
    4. {
    5.     LockedOn,
    6.     LookAt,
    7.     Free
    8. }
    9.  
    10. public class LookAtPoint : MonoBehaviour
    11. {
    12.     [SerializeField] private Transform Target;
    13.     [SerializeField] private CameraMode CamMode;
    14.  
    15.     public Vector3 PositionTarget => Target.position;
    16.  
    17.     void Update()
    18.     {
    19.         transform.LookAt(Target);
    20.     }
    21.  
    22.     public void UpdatePositionTarget(Vector3 newPostiion)
    23.     {
    24.         Target.position = newPostiion;
    25.         Update();
    26.     }
    27. }
    Simple Drawer for CameraMode (only change label before field in Inspector)
    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEditor;
    3.  
    4. [CustomPropertyDrawer(typeof(CameraMode))]
    5. public class CameraModeDrawer : PropertyDrawer
    6. {
    7.     public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    8.     {
    9.         EditorGUI.BeginProperty(position, null, property);
    10.         EditorGUI.PropertyField(position, property, new GUIContent("Mode of Cam"));
    11.         EditorGUI.EndProperty();
    12.     }
    13. }
    And Drawer for LookAtPoint also very simple only change order of fields in Inspector and also add functionality to Scene View
    Code (CSharp):
    1. using UnityEditor;
    2. using UnityEngine;
    3.  
    4. [CustomEditor(typeof(LookAtPoint))]
    5. public class LookAtPointEditor : Editor
    6. {
    7.     SerializedProperty lookAtPoint;
    8.     SerializedProperty camMode;
    9.  
    10.     void OnEnable()
    11.     {
    12.         lookAtPoint = serializedObject.FindProperty("Target");
    13.         camMode = serializedObject.FindProperty("CamMode");
    14.     }
    15.  
    16.     public override void OnInspectorGUI()
    17.     {
    18.         serializedObject.Update();
    19.         EditorGUILayout.PropertyField(camMode);
    20.         EditorGUILayout.PropertyField(lookAtPoint);
    21.         serializedObject.ApplyModifiedProperties();
    22.     }
    23.  
    24.     public void OnSceneGUI()
    25.     {
    26.         LookAtPoint t = (target as LookAtPoint);
    27.         EditorGUI.BeginChangeCheck();
    28.         Vector3 pos = Handles.PositionHandle(new Vector3(t.PositionTarget.x, t.PositionTarget.y, t.PositionTarget.z), Quaternion.identity);
    29.         if (EditorGUI.EndChangeCheck())
    30.         {
    31.             Undo.RecordObject(t.Target, "Move point");
    32.             t.UpdatePositionTarget(new Vector3(pos.x, pos.y, pos.z));
    33.         }
    34.     }
    35. }
    P.S> The Property Attributes is a more universal PropertyDrawer (it give many useful possibilities but you must design it more careful because the attribute can set to any field type). @BMayne and other wrote many useful, but more detail
     
    Last edited: Oct 7, 2021
    Hozgen90 and archangel007 like this.