Search Unity

Property Drawer on elements of an array changing all elements.

Discussion in 'Immediate Mode GUI (IMGUI)' started by LMan, Jan 10, 2017.

  1. LMan

    LMan

    Joined:
    Jun 1, 2013
    Posts:
    493
    I have a property drawer that is supposed to allow the selection of animator parameters from an enumPopup.


    Code (CSharp):
    1. [CustomPropertyDrawer(typeof(Reaction))]
    2. [CanEditMultipleObjects]
    3. public class ReactionDrawer : PropertyDrawer
    4. {
    5.     AnimatorController animCntrl;
    6.     AnimatorControllerParameter[] parameters;
    7.     string[] parameter_names = new string[0];
    8.     int selectedParameter = 0;
    9.  
    10.     // Draw the property inside the given rect
    11.     public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    12.     {
    13.        if(animCntrl == null)
    14.         {
    15.             if(Selection.activeGameObject != null)
    16.             {
    17.                 animCntrl = (AnimatorController)Selection.activeGameObject.GetComponent<Animator>().runtimeAnimatorController;
    18.             }
    19.             else
    20.             {
    21.                 return;
    22.             }
    23.                      
    24.             _init(); //populates the parameter and parameter names array
    25.         }
    26.         SerializedProperty parameter = property.FindPropertyRelative("parameter");
    27.         SerializedProperty type = property.FindPropertyRelative("type");
    28.  
    29.         // Using BeginProperty / EndProperty on the parent property means that
    30.         // prefab override logic works on the entire property.
    31.         EditorGUI.BeginProperty(position, label, property);
    32.  
    33.         // Draw label
    34.         position = EditorGUI.PrefixLabel(position, GUIUtility.GetControlID(FocusType.Passive), label);
    35.  
    36.         var paramRect = new Rect(position.x, position.y, position.width/2, position.height);
    37.         var typeRect = new Rect(position.x + position.width/2 + 5, position.y, position.width/4, position.height);
    38.         var valRect = new Rect(position.x + (position.width/4)*3 + 5 , position.y, position.width/4, position.height);
    39.  
    40.         if (parameters.Length > 0)
    41.         {
    42.             selectedParameter = EditorGUI.Popup(paramRect, selectedParameter, parameter_names);
    43.             parameter.stringValue = parameters[selectedParameter].name;
    44.             SetParameterType(type);
    45.             EditorGUI.LabelField(typeRect, type.enumDisplayNames[type.enumValueIndex]);
    46.             DrawValueField(valRect, property);
    47.         }
    48.         else
    49.         {
    50.             EditorGUI.LabelField(new Rect(position.x, position.y, position.width, position.height), "No parameters found in Animator Controller");
    51.         }
    52.         EditorGUI.EndProperty();
    53.        
    54.     }
    The Reaction Property itself:
    Code (CSharp):
    1. [System.Serializable]
    2. public class Reaction
    3. {
    4.     public enum reactionType {boolean = 0, trigger = 1, integer = 2, floating = 3 , none = 4};
    5.  
    6.     public reactionType type = reactionType.none;
    7.     public string parameter;
    8.  
    9.     //rather than fool with types, keep them all.
    10.     public bool value_bool;
    11.     public int value_int;
    12.     public float value_float;
    13. }
    Pictured, you see the Reactor component, which keeps an array of Reactions.
    Reaction property Drawer.png

    What happens is that when I change the popup value in one box, it changes for all the elements in the array. What am I missing?
     
  2. CDF

    CDF

    Joined:
    Sep 14, 2013
    Posts:
    1,311
    maybe put a BeginChangeCheck and EndChangeCheck around setting the parameter.stringValue.
    "selectedParameter" is probably being re-used.

    Code (CSharp):
    1. EditorGUI.BeginChangeCheck();
    2.  
    3. int newSelectedParameter = EditorGUI.Popup(paramRect, selectedParameter, parameter_names);
    4.  
    5. if (EditorGUI.EndChangeCheck()) {
    6.  
    7.     selectedParameter = newSelectedParameter;
    8.     parameter.stringValue = parameters[selectedParameter].name;
    9.     SetParameterType(type);
    10. }
     
    NS_Joe likes this.
  3. LMan

    LMan

    Joined:
    Jun 1, 2013
    Posts:
    493
    Aha, I found the answer in another thread at last.

     
    daniFMdev likes this.
  4. LMan

    LMan

    Joined:
    Jun 1, 2013
    Posts:
    493
    At the moment, my solution is just to keep an int in the reaction property for recalling the index.
     
  5. mo_star

    mo_star

    Joined:
    Jul 2, 2014
    Posts:
    1
    Hey man I know its old topic, but could you elaborate how you achieved this. Where did you hold the reference to reactive property? I tried putting the reactive property inside the propertydrawer and it still does the same thing.

    I'm creating some custom editors and they are really pain in the butt. Took me 3 days to figure out the workflow of the editor creation code.

    Please help !!!

    Thanks in advance,
    Miran
     
  6. NolwennB

    NolwennB

    Joined:
    Mar 4, 2014
    Posts:
    15
    What I did, based on what LMan said :
    In your serializable class add
    public int index;

    In your PropertyDrawer, replace
    selectedParameter
    with
    property.FindPropertyRelative("index").intValue

    It works for me!
     
    pepperopigeon likes this.
  7. BinaryCats

    BinaryCats

    Joined:
    Feb 8, 2016
    Posts:
    317
    What I do is parse the full path of the property, looking for "Array.Data[x]", and grab the value of x, and that is the index of the thing being drawn
     
  8. pethersk

    pethersk

    Joined:
    Mar 18, 2023
    Posts:
    1
    I'm pretty sure all these guys have moved on or figured it out by now but for anyone else who might find this thread, here's something that might help that i found on a youtube comment hours into my search...

    "Be careful: declaring fields like this (as PropertyDrawer class field) made all the drawn properties to reference single property object. Like if those fields were static and belonged to the class itself. Moving the fields declaration to OnGui (making them local) solved this problem" - John Sailor

    In practice this means you might just have to move your variables into the OnGUI function rather than storing them...
    From this...
    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using UnityEditor;
    5.  
    6. [CustomPropertyDrawer(typeof(Area))]
    7. public class AreaDrawer : PropertyDrawer
    8. {
    9.     const float imageSize = 128;
    10.     const float variableGap = 1;
    11.  
    12.     [B][I]SerializedProperty areaType;
    13.     SerializedProperty areaTiles;
    14.     SerializedProperty areaTexture;
    15.     SerializedProperty selectionVisualTex;[/I][/B]
    16.  
    17.     public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
    18.     {
    19.         float height = variableGap;
    20.         height += imageSize;
    21.  
    22.         return height;
    23.     }
    24.  
    25.     public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    26.     {
    27.         [I][B]if (areaType == null) areaType = property.FindPropertyRelative("areaType");
    28.         if (areaTiles == null) areaTiles = property.FindPropertyRelative("areaTiles");
    29.         if (areaTexture == null) areaTexture = property.FindPropertyRelative("areaTexture");
    30.         if (selectionVisualTex == null) selectionVisualTex = property.FindPropertyRelative("selectionVisualTex");[/B][/I]
    31.  
    32.         EditorGUI.BeginProperty(position, label, property);      
    33.  
    34.  
    35.  
    36.         EditorGUI.EndProperty();
    37.     }
    38. }
    39.  
    To this...
    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using UnityEditor;
    5.  
    6. [CustomPropertyDrawer(typeof(Area))]
    7. public class AreaDrawer : PropertyDrawer
    8. {
    9.     const float imageSize = 128;
    10.     const float variableGap = 1;
    11.  
    12.     public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
    13.     {
    14.         float height = variableGap;
    15.         height += imageSize;
    16.  
    17.         return height;
    18.     }
    19.  
    20.     public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    21.     {
    22.         [I][B]SerializedProperty areaType = property.FindPropertyRelative("areaType");
    23.         SerializedProperty areaTiles = property.FindPropertyRelative("areaTiles");
    24.         SerializedProperty areaTexture = property.FindPropertyRelative("areaTexture");
    25.         SerializedProperty selectionVisualTex = property.FindPropertyRelative("selectionVisualTex");[/B][/I]
    26.  
    27.         EditorGUI.BeginProperty(position, label, property);    
    28.  
    29.  
    30.  
    31.         EditorGUI.EndProperty();
    32.     }
    33. }
    Hope this helps someone :)