Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Resolved Hitboxes from PropertyDrawer Extends too Far

Discussion in 'Scripting' started by RadRedPanda, Sep 28, 2023.

  1. RadRedPanda

    RadRedPanda

    Joined:
    May 9, 2018
    Posts:
    1,593
    My PropertyDrawer's hitbox covers everything within the property, when I click on any field below it, it opens the top drop-down field. I'm quite new to PropertyDrawers, so any help would be appreciated.

    upload_2023-9-28_15-48-14.png
    I click here, and yet it's the dropdown above it (the Action Type, 2 fields above it) which gets opened.

    Here's code. (edited to remove some fluff)
    Code (CSharp):
    1. [CustomPropertyDrawer(typeof(CutsceneAction))]
    2. public class CutsceneActionDrawer : PropertyDrawer
    3. {
    4.     private Rect _nextRect;
    5.  
    6.     public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    7.     {
    8.         _nextRect = position;
    9.  
    10.         SerializedProperty actionType = createPropertyField(property, "ActionType", "Action Type");
    11.  
    12.         switch (actionType.enumNames[actionType.enumValueIndex])
    13.         {
    14.             case "Wait":
    15.                 createPropertyField(property, "Duration", "Duration");
    16.                 break;
    17.             case "Move":
    18.                 createPropertyField(property, "ActorTransform", "Actor");
    19.                 createPropertyField(property, "TargetTransform", "Target");
    20.                 createPropertyField(property, "Duration", "Duration");
    21.                 break;
    22.         }
    23.     }
    24.  
    25.     private SerializedProperty createPropertyField(SerializedProperty property, string propertyName, string labelString)
    26.     {
    27.         Rect newRect = new Rect(_nextRect);
    28.         SerializedProperty targetProperty = property.FindPropertyRelative(propertyName);
    29.         EditorGUI.PropertyField(newRect, targetProperty, new GUIContent(labelString));
    30.  
    31.         _nextRect = newRect;
    32.         _nextRect.yMin += EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing;
    33.         _nextRect.height = EditorGUIUtility.singleLineHeight;
    34.         return targetProperty;
    35.     }
    36.  
    37.     public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
    38.     {
    39.         SerializedProperty actionType = property.FindPropertyRelative("ActionType");
    40.         int lines;
    41.         switch (actionType.enumNames[actionType.enumValueIndex])
    42.         {
    43.             case "Wait":
    44.                 lines = 2;
    45.                 break;
    46.             default:
    47.                 lines = 4;
    48.                 break;
    49.         }
    50.         return EditorGUIUtility.singleLineHeight * lines + EditorGUIUtility.standardVerticalSpacing * lines - 1;
    51.     }
    52. }
    I'm not really following a tutorial on PropertyDrawers, so if I'm doing something terrible please let me know. Most if it has been from trial and error and looking at snippets of other people's code.
     
  2. RadRedPanda

    RadRedPanda

    Joined:
    May 9, 2018
    Posts:
    1,593
    Bumping the thread, as I've made no progress.
     
  3. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    3,495
    Well, the key of a property drawer is that it is responsible for drawing "one" property. You can construct more complex properties, however GetPropertyHeight is responsible for allocating the total vertical space that your drawer needs. The Position Rect that is passed to OnGUI has that height. Currently you set the height of your "_nextRect" AFTER you've drawn your first property. So your first property will occupy the full space.

    You should set the height of one line once before you draw your properties. Also since Rect has a yMax property you can simply do

    Code (CSharp):
    1. rect.y = rect.yMax + EditorGUIUtility.standardVerticalSpacing;
    assuming rect.height is actually a single line height. This simply positions the rect at the bottom of the previous one + the spacing.

    Also I guess inside GetPropertyHeight the last part should be
    Code (CSharp):
    1. ... + EditorGUIUtility.standardVerticalSpacing * (lines - 1);
    instead.

    IMGUI is processed "in order". Since your first Rect is currently spanning the whole area of the drawer, the first element would "eat" all input events.

    For "handing out" individual rects it might make sense to use a method like;
    Code (CSharp):
    1. public static Rect GetRect(ref Rect aRect, float aHeight = 0f)
    2. {
    3.     var r = aRect;
    4.     if (aHeight > 0)
    5.         r.height = aHeight;
    6.     else
    7.         r.height = EditorGUIUtility.singleLineHeight;
    8.     aRect.yMin = r.yMax + EditorGUIUtility.standardVerticalSpacing;
    9.     return r;
    10. }
    This method can be used to "cut off" a line from the initial rect. The original rect would be reduced to cover the remaining space. So you can simply do

    Code (CSharp):
    1. GetRect(ref position)
    to get the next line rect.
     
    karl_jones and RadRedPanda like this.
  4. RadRedPanda

    RadRedPanda

    Joined:
    May 9, 2018
    Posts:
    1,593
    Thanks for the response! I'll take a look through it later when I get around to it, but wanted to let you know I appreciate you helping.
     
  5. RadRedPanda

    RadRedPanda

    Joined:
    May 9, 2018
    Posts:
    1,593
    That really helped a lot! My problem was I had a fundamental misunderstanding of what
    position
    was from the OnGUI method, and had been looking in the wrong place the whole time.