Field value using custom PropertyAttribute is duplicated across list entries

Hi,

I am trying to have a list of objects A in component B. In object type A, a public variable is set with a custom property attribute, which provides a drop-down-list in inspector for that field.

The problem is, that the selection in one entry object in the list seems to change all other fields of that type in all the objects in the list as well, and this is of course not what I want.

The attribute and drawer code I downloaded somewhere from a link provided by Unity. Here is my test code:

PopupTest.cs

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

[System.Serializable]
public class ListEntry
{
    [Popup ("value1", "value2", "value3")]
    public string value;
}

public class PopupTest : MonoBehaviour {

    public List<ListEntry> entries;
}

PopupAttribute.cs

using UnityEngine;
using System;
using System.Linq;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;

/// <summary>
/// Prepares variables to be used by PopupDrawer.
/// </summary>

public class PopupAttribute : PropertyAttribute
{
    public string[] list;
    public object variableType;
  
    #region PopupAttribute()
  
    /// <summary>
    /// Makes necessary operations to prepare the variables for later use by PopupDrawer.
    /// </summary>
    /// <param name="list">Parameters array to be analized and assigned.</param>
  
    public PopupAttribute(params object[] list)
    {
        if (IsVariablesTypeConsistent(list) && AssignVariableType(list[0]))
        {
            this.list = new string[list.Length];
            for (int i = 0; i < list.Length; i++)
            {
                this.list[i] = list[i].ToString();
            }
        }
        else
        {
            return;
        }
      
    }
    #endregion
  
    #region Helper Methods.
    #region AssignVariableType()
  
    /// <summary>
    /// Checks if variable type is valid, and assignes the variable type to the proper variable.
    /// </summary>
    /// <param name="variable">Object to get type from.</param>
    /// <returns>Returns true if variable type is valid, and false if it isn't.</returns>
  
    private bool AssignVariableType(object variable)
    {
        if (variable.GetType() == typeof(int))
        {
            variableType = typeof(int[]);
            return true;
        }
        else if (variable.GetType() == typeof(float))
        {
            variableType = typeof(float[]);
            return true;
        }
        else if (variable.GetType() == typeof(double))
        {
            Debug.LogWarning("Popup Drawer doesn't properly support double type, for float variables please use 'f' at the end of each value.");
            variableType = typeof(float[]);
            return true;
        }
        else if (variable.GetType() == typeof(string))
        {
            variableType = typeof(string[]);
            return true;
        }
        else
        {
            Debug.LogError("Popup Property Drawer doesn't support " + variable.GetType() + " this type of variable");
            return false;
        }
    }
    #endregion
  
    #region IsVariablesTypeConsistent()
  
    /// <summary>
    /// Checks to see if there is only one variable type in the given value.
    /// </summary>
    /// <param name="list">Array of variables to be checked.</param>
    /// <returns>True if there is only one type, false if there is 2 or more.</returns>
  
    private bool IsVariablesTypeConsistent(object[] list)
    {
        for (int i = 0; i < list.Length; i++)
        {
            if (i == 0)
            {
                variableType = list[i].GetType();
            }
            else if (variableType != list[i].GetType())
            {
                Debug.LogError("Popup Property Drawer can only contain one type per variable");
                return false;
            }
        }
      
        return true;
    }
    #endregion
    #endregion
}

PopupDrawer.cs

using UnityEditor;
using UnityEngine;
using System;

/// <summary>
/// Creates a popup list with the provided values.
/// </summary>

[CustomPropertyDrawer(typeof(PopupAttribute))]
public class PopupDrawer : PropertyDrawer
{
    PopupAttribute popupAttribute { get { return ((PopupAttribute)attribute); } }
    int index;
  
    public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    {
        // Checks to see what is the type of the provided values and acts accordingly.
        if (popupAttribute.variableType == typeof(int[]))
        {
            EditorGUI.BeginChangeCheck();
            index = EditorGUI.Popup(position, label.text, property.intValue, popupAttribute.list);
            if (EditorGUI.EndChangeCheck())
            {
                property.intValue = index;
            }
        }
        else if (popupAttribute.variableType == typeof(float[]))
        {
            EditorGUI.BeginChangeCheck();
            // Checks all items in the provided list, to see if any of them is a match with the property value, if so assigns that value to the index.
            for (int i = 0; i < popupAttribute.list.Length; i++)
            {
                if (property.floatValue == Convert.ToSingle(popupAttribute.list[i]))
                {
                    index = i;
                }
            }
            index = EditorGUI.Popup(position, label.text, index, popupAttribute.list);
            if (EditorGUI.EndChangeCheck())
            {
                property.floatValue = Convert.ToSingle(popupAttribute.list[index]);
            }
        }
        else if (popupAttribute.variableType == typeof(string[]))
        {
            EditorGUI.BeginChangeCheck();
            // Checks all items in the provided list, to see if any of them is a match with the property value, if so assigns that value to the index.
            for (int i = 0; i < popupAttribute.list.Length; i++)
            {
                if (property.stringValue == popupAttribute.list[i])
                {
                    index = i;
                }
            }
            index = EditorGUI.Popup(position, label.text, index, popupAttribute.list);
            if (EditorGUI.EndChangeCheck())
            {
                property.stringValue = popupAttribute.list[index];
            }
        }
        else
        {
            EditorGUI.LabelField(position, "ERROR READ CONSOLE FOR MORE INFO");
        }
    }
}

I am not well acquainted with editor customization. Help appreciated!

Cheers,
A888

The problem is that you declare the index field at the class level.

The same drawer is probably used to draw all of your list elements. If you cache the selected index at the class level, the same index is applied to all list elements :slight_smile:

Instead, place this declaration inside OnGUI(). This should be OK, since you are checking the current property value to see which index it is anyway before calling the Popup method anyway.

public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
    int index = 0;

    // The rest of the method's code...
}
1 Like

Thank you very much! For anyone still confused, the index value goes to the drawer class’s on GUI.

1 Like