I’m working on a prefab which spawns a ‘throwaway’ child, both in play mode and in edit mode through [ExecuteInEditMode].
[ExecuteInEditMode]
public class CubeScript : MonoBehaviour
{
public GameObject childObject;
void Start()
{
if (childObject == null)
{
childObject = new GameObject();
childObject.transform.parent = transform;
}
}
}
This game object is a prefab, placed in a scene. The idea is that it always spawns a child if it has none. This child is dynamically generated instead of saved in the prefab. See it as derived state from the prefab, based on any dynamic context. I want to maintain this derived state between play and edit mode to avoid computationally expensive recreation. The parent object stores a reference to this child in the childObject variable. This last bit is important.
When starting play mode, if there was a child in the scene hierarchy, it is kept during play mode. This is how Unity works. However, the reference (in childObject) is lost!
The above code therefore cannot find the child, and will make an additional one. We now have 2 duplicates, which is unintended. I’m tying to find out why the reference was lost.
Now, something similar happens when toggling back to edit mode (exiting play mode). Edit mode is entered without the childObject reference, so a new one (3rd child) is created. Fortunately, any play mode state is reset, so the 2nd child was already deleted. We still have 2 duplicate children remaining (child #1 and #3).
I already tracked the issue to the underlying issue that this childObject reference is not serialized into the scene data. While the object’s existence itself is serialized, the reference from parent to child is not. Now the confusing thing is that whether this childObject reference is lost or not, depends on how/when the childObject reference is set. If in edit mode I manually set the (generated) reference to ‘None’ in the inspector and then assign it again. The reference will be serialized into the scene data. This can be verified by saving the scene and checking the file on disk. If however the childObject reference stems from an object that was generated at Unity startup or entering of edit mode by exiting play mode, the reference is not serialized in the scene. Not even when it is manually saved.
If the parent is not a prefab, everything works fine. But somewhere on the intersection between the object lifecycle, prefabs and ExecuteInEditMode, a discrepancy emerges between creating GameObjects and keeping a reference to it. My suspicion is that auto-generating the child-object in Start() (or any other Unity message) does not properly mark the object as dirty for serialization, preventing saving of the reference. However I’m weary of getting into editor-specific scripting (SerializedObjects or EditorSceneManager.MarkSceneDirty), because this code is designed to be editor-agnostic: it simply states that this prefab should always have a dynamically generated child if it has none. No matter edit mode or play mode.
My question is: how do I make sure that childObject reference is properly integrated in the scene file at all times so that I do not end up with duplicates?
Please note that I’m not looking to solve a just the latter part, but instead I’m trying to understand what is going on and whether this is intended Unity behavior. Any further insights are very welcome!
I’m using Unity 2020.1.10f1.
A project file with reproduces the above behavior is here:
Thanks a lot,