Can't set objectReferenceValue on array items from custom editors

I’m trying to make a custom editor for a component that contains an array of sprites, but it seems that the editor refuses to let me add items to this array.

I’m making my own drag and drop operation resolution. If you add the two following scripts in your project, you’ll get the inspector layout as in the screenshot linked to this post.

So, if you drag a Sprite in the drag and drop area of the component, you’ll see the array with 1 item during a frame, then resets to 0 item.
I have the same issue on Unity 2017.4 and 2018.3.

Does someone know why it happens and how can I fix it? Thanks in advance !

Test this behavior in your projet by adding these scripts:

Here is the code of my component:

using UnityEngine;
public class SpriteArrayTester : MonoBehaviour
{
    public Sprite[] sprites = { };
}

And here is my custom editor:

using UnityEngine;
using UnityEditor;
[CustomEditor(typeof(SpriteArrayTester))]
public class SpriteArrayTesterEditor : Editor
{
    private SerializedProperty _spritesArrayProperty = null;
    private Object[] _referencesToAdd = null;

    private void OnEnable()
    {
        _spritesArrayProperty = serializedObject.FindProperty("sprites");
    }

    public override void OnInspectorGUI()
    {
        // Adds object references if there's one or more.
        // I need to do that at a different moment of the drag and drop resolution to avoid errors.
        AddObjectReferences(_spritesArrayProperty);

        // Draw drag and drop area
        Rect dropArea = EditorGUILayout.GetControlRect(false, 200f);
        GUI.Box(dropArea, "Drag and drop sprites here...");
        ManageDragAndDrop(dropArea);

        // Display array elements
        EditorGUILayout.LabelField("Array content (" + _spritesArrayProperty.arraySize + ")", EditorStyles.boldLabel);
        for(int i = 0; i < _spritesArrayProperty.arraySize; i++)
        {
            SerializedProperty itemProperty = _spritesArrayProperty.GetArrayElementAtIndex(i);
            EditorGUILayout.LabelField(i + ": " + itemProperty.displayName);
        }
    }

    private void ManageDragAndDrop(Rect dropArea)
    {
        switch (Event.current.type)
        {
            // If user dropped something out of the area, ignore the event
            case EventType.DragUpdated:
            case EventType.DragPerform:
            if (!dropArea.Contains(Event.current.mousePosition))
                break;

            DragAndDrop.visualMode = DragAndDropVisualMode.Copy;

            if (Event.current.type == EventType.DragPerform)
            {
                // Accept drag and drop operation, and store dragged objects references
                DragAndDrop.AcceptDrag();
                _referencesToAdd = DragAndDrop.objectReferences;
                Debug.Log("Accepting drag , nb references to add = " + _referencesToAdd.Length);
            }
            break;
        }
    }

    private void AddObjectReferences(SerializedProperty arrayProperty)
    {
        if(_referencesToAdd != null)
        {
            foreach (Object draggedObject in _referencesToAdd)
            {
                int index = arrayProperty.arraySize;
                // Add an item at the last index
                arrayProperty.InsertArrayElementAtIndex(index);
                // Set the object reference to the added item
                arrayProperty.GetArrayElementAtIndex(index).objectReferenceValue = draggedObject;
                Debug.Log("Adding object reference = " + draggedObject.name + " at index " + index + ", reference is: " + arrayProperty.GetArrayElementAtIndex(index).objectReferenceValue);
            }

            // Apply changes
            serializedObject.ApplyModifiedProperties();
            _referencesToAdd = null;
        }
    }
}

Thanks in advance ! :slight_smile:

4532236--419920--Screenshot.jpg

I tested something (not a definitive solution) that works, but I have another issue.

First, I saw that changing the DragAndDrop.visualMode value changes the behavior: if I use DragAndDropVisualMode.Rejected or DragAndDropVisualMode.None, absolutely nothing happens.

So, I tried to delay the drag and drop resolution and the array copy. It works, new items stay in the array… but they’re always null. Even by assigning items’ objectReferenceValue property.

Here is my new code:

using UnityEngine;
using UnityEditor;
[CustomEditor(typeof(SpriteArrayTester))]
public class SpriteArrayTesterEditor : Editor
{
    private SerializedProperty _spritesArrayProperty = null;
    private Object[] _referencesToAdd = null;
    private int timer = 0;

    private void OnEnable()
    {
        _spritesArrayProperty = serializedObject.FindProperty("sprites");
        _referencesToAdd = null;
    }

    public override void OnInspectorGUI()
    {
        // Adds object references if there's one or more.
        // I need to do that at a different moment of the drag and drop resolution to avoid errors.
        AddObjectReferences(_spritesArrayProperty);

        // Draw drag and drop area
        Rect dropArea = EditorGUILayout.GetControlRect(false, 200f);
        GUI.Box(dropArea, "Drag and drop sprites here...");
        ManageDragAndDrop(dropArea);

        // Display array elements
        EditorGUILayout.LabelField("Array content (" + _spritesArrayProperty.arraySize + ")", EditorStyles.boldLabel);
        for (int i = 0; i < _spritesArrayProperty.arraySize; i++)
        {
            SerializedProperty itemProperty = _spritesArrayProperty.GetArrayElementAtIndex(i);
            EditorGUILayout.LabelField("[" + i + "] " + itemProperty.displayName + ": " + (itemProperty.objectReferenceValue != null ? itemProperty.objectReferenceValue.name : "null"));
        }

        EditorUtility.SetDirty(serializedObject.targetObject);
    }

    private void ManageDragAndDrop(Rect dropArea)
    {
        switch (Event.current.type)
        {
            // If user dropped something out of the area, ignore the event
            case EventType.DragUpdated:
            case EventType.DragPerform:
            if (!dropArea.Contains(Event.current.mousePosition))
                break;

            DragAndDrop.visualMode = DragAndDropVisualMode.Copy;

            if (Event.current.type == EventType.DragPerform)
            {
                // Accept drag and drop operation, and store dragged objects references
                DragAndDrop.AcceptDrag();
                _referencesToAdd = DragAndDrop.objectReferences;
                Debug.Log("Accepting drag , nb references to add = " + _referencesToAdd.Length);
            }
            break;
        }
    }

    private void AddObjectReferences(SerializedProperty arrayProperty)
    {
        if (_referencesToAdd != null)
        {
            Debug.Log("References to add detected");

            // Wait 10 UI refresh turns
            if(timer < 10)
            {
                timer++;
                Debug.Log("Increase timer: " + timer);
                return;
            }

            timer = 0;
            foreach (Object draggedObject in _referencesToAdd)
            {
                int index = arrayProperty.arraySize;
                // Add an item at the last index
                arrayProperty.InsertArrayElementAtIndex(index);
                // Set the object reference to the added item
                arrayProperty.GetArrayElementAtIndex(index).objectReferenceValue = draggedObject;
                Debug.Log("Adding object reference = " + draggedObject.name + " at index " + index + ", reference is: " + arrayProperty.GetArrayElementAtIndex(index).objectReferenceValue);
            }

            // Apply changes
            serializedObject.ApplyModifiedProperties();
            _referencesToAdd = null;
        }
    }
}