- Home /
Inconsistancies between EditorWindow and MonoBehaviour Serialization
Its my understanding that that all scripts are supposed to get serialized and rebuilt upon entering and exiting play mode, and that only public fields and [Serializable] fields are included in the serialization. However, this doesn't seem to be the case for editor scripts...
Private and unspecified (protected?) bool appear to be getting serialized since their values don't get reset whereas the [System.NonSerialized] value does...
I always thought [System.Serializable] just flagged the class as something that can be considered for serialization, not something where every field should be serialized, and experience with working with Unity serialization in MonoBehaviour supports this (I've also included a script that shows it)...
Is my understanding of what [System.Serializable] does flawed? Or is this just a bug?
Googling didn't come up with anything particularly concrete, only that "if you want it serialized, use System.Serializable".
I also noticed that the OnDisable/OnEnable rotation only happens when entering play mode and recompiling, and not when exiting...
Anyway- The code:
using UnityEngine;
using UnityEditor;
public class EditorSerializationTest : EditorWindow {
[MenuItem("Tarlius/Open Serialization Window Test")]
static void EditorSerializationWindowTest() {
EditorWindow.GetWindow<EditorSerializationTest>();
}
[SerializeField] DataHolder dataHolder = null;
void OnEnable() {
if(dataHolder == null) dataHolder = new DataHolder();
dataHolder.OnEnable();
}
void OnDisable() { dataHolder.OnDisable(); }
void OnGUI() { dataHolder.OnGUI(); }
}
[System.Serializable]
public class DataHolder {
bool defaultBool = false;
public bool publicBool = false;
[System.NonSerialized] bool nonSerializedBool = false;
[SerializeField] bool serializedBool = false;
public void OnGUI() {
if(GUILayout.Button("Set True" )) Set(true );
if(GUILayout.Button("Set False")) Set(false);
if(GUILayout.Button("Status")) LogButton();
}
public void OnEnable() { Log("Enable:"); }
public void OnDisable() { Log("Disable:"); }
public void LogButton() { Log("Button:"); }
public void Set(bool value) {
defaultBool = value;
publicBool = value;
nonSerializedBool = value;
serializedBool = value;
}
void Log(string context) {
Debug.Log(context);
Debug.Log("default" + defaultBool.ToString());
Debug.Log("public" + publicBool.ToString());
Debug.Log("nonSerialize" + nonSerializedBool.ToString());
Debug.Log("serialized" + serializedBool.ToString());
}
}
Use the menu item to open the window, press the button to set all the fields to true, then enter play mode. Trues go in, all but the NonSerialize gets trues back. And for those interested, here is a MonoBehaviour that shows the inconsistency. Make a gameObject in edit-mode, use the menu to set all to true, and then enter play mode. In OnEnable only the public and SerializeField come back true (which is what I expect).
using UnityEditor;
using UnityEngine;
public class SceneSerializationTest : MonoBehaviour {
[MenuItem("Tarlius/Set SceneTest True")]
static void SetupSceneTest() {
SceneSerializationTest sf = GameObject.FindObjectOfType(typeof(SceneSerializationTest)) as SceneSerializationTest;
sf.dataHolder.Set(true);
}
[SerializeField] DataHolder dataHolder = null;
void OnEnable() {
if(dataHolder == null) dataHolder = new DataHolder();
dataHolder.OnEnable();
}
void OnDisable() { dataHolder.OnDisable(); }
void OnGUI() { dataHolder.OnGUI(); }
}
Answer by numberkruncher · Oct 08, 2013 at 10:16 AM
Private fields are serialized under some circumstances (editor).
http://forum.unity3d.com/threads/155352-Serialization-Best-Practices-Megapost
You shouldn't need to add System.Serializable to your MonoBehaviour, ScriptableObject or EditorWindow classes since these are serialized anyway, though you do need this attribute for custom data classes.
Answer by Jamora · Oct 08, 2013 at 08:16 AM
I have the same understanding of [System.Serializable]: it only flags the class as something that can be considered for serialization. In Unity, by default, all public primitive types are serialized, and other Unity types (the list is here).
Unity says it does not serialize private fields, yet your test shows otherwise. This is because your DataHolder class is NOT a Unity class; it extends from System.Object, thus none of the Unity rules apply on it. And apparently, the .NET framework will serialize all primitives, private or not. Interestingly enough, when I tried to test this by making DataHolder a ScriptableObject, my Unity crashes.
Unity does not serialize when switching state from play to editor mode. That could cause potentially harmful side-effects because of the changed state during gameplay. There are ways around this, but you'll need a custom serializer (the asset store has a few) or you could make your own that saves data as assets to disk and then reloads them.
Also the default access modifier (unspecified) is private
.
For EditorWindow
's the rule is slightly different. Private fields are serializable by default and must be disabled using the NonSerialized
attribute.
Note for UnityScript users, the default access modifier if unspecified is public
.
Unity says it does not serialize private fields, yet your test shows otherwise. This is because your DataHolder class is NOT a Unity class; it extends from System.Object, thus none of the Unity rules apply on it.
At first I had an "ah-huh" moment, but then I realised that cannot be true. Note that I am using the same class (ie: the one inheriting from System.Object) across both the tests and getting different results. Perhaps the editor uses a different serialization that is more true to the .NET implementation or something? I was not aware of .NET enforcing serialization on private stuff too (I've only ever used Unity's implementation).
Unity does not serialize when switching state from play to editor mode. That could cause potentially harmful side-effects because of the changed state during gameplay.
It does, however do the OnDisable/OnEnable loop (if [ExecuteInEdit$$anonymous$$ode] is used), and deserializing (to restore the state of the object stored in the scene before play). And thank god it does! $$anonymous$$y problem is that I'm relying on the data getting reset in an editor script, and it isn't! I can easily work around the problem, but I'm trying to work out why it isn't behaving as expected. I don't want to submit a bug report if its expected and I've missed something trivial though!
Also the default access modifier (unspecified) is private.
Of course it is. You can't access stuff in base classes from child classes without explicitly making it protected. What was I thinking? ><
For EditorWindow's the rule is slightly different. Private fields are serializable by default and must be disabled using the NonSerialized attribute.
Got a source for this? That was kinda the conclusion I came to, but I want to see it in docs or hear some reasoning before I stop seeing it as a bug. Its probably worth noting that if [System.Serializable] isn't used nothing gets serialized though.
Private fields are serialized under some circumstances (editor).
http://forum.unity3d.com/threads/155352-Serialization-Best-Practices-$$anonymous$$egapost
You shouldn't need to add System.Serializable
to your $$anonymous$$onoBehaviour
, ScriptableObject
or EditorWindow
classes since these are serialized anyway, though you do need this attribute for custom data classes.
Don't get the "why" but thats pretty official. I was hoping for something more... "Satisfying". If you want to put that in an answer I'll accept it. I've read that passage before in a blog post I think but didn't notice/remember that caveat. Nice catch!
Either way, I guess its not a bug...
Your answer
Follow this Question
Related Questions
Multiple Cars not working 1 Answer
Distribute terrain in zones 3 Answers
SerializedProperty and PropertyField NullReference 1 Answer