summaryrefslogtreecommitdiff
path: root/Assets/SerializableDictionary
diff options
context:
space:
mode:
Diffstat (limited to 'Assets/SerializableDictionary')
-rw-r--r--Assets/SerializableDictionary/Editor.meta5
-rw-r--r--Assets/SerializableDictionary/Editor/SerializableDictionaryPropertyDrawer.cs565
-rw-r--r--Assets/SerializableDictionary/Editor/SerializableDictionaryPropertyDrawer.cs.meta12
-rw-r--r--Assets/SerializableDictionary/Example.meta5
-rw-r--r--Assets/SerializableDictionary/Example/Editor.meta5
-rw-r--r--Assets/SerializableDictionary/Example/Editor/UserSerializableDictionaryPropertyDrawers.cs12
-rw-r--r--Assets/SerializableDictionary/Example/Editor/UserSerializableDictionaryPropertyDrawers.cs.meta12
-rw-r--r--Assets/SerializableDictionary/Example/SerializableDicitonary Example.unity251
-rw-r--r--Assets/SerializableDictionary/Example/SerializableDicitonary Example.unity.meta4
-rw-r--r--Assets/SerializableDictionary/Example/SerializableDictionaryExample.cs24
-rw-r--r--Assets/SerializableDictionary/Example/SerializableDictionaryExample.cs.meta12
-rw-r--r--Assets/SerializableDictionary/Example/UserSerializableDictionaries.cs26
-rw-r--r--Assets/SerializableDictionary/Example/UserSerializableDictionaries.cs.meta12
-rw-r--r--Assets/SerializableDictionary/SerializableDictionary.cs126
-rw-r--r--Assets/SerializableDictionary/SerializableDictionary.cs.meta12
-rw-r--r--Assets/SerializableDictionary/SerializableDictionary.pdfbin0 -> 118820 bytes
-rw-r--r--Assets/SerializableDictionary/SerializableDictionary.pdf.meta8
17 files changed, 1091 insertions, 0 deletions
diff --git a/Assets/SerializableDictionary/Editor.meta b/Assets/SerializableDictionary/Editor.meta
new file mode 100644
index 00000000..5bfd990a
--- /dev/null
+++ b/Assets/SerializableDictionary/Editor.meta
@@ -0,0 +1,5 @@
+fileFormatVersion: 2
+guid: 20029ee1c099f4b47afbac48208879b9
+folderAsset: yes
+DefaultImporter:
+ userData:
diff --git a/Assets/SerializableDictionary/Editor/SerializableDictionaryPropertyDrawer.cs b/Assets/SerializableDictionary/Editor/SerializableDictionaryPropertyDrawer.cs
new file mode 100644
index 00000000..f631a96a
--- /dev/null
+++ b/Assets/SerializableDictionary/Editor/SerializableDictionaryPropertyDrawer.cs
@@ -0,0 +1,565 @@
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+using UnityEditor;
+using System.Reflection;
+using System;
+
+public class SerializableDictionaryPropertyDrawer : PropertyDrawer
+{
+ const string KeysFieldName = "m_keys";
+ const string ValuesFieldName = "m_values";
+ protected const float IndentWidth = 15f;
+
+ static GUIContent s_iconPlus = IconContent ("Toolbar Plus", "Add entry");
+ static GUIContent s_iconMinus = IconContent ("Toolbar Minus", "Remove entry");
+ static GUIContent s_warningIconConflict = IconContent ("console.warnicon.sml", "Conflicting key, this entry will be lost");
+ static GUIContent s_warningIconOther = IconContent ("console.infoicon.sml", "Conflicting key");
+ static GUIContent s_warningIconNull = IconContent ("console.warnicon.sml", "Null key, this entry will be lost");
+ static GUIStyle s_buttonStyle = GUIStyle.none;
+ static GUIContent s_tempContent = new GUIContent();
+
+
+ class ConflictState
+ {
+ public object conflictKey = null;
+ public object conflictValue = null;
+ public int conflictIndex = -1 ;
+ public int conflictOtherIndex = -1 ;
+ public bool conflictKeyPropertyExpanded = false;
+ public bool conflictValuePropertyExpanded = false;
+ public float conflictLineHeight = 0f;
+ }
+
+ struct PropertyIdentity
+ {
+ public PropertyIdentity(SerializedProperty property)
+ {
+ this.instance = property.serializedObject.targetObject;
+ this.propertyPath = property.propertyPath;
+ }
+
+ public UnityEngine.Object instance;
+ public string propertyPath;
+ }
+
+ static Dictionary<PropertyIdentity, ConflictState> s_conflictStateDict = new Dictionary<PropertyIdentity, ConflictState>();
+
+ enum Action
+ {
+ None,
+ Add,
+ Remove
+ }
+
+ public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
+ {
+ label = EditorGUI.BeginProperty(position, label, property);
+
+ Action buttonAction = Action.None;
+ int buttonActionIndex = 0;
+
+ var keyArrayProperty = property.FindPropertyRelative(KeysFieldName);
+ var valueArrayProperty = property.FindPropertyRelative(ValuesFieldName);
+
+ ConflictState conflictState = GetConflictState(property);
+
+ if(conflictState.conflictIndex != -1)
+ {
+ keyArrayProperty.InsertArrayElementAtIndex(conflictState.conflictIndex);
+ var keyProperty = keyArrayProperty.GetArrayElementAtIndex(conflictState.conflictIndex);
+ SetPropertyValue(keyProperty, conflictState.conflictKey);
+ keyProperty.isExpanded = conflictState.conflictKeyPropertyExpanded;
+
+ valueArrayProperty.InsertArrayElementAtIndex(conflictState.conflictIndex);
+ var valueProperty = valueArrayProperty.GetArrayElementAtIndex(conflictState.conflictIndex);
+ SetPropertyValue(valueProperty, conflictState.conflictValue);
+ valueProperty.isExpanded = conflictState.conflictValuePropertyExpanded;
+ }
+
+ var buttonWidth = s_buttonStyle.CalcSize(s_iconPlus).x;
+
+ var labelPosition = position;
+ labelPosition.height = EditorGUIUtility.singleLineHeight;
+ if (property.isExpanded)
+ labelPosition.xMax -= s_buttonStyle.CalcSize(s_iconPlus).x;
+
+ EditorGUI.PropertyField(labelPosition, property, label, false);
+ // property.isExpanded = EditorGUI.Foldout(labelPosition, property.isExpanded, label);
+ if (property.isExpanded)
+ {
+ var buttonPosition = position;
+ buttonPosition.xMin = buttonPosition.xMax - buttonWidth;
+ buttonPosition.height = EditorGUIUtility.singleLineHeight;
+ EditorGUI.BeginDisabledGroup(conflictState.conflictIndex != -1);
+ if(GUI.Button(buttonPosition, s_iconPlus, s_buttonStyle))
+ {
+ buttonAction = Action.Add;
+ buttonActionIndex = keyArrayProperty.arraySize;
+ }
+ EditorGUI.EndDisabledGroup();
+
+ EditorGUI.indentLevel++;
+ var linePosition = position;
+ linePosition.y += EditorGUIUtility.singleLineHeight;
+ linePosition.xMax -= buttonWidth;
+
+ foreach(var entry in EnumerateEntries(keyArrayProperty, valueArrayProperty))
+ {
+ var keyProperty = entry.keyProperty;
+ var valueProperty = entry.valueProperty;
+ int i = entry.index;
+
+ float lineHeight = DrawKeyValueLine(keyProperty, valueProperty, linePosition, i);
+
+ buttonPosition = linePosition;
+ buttonPosition.x = linePosition.xMax;
+ buttonPosition.height = EditorGUIUtility.singleLineHeight;
+ if(GUI.Button(buttonPosition, s_iconMinus, s_buttonStyle))
+ {
+ buttonAction = Action.Remove;
+ buttonActionIndex = i;
+ }
+
+ if(i == conflictState.conflictIndex && conflictState.conflictOtherIndex == -1)
+ {
+ var iconPosition = linePosition;
+ iconPosition.size = s_buttonStyle.CalcSize(s_warningIconNull);
+ GUI.Label(iconPosition, s_warningIconNull);
+ }
+ else if(i == conflictState.conflictIndex)
+ {
+ var iconPosition = linePosition;
+ iconPosition.size = s_buttonStyle.CalcSize(s_warningIconConflict);
+ GUI.Label(iconPosition, s_warningIconConflict);
+ }
+ else if(i == conflictState.conflictOtherIndex)
+ {
+ var iconPosition = linePosition;
+ iconPosition.size = s_buttonStyle.CalcSize(s_warningIconOther);
+ GUI.Label(iconPosition, s_warningIconOther);
+ }
+
+
+ linePosition.y += lineHeight;
+ }
+
+ EditorGUI.indentLevel--;
+ }
+
+ if(buttonAction == Action.Add)
+ {
+ keyArrayProperty.InsertArrayElementAtIndex(buttonActionIndex);
+ valueArrayProperty.InsertArrayElementAtIndex(buttonActionIndex);
+ }
+ else if(buttonAction == Action.Remove)
+ {
+ DeleteArrayElementAtIndex(keyArrayProperty, buttonActionIndex);
+ DeleteArrayElementAtIndex(valueArrayProperty, buttonActionIndex);
+ }
+
+ conflictState.conflictKey = null;
+ conflictState.conflictValue = null;
+ conflictState.conflictIndex = -1;
+ conflictState.conflictOtherIndex = -1;
+ conflictState.conflictLineHeight = 0f;
+ conflictState.conflictKeyPropertyExpanded = false;
+ conflictState.conflictValuePropertyExpanded = false;
+
+ foreach(var entry1 in EnumerateEntries(keyArrayProperty, valueArrayProperty))
+ {
+ var keyProperty1 = entry1.keyProperty;
+ int i = entry1.index;
+ object keyProperty1Value = GetPropertyValue(keyProperty1);
+
+ if(keyProperty1Value == null)
+ {
+ var valueProperty1 = entry1.valueProperty;
+ SaveProperty(keyProperty1, valueProperty1, i, -1, conflictState);
+ DeleteArrayElementAtIndex(valueArrayProperty, i);
+ DeleteArrayElementAtIndex(keyArrayProperty, i);
+
+ break;
+ }
+
+
+ foreach(var entry2 in EnumerateEntries(keyArrayProperty, valueArrayProperty, i + 1))
+ {
+ var keyProperty2 = entry2.keyProperty;
+ int j = entry2.index;
+ object keyProperty2Value = GetPropertyValue(keyProperty2);
+
+ if(ComparePropertyValues(keyProperty1Value, keyProperty2Value))
+ {
+ var valueProperty2 = entry2.valueProperty;
+ SaveProperty(keyProperty2, valueProperty2, j, i, conflictState);
+ DeleteArrayElementAtIndex(keyArrayProperty, j);
+ DeleteArrayElementAtIndex(valueArrayProperty, j);
+
+ goto breakLoops;
+ }
+ }
+ }
+ breakLoops:
+
+ EditorGUI.EndProperty();
+ }
+
+ static float DrawKeyValueLine(SerializedProperty keyProperty, SerializedProperty valueProperty, Rect linePosition, int index)
+ {
+ bool keyCanBeExpanded = CanPropertyBeExpanded(keyProperty);
+ bool valueCanBeExpanded = CanPropertyBeExpanded(valueProperty);
+
+ if(!keyCanBeExpanded && valueCanBeExpanded)
+ {
+ return DrawKeyValueLineExpand(keyProperty, valueProperty, linePosition);
+ }
+ else
+ {
+ var keyLabel = keyCanBeExpanded ? ("Key " + index.ToString()) : "";
+ var valueLabel = valueCanBeExpanded ? ("Value " + index.ToString()) : "";
+ return DrawKeyValueLineSimple(keyProperty, valueProperty, keyLabel, valueLabel, linePosition);
+ }
+ }
+
+ static float DrawKeyValueLineSimple(SerializedProperty keyProperty, SerializedProperty valueProperty, string keyLabel, string valueLabel, Rect linePosition)
+ {
+ float labelWidth = EditorGUIUtility.labelWidth;
+ float labelWidthRelative = labelWidth / linePosition.width;
+
+ float keyPropertyHeight = EditorGUI.GetPropertyHeight(keyProperty);
+ var keyPosition = linePosition;
+ keyPosition.height = keyPropertyHeight;
+ keyPosition.width = labelWidth - IndentWidth;
+ EditorGUIUtility.labelWidth = keyPosition.width * labelWidthRelative;
+ EditorGUI.PropertyField(keyPosition, keyProperty, TempContent(keyLabel), true);
+
+ float valuePropertyHeight = EditorGUI.GetPropertyHeight(valueProperty);
+ var valuePosition = linePosition;
+ valuePosition.height = valuePropertyHeight;
+ valuePosition.xMin += labelWidth;
+ EditorGUIUtility.labelWidth = valuePosition.width * labelWidthRelative;
+ EditorGUI.indentLevel--;
+ EditorGUI.PropertyField(valuePosition, valueProperty, TempContent(valueLabel), true);
+ EditorGUI.indentLevel++;
+
+ EditorGUIUtility.labelWidth = labelWidth;
+
+ return Mathf.Max(keyPropertyHeight, valuePropertyHeight);
+ }
+
+ static float DrawKeyValueLineExpand(SerializedProperty keyProperty, SerializedProperty valueProperty, Rect linePosition)
+ {
+ float labelWidth = EditorGUIUtility.labelWidth;
+
+ float keyPropertyHeight = EditorGUI.GetPropertyHeight(keyProperty);
+ var keyPosition = linePosition;
+ keyPosition.height = keyPropertyHeight;
+ keyPosition.width = labelWidth - IndentWidth;
+ EditorGUI.PropertyField(keyPosition, keyProperty, GUIContent.none, true);
+
+ float valuePropertyHeight = EditorGUI.GetPropertyHeight(valueProperty);
+ var valuePosition = linePosition;
+ valuePosition.height = valuePropertyHeight;
+ EditorGUI.PropertyField(valuePosition, valueProperty, GUIContent.none, true);
+
+ EditorGUIUtility.labelWidth = labelWidth;
+
+ return Mathf.Max(keyPropertyHeight, valuePropertyHeight);
+ }
+
+ static bool CanPropertyBeExpanded(SerializedProperty property)
+ {
+ switch(property.propertyType)
+ {
+ case SerializedPropertyType.Generic:
+ case SerializedPropertyType.Vector4:
+ case SerializedPropertyType.Quaternion:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ static void SaveProperty(SerializedProperty keyProperty, SerializedProperty valueProperty, int index, int otherIndex, ConflictState conflictState)
+ {
+ conflictState.conflictKey = GetPropertyValue(keyProperty);
+ conflictState.conflictValue = GetPropertyValue(valueProperty);
+ float keyPropertyHeight = EditorGUI.GetPropertyHeight(keyProperty);
+ float valuePropertyHeight = EditorGUI.GetPropertyHeight(valueProperty);
+ float lineHeight = Mathf.Max(keyPropertyHeight, valuePropertyHeight);
+ conflictState.conflictLineHeight = lineHeight;
+ conflictState.conflictIndex = index;
+ conflictState.conflictOtherIndex = otherIndex;
+ conflictState.conflictKeyPropertyExpanded = keyProperty.isExpanded;
+ conflictState.conflictValuePropertyExpanded = valueProperty.isExpanded;
+ }
+
+ public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
+ {
+ float propertyHeight = EditorGUIUtility.singleLineHeight;
+
+ if (property.isExpanded)
+ {
+ var keysProperty = property.FindPropertyRelative(KeysFieldName);
+ var valuesProperty = property.FindPropertyRelative(ValuesFieldName);
+
+ foreach(var entry in EnumerateEntries(keysProperty, valuesProperty))
+ {
+ var keyProperty = entry.keyProperty;
+ var valueProperty = entry.valueProperty;
+ float keyPropertyHeight = EditorGUI.GetPropertyHeight(keyProperty);
+ float valuePropertyHeight = EditorGUI.GetPropertyHeight(valueProperty);
+ float lineHeight = Mathf.Max(keyPropertyHeight, valuePropertyHeight);
+ propertyHeight += lineHeight;
+ }
+
+ ConflictState conflictState = GetConflictState(property);
+
+ if(conflictState.conflictIndex != -1)
+ {
+ propertyHeight += conflictState.conflictLineHeight;
+ }
+ }
+
+ return propertyHeight;
+ }
+
+ static ConflictState GetConflictState(SerializedProperty property)
+ {
+ ConflictState conflictState;
+ PropertyIdentity propId = new PropertyIdentity(property);
+ if(!s_conflictStateDict.TryGetValue(propId, out conflictState))
+ {
+ conflictState = new ConflictState();
+ s_conflictStateDict.Add(propId, conflictState);
+ }
+ return conflictState;
+ }
+
+ static Dictionary<SerializedPropertyType, PropertyInfo> s_serializedPropertyValueAccessorsDict;
+
+ static SerializableDictionaryPropertyDrawer()
+ {
+ Dictionary<SerializedPropertyType, string> serializedPropertyValueAccessorsNameDict = new Dictionary<SerializedPropertyType, string>() {
+ { SerializedPropertyType.Integer, "intValue" },
+ { SerializedPropertyType.Boolean, "boolValue" },
+ { SerializedPropertyType.Float, "floatValue" },
+ { SerializedPropertyType.String, "stringValue" },
+ { SerializedPropertyType.Color, "colorValue" },
+ { SerializedPropertyType.ObjectReference, "objectReferenceValue" },
+ { SerializedPropertyType.LayerMask, "intValue" },
+ { SerializedPropertyType.Enum, "intValue" },
+ { SerializedPropertyType.Vector2, "vector2Value" },
+ { SerializedPropertyType.Vector3, "vector3Value" },
+ { SerializedPropertyType.Vector4, "vector4Value" },
+ { SerializedPropertyType.Rect, "rectValue" },
+ { SerializedPropertyType.ArraySize, "intValue" },
+ { SerializedPropertyType.Character, "intValue" },
+ { SerializedPropertyType.AnimationCurve, "animationCurveValue" },
+ { SerializedPropertyType.Bounds, "boundsValue" },
+ { SerializedPropertyType.Quaternion, "quaternionValue" },
+ };
+ Type serializedPropertyType = typeof(SerializedProperty);
+
+ s_serializedPropertyValueAccessorsDict = new Dictionary<SerializedPropertyType, PropertyInfo>();
+ BindingFlags flags = BindingFlags.Instance | BindingFlags.Public;
+
+ foreach(var kvp in serializedPropertyValueAccessorsNameDict)
+ {
+ PropertyInfo propertyInfo = serializedPropertyType.GetProperty(kvp.Value, flags);
+ s_serializedPropertyValueAccessorsDict.Add(kvp.Key, propertyInfo);
+ }
+ }
+
+ static GUIContent IconContent(string name, string tooltip)
+ {
+ var builtinIcon = EditorGUIUtility.IconContent (name);
+ return new GUIContent(builtinIcon.image, tooltip);
+ }
+
+ static GUIContent TempContent(string text)
+ {
+ s_tempContent.text = text;
+ return s_tempContent;
+ }
+
+ static void DeleteArrayElementAtIndex(SerializedProperty arrayProperty, int index)
+ {
+ var property = arrayProperty.GetArrayElementAtIndex(index);
+ // if(arrayProperty.arrayElementType.StartsWith("PPtr<$"))
+ if(property.propertyType == SerializedPropertyType.ObjectReference)
+ {
+ property.objectReferenceValue = null;
+ }
+
+ arrayProperty.DeleteArrayElementAtIndex(index);
+ }
+
+ public static object GetPropertyValue(SerializedProperty p)
+ {
+ PropertyInfo propertyInfo;
+ if(s_serializedPropertyValueAccessorsDict.TryGetValue(p.propertyType, out propertyInfo))
+ {
+ return propertyInfo.GetValue(p, null);
+ }
+ else
+ {
+ if(p.isArray)
+ return GetPropertyValueArray(p);
+ else
+ return GetPropertyValueGeneric(p);
+ }
+ }
+
+ static void SetPropertyValue(SerializedProperty p, object v)
+ {
+ PropertyInfo propertyInfo;
+ if(s_serializedPropertyValueAccessorsDict.TryGetValue(p.propertyType, out propertyInfo))
+ {
+ propertyInfo.SetValue(p, v, null);
+ }
+ else
+ {
+ if(p.isArray)
+ SetPropertyValueArray(p, v);
+ else
+ SetPropertyValueGeneric(p, v);
+ }
+ }
+
+ static object GetPropertyValueArray(SerializedProperty property)
+ {
+ object[] array = new object[property.arraySize];
+ for(int i = 0; i < property.arraySize; i++)
+ {
+ SerializedProperty item = property.GetArrayElementAtIndex(i);
+ array[i] = GetPropertyValue(item);
+ }
+ return array;
+ }
+
+ static object GetPropertyValueGeneric(SerializedProperty property)
+ {
+ Dictionary<string, object> dict = new Dictionary<string, object>();
+ var iterator = property.Copy();
+ if(iterator.Next(true))
+ {
+ var end = property.GetEndProperty();
+ do
+ {
+ string name = iterator.name;
+ object value = GetPropertyValue(iterator);
+ dict.Add(name, value);
+ } while(iterator.Next(false) && iterator.propertyPath != end.propertyPath);
+ }
+ return dict;
+ }
+
+ static void SetPropertyValueArray(SerializedProperty property, object v)
+ {
+ object[] array = (object[]) v;
+ property.arraySize = array.Length;
+ for(int i = 0; i < property.arraySize; i++)
+ {
+ SerializedProperty item = property.GetArrayElementAtIndex(i);
+ SetPropertyValue(item, array[i]);
+ }
+ }
+
+ static void SetPropertyValueGeneric(SerializedProperty property, object v)
+ {
+ Dictionary<string, object> dict = (Dictionary<string, object>) v;
+ var iterator = property.Copy();
+ if(iterator.Next(true))
+ {
+ var end = property.GetEndProperty();
+ do
+ {
+ string name = iterator.name;
+ SetPropertyValue(iterator, dict[name]);
+ } while(iterator.Next(false) && iterator.propertyPath != end.propertyPath);
+ }
+ }
+
+ static bool ComparePropertyValues(object value1, object value2)
+ {
+ if(value1 is Dictionary<string, object> && value2 is Dictionary<string, object>)
+ {
+ var dict1 = (Dictionary<string, object>)value1;
+ var dict2 = (Dictionary<string, object>)value2;
+ return CompareDictionaries(dict1, dict2);
+ }
+ else
+ {
+ return object.Equals(value1, value2);
+ }
+ }
+
+ static bool CompareDictionaries(Dictionary<string, object> dict1, Dictionary<string, object> dict2)
+ {
+ if(dict1.Count != dict2.Count)
+ return false;
+
+ foreach(var kvp1 in dict1)
+ {
+ var key1 = kvp1.Key;
+ object value1 = kvp1.Value;
+
+ object value2;
+ if(!dict2.TryGetValue(key1, out value2))
+ return false;
+
+ if(!ComparePropertyValues(value1, value2))
+ return false;
+ }
+
+ return true;
+ }
+
+ struct EnumerationEntry
+ {
+ public SerializedProperty keyProperty;
+ public SerializedProperty valueProperty;
+ public int index;
+
+ public EnumerationEntry(SerializedProperty keyProperty, SerializedProperty valueProperty, int index)
+ {
+ this.keyProperty = keyProperty;
+ this.valueProperty = valueProperty;
+ this.index = index;
+ }
+ }
+
+ static IEnumerable<EnumerationEntry> EnumerateEntries(SerializedProperty keyArrayProperty, SerializedProperty valueArrayProperty, int startIndex = 0)
+ {
+ if(keyArrayProperty.arraySize > startIndex)
+ {
+ int index = startIndex;
+ var keyProperty = keyArrayProperty.GetArrayElementAtIndex(startIndex);
+ var valueProperty = valueArrayProperty.GetArrayElementAtIndex(startIndex);
+ var endProperty = keyArrayProperty.GetEndProperty();
+
+ do
+ {
+ yield return new EnumerationEntry(keyProperty, valueProperty, index);
+ index++;
+ } while(keyProperty.Next(false) && valueProperty.Next(false) && !SerializedProperty.EqualContents(keyProperty, endProperty));
+ }
+ }
+}
+
+public class SerializableDictionaryStoragePropertyDrawer : PropertyDrawer
+{
+ public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
+ {
+ property.Next(true);
+ EditorGUI.PropertyField(position, property, label, true);
+ }
+
+ public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
+ {
+ property.Next(true);
+ return EditorGUI.GetPropertyHeight(property);
+ }
+}
diff --git a/Assets/SerializableDictionary/Editor/SerializableDictionaryPropertyDrawer.cs.meta b/Assets/SerializableDictionary/Editor/SerializableDictionaryPropertyDrawer.cs.meta
new file mode 100644
index 00000000..e10f405f
--- /dev/null
+++ b/Assets/SerializableDictionary/Editor/SerializableDictionaryPropertyDrawer.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 91da51d02ab9ebc459d80d5965d40d19
+timeCreated: 1492869349
+licenseType: Store
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/SerializableDictionary/Example.meta b/Assets/SerializableDictionary/Example.meta
new file mode 100644
index 00000000..5bbdabde
--- /dev/null
+++ b/Assets/SerializableDictionary/Example.meta
@@ -0,0 +1,5 @@
+fileFormatVersion: 2
+guid: ea574807be3814126807ca7b22d13988
+folderAsset: yes
+DefaultImporter:
+ userData:
diff --git a/Assets/SerializableDictionary/Example/Editor.meta b/Assets/SerializableDictionary/Example/Editor.meta
new file mode 100644
index 00000000..f3048e56
--- /dev/null
+++ b/Assets/SerializableDictionary/Example/Editor.meta
@@ -0,0 +1,5 @@
+fileFormatVersion: 2
+guid: b9e966b834ddb48d0815c494d4f511da
+folderAsset: yes
+DefaultImporter:
+ userData:
diff --git a/Assets/SerializableDictionary/Example/Editor/UserSerializableDictionaryPropertyDrawers.cs b/Assets/SerializableDictionary/Example/Editor/UserSerializableDictionaryPropertyDrawers.cs
new file mode 100644
index 00000000..5c63f1ea
--- /dev/null
+++ b/Assets/SerializableDictionary/Example/Editor/UserSerializableDictionaryPropertyDrawers.cs
@@ -0,0 +1,12 @@
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+using UnityEditor;
+
+[CustomPropertyDrawer(typeof(StringStringDictionary))]
+[CustomPropertyDrawer(typeof(ObjectColorDictionary))]
+[CustomPropertyDrawer(typeof(StringColorArrayDictionary))]
+public class AnySerializableDictionaryPropertyDrawer : SerializableDictionaryPropertyDrawer {}
+
+[CustomPropertyDrawer(typeof(ColorArrayStorage))]
+public class AnySerializableDictionaryStoragePropertyDrawer: SerializableDictionaryStoragePropertyDrawer {}
diff --git a/Assets/SerializableDictionary/Example/Editor/UserSerializableDictionaryPropertyDrawers.cs.meta b/Assets/SerializableDictionary/Example/Editor/UserSerializableDictionaryPropertyDrawers.cs.meta
new file mode 100644
index 00000000..0c101e13
--- /dev/null
+++ b/Assets/SerializableDictionary/Example/Editor/UserSerializableDictionaryPropertyDrawers.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 3089b6c771301cc44ba23b7f8f469e14
+timeCreated: 1493639898
+licenseType: Store
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/SerializableDictionary/Example/SerializableDicitonary Example.unity b/Assets/SerializableDictionary/Example/SerializableDicitonary Example.unity
new file mode 100644
index 00000000..ce09708d
--- /dev/null
+++ b/Assets/SerializableDictionary/Example/SerializableDicitonary Example.unity
@@ -0,0 +1,251 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!29 &1
+OcclusionCullingSettings:
+ m_ObjectHideFlags: 0
+ serializedVersion: 2
+ m_OcclusionBakeSettings:
+ smallestOccluder: 5
+ smallestHole: 0.25
+ backfaceThreshold: 100
+ m_SceneGUID: 00000000000000000000000000000000
+ m_OcclusionCullingData: {fileID: 0}
+--- !u!104 &2
+RenderSettings:
+ m_ObjectHideFlags: 0
+ serializedVersion: 9
+ m_Fog: 0
+ m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1}
+ m_FogMode: 3
+ m_FogDensity: 0.01
+ m_LinearFogStart: 0
+ m_LinearFogEnd: 300
+ m_AmbientSkyColor: {r: 0.2, g: 0.2, b: 0.2, a: 1}
+ m_AmbientEquatorColor: {r: 0.2, g: 0.2, b: 0.2, a: 1}
+ m_AmbientGroundColor: {r: 0.2, g: 0.2, b: 0.2, a: 1}
+ m_AmbientIntensity: 1
+ m_AmbientMode: 3
+ m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1}
+ m_SkyboxMaterial: {fileID: 0}
+ m_HaloStrength: 0.5
+ m_FlareStrength: 1
+ m_FlareFadeSpeed: 3
+ m_HaloTexture: {fileID: 0}
+ m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0}
+ m_DefaultReflectionMode: 0
+ m_DefaultReflectionResolution: 128
+ m_ReflectionBounces: 1
+ m_ReflectionIntensity: 1
+ m_CustomReflection: {fileID: 0}
+ m_Sun: {fileID: 0}
+ m_IndirectSpecularColor: {r: 0, g: 0, b: 0, a: 1}
+ m_UseRadianceAmbientProbe: 0
+--- !u!157 &4
+LightmapSettings:
+ m_ObjectHideFlags: 0
+ serializedVersion: 11
+ m_GIWorkflowMode: 1
+ m_GISettings:
+ serializedVersion: 2
+ m_BounceScale: 1
+ m_IndirectOutputScale: 1
+ m_AlbedoBoost: 1
+ m_TemporalCoherenceThreshold: 1
+ m_EnvironmentLightingMode: 0
+ m_EnableBakedLightmaps: 1
+ m_EnableRealtimeLightmaps: 0
+ m_LightmapEditorSettings:
+ serializedVersion: 10
+ m_Resolution: 1
+ m_BakeResolution: 50
+ m_AtlasSize: 1024
+ m_AO: 1
+ m_AOMaxDistance: 1
+ m_CompAOExponent: 1
+ m_CompAOExponentDirect: 0
+ m_Padding: 2
+ m_LightmapParameters: {fileID: 0}
+ m_LightmapsBakeMode: 1
+ m_TextureCompression: 0
+ m_FinalGather: 0
+ m_FinalGatherFiltering: 1
+ m_FinalGatherRayCount: 256
+ m_ReflectionCompression: 2
+ m_MixedBakeMode: 1
+ m_BakeBackend: 0
+ m_PVRSampling: 1
+ m_PVRDirectSampleCount: 32
+ m_PVRSampleCount: 500
+ m_PVRBounces: 2
+ m_PVRFilterTypeDirect: 0
+ m_PVRFilterTypeIndirect: 0
+ m_PVRFilterTypeAO: 0
+ m_PVRFilteringMode: 0
+ m_PVRCulling: 1
+ m_PVRFilteringGaussRadiusDirect: 1
+ m_PVRFilteringGaussRadiusIndirect: 5
+ m_PVRFilteringGaussRadiusAO: 2
+ m_PVRFilteringAtrousPositionSigmaDirect: 0.5
+ m_PVRFilteringAtrousPositionSigmaIndirect: 2
+ m_PVRFilteringAtrousPositionSigmaAO: 1
+ m_ShowResolutionOverlay: 1
+ m_LightingDataAsset: {fileID: 0}
+ m_UseShadowmask: 0
+--- !u!196 &5
+NavMeshSettings:
+ serializedVersion: 2
+ m_ObjectHideFlags: 0
+ m_BuildSettings:
+ serializedVersion: 2
+ agentTypeID: 0
+ agentRadius: 0.5
+ agentHeight: 2
+ agentSlope: 45
+ agentClimb: 0.4
+ ledgeDropHeight: 0
+ maxJumpAcrossDistance: 0
+ minRegionArea: 2
+ manualCellSize: 0
+ cellSize: 0.16666666
+ manualTileSize: 0
+ tileSize: 256
+ accuratePlacement: 0
+ debug:
+ m_Flags: 0
+ m_NavMeshData: {fileID: 0}
+--- !u!1 &714127983
+GameObject:
+ m_ObjectHideFlags: 0
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 0}
+ serializedVersion: 5
+ m_Component:
+ - component: {fileID: 714127989}
+ - component: {fileID: 714127988}
+ - component: {fileID: 714127987}
+ - component: {fileID: 714127986}
+ - component: {fileID: 714127985}
+ - component: {fileID: 714127984}
+ m_Layer: 0
+ m_Name: Main Camera
+ m_TagString: MainCamera
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!114 &714127984
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 0}
+ m_GameObject: {fileID: 714127983}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: d5ff00a74ccb984498070d9930a7944c, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ m_stringStringDictionary:
+ m_keys:
+ - first key
+ - second key
+ - third key
+ m_values:
+ - value A
+ - value B
+ - value C
+ m_objectColorDictionary:
+ m_keys:
+ - {fileID: 714127983}
+ - {fileID: 714127984}
+ m_values:
+ - {r: 0, g: 0, b: 1, a: 1}
+ - {r: 1, g: 0, b: 0, a: 1}
+ m_objectColorArrayDictionary:
+ m_keys:
+ - rainbow
+ - grayscale
+ m_values:
+ - data:
+ - {r: 1, g: 0, b: 0, a: 1}
+ - {r: 1, g: 0.40000004, b: 0, a: 1}
+ - {r: 1, g: 0.9333334, b: 0, a: 1}
+ - {r: 0, g: 1, b: 0, a: 1}
+ - {r: 0, g: 0.6, b: 1, a: 1}
+ - {r: 0.26666668, g: 0, b: 1, a: 1}
+ - {r: 0.6, g: 0, b: 1, a: 1}
+ - data:
+ - {r: 0, g: 0, b: 0, a: 0}
+ - {r: 0.2509804, g: 0.2509804, b: 0.2509804, a: 0.2509804}
+ - {r: 0.5019608, g: 0.5019608, b: 0.5019608, a: 0.5019608}
+ - {r: 0.7529412, g: 0.7529412, b: 0.7529412, a: 0.7529412}
+ - {r: 1, g: 1, b: 1, a: 1}
+--- !u!81 &714127985
+AudioListener:
+ m_ObjectHideFlags: 0
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 0}
+ m_GameObject: {fileID: 714127983}
+ m_Enabled: 1
+--- !u!124 &714127986
+Behaviour:
+ m_ObjectHideFlags: 0
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 0}
+ m_GameObject: {fileID: 714127983}
+ m_Enabled: 1
+--- !u!92 &714127987
+Behaviour:
+ m_ObjectHideFlags: 0
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 0}
+ m_GameObject: {fileID: 714127983}
+ m_Enabled: 1
+--- !u!20 &714127988
+Camera:
+ m_ObjectHideFlags: 0
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 0}
+ m_GameObject: {fileID: 714127983}
+ m_Enabled: 1
+ serializedVersion: 2
+ m_ClearFlags: 1
+ m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0.019607844}
+ m_NormalizedViewPortRect:
+ serializedVersion: 2
+ x: 0
+ y: 0
+ width: 1
+ height: 1
+ near clip plane: 0.3
+ far clip plane: 1000
+ field of view: 60
+ orthographic: 0
+ orthographic size: 5
+ m_Depth: -1
+ m_CullingMask:
+ serializedVersion: 2
+ m_Bits: 4294967295
+ m_RenderingPath: -1
+ m_TargetTexture: {fileID: 0}
+ m_TargetDisplay: 0
+ m_TargetEye: 3
+ m_HDR: 0
+ m_AllowMSAA: 1
+ m_AllowDynamicResolution: 0
+ m_ForceIntoRT: 0
+ m_OcclusionCulling: 1
+ m_StereoConvergence: 10
+ m_StereoSeparation: 0.022
+--- !u!4 &714127989
+Transform:
+ m_ObjectHideFlags: 0
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 0}
+ m_GameObject: {fileID: 714127983}
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 0, y: 1, z: -10}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_Children: []
+ m_Father: {fileID: 0}
+ m_RootOrder: 0
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
diff --git a/Assets/SerializableDictionary/Example/SerializableDicitonary Example.unity.meta b/Assets/SerializableDictionary/Example/SerializableDicitonary Example.unity.meta
new file mode 100644
index 00000000..25ff160d
--- /dev/null
+++ b/Assets/SerializableDictionary/Example/SerializableDicitonary Example.unity.meta
@@ -0,0 +1,4 @@
+fileFormatVersion: 2
+guid: 6c0758ade4ba74b7493ce3806fa2f38b
+DefaultImporter:
+ userData:
diff --git a/Assets/SerializableDictionary/Example/SerializableDictionaryExample.cs b/Assets/SerializableDictionary/Example/SerializableDictionaryExample.cs
new file mode 100644
index 00000000..f47a8923
--- /dev/null
+++ b/Assets/SerializableDictionary/Example/SerializableDictionaryExample.cs
@@ -0,0 +1,24 @@
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+
+public class SerializableDictionaryExample : MonoBehaviour {
+ // The dictionaries can be accessed throught a property
+ [SerializeField]
+ StringStringDictionary m_stringStringDictionary;
+ public IDictionary<string, string> StringStringDictionary
+ {
+ get { return m_stringStringDictionary; }
+ set { m_stringStringDictionary.CopyFrom (value); }
+ }
+
+ public ObjectColorDictionary m_objectColorDictionary;
+ public StringColorArrayDictionary m_objectColorArrayDictionary;
+
+ void Reset ()
+ {
+ // access by property
+ StringStringDictionary = new Dictionary<string, string>() { {"first key", "value A"}, {"second key", "value B"}, {"third key", "value C"} };
+ m_objectColorDictionary = new ObjectColorDictionary() { {gameObject, Color.blue}, {this, Color.red} };
+ }
+}
diff --git a/Assets/SerializableDictionary/Example/SerializableDictionaryExample.cs.meta b/Assets/SerializableDictionary/Example/SerializableDictionaryExample.cs.meta
new file mode 100644
index 00000000..84a0fd47
--- /dev/null
+++ b/Assets/SerializableDictionary/Example/SerializableDictionaryExample.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: d5ff00a74ccb984498070d9930a7944c
+timeCreated: 1492868873
+licenseType: Store
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/SerializableDictionary/Example/UserSerializableDictionaries.cs b/Assets/SerializableDictionary/Example/UserSerializableDictionaries.cs
new file mode 100644
index 00000000..b20b24bd
--- /dev/null
+++ b/Assets/SerializableDictionary/Example/UserSerializableDictionaries.cs
@@ -0,0 +1,26 @@
+using System.Collections;
+using System.Collections.Generic;
+using System;
+using UnityEngine;
+
+[Serializable]
+public class StringStringDictionary : SerializableDictionary<string, string> {}
+
+[Serializable]
+public class ObjectColorDictionary : SerializableDictionary<UnityEngine.Object, Color> {}
+
+[Serializable]
+public class ColorArrayStorage : SerializableDictionary.Storage<Color[]> {}
+
+[Serializable]
+public class StringColorArrayDictionary : SerializableDictionary<string, Color[], ColorArrayStorage> {}
+
+[Serializable]
+public class MyClass
+{
+ public int i;
+ public string str;
+}
+
+[Serializable]
+public class QuaternionMyClassDictionary : SerializableDictionary<Quaternion, MyClass> {} \ No newline at end of file
diff --git a/Assets/SerializableDictionary/Example/UserSerializableDictionaries.cs.meta b/Assets/SerializableDictionary/Example/UserSerializableDictionaries.cs.meta
new file mode 100644
index 00000000..0a39089b
--- /dev/null
+++ b/Assets/SerializableDictionary/Example/UserSerializableDictionaries.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: bd910530dd8f6b5429c65a494644ed09
+timeCreated: 1493639913
+licenseType: Store
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/SerializableDictionary/SerializableDictionary.cs b/Assets/SerializableDictionary/SerializableDictionary.cs
new file mode 100644
index 00000000..4614ed7f
--- /dev/null
+++ b/Assets/SerializableDictionary/SerializableDictionary.cs
@@ -0,0 +1,126 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Runtime.Serialization;
+using UnityEngine;
+
+public abstract class SerializableDictionaryBase<TKey, TValue, TValueStorage> : Dictionary<TKey, TValue>, ISerializationCallbackReceiver
+{
+ [SerializeField]
+ TKey[] m_keys;
+ [SerializeField]
+ TValueStorage[] m_values;
+
+ public SerializableDictionaryBase()
+ {
+ }
+
+ public SerializableDictionaryBase(IDictionary<TKey, TValue> dict) : base(dict.Count)
+ {
+ foreach (var kvp in dict)
+ {
+ this[kvp.Key] = kvp.Value;
+ }
+ }
+
+ protected SerializableDictionaryBase(SerializationInfo info, StreamingContext context) : base(info,context){}
+
+ protected abstract void SetValue(TValueStorage[] storage, int i, TValue value);
+ protected abstract TValue GetValue(TValueStorage[] storage, int i);
+
+ public void CopyFrom(IDictionary<TKey, TValue> dict)
+ {
+ this.Clear();
+ foreach (var kvp in dict)
+ {
+ this[kvp.Key] = kvp.Value;
+ }
+ }
+
+ public void OnAfterDeserialize()
+ {
+ if(m_keys != null && m_values != null && m_keys.Length == m_values.Length)
+ {
+ this.Clear();
+ int n = m_keys.Length;
+ for(int i = 0; i < n; ++i)
+ {
+ this[m_keys[i]] = GetValue(m_values, i);
+ }
+
+ m_keys = null;
+ m_values = null;
+ }
+
+ }
+
+ public void OnBeforeSerialize()
+ {
+ int n = this.Count;
+ m_keys = new TKey[n];
+ m_values = new TValueStorage[n];
+
+ int i = 0;
+ foreach(var kvp in this)
+ {
+ m_keys[i] = kvp.Key;
+ SetValue(m_values, i, kvp.Value);
+ ++i;
+ }
+ }
+}
+
+public class SerializableDictionary<TKey, TValue> : SerializableDictionaryBase<TKey, TValue, TValue>
+{
+ public SerializableDictionary()
+ {
+ }
+
+ public SerializableDictionary(IDictionary<TKey, TValue> dict) : base(dict)
+ {
+ }
+
+ protected SerializableDictionary(SerializationInfo info, StreamingContext context) : base(info,context){}
+
+ protected override TValue GetValue(TValue[] storage, int i)
+ {
+ return storage[i];
+ }
+
+ protected override void SetValue(TValue[] storage, int i, TValue value)
+ {
+ storage[i] = value;
+ }
+}
+
+public static class SerializableDictionary
+{
+ public class Storage<T>
+ {
+ public T data;
+ }
+}
+
+public class SerializableDictionary<TKey, TValue, TValueStorage> : SerializableDictionaryBase<TKey, TValue, TValueStorage> where TValueStorage : SerializableDictionary.Storage<TValue>, new()
+{
+ public SerializableDictionary()
+ {
+ }
+
+ public SerializableDictionary(IDictionary<TKey, TValue> dict) : base(dict)
+ {
+ }
+
+ protected SerializableDictionary(SerializationInfo info, StreamingContext context) : base(info,context){}
+
+ protected override TValue GetValue(TValueStorage[] storage, int i)
+ {
+ return storage[i].data;
+ }
+
+ protected override void SetValue(TValueStorage[] storage, int i, TValue value)
+ {
+ storage[i] = new TValueStorage();
+ storage[i].data = value;
+ }
+}
diff --git a/Assets/SerializableDictionary/SerializableDictionary.cs.meta b/Assets/SerializableDictionary/SerializableDictionary.cs.meta
new file mode 100644
index 00000000..cdf58f09
--- /dev/null
+++ b/Assets/SerializableDictionary/SerializableDictionary.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: e7be1c9624387604fba4005ccf7dbd5a
+timeCreated: 1492868176
+licenseType: Store
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/SerializableDictionary/SerializableDictionary.pdf b/Assets/SerializableDictionary/SerializableDictionary.pdf
new file mode 100644
index 00000000..cad88c37
--- /dev/null
+++ b/Assets/SerializableDictionary/SerializableDictionary.pdf
Binary files differ
diff --git a/Assets/SerializableDictionary/SerializableDictionary.pdf.meta b/Assets/SerializableDictionary/SerializableDictionary.pdf.meta
new file mode 100644
index 00000000..4325a38f
--- /dev/null
+++ b/Assets/SerializableDictionary/SerializableDictionary.pdf.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 8eb73a56b3a6ee64ab2c30cac8878d8f
+timeCreated: 1492869030
+licenseType: Store
+DefaultImporter:
+ userData:
+ assetBundleName:
+ assetBundleVariant: