Unity Dictionary

Finally, a serializable dictionary for Unity! (extracted from System.Collections.Generic) 




 


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

[Serializable, DebuggerDisplay("Count = {Count}")]
public class SerializableDictionary<TKey, TValue> : IDictionary<TKey, TValue>
{
    [SerializeField, HideInInspector] int[] _Buckets;
    [SerializeField, HideInInspector] int[] _HashCodes;
    [SerializeField, HideInInspector] int[] _Next;
    [SerializeField, HideInInspector] int _Count;
    [SerializeField, HideInInspector] int _Version;
    [SerializeField, HideInInspector] int _FreeList;
    [SerializeField, HideInInspector] int _FreeCount;
    [SerializeField, HideInInspector] TKey[] _Keys;
    [SerializeField, HideInInspector] TValue[] _Values;

    readonly IEqualityComparer<TKey> _Comparer;

    // Mainly for debugging purposes - to get the key-value pairs display
    public Dictionary<TKey, TValue> AsDictionary
    {
        get { return new Dictionary<TKey, TValue>(this); }
    }

    public int Count
    {
        get { return _Count - _FreeCount; }
    }

    public TValue this[TKey key, TValue defaultValue]
    {
        get
        {
            int index = FindIndex(key);
            if (index >= 0)
                return _Values[index];
            return defaultValue;
        }
    }

    public TValue this[TKey key]
    {
        get
        {
            int index = FindIndex(key);
            if (index >= 0)
                return _Values[index];
            throw new KeyNotFoundException(key.ToString());
        }

        set { Insert(key, value, false); }
    }

    public SerializableDictionary()
        : this(0, null)
    {
    }

    public SerializableDictionary(int capacity)
        : this(capacity, null)
    {
    }

    public SerializableDictionary(IEqualityComparer<TKey> comparer)
        : this(0, comparer)
    {
    }

    public SerializableDictionary(int capacity, IEqualityComparer<TKey> comparer)
    {
        if (capacity < 0)
            throw new ArgumentOutOfRangeException("capacity");

        Initialize(capacity);

        _Comparer = (comparer ?? EqualityComparer<TKey>.Default);
    }

    public SerializableDictionary(IDictionary<TKey, TValue> dictionary)
        : this(dictionary, null)
    {
    }

    public SerializableDictionary(IDictionary<TKey, TValue> dictionary, IEqualityComparer<TKey> comparer)
        : this((dictionary != null) ? dictionary.Count : 0, comparer)
    {
        if (dictionary == null)
            throw new ArgumentNullException("dictionary");

        foreach (KeyValuePair<TKey, TValue> current in dictionary)
            Add(current.Key, current.Value);
    }

    public bool ContainsValue(TValue value)
    {
        if (value == null)
        {
            for (int i = 0; i < _Count; i++)
            {
                if (_HashCodes[i] >= 0 && _Values[i] == null)
                    return true;
            }
        }
        else
        {
            var defaultComparer = EqualityComparer<TValue>.Default;
            for (int i = 0; i < _Count; i++)
            {
                if (_HashCodes[i] >= 0 && defaultComparer.Equals(_Values[i], value))
                    return true;
            }
        }
        return false;
    }

    public bool ContainsKey(TKey key)
    {
        return FindIndex(key) >= 0;
    }

    public void Clear()
    {
        if (_Count <= 0)
            return;

        for (int i = 0; i < _Buckets.Length; i++)
            _Buckets[i] = -1;

        Array.Clear(_Keys, 0, _Count);
        Array.Clear(_Values, 0, _Count);
        Array.Clear(_HashCodes, 0, _Count);
        Array.Clear(_Next, 0, _Count);

        _FreeList = -1;
        _Count = 0;
        _FreeCount = 0;
        _Version++;
    }

    public void Add(TKey key, TValue value)
    {
        Insert(key, value, true);
    }

    private void Resize(int newSize, bool forceNewHashCodes)
    {
        int[] bucketsCopy = new int[newSize];
        for (int i = 0; i < bucketsCopy.Length; i++)
            bucketsCopy[i] = -1;

        var keysCopy = new TKey[newSize];
        var valuesCopy = new TValue[newSize];
        var hashCodesCopy = new int[newSize];
        var nextCopy = new int[newSize];

        Array.Copy(_Values, 0, valuesCopy, 0, _Count);
        Array.Copy(_Keys, 0, keysCopy, 0, _Count);
        Array.Copy(_HashCodes, 0, hashCodesCopy, 0, _Count);
        Array.Copy(_Next, 0, nextCopy, 0, _Count);

        if (forceNewHashCodes)
        {
            for (int i = 0; i < _Count; i++)
            {
                if (hashCodesCopy[i] != -1)
                    hashCodesCopy[i] = (_Comparer.GetHashCode(keysCopy[i]) & 2147483647);
            }
        }

        for (int i = 0; i < _Count; i++)
        {
            int index = hashCodesCopy[i] % newSize;
            nextCopy[i] = bucketsCopy[index];
            bucketsCopy[index] = i;
        }

        _Buckets = bucketsCopy;
        _Keys = keysCopy;
        _Values = valuesCopy;
        _HashCodes = hashCodesCopy;
        _Next = nextCopy;
    }

    private void Resize()
    {
        Resize(PrimeHelper.ExpandPrime(_Count), false);
    }

    public bool Remove(TKey key)
    {
        if (key == null)
            throw new ArgumentNullException("key");

        int hash = _Comparer.GetHashCode(key) & 2147483647;
        int index = hash % _Buckets.Length;
        int num = -1;
        for (int i = _Buckets[index]; i >= 0; i = _Next[i])
        {
            if (_HashCodes[i] == hash && _Comparer.Equals(_Keys[i], key))
            {
                if (num < 0)
                    _Buckets[index] = _Next[i];
                else
                    _Next[num] = _Next[i];

                _HashCodes[i] = -1;
                _Next[i] = _FreeList;
                _Keys[i] = default(TKey);
                _Values[i] = default(TValue);
                _FreeList = i;
                _FreeCount++;
                _Version++;
                return true;
            }
            num = i;
        }
        return false;
    }

    private void Insert(TKey key, TValue value, bool add)
    {
        if (key == null)
            throw new ArgumentNullException("key");

        if (_Buckets == null)
            Initialize(0);

        int hash = _Comparer.GetHashCode(key) & 2147483647;
        int index = hash % _Buckets.Length;
        int num1 = 0;
        for (int i = _Buckets[index]; i >= 0; i = _Next[i])
        {
            if (_HashCodes[i] == hash && _Comparer.Equals(_Keys[i], key))
            {
                if (add)
                    throw new ArgumentException("Key already exists: " + key);

                _Values[i] = value;
                _Version++;
                return;
            }
            num1++;
        }
        int num2;
        if (_FreeCount > 0)
        {
            num2 = _FreeList;
            _FreeList = _Next[num2];
            _FreeCount--;
        }
        else
        {
            if (_Count == _Keys.Length)
            {
                Resize();
                index = hash % _Buckets.Length;
            }
            num2 = _Count;
            _Count++;
        }
        _HashCodes[num2] = hash;
        _Next[num2] = _Buckets[index];
        _Keys[num2] = key;
        _Values[num2] = value;
        _Buckets[index] = num2;
        _Version++;

        //if (num3 > 100 && HashHelpers.IsWellKnownEqualityComparer(comparer))
        //{
        //    comparer = (IEqualityComparer<TKey>)HashHelpers.GetRandomizedEqualityComparer(comparer);
        //    Resize(entries.Length, true);
        //}
    }

    private void Initialize(int capacity)
    {
        int prime = PrimeHelper.GetPrime(capacity);

        _Buckets = new int[prime];
        for (int i = 0; i < _Buckets.Length; i++)
            _Buckets[i] = -1;

        _Keys = new TKey[prime];
        _Values = new TValue[prime];
        _HashCodes = new int[prime];
        _Next = new int[prime];

        _FreeList = -1;
    }

    private int FindIndex(TKey key)
    {
        if (key == null)
            throw new ArgumentNullException("key");

        if (_Buckets != null)
        {
            int hash = _Comparer.GetHashCode(key) & 2147483647;
            for (int i = _Buckets[hash % _Buckets.Length]; i >= 0; i = _Next[i])
            {
                if (_HashCodes[i] == hash && _Comparer.Equals(_Keys[i], key))
                    return i;
            }
        }
        return -1;
    }

    public bool TryGetValue(TKey key, out TValue value)
    {
        int index = FindIndex(key);
        if (index >= 0)
        {
            value = _Values[index];
            return true;
        }
        value = default(TValue);
        return false;
    }

    private static class PrimeHelper
    {
        public static readonly int[] Primes = new int[]
        {
            3,
            7,
            11,
            17,
            23,
            29,
            37,
            47,
            59,
            71,
            89,
            107,
            131,
            163,
            197,
            239,
            293,
            353,
            431,
            521,
            631,
            761,
            919,
            1103,
            1327,
            1597,
            1931,
            2333,
            2801,
            3371,
            4049,
            4861,
            5839,
            7013,
            8419,
            10103,
            12143,
            14591,
            17519,
            21023,
            25229,
            30293,
            36353,
            43627,
            52361,
            62851,
            75431,
            90523,
            108631,
            130363,
            156437,
            187751,
            225307,
            270371,
            324449,
            389357,
            467237,
            560689,
            672827,
            807403,
            968897,
            1162687,
            1395263,
            1674319,
            2009191,
            2411033,
            2893249,
            3471899,
            4166287,
            4999559,
            5999471,
            7199369
        };

        public static bool IsPrime(int candidate)
        {
            if ((candidate & 1) != 0)
            {
                int num = (int)Math.Sqrt((double)candidate);
                for (int i = 3; i <= num; i += 2)
                {
                    if (candidate % i == 0)
                    {
                        return false;
                    }
                }
                return true;
            }
            return candidate == 2;
        }

        public static int GetPrime(int min)
        {
            if (min < 0)
                throw new ArgumentException("min < 0");

            for (int i = 0; i < PrimeHelper.Primes.Length; i++)
            {
                int prime = PrimeHelper.Primes[i];
                if (prime >= min)
                    return prime;
            }
            for (int i = min | 1; i < 2147483647; i += 2)
            {
                if (PrimeHelper.IsPrime(i) && (i - 1) % 101 != 0)
                    return i;
            }
            return min;
        }

        public static int ExpandPrime(int oldSize)
        {
            int num = 2 * oldSize;
            if (num > 2146435069 && 2146435069 > oldSize)
            {
                return 2146435069;
            }
            return PrimeHelper.GetPrime(num);
        }
    }

    public ICollection<TKey> Keys
    {
        get { return _Keys.Take(Count).ToArray(); }
    }

    public ICollection<TValue> Values
    {
        get { return _Values.Take(Count).ToArray(); }
    }

    public void Add(KeyValuePair<TKey, TValue> item)
    {
        Add(item.Key, item.Value);
    }

    public bool Contains(KeyValuePair<TKey, TValue> item)
    {
        int index = FindIndex(item.Key);
        return index >= 0 &&
            EqualityComparer<TValue>.Default.Equals(_Values[index], item.Value);
    }

    public void CopyTo(KeyValuePair<TKey, TValue>[] array, int index)
    {
        if (array == null)
            throw new ArgumentNullException("array");

        if (index < 0 || index > array.Length)
            throw new ArgumentOutOfRangeException(string.Format("index = {0} array.Length = {1}", index, array.Length));

        if (array.Length - index < Count)
            throw new ArgumentException(string.Format("The number of elements in the dictionary ({0}) is greater than the available space from index to the end of the destination array {1}.", Count, array.Length));

        for (int i = 0; i < _Count; i++)
        {
            if (_HashCodes[i] >= 0)
                array[index++] = new KeyValuePair<TKey, TValue>(_Keys[i], _Values[i]);
        }
    }

    public bool IsReadOnly
    {
        get { return false; }
    }

    public bool Remove(KeyValuePair<TKey, TValue> item)
    {
        return Remove(item.Key);
    }

    public Enumerator GetEnumerator()
    {
        return new Enumerator(this);
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }

    IEnumerator<KeyValuePair<TKey, TValue>> IEnumerable<KeyValuePair<TKey, TValue>>.GetEnumerator()
    {
        return GetEnumerator();
    }

    public struct Enumerator : IEnumerator<KeyValuePair<TKey, TValue>>
    {
        private readonly SerializableDictionary<TKey, TValue> _Dictionary;
        private int _Version;
        private int _Index;
        private KeyValuePair<TKey, TValue> _Current;

        public KeyValuePair<TKey, TValue> Current
        {
            get { return _Current; }
        }

        internal Enumerator(SerializableDictionary<TKey, TValue> dictionary)
        {
            _Dictionary = dictionary;
            _Version = dictionary._Version;
            _Current = default(KeyValuePair<TKey, TValue>);
            _Index = 0;
        }

        public bool MoveNext()
        {
            if (_Version != _Dictionary._Version)
                throw new InvalidOperationException(string.Format("Enumerator version {0} != Dictionary version {1}", _Version, _Dictionary._Version));

            while (_Index < _Dictionary._Count)
            {
                if (_Dictionary._HashCodes[_Index] >= 0)
                {
                    _Current = new KeyValuePair<TKey, TValue>(_Dictionary._Keys[_Index], _Dictionary._Values[_Index]);
                    _Index++;
                    return true;
                }
                _Index++;
            }

            _Index = _Dictionary._Count + 1;
            _Current = default(KeyValuePair<TKey, TValue>);
            return false;
        }

        void IEnumerator.Reset()
        {
            if (_Version != _Dictionary._Version)
                throw new InvalidOperationException(string.Format("Enumerator version {0} != Dictionary version {1}", _Version, _Dictionary._Version));

            _Index = 0;
            _Current = default(KeyValuePair<TKey, TValue>);
        }

        object IEnumerator.Current
        {
            get { return Current; }
        }

        public void Dispose()
        {
        }
    }
}
using System;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
using UnityObject = UnityEngine.Object;

public abstract class DictionaryDrawer<TK, TV> : PropertyDrawer
{
    private SerializableDictionary<TK, TV> _Dictionary;
    private bool _Foldout;
    private const float kButtonWidth = 22f;
    private const int kMargin = 3;

    static GUIContent iconToolbarMinus = EditorGUIUtility.IconContent("Toolbar Minus", "Remove selection from list");
    static GUIContent iconToolbarPlus = EditorGUIUtility.IconContent("Toolbar Plus", "Add to list");
    static GUIStyle preButton = "RL FooterButton";
    static GUIStyle boxBackground = "RL Background";


    public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
    {
        CheckInitialize(property, label);
        if (_Foldout)
            return Mathf.Max((_Dictionary.Count + 1) * 17f, 17 + 16) + kMargin * 2;
        return 17f + kMargin * 2;
    }

    public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    {
        Undo.RecordObject(property.serializedObject.targetObject, "Undo Dictionary Change");

        CheckInitialize(property, label);

        var backgroundRect = position;
        backgroundRect.xMin -= 7;
        backgroundRect.height += kMargin;
        if (Event.current.type == EventType.Repaint)
            boxBackground.Draw(backgroundRect, false, false, false, false);

        position.y += kMargin;
        position.height = 17f;

        var foldoutRect = position;
        foldoutRect.width -= 2 * kButtonWidth;
        EditorGUI.BeginChangeCheck();
        _Foldout = EditorGUI.Foldout(foldoutRect, _Foldout, label, true);
        if (EditorGUI.EndChangeCheck())
        {
            EditorPrefs.SetBool(label.text, _Foldout);
            EditorUtility.SetDirty(property.serializedObject.targetObject);
        }
        position.xMin += kMargin;
        position.xMax -= kMargin;

        var buttonRect = position;
        buttonRect.xMin = position.xMax - kButtonWidth;
        //buttonRect.x -= 5;

        if (GUI.Button(buttonRect, iconToolbarMinus, preButton))
        {
            ClearDictionary();
        }

        buttonRect.x -= kButtonWidth - 1;

        if (GUI.Button(buttonRect, iconToolbarPlus, preButton))
        {
            AddNewItem();
        }

        if (!_Foldout)
            return;

        var labelRect = position;
        labelRect.y += 16;
        if (_Dictionary.Count == 0)
            GUI.Label(labelRect, "This dictionary doesn't have any items. Click + to add one!");

        foreach (var item in _Dictionary)
        {
            var key = item.Key;
            var value = item.Value;

            position.y += 17f;

            var keyRect = position;
            keyRect.width /= 2;
            keyRect.width -= 4;
            EditorGUI.BeginChangeCheck();
            var newKey = DoField(keyRect, typeof(TK), key);
            if (EditorGUI.EndChangeCheck())
            {
                try
                {
                    _Dictionary.Remove(key);
                    _Dictionary.Add(newKey, value);
                    EditorUtility.SetDirty(property.serializedObject.targetObject);
                }
                catch (Exception e)
                {
                    Debug.Log(e.Message);
                }
                break;
            }

            var valueRect = position;
            valueRect.xMin = keyRect.xMax;
            valueRect.xMax = position.xMax - kButtonWidth;
            EditorGUI.BeginChangeCheck();
            value = DoField(valueRect, typeof(TV), value);
            if (EditorGUI.EndChangeCheck())
            {
                _Dictionary[key] = value;
                EditorUtility.SetDirty(property.serializedObject.targetObject);
                break;
            }

            var removeRect = position;
            removeRect.xMin = removeRect.xMax - kButtonWidth;
            if (GUI.Button(removeRect, iconToolbarMinus, preButton))
            {
                RemoveItem(key);
                break;
            }
        }
    }

    private void RemoveItem(TK key)
    {
        _Dictionary.Remove(key);
    }

    private void CheckInitialize(SerializedProperty property, GUIContent label)
    {
        if (_Dictionary == null)
        {
            var target = property.serializedObject.targetObject;
            _Dictionary = fieldInfo.GetValue(target) as SerializableDictionary<TK, TV>;
            if (_Dictionary == null)
            {
                _Dictionary = new SerializableDictionary<TK, TV>();
                fieldInfo.SetValue(target, _Dictionary);
            }

            _Foldout = EditorPrefs.GetBool(label.text);
        }
    }

    private static readonly Dictionary<Type, Func<Rect, object, object>> _Fields =
        new Dictionary<Type, Func<Rect, object, object>>()
        {
                { typeof(int), (rect, value) => EditorGUI.IntField(rect, (int)value) },
                { typeof(float), (rect, value) => EditorGUI.FloatField(rect, (float)value) },
                { typeof(string), (rect, value) => EditorGUI.TextField(rect, (string)value) },
                { typeof(bool), (rect, value) => EditorGUI.Toggle(rect, (bool)value) },
                { typeof(Vector2), (rect, value) => EditorGUI.Vector2Field(rect, GUIContent.none, (Vector2)value) },
                { typeof(Vector3), (rect, value) => EditorGUI.Vector3Field(rect, GUIContent.none, (Vector3)value) },
                { typeof(Bounds), (rect, value) => EditorGUI.BoundsField(rect, (Bounds)value) },
                { typeof(Rect), (rect, value) => EditorGUI.RectField(rect, (Rect)value) },
        };

    private static T DoField<T>(Rect rect, Type type, T value)
    {
        Func<Rect, object, object> field;
        if (_Fields.TryGetValue(type, out field))
            return (T)field(rect, value);

        if (type.IsEnum)
            return (T)(object)EditorGUI.EnumPopup(rect, (Enum)(object)value);

        if (typeof(UnityObject).IsAssignableFrom(type))
            return (T)(object)EditorGUI.ObjectField(rect, (UnityObject)(object)value, type, true);

        Debug.Log("Type is not supported: " + type);
        return value;
    }

    private void ClearDictionary()
    {
        _Dictionary.Clear();
    }

    private void AddNewItem()
    {
        TK key;
        if (typeof(TK) == typeof(string))
            key = (TK)(object)"";
        else key = default(TK);

        var value = default(TV);
        try
        {
            _Dictionary.Add(key, value);
        }
        catch (Exception e)
        {
            Debug.Log(e.Message);
        }
    }
}

// This one is for your custom-made dictionaries.
[CustomPropertyDrawer(typeof(VideoClipDictionary))]
public class VideoClipDictionaryDrawer : DictionaryDrawer<string, UnityEngine.Video.VideoClip> { }

[CustomPropertyDrawer(typeof(AudioClipDictionary))]
public class AudioClipDictionaryDrawer : DictionaryDrawer<string, AudioClip> { }


[CustomPropertyDrawer(typeof(TextureDictionary))]
public class TextureDictionaryDrawer : DictionaryDrawer<string, Texture> { }

引用参考:

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

[Serializable]public class VideoClipDictionary : SerializableDictionary<string, UnityEngine.Video.VideoClip>{   }
using System;
using UnityEngine;

[Serializable]public class AudioClipDictionary : SerializableDictionary<string, AudioClip>{   }
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

[Serializable] public class TextureDictionary : SerializableDictionary<string, Texture> { }

ScriptableObject 配置文件:

using UnityEngine;
using UnityEngine.Video;

[CreateAssetMenu(fileName = "Cfg_AppVideoDictionary", menuName = "Configures/Cfg_Videos", order = 1)]
public class VideoDictionaryConfig : ScriptableObject
{

    public VideoClipDictionary VideoPool;

}

Unity中的Dictionary是一种可序列化的字典类,它可以在检查器中进行编辑,无需实现自定义编辑器或属性抽屉。你可以使用任何可序列化的类型作为键或值。 Dictionary的用法有以下几个方面: 1. 实例化:你可以通过以下方式来实例化一个Dictionary: ``` Dictionary<键key, 值value> 名字dic = new Dictionary<键key, 值value>(); Dictionary<Tkey, Tvalue> Dic = new Dictionary<Tkey, Tvalue>(); ``` 2. 添加键值对:你可以使用`Add`方法来向Dictionary中添加键值对。例如: ``` dic.Add(key, value); ``` 3. 读取值:你可以使用索引器来读取Dictionary中特定键对应的值。例如: ``` print(dic[key]); ``` 4. 嵌套实例化:如果你需要在Dictionary中嵌套另一个Dictionary,你可以使用以下方式进行实例化和操作: ``` Dictionary<key, Dictionary<key, value>> dic = new Dictionary<key, Dictionary<key, value>>(); dic.Add(key, new Dictionary<key, value>()); // 实例化内嵌的字典 dic[key].Add(key, value); // 添加内层值 dic[key = new Dictionary<key, value>(); // 给外层的某个值赋值字典值 print(dic[key][key]); // 读取嵌套字典里的某个值 ``` 通过上述方法,你可以在Unity中使用Dictionary来管理键值对的数据。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [Unity-SerializableDictionaryUnity的可序列化字典类](https://download.csdn.net/download/weixin_42143092/15104075)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [unity c# dictionary字典用法,dictionary嵌套用法。](https://blog.csdn.net/u011644138/article/details/81562606)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值