Skip to main content
added 611 characters in body
Source Link

The issue is, that a dictionary cannot contain the same key multiple times, but when Unity creates a new entry of your list, whenever you resize it in the inspector, it will just copy the last item. In this caseThis means, or even if it would createyou are trying to add a new empty instance, the key of the entry would be either empty string or the last items key, which causesalready exists to the dictionary to. This doesn't work and could throw an exception (in my case where I've changed the code to use the Add method). In, but in your case, you were simply replacing the existing key with a new value of the copied entry, so the first entry was always override bywith the new one, therefore never growing bigger than one.

public void OnAfterDeserialize()
{
    // No need to create the dictionary again, just clear it out.
    resources.Clear();

    foreach (ResourceEntry entry in entries)
    {
        // This "works" but hides your mistake.
        // If resources[key] doesn't exist or already exists, 
        // this key will simply be created or overriden.
        resources[entry.key] = entry.value;

        // To show the mistake, make this line explicitly throw an error
        // when you try to add a key, that already exists:
        resources.Add(entry.key, entry.value);
    }
}

Now you will notice, that you are trying to add the same key twice, which throws an error. To work around this, you can easily use your existing custom editor and instead of copying the last item, create a new one with a unique key.

The issue is, that a dictionary cannot contain the same key multiple times, but when Unity creates a new entry of your list, whenever you resize it in the inspector, it will just copy the last item. In this case, or even if it would create a new empty instance, the key of the entry would be either empty string or the last items key, which causes the dictionary to throw an exception (in my case where I've changed the code to use the Add method). In your case, you were simply replacing the existing key with a new value of the copied entry, so the first entry was always override by the new one.

To work around this, you can easily use your existing custom editor and instead of copying the last item, create a new one with a unique key.

The issue is, that a dictionary cannot contain the same key multiple times, but when Unity creates a new entry of your list, whenever you resize it in the inspector, it will just copy the last item. This means, you are trying to add a key, which already exists to the dictionary. This doesn't work and could throw an exception, but in your case, you were simply replacing the existing first entry with the new one, therefore never growing bigger than one.

public void OnAfterDeserialize()
{
    // No need to create the dictionary again, just clear it out.
    resources.Clear();

    foreach (ResourceEntry entry in entries)
    {
        // This "works" but hides your mistake.
        // If resources[key] doesn't exist or already exists, 
        // this key will simply be created or overriden.
        resources[entry.key] = entry.value;

        // To show the mistake, make this line explicitly throw an error
        // when you try to add a key, that already exists:
        resources.Add(entry.key, entry.value);
    }
}

Now you will notice, that you are trying to add the same key twice, which throws an error. To work around this, you can easily use your existing custom editor and instead of copying the last item, create a new one with a unique key.

added 213 characters in body
Source Link

The issue is, that a dictionary cannot contain the same key multiple times, but when Unity creates a new entry of your list, whenever you resize it in the inspector, it will just copy the last item. In this case, or even if it would create a new empty instance, the key of the entry would be either empty string or the last items key, which causes the dictionary to throw an exception (in my case where I've changed the code to use the Add method). In your case, you were simply replacing the existing key with a new value of the copied entry, so the first entry was always override by the new one.

The issue is, that a dictionary cannot contain the same key multiple times, but when Unity creates a new entry of your list, whenever you resize it in the inspector, it will just copy the last item. In this case, or even if it would create a new empty instance, the key of the entry would be either empty string or the last items key, which causes the dictionary to throw an exception.

The issue is, that a dictionary cannot contain the same key multiple times, but when Unity creates a new entry of your list, whenever you resize it in the inspector, it will just copy the last item. In this case, or even if it would create a new empty instance, the key of the entry would be either empty string or the last items key, which causes the dictionary to throw an exception (in my case where I've changed the code to use the Add method). In your case, you were simply replacing the existing key with a new value of the copied entry, so the first entry was always override by the new one.

added 137 characters in body
Source Link
using UnityEngine;
using System.Collections.Generic;
using UnityEditor; // Remember to move editor code into Editor folder or #if UNITY_EDITOR
using UnityEditorInternal;

public class ResourceManager : MonoBehaviour, ISerializationCallbackReceiver
{
    private Dictionary<string, Object> resources = new Dictionary<string, Object>();

    // Hide this, because it will not work correctly without the custom editor.
    //[HideInInspector]
    [SerializeField]
    private List<ResourceEntry> entries = new List<ResourceEntry>();

    // Dictionary -> List
    public void OnBeforeSerialize()
    {
        entries.Clear();
        // You can iterate a dictionary like this if you want.
        foreach (KeyValuePair<string, Object> kvp in resources)
        {
            entries.Add(new ResourceEntry(kvp.Key, kvp.Value));
        }
    }

    // List -> Dictionary
    public void OnAfterDeserialize()
    {
        resources.Clear();
        foreach (ResourceEntry entry in entries)
        {
            resources.Add(entry.key, entry.value);
        }
    }

    [System.Serializable]
    public class ResourceEntry
    {
        public string key;
        public Object value;

        public ResourceEntry() { }

        public ResourceEntry(string key, Object value)
        {
            this.key = key;
            this.value = value;
        }
    }
}

[CustomEditor(typeof(ResourceManager))]
public class ResourceManagerEditor : Editor
{
    private ReorderableList list;

    public void OnEnable()
    {
        list = new ReorderableList(serializedObject, serializedObject.FindProperty("entries"), true, true, true, true);
        list.drawElementCallback = (Rect rect, int index, bool isActive, bool isFocused) =>
        {
            SerializedProperty element = list.serializedProperty.GetArrayElementAtIndex(index);
            rect.y += 2;
            EditorGUI.PropertyField(new Rect(rect.x, rect.y, 60, EditorGUIUtility.singleLineHeight), element.FindPropertyRelative("key"), GUIContent.none);
            EditorGUI.PropertyField(new Rect(rect.x + 70, rect.y, 160, EditorGUIUtility.singleLineHeight), element.FindPropertyRelative("value"), GUIContent.none);
        };

        list.onAddCallback = (ReorderableList list) =>
        {
            int index = list.serializedProperty.arraySize;
            list.serializedProperty.arraySize++;
            list.index = index;

            // Important! When adding a new element to the list (and dictionary),
            // the newly created key must be unique.
            SerializedProperty element = list.serializedProperty.GetArrayElementAtIndex(index);
            element.FindPropertyRelative("key").stringValue = System.Guid.NewGuid().ToString(); // Create some kind of unique key, like a GUID or adding a number to the last one.
        };
    }

    public override void OnInspectorGUI()
    {
        serializedObject.Update();
        list.DoLayoutList();
        serializedObject.ApplyModifiedProperties();
    }
}
using UnityEngine;
using System.Collections.Generic;
using UnityEditor;
using UnityEditorInternal;

public class ResourceManager : MonoBehaviour, ISerializationCallbackReceiver
{
    private Dictionary<string, Object> resources = new Dictionary<string, Object>();

    // Hide this, because it will not work correctly without the custom editor.
    //[HideInInspector]
    [SerializeField]
    private List<ResourceEntry> entries = new List<ResourceEntry>();

    // Dictionary -> List
    public void OnBeforeSerialize()
    {
        entries.Clear();
        foreach (KeyValuePair<string, Object> kvp in resources)
        {
            entries.Add(new ResourceEntry(kvp.Key, kvp.Value));
        }
    }

    // List -> Dictionary
    public void OnAfterDeserialize()
    {
        resources.Clear();
        foreach (ResourceEntry entry in entries)
        {
            resources.Add(entry.key, entry.value);
        }
    }

    [System.Serializable]
    public class ResourceEntry
    {
        public string key;
        public Object value;

        public ResourceEntry() { }

        public ResourceEntry(string key, Object value)
        {
            this.key = key;
            this.value = value;
        }
    }
}

[CustomEditor(typeof(ResourceManager))]
public class ResourceManagerEditor : Editor
{
    private ReorderableList list;

    public void OnEnable()
    {
        list = new ReorderableList(serializedObject, serializedObject.FindProperty("entries"), true, true, true, true);
        list.drawElementCallback = (Rect rect, int index, bool isActive, bool isFocused) =>
        {
            SerializedProperty element = list.serializedProperty.GetArrayElementAtIndex(index);
            rect.y += 2;
            EditorGUI.PropertyField(new Rect(rect.x, rect.y, 60, EditorGUIUtility.singleLineHeight), element.FindPropertyRelative("key"), GUIContent.none);
            EditorGUI.PropertyField(new Rect(rect.x + 70, rect.y, 160, EditorGUIUtility.singleLineHeight), element.FindPropertyRelative("value"), GUIContent.none);
        };

        list.onAddCallback = (ReorderableList list) =>
        {
            int index = list.serializedProperty.arraySize;
            list.serializedProperty.arraySize++;
            list.index = index;

            // Important! When adding a new element to the list (and dictionary),
            // the newly created key must be unique.
            SerializedProperty element = list.serializedProperty.GetArrayElementAtIndex(index);
            element.FindPropertyRelative("key").stringValue = System.Guid.NewGuid().ToString(); // Create some kind of unique key, like a GUID or adding a number to the last one.
        };
    }

    public override void OnInspectorGUI()
    {
        serializedObject.Update();
        list.DoLayoutList();
        serializedObject.ApplyModifiedProperties();
    }
}
using UnityEngine;
using System.Collections.Generic;
using UnityEditor; // Remember to move editor code into Editor folder or #if UNITY_EDITOR
using UnityEditorInternal;

public class ResourceManager : MonoBehaviour, ISerializationCallbackReceiver
{
    private Dictionary<string, Object> resources = new Dictionary<string, Object>();

    // Hide this, because it will not work correctly without the custom editor.
    [HideInInspector]
    [SerializeField]
    private List<ResourceEntry> entries = new List<ResourceEntry>();

    // Dictionary -> List
    public void OnBeforeSerialize()
    {
        entries.Clear();
        // You can iterate a dictionary like this if you want.
        foreach (KeyValuePair<string, Object> kvp in resources)
        {
            entries.Add(new ResourceEntry(kvp.Key, kvp.Value));
        }
    }

    // List -> Dictionary
    public void OnAfterDeserialize()
    {
        resources.Clear();
        foreach (ResourceEntry entry in entries)
        {
            resources.Add(entry.key, entry.value);
        }
    }

    [System.Serializable]
    public class ResourceEntry
    {
        public string key;
        public Object value;

        public ResourceEntry() { }

        public ResourceEntry(string key, Object value)
        {
            this.key = key;
            this.value = value;
        }
    }
}

[CustomEditor(typeof(ResourceManager))]
public class ResourceManagerEditor : Editor
{
    private ReorderableList list;

    public void OnEnable()
    {
        list = new ReorderableList(serializedObject, serializedObject.FindProperty("entries"), true, true, true, true);
        list.drawElementCallback = (Rect rect, int index, bool isActive, bool isFocused) =>
        {
            SerializedProperty element = list.serializedProperty.GetArrayElementAtIndex(index);
            rect.y += 2;
            EditorGUI.PropertyField(new Rect(rect.x, rect.y, 60, EditorGUIUtility.singleLineHeight), element.FindPropertyRelative("key"), GUIContent.none);
            EditorGUI.PropertyField(new Rect(rect.x + 70, rect.y, 160, EditorGUIUtility.singleLineHeight), element.FindPropertyRelative("value"), GUIContent.none);
        };

        list.onAddCallback = (ReorderableList list) =>
        {
            int index = list.serializedProperty.arraySize;
            list.serializedProperty.arraySize++;
            list.index = index;

            // Important! When adding a new element to the list (and dictionary),
            // the newly created key must be unique.
            SerializedProperty element = list.serializedProperty.GetArrayElementAtIndex(index);
            element.FindPropertyRelative("key").stringValue = System.Guid.NewGuid().ToString(); // Create some kind of unique key, like a GUID or adding a number to the last one.
        };
    }

    public override void OnInspectorGUI()
    {
        serializedObject.Update();
        list.DoLayoutList();
        serializedObject.ApplyModifiedProperties();
    }
}
Source Link
Loading