Because Unity 4.5 now offer serialization callback, it's now possible to serialize dictionaries without horrible fancy hacking. We are now able to have dictionary that handles their own serialization without having to write anything extra. And because rewriting a generic collection with all its proper interface is always one major pain in the ass, here's a fully implemented one; Code (csharp): using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Runtime.InteropServices; using System.Runtime.Serialization; using System.Text; using UnityEngine; #if UNITY_4_5 [Serializable] [ComVisible(false)] [DebuggerDisplay("Count = {Count}")] public class UDictionary<TKey, TValue> : IDictionary<TKey, TValue>, ICollection<KeyValuePair<TKey, TValue>>, IEnumerable<KeyValuePair<TKey, TValue>>, IDictionary, ICollection, IEnumerable, ISerializable, IDeserializationCallback, ISerializationCallbackReceiver { [SerializeField] private List<TKey> keys = new List<TKey>(); [SerializeField] private List<TValue> values = new List<TValue>(); [NonSerialized] private Dictionary<TKey, TValue> dictionary = new Dictionary<TKey, TValue>(); #region Implementation of ISerializationCallbackReceiver public void OnAfterDeserialize() { dictionary.Clear(); for (int i = 0; i < keys.Count; i++) dictionary.Add(keys[i], values[i]); } public void OnBeforeSerialize() { keys.Clear(); values.Clear(); foreach (KeyValuePair<TKey, TValue> pair in dictionary) { keys.Add(pair.Key); values.Add(pair.Value); } } #endregion #region Implementation of ISerializable public void GetObjectData(SerializationInfo info, StreamingContext context) { dictionary.GetObjectData(info, context); } #endregion #region Implementation of IDeserializationCallback public void OnDeserialization(object sender) { dictionary.OnDeserialization(sender); } #endregion #region Implementation IDictionary public bool IsFixedSize { get { return false; } } public ICollection<TKey> Keys { get { return dictionary.Keys; } } ICollection IDictionary.Keys { get { return dictionary.Keys; } } public ICollection<TValue> Values { get { return dictionary.Values; } } ICollection IDictionary.Values { get { return dictionary.Values; } } public TValue this[TKey key] { get { return dictionary[key]; } set { dictionary[key] = value; } } object IDictionary.this[object key] { get { if (!(key is TKey)) return null; return dictionary[(TKey)key]; } set { if (!(key is TKey)) return; if (!(value is TValue)) return; dictionary[(TKey)key] = (TValue)value; } } public void Add(TKey key, TValue value) { dictionary.Add(key, value); } void IDictionary.Add(object key, object value) { if (!(key is TKey)) return; if (!(value is TValue)) return; dictionary.Add((TKey)key, (TValue)value); } public bool ContainsKey(TKey key) { return dictionary.ContainsKey(key); } bool IDictionary.Contains(object key) { if (!(key is TKey)) return false; return dictionary.ContainsKey((TKey)key); } public bool Remove(TKey key) { return dictionary.Remove(key); } void IDictionary.Remove(object key) { if (!(key is TKey)) return; dictionary.Remove((TKey)key); } public bool TryGetValue(TKey key, out TValue value) { return dictionary.TryGetValue(key, out value); } IDictionaryEnumerator IDictionary.GetEnumerator() { return dictionary.GetEnumerator(); } #endregion #region Implementation ICollection public int Count { get { return dictionary.Count; } } public bool IsReadOnly { get { return false; } } public bool IsSynchronized { get { return false; } } public object SyncRoot { get { return null; } } public void Add(KeyValuePair<TKey, TValue> item) { dictionary.Add(item.Key, item.Value); } public void Clear() { dictionary.Clear(); } public bool Contains(KeyValuePair<TKey, TValue> item) { return dictionary.ContainsKey(item.Key) && dictionary[item.Key].Equals(item.Value); } void ICollection.CopyTo(Array array, int index) { } void ICollection<KeyValuePair<TKey, TValue>>.CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex) { } public bool Remove(KeyValuePair<TKey, TValue> item) { return dictionary.Remove(item.Key); } #endregion #region Implementation of IEnumerable public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() { return dictionary.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return dictionary.GetEnumerator(); } #endregion } #endif Example of implementation; Code (csharp): public class AIExample_Dictionary : MonoBehaviour { /// <summary> /// Please note that Unity is still not friendly with generic dictionary. /// Therefor, you need a non-generic wrapping class. /// </summary> [Serializable] private class SFDictionary : UDictionary<string, float> { } [SerializeField] private SFDictionary dictionary = new SFDictionary(); } UDictionary will be a core class in a future Advanced Inspector update that will properly support dictionary inspection.
Just noticed... You can probably dump Code (csharp): using System.Diagnostics; using System.Linq; using System.Text;
Cool Interface example. Thanks for advice. But, you use two List for showing a Dictionary data? And only is readable. We can not edit Dictionary content from Inspector. ___________________________ I create another code, a derived Dictionary class, where I have added a DictionaryElements class array with two Dictionary elements, key and value. From this array, we can edit a Dictionary from Inspector, and look at the Dictionary elements at runtime. Code (CSharp): //******************************************** // script1.cs //******************************************** using UnityEngine; using System; using System.Collections.Generic; [Serializable] public class SFDictionary : Dictionary<string,float> { [Serializable] public class DictionaryElements{ public string key; public float value; public DictionaryElements(string _key, float _value){ this.key = _key; this.value = _value; } } [SerializeField] public DictionaryElements[] elements; private void UpdateElements(){ string[] arrayString = new string[this.Count]; this.Keys.CopyTo(arrayString,0); float[] arrayFloat = new float[this.Count]; this.Values.CopyTo(arrayFloat,0); this.elements = new DictionaryElements[this.Count]; for(int i=0;i<this.Count;i++){ this.elements[i] = new DictionaryElements(arrayString[i],arrayFloat [i]); } } public void Start(){ for(int i=0;i<this.elements.Length;i++){ base.Add(this.elements[i].key,this.elements[i].value); } } public void Add(string key, float value){ base.Add(key,value); UpdateElements(); } public bool Remove(string key){ if(base.Remove(key)){ this.UpdateElements(); return true; } return false; } } public class script1 : MonoBehaviour { public SFDictionary dictionary = new SFDictionary(); void Start(){ this.dictionary.Start(); } void Update(){ if(Input.GetKeyDown(KeyCode.Return)){ this.dictionary.Add(Time.time.ToString(),Time.time); } if(Input.GetKeyDown(KeyCode.Backspace)){ if(this.dictionary.Count>0){ string[] arrayString = new string[this.dictionary.Keys.Count]; this.dictionary.Keys.CopyTo(arrayString,0); this.dictionary.Remove(arrayString[arrayString.Length-1]); } } } } _____________________________ But, I do not get it to show dictionary elements array in Inspector from a class that uses generic type parameter.
No, you cannot display Dictionary in the Inspector. Because the class can now be serialized without fancy hacking doesn't mean Unity suddenly rewrote the inspector to support that type. You can - somewhat easily - write your own custom editor/property drawer to display and edit that type. Or you can go the way you did and hide the dictionary in background while displaying lists up front. Or you can use AssetStore packages that support dictionary types. (Ex.: http://i.imgur.com/0NYND7h.png ) Yup, Unity is very bad with generic. You always need a non-generic top-wrapping class to be able to serialize properly, and in the same extension, be able to show up in the Inspector. The only type Unity is able to serialize with generic argument is List<>.
I do... Check my signature for "Advanced Inspector". Dictionary of <int, Example_ComponentBase> where Example_ComponentBase is an abstract base class of sub-components;