- Home /
In this case the lists do not serialize. Why?
In the following code an attempt is made to use two lists as a natively serializable storage for a dictionary.
A very productive discussion and a number of alternative solutions have been proposed and discussed in this question. However, I'd like this question to focus on why this class doesn't work rather than finding ways to go around the problem.
Specifically, let's assume we start with an empty dictionary/lists and in edit mode we fill the dictionary with some values, i.e. through a custom inspector or a simple menu-based function.
First of all, I'm puzzled by the log of calls. The OnDisable/OnEnable pair get called three times, in this order: OnDisable(), OnEnable(), OnDisable(), OnEnable(), OnDisable(), OnEnable(). Why so many times?
More crucially, the first OnDisable() call seems to correctly store the content of the dictionary in the lists, as witnessed by logging the values of keys.Count and values.Count. But the first OnEnable() finds empty lists, as if they hadn't been serialized! What is going on? Lists, even generic ones, should be automatically serialized!
using UnityEngine;
using UnityEditor;
using System.Collections.Generic;
// the next line is crucial: switching from edit mode to play mode without it
// would only invoke Awake(), and there wouldn't be a chance (i.e. in OnDisable())
// to save the dictionary data into the lists.
[ExecuteInEditMode]
public class MyCustomDict : MonoBehaviour
{
// I know, it'd be better to use a single list:
// bear with me for the sake of simplicity
public List<string> keys = new List<string>();
public List<int> values = new List<int>();
// Unity doesn't serialize dictionaries even if you pray in Klingon,
// that's why we need to use the lists.
private Dictionary<string, int> dictionary = new Dictionary<string, int>();
public void OnEnable()
{
Debug.Log("OnEnable!");
// the following statement says the lists are empty
// when switching from edit mode to play mode, but a
// previous invocation of OnDisable() seems to have
// saved the content of the dictionary in them!
Debug.Log("Count:"+keys.Count+"::"+values.Count);
// here we -should- deserialize the lists and fill the dictionary
string aKey;
int aValue;
for(int index = 0; index < keys.Count; index++)
{
aKey = keys[index];
aValue = values[index];
dictionary[aKey] = aValue;
}
// here we clear the lists in the hope to free
// some memory and to clean things up for the
// time we'll need the lists again
keys.Clear();
values.Clear();
}
public void OnDisable()
{
Debug.Log("OnDisable!");
// here we serialize the content of the dictionary into
// the lists, so that it gets "indirectly" serialized.
int index = 0;
foreach(var item in dictionary)
{
keys.Add(item.Key);
values.Add(item.Value);
}
dictionary.Clear();
// The following statement suggests that the dictionary
// data has been moved into the lists, but the first
// invocation to OnEnable() says otherwise.
Debug.Log("Count:"+keys.Count+"::"+values.Count);
}
// method to add an item to the dictionary (but not to the lists)
// can be driven by functionality in a custom inspector or in a
// menu-based function
public void AddItem(string aKey, int anInt)
{
dictionary[aKey] = anInt;
// this trigger the refresh of a custom inspector
EditorUtility.SetDirty(this);
}
// method useful for a custom inspector,
// to show the content of the dictionary
public Dictionary<string, int>.Enumerator GetEnumerator()
{
return dictionary.GetEnumerator();
}
}
Hmm it's questions like these that make me want to study program$$anonymous$$g ins$$anonymous$$d of goofing around in Unity with no other knowledge other than trial and error while designing my games.
But I must ask what exactly are you trying to accomplish here? Are you trying to build a game or a database to store customer information for 1000's of clients?
Also why would you ever need to have functionality designed for transitioning from edit mode to play mode or vice versa? Once again it seems like you are trying to do something Unity is not designed for. Unity is designed for what the game does in Play $$anonymous$$ode it's for games :)
I think the problem here is that Unity was designed only to save with PlayerPrefs which do not support Lists. This happens to be my biggest problem while trying to design a randomized item system for my ARPG project.
So while I appreciate you asking these questions about why this doesn't work it seems the real question should be why are lists not supported by PlayerPrefs?!
Unity was just not designed with traditional program$$anonymous$$g in $$anonymous$$d, it plays by its own rules so I don't think you are going to get anywhere by trying to do it that way. Roll up your sleeves and prepare to utilize hundreds of individual PlayerPrefs because Unity can't use lists properly.
Valid points Ryan, I need to prepare lunch for my family right now but I'll get back to you.
Lunch is gone, baby's napping, here I am! =)
I suspect that telling you what I'm trying to achieve exactly will only muddle things more, but as you dare to ask... =D
I have a bunch of game objects. I need to connect them to each other in linked lists. If the objects were arranged in a simple grid it'd be about connecting them as rows and columns, each game object storing the previous and next game object along the row AND along the column they are part of. But in my case the arrangement is far more free form, with each game object potentially being part of an number N>0 of lists.
Before I can even use those lists of game objects I must set them up. Through the editor I first place the game objects in 3D Then I must connect them to each other. The vision is to do this by selecting an appropriate set of objects and then run a command or use a menu-item to link them up. This is why I need the editor-based functionality and obviously the data must survive into play mode.
However, eventually I'd like to be able to do the same in play mode. That is, I'd like the user to be able to place its own game objects the way he sees fit and then connect them together (user generated content). And this is why, from a developer/testing point of view I must be able to go back from play mode into the editor, the data again surviving the trip.
Concerning the issue of what PlayerPrefs support or not, I'd suggest that is a question for Unity's developer. Serialization of lists in the context I'm describing, by my understanding, IS supported. So, I think it is appropriate to ask why in this case doesn't work. Assu$$anonymous$$g it's not a bug, what am I missing?
Yeah, that does seem like a very annoying problem. Unfortunately I don't know enough about it to actually try to help you solve it.
I need to look more into this sort of thing sounds like it could help a lot with a big data system that I would like to have for all the armor in my game. Right now I only have 2 sets of armor and the code for all that is probably around 10,000 lines already :(
$$anonymous$$akes it very slow to work with and I need to come up with a better solution soon or at least greatly compact the code.
Right now I'm just using a Simple list which stores several variables for each armor you pick up. And then I use playerPrefs to store variables from the list for the inventory position, if it's equipped, and I would like to use playerPrefs to store the stats on the items so I could randomize them. But currently I'm forced to use a 2 PlayerPrefs for the item type (gloves,boots,etc.) and the specific kind (Power Gloves, Noobie Gloves, etc.).
It's already a pain to deal with and I'm not even randomizing the stats on stuff yet because I don't want my code to become even more ridiculously over sized :(
I'm not sure if serialized lists could help me with my problem, but I agree that easily storing data from lists is one of the biggest problems in Unity currently.
RyanZimmerman87, Have you checked the Unify wiki? ArrayPrefs2 will let you save lots of different types, including arrays. It uses Playerprefs internally, so I think you could find it useful.
$$anonymous$$anu3d, By default, Unity doesn't do its internal serialization when moving from game to edit mode. There are ways to get serialization from game to edit more, however; you have to make it yourself. For instance, if your game has save/load mechanics, you can use that to save your game in game mode, then load it in editor mode.
Answer by Jamora · Jul 29, 2013 at 07:47 PM
I experimented some, and by serendipity (I never though I'd get to use that word in a sentence) I found out that by commenting out the clearing of the lists in OnEnable, it works fine. I found out because I set them to different sizes, causing an exception whereby they serialized. The only lines not run because of that error were the clearing ones...
Empirical knowledge: If they serialized when moving from game to edit, all the changes you made during game would remain in edit mode. I think it's possible to have custom serialization from game to edit using the onplaymodechange callback, but that's still uncofirmed.
I don't really know of any tutorials.. serialization seems to be quite skimpily documented. I did read these pages, among others
http://blogs.unity3d.com/2012/10/25/unity-serialization/
http://m50blog.wordpress.com/2010/12/04/joys-of-serialization-in-unity/
However, I think the best way is to get creative with the Unity's API. I would imagine for custom serialization you'll at least need
http://docs.unity3d.com/Documentation/ScriptReference/AssetDatabase.html
Very interesting Jamora! Have you tried to move those clearing lines to the beginning of OnDisable()? Ultimately they are necessary, or the content of the dictionary gets added to the content of the lists ins$$anonymous$$d of overwriting. I can't do the test right now (nearly bedtime) but I shall do it tomorrow. Perhaps I'll simplify the whole thing to one serializable list and one public but explicitely non-serializable one, removing completely the dictionary from the equation. Perhaps the problem persists.
Also thanks for the links provided. I was familiar with the first two, must investigate more on the AssetDatabase. Good night!
Answer by _methotec_ · Jul 27, 2013 at 09:24 AM
Hi there - maybe looking at this how-to-serialize-dictionary-with-unity-serialization may help
Dear methotec, the focus of this question is not on how to do things differently (that's the focus of the question I provided the link for) but why the code provided doesn't work.
Oh - well... The OnEnable(), OnDisable() issue is because when Unity goes from the editor mode in playmode all objects are thrown away and all is freshly new instatiated.... so they have to come up at least twice - going from edit mode to play - disable them enable them all and back - disable and enable them all... this is especially making trouble if you're working with singletons... If it's co$$anonymous$$g up 4 times - look into the Debug.Log who's calling.. I guess 2 come from engine 2 from editor... that's because using UnityEditor will cause additional problems when mixed within UnityEngine. $$anonymous$$eep in $$anonymous$$d that the UnityEditor is NOT part of the release. You could exclude all Editor related calls with
#if UNITY_EDITOR
using UnityEditor;
#endif
When doing so - be sure you exclude them all! Because EditorUtility.SetDirty(this); is not going to work in Release...
And - I personally try to avoid OnEnable() whenever possible... I'd recommend to use Awake() ins$$anonymous$$d... BUT - this also has to come twice...
To show your Class in the Editor - you should use an editor script.. Try extending the editor - don't mix it with runtime logic
Thank you methotec for your contribution.
What you mention about the OnEnable/OnDisable issue is partially correct. Switching from edit to play mode involves, I seem to understand, a recompilation step (which accounts for one OnDisable-OnEnable pair) and then starting the play mode, which accounts for a OnDisable-OnEnable-OnDestroy-Awake-OnEnable sequence of calls. One OnDisable/OnEnable pair, between edit mode and play mode, therefore remain unaccounted for. To be clear, what I see switching from edit mode to play mode is:
OnDisable (edit mode before recompilation?)
OnEnable (edit mode after recompilation?)
OnDisable (unaccounted for)
OnEnable (unaccounted for)
OnDisable (edit mode tyding up for the switch to play mode?)
OnDestroy (edit mode tyding up for the switch to play mode?)
Awake (play mode)
OnEnable (play mode)
Furthermore, it still not clear why the lists are empty on the first OnEnable after the first OnDisable, when they are filled with the dictionary data.
Concerning showing the class in an editor, indeed I do use that, I haven't included the code because it is simple enough. However, I needed a way to tell the editor when the dictionary has been updated, hence the EditorUtility.SetDirty() call which, somewhat surprisingly, works!
Thank you though for mentioning the issue of Editor-related calls in Release. It is a useful point and I must remember that.
probably the c# first pass compilation? Don't know that just guessing! What does the Debug.log() say who's calling? You could add an instance id to the log so you can figure out for which object the log is written. You know that the enable is also called if you enable / disable the script in the inspector? Is this really what you want? I guess your Lists are emptied before the new instances are there and so you are copying empty lists... Also you could check with:
if (keys.Count == 0)
Debug.Log(keys + " - it's empty!");
Thank you again for your effort methotec.
The list of method calls I've compiled above comes from the Debug.Log() statements in the code above.
At this stage there is no need to check the instance id because all the tests I'm doing are happening on a single cube in an empty scene.
I am aware Enabling/Disabling causes those method's invocation and I don't think it's related: I need the data to survive going into play mode and back.
Also, if you notice my code does have checks like the one you suggest, printing out the Count attribute of the lists.
Your answer
Follow this Question
Related Questions
Multiple Cars not working 1 Answer
Distribute terrain in zones 3 Answers
does this key not found error have to do with serialization? 2 Answers
Better Dictionary? (serializable with more functionality) 1 Answer
[Help] Dictionary bug 2 Answers