To answer your question where you need to perform the serialization, the answer is in the callback of EditorApplication.playmodeStateChanged.
As to why I inherit from ScriptableObject, I could get a System.Object to serialize properly. I also don’t think a dictionary should be a component in a GameObject, because it’s a data structure. Instead, a Component should use it; hence a ScriptableObject.
EDIT:At some point in my coding the goal shifted from making a bare bones serializing dictionary to a serializing dictionary I can use in my projects, which is why all the unrelevant stuff in the previous code.
I will probably send this to the Asset Store when I’m finished; I’ve seen at least a few questions asking for a serializable dictionary.
EDIT: No, I went over the code too heavy handedly; it’s not every day I get to remove functionality from my code. This code does work for me; tested.
It’s also easy to break this code by changing values inside the dictionary; foreach doesn’t like its lists changing on it.
The Dictionary:
[System.Serializable] //<- important
public abstract class SerializableDictionary<K,V> : ScriptableObject{
/* Lists to serialize the dictionary to.
* Notice that as they are generic fields, they will not serialize as-is.
* Instead, this class must be inherited by a non-generic class so Unity
* knows what types they are and serializes correctly. That concrete class
* is then used in applications
*
* I.E. public class StringIntDictionary : SerializableDictionary<string,int> {}
*/
[SerializeField] //<- important
public List<V> v;
[SerializeField] //<- important
public List<K> k;
public Dictionary<K,V> dictionary;
public SerializableDictionary(){
dictionary = new Dictionary<K, V>();
//Lists are null if it's the first time, otherwise they've been deserialized.
if (v == null){
v = new List<V>();
k = new List<K>();
}
}
//Does the deserializing.
void OnEnable(){
dictionary = new Dictionary<K, V>();
for(int i=0;i<k.Count;i++){
dictionary.Add( k_,v*);*_
* }*
* k.Clear();*
* v.Clear();*
* }*
* //Serializes dictionary*
* public void SerializeDictionary(){*
* v.Clear();*
* k.Clear();*
* foreach(KeyValuePair<K,V> kvp in dictionary)*
* {*
* k.Add(kvp.Key);*
* v.Add(kvp.Value);*
* }*
* }*
}
The Editor Script
=================
/We need this editor script to call the Serialize function of the serializing dictionary/
[CustomEditor(typeof(DictionaryTest))]
public class DictionaryEditor : Editor {
* [SerializeField]*
* DictionaryTest editorTarget;*
* private bool unfolded = true;*
* private string newKey = “”;*
_ //
/ The important stuff /
//_
* void Awake(){*
* editorTarget = (DictionaryTest)target;*
* //Register for the scene change callback.*
* EditorApplication.playmodeStateChanged += PlayChanged;*
* }*
* void OnEnable(){*
* if(editorTarget == null) {*
* editorTarget = (DictionaryTest)target;*
* }*
* if(editorTarget.dictionary == null){*
* editorTarget.dictionary = ScriptableObject.CreateInstance();*
* }*
* }*
* //This is called whenever the playmode changes*
* void PlayChanged(){*
* editorTarget.dictionary.SerializeDictionary();*
* }*
* void OnDestroy(){*
* //Unregister the scene change callback*
* EditorApplication.playmodeStateChanged -= PlayChanged;*
* }*
_ //
/ End of important stuff /
//_
* public override void OnInspectorGUI ()*
* { *
* DrawDefaultInspector();*
* EditorGUILayout.LabelField(“”);*
* unfolded = EditorGUILayout.Foldout(unfolded,“Dictionary”);*
* if(unfolded){*
* foreach(KeyValuePair<string,int> kvp in editorTarget.dictionary.dictionary){*
* GUILayout.BeginHorizontal();*
* ShowDictionaryEntry(kvp.Key, kvp.Value);*
* if(GUILayout.Button(“Remove Entry”))*
* editorTarget.dictionary.dictionary.Remove(kvp.Key);*
* GUILayout.EndHorizontal();*
* }*
* ShowAddEntryButton();*
* }*
* }*
* ///
*
* /// Shows the add button.*
* /// *
* void ShowAddEntryButton(){*
* GUILayout.BeginHorizontal();*
* EditorGUILayout.PrefixLabel("Key: ");*
* newKey = GUILayout.TextField(newKey);*
* if(GUILayout.Button(“Add Entry”)){*
* editorTarget.dictionary.dictionary.Add(newKey,0);*
* newKey = string.Empty;*
* }*
* GUILayout.EndHorizontal(); *
* }*
* ///
*
* /// Shows the dictionary values.*
* /// *
* void ShowDictionaryEntry(string k, int v){*
* int oldValue = v;*
* EditorGUILayout.PrefixLabel(k);*
* v = EditorGUILayout.IntField(v);*
* if(oldValue != v)*
* editorTarget.dictionary.dictionary[k] = v;*
* GUILayout.Label(" ");*
* }*
}
The test class
==============
[System.Serializable]
public class DictionaryTest : MonoBehaviour {
* [SerializeField]*
* [HideInInspector]*
* public StringIntDictionary dictionary;*
* void OnEnable(){*
* if( dictionary == null){*
* dictionary = ScriptableObject.CreateInstance();*
* } *
* }*
* void Start(){*
* dictionary.dictionary.Add(“test”,5);*
* }*
* void OnGUI(){*
* foreach(KeyValuePair<string,int> kvp in dictionary.dictionary){*
* GUILayout.Label(kvp.Key+" "+kvp.Value.ToString());*
* }*
* }*
}