A weird issue in Unity related to scripts and Editor.

I have a class called PlayerData, marked as serializable, and it doesn’t inherit any classes including MonoBehaviour. and it also doesn’t contain any “InitializeOnLoad” things related to the Editor.

This constructor method contains a “Monobehaviour.Print(“abc”)” for example.

Now the issue happens, when I start the Unity, open this project, I didn’t run anything, the constructor will be automatically run, because the “abc” will be automatically printed even the scene is not in running. When I see the stacktrace in Unity, it starts with this constructor, nothing else is calling it.

And, when I run the scene, this print() will be called multiple times, and some of them doesn’t contain stacktraces in Unity, just a single line, which never happened before.

I don’t have any other scripts which contains Editor or Editor related script.

What happened? I know it’s related to the project, because it doesn’t happen on other projects wich have the similar structure. I just want to know where to look into it.

I’m using Unity 5.0.2f1, this issue happened on both Windows and MacOS.

This happens because you have some serialized object which has a field of the PlayerData type.

Unity serialized and deserializes data A LOT. When you enter play mode, Unity serializes (saves to disk) the enitre scene you’re in, starts the runtime, and then recreates everything from the serialized data - ie. deserializes it.

As a part of that process, the object has to get constructed. When it’s constructed, the constructor is called and you see your print message.

Things that can cause Unity to serialize or deserialize data, and in the process call constructors, is for example:

  • going into play mode
  • exiting play mode
  • opening a scene in the editor
  • saving the scene/project
  • selecting an object in the editor, and viewing it in the inspector
  • a lot more!

Check out this blog post to read more informaiton about serialization, and the issues that might crop up.

To fix this, you might want to ditch the constructor, and move that code to some “Initialize()” function. Then you call that on Awake from whatever MonoBehaviour that contains this object. In general, [Serializeable] objects should follow the same rules that goes from MonoBehaviours - don’t use constructors. This if the reason for that.

As an alternative, you could wrap all the stuff in the constructor you don’t want to be happening on play in

if(Application.isPlaying) {
    Monobehaviour.print("abc");
    ... //other runtime-only code
}

That should take care of the messages, but the constructor will still be called.

Also, note that MonoBehaviour.print is the same thing as Debug.Log. MonoBehaviour.print exists so you can write

print("hello world");

instead of:

Debug.Log("hello world");

inside of a MonoBehaviour. If you’re outside a MB, I think Debug.Log looks nicer.