- Home /
Why does the value of the public property of [Serializable] class persist in ScriptableObject between Editor runs ?
I have a class like this
[Serializable]
public class Team
{
public string Name;
public int NumberOfPlayers { get; set; }
}
and I have a ScriptableObject class like this
public class GameSettings : ScriptableObject
{
public Team[] Teams = new Team[2];
}
when the instance of the GameSettings
is selected in the Editor, in Inspector panel I can edit (as expected) only the Name
field of each Team
instance. Then, when the game is running, I change the values of the NumberOfPlayers
properties via code, and to my surprise, these values are kept between runs of the game in the Editor.
IOW when I stop a game running in the editor and start it again, these values are not 0 (as I expect them to be since they are properties and, as I understand, shouldn't be serialized and saved especially when the game is running).
Having a property in ScriptableObject to keep values during the game probably is not the best approach but the question is still why and where the values are kept?
I checked the GameSettings.asset
and there is no such value/name serialized, neither I could find it in other files within the project folders.
Answer by Hellium · Jan 10, 2020 at 10:59 AM
Then, when the game is running, I change the values of the NumberOfPlayers properties via code, and to my surprise, these values are kept between runs of the game in the Editor.
This is one of the purpose of the Scriptable Objects.
Scriptable Objects are project assets, the same way materials, textures or prefabs are. When you change a value on those object (even at runtime), quitting play mode won't reset the values of the assets, the same happens for ScriptableObjects.
Having a property in ScriptableObject to keep values during the game probably is not the best approach but the question is still why and where the values are kept?
I checked the GameSettings.asset and there is no such value/name serialized, neither I could find it in other files within the project folders.
The changes must be in the .asset
. Make sure you have saved your project (not only the scene) : File
> Save Project
.
But NumberOfPlayers
is a property, not a field, should it be serialized? And if so why it's not visible in the Inspector? I checked again that it is not saved to the .asset
file. And nothing is marked as changed in the GitHub files statuses.
NumberOfPlayers
is an auto-property, and a backing field is created. You can see its value by changing the inspector to debug view.
This field is saved while the editor is running, but looses its value when the editor is restarted.
I must admit I have read your question too fast and was not even aware of the backing field problem.
I couldn't find in the documentation about the properties backing fields, what is the logic behind it? I mean why to keep their values while the editor is not restarted, where it could be useful?
Answer by benthroop · Feb 24, 2021 at 12:32 PM
The comment below by @Bunny83 shows how to make a backing field NonSerialized. I have erased my answer here which took a different, worse, approach.
Well, if that behaviour is desired or not is up to you, it for sure is documented here. Please scroll to the very end and read the last paragraph:
Making Editor code hot reloadable
When reloading scripts, Unity serializes and stores all variables in all loaded scripts. After reloading the scripts, Unity then restores them to their original, pre-serialization values.
When reloading scripts, Unity restores all variables - including private variables - that fulfill the requirements for serialization, even if a variable has no SerializeField attribute. In some cases, you specifically need to prevent private variables from being restored: For example, if you want a reference to be null after reloading from scripts. In this case, use the NonSerialized attribute.
Unity never restores static variables, so do not use static variables for states that you need to keep after reloading a script.
So if you really want to prevent Unity from ever serializing a field, use the NonSerialized attribute. The same is true for auto properties. The MSDN has exactly this usecase as example. So [field: NonSerialized]
can be used on an auto property to tag the backing field with the NonSerialized attribute.