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(); }
}

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.

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.