Serialization issue for newly added Fields in a Prefabs Component?

I’ve stumbled into this issue, using unity 2023.1.0b20.

If I add a new serializable field (public enum) to a component which is already used in a prefab and then change the value of that field on an instance of that prefab, then I’d expect Unity to create an override for it.

But it doesn’t. And what’s worse…

Unity also resets the value of the field during deserialization. However, if I break the connection to the prefab, then everything works as expected and the new field is serialized normally.

Is this somehow expected behavior or is it a bug?

Kinda somewhat expected as, even when you add new serialised fields to objects, the data isn’t immediately serialised into said assets. The data only gets added to the object’s yaml when it’s been dirtied and then saved, or force-re-serialised (more on that later). The objects will still have a default value for this new field, of course, it’s just not written to disk yet.

Prefabs work by keeping track of a list of modifications. If the data doesn’t exist in the prefab then it can’t considered an override as there’s no underlying data to compare to.

When situations like this come up you either need to dirty the objects in question (just edit some field in them), or use this method from the editor API: Unity - Scripting API: AssetDatabase.ForceReserializeAssets

I made a little editor window once to let me re-serialise directories to ensure that - when I add/remove serialised members - I can update all the objects in question as well.

3 Likes

Hi, this makes a lot of sense. Thank you a lot for your explanation, @spiney199 !

I experimented a bit and if I dirty the prefab instance, then the new field persists on that instance. New instances of the prefab, however, seem to not serialize the new fields, unless I dirty one of their existing (old) fields.

Now I wanted the new fields to be serialized as part of the prefab, so I disconnected the instances and deleted the prefab, recreated it from one of the former instances (after dirtying it, just to be sure) and reconnected the other instances, but still, the behavior (new fields not recognized) does not change for new instances of that prefab, even though I’ve recreated it.

It’s like the previous prefabs data is taken out of a cache or something - and I guess forcing re-serialization would fix that? I still have to try that.

You need to dirty the actual prefab asset, not the instance.

Good stuff Spiney, well put about the “list of modifications.”

Here’s my scribbles about how things load / overwrite:

Serialized / public fields in Unity are initialized as a cascade of possible values, each subsequent value (if present) overwriting the previous value:

  • what the class constructor makes (either default(T) or else field initializers, eg “what’s in your code”)

  • what may be saved with the prefab

  • what may be saved with the prefab override(s)/variant(s)

  • what may be saved in the scene and not applied to the prefab

  • what may be changed in the scene and not yet saved to disk

  • what may be changed in OnEnable(), Awake(), Start(), or even later

Make sure you only initialize things at ONE of the above levels, or if necessary, at levels that you specifically understand in your use case. Otherwise errors will seem very mysterious.

Here’s the official discussion: Serialization in Unity

If you must initialize fields, then do so in the void Reset() method, which ONLY runs in the UnityEditor.

Field initializers versus using Reset() function and Unity serialization:

To avoid complexity in your prefabs / scenes, I recommend NEVER using the FormerlySerializedAsAttribute

2 Likes

OMG

I did not see the forest for the trees there.

I’ve been hellbent on using overrides to upgrade the prefabs yml, since that’s my entire workflow when interacting with prefabs…

Thank you, again @spiney199

@Kurt-Dekker that’s a nice overview, thanks mate!

1 Like