Property Drawer on elements of an array changing all elements.

I have a property drawer that is supposed to allow the selection of animator parameters from an enumPopup.

[CustomPropertyDrawer(typeof(Reaction))]
[CanEditMultipleObjects]
public class ReactionDrawer : PropertyDrawer
{
    AnimatorController animCntrl;
    AnimatorControllerParameter[] parameters;
    string[] parameter_names = new string[0];
    int selectedParameter = 0;

    // Draw the property inside the given rect
    public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    {
       if(animCntrl == null)
        {
            if(Selection.activeGameObject != null)
            {
                animCntrl = (AnimatorController)Selection.activeGameObject.GetComponent<Animator>().runtimeAnimatorController;
            }
            else
            {
                return;
            }
                      
            _init(); //populates the parameter and parameter names array
        }
        SerializedProperty parameter = property.FindPropertyRelative("parameter");
        SerializedProperty type = property.FindPropertyRelative("type");

        // Using BeginProperty / EndProperty on the parent property means that
        // prefab override logic works on the entire property.
        EditorGUI.BeginProperty(position, label, property);

        // Draw label
        position = EditorGUI.PrefixLabel(position, GUIUtility.GetControlID(FocusType.Passive), label);

        var paramRect = new Rect(position.x, position.y, position.width/2, position.height);
        var typeRect = new Rect(position.x + position.width/2 + 5, position.y, position.width/4, position.height);
        var valRect = new Rect(position.x + (position.width/4)*3 + 5 , position.y, position.width/4, position.height);

        if (parameters.Length > 0)
        {
            selectedParameter = EditorGUI.Popup(paramRect, selectedParameter, parameter_names);
            parameter.stringValue = parameters[selectedParameter].name;
            SetParameterType(type);
            EditorGUI.LabelField(typeRect, type.enumDisplayNames[type.enumValueIndex]);
            DrawValueField(valRect, property);
        }
        else
        {
            EditorGUI.LabelField(new Rect(position.x, position.y, position.width, position.height), "No parameters found in Animator Controller");
        }
        EditorGUI.EndProperty();
       
    }

The Reaction Property itself:

[System.Serializable]
public class Reaction
{
    public enum reactionType {boolean = 0, trigger = 1, integer = 2, floating = 3 , none = 4};

    public reactionType type = reactionType.none;
    public string parameter;

    //rather than fool with types, keep them all.
    public bool value_bool;
    public int value_int;
    public float value_float;
}

Pictured, you see the Reactor component, which keeps an array of Reactions.
2914687--215015--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?

maybe put a BeginChangeCheck and EndChangeCheck around setting the parameter.stringValue.
“selectedParameter” is probably being re-used.

EditorGUI.BeginChangeCheck();

int newSelectedParameter = EditorGUI.Popup(paramRect, selectedParameter, parameter_names);

if (EditorGUI.EndChangeCheck()) {

    selectedParameter = newSelectedParameter;
    parameter.stringValue = parameters[selectedParameter].name;
    SetParameterType(type);
}
1 Like

Aha, I found the answer in another thread at last.

1 Like

At the moment, my solution is just to keep an int in the reaction property for recalling the index.

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

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!

1 Like

What I do is parse the full path of the property, looking for “Array.Data[×]”, and grab the value of x, and that is the index of the thing being drawn

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…

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;

[CustomPropertyDrawer(typeof(Area))]
public class AreaDrawer : PropertyDrawer
{
    const float imageSize = 128;
    const float variableGap = 1;
  
    [B][I]SerializedProperty areaType;
    SerializedProperty areaTiles;
    SerializedProperty areaTexture;
    SerializedProperty selectionVisualTex;[/I][/B]

    public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
    {
        float height = variableGap;
        height += imageSize;

        return height;
    }

    public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    {
        [I][B]if (areaType == null) areaType = property.FindPropertyRelative("areaType");
        if (areaTiles == null) areaTiles = property.FindPropertyRelative("areaTiles");
        if (areaTexture == null) areaTexture = property.FindPropertyRelative("areaTexture");
        if (selectionVisualTex == null) selectionVisualTex = property.FindPropertyRelative("selectionVisualTex");[/B][/I]

        EditorGUI.BeginProperty(position, label, property);      



        EditorGUI.EndProperty();
    }
}

To this…

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;

[CustomPropertyDrawer(typeof(Area))]
public class AreaDrawer : PropertyDrawer
{
    const float imageSize = 128;
    const float variableGap = 1;

    public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
    {
        float height = variableGap;
        height += imageSize;

        return height;
    }

    public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    {
        [I][B]SerializedProperty areaType = property.FindPropertyRelative("areaType");
        SerializedProperty areaTiles = property.FindPropertyRelative("areaTiles");
        SerializedProperty areaTexture = property.FindPropertyRelative("areaTexture");
        SerializedProperty selectionVisualTex = property.FindPropertyRelative("selectionVisualTex");[/B][/I]

        EditorGUI.BeginProperty(position, label, property);     



        EditorGUI.EndProperty();
    }
}

Hope this helps someone :slight_smile: