HideFlags.DontSave causes "CheckConsistency: Transform child can't be loaded" (Unity Bug?)

Hi all,

I’ve managed to reduce my problem case to a very simple example. I have a GameObject at the root of my scene with the following component attached:

[ExecuteInEditMode]
public class ParentComponent : MonoBehaviour
{
	void Awake()
	{
		GameObject child = new GameObject("Child");
		child.transform.parent = transform;		
		child.hideFlags = HideFlags.DontSave;
	}
}

So my GameObject creates a child when it is initialized, and because it is created automatically I don’t want to save it with the scene. In my real-world scenario this is used as the basic for creating a hierarchy of runtime-generated meshes.

As expected, the child object is not saved. However, the parent does still save a reference to the child object. This is a problem because it then tries to load the child object when it loads the scene, and of course the child object doesn’t exist. Hence the following error is given:

“CheckConsistency: Transform child can’t be loaded”

It seems to me that Unity should not save a reference to an object when it is not going to save the object itself. Yet it clearly does, because the saved scene looks like this:

.
.
  m_Children:
  - {fileID: 0}
.
.

Whereas if it truly has no children then the saved scene looks like this:

.
.
  m_Children: []
.
.

Has anyone else encountered this, and how did you get around it? One possible solution would be to break the parent-child relationship before serialization occurs. I tried:

void OnDisable()
{
	child.transform.parent = null;
}

But was told:

“Cannot change GameObject hierarchy while activating or deactivating the parent.”

I also tried breaking the parent-child relationship in OnDestroy(), but it seems serialization has already occurred by this point.

Any thoughts are welcome, otherwise I will report this as a bug.

I remember running into a similar problem a long time ago when Unity 3.x was the latest version. I agree with you that the current behaviour is not really acceptable, but that might be because I don’t understand some things.

This is one possible workaround, but nobody would say it’s pretty.

We can make use of [AssetModificationProcessor.OnWillSaveAssets][1] to take some action when saving a scene:

using UnityEngine;
using UnityEditor;

[ExecuteInEditMode]
public class DontSave : UnityEditor.AssetModificationProcessor  
{
	static string[] OnWillSaveAssets (string[] paths) 
	{
		GameObject[] dontSaveThese = GameObject.FindGameObjectsWithTag("DontSave");
		foreach (GameObject go in dontSaveThese)
		{
			go.transform.parent = null;
		}

		return paths;
	}
}

There I’m checking for any objects we’ve explicitly tagged with “DontSave” (which has no connection to HideFlags), and severing their parent relationship. That way the former parent won’t save a reference to a child that doesn’t exist.

Now in your ParentComponent class, we do a couple of things.
First, we tag the Child GameObject with “DontSave”.
Second, we cache the Child Transform so that we can check if it has a parent in the Update function. If it doesn’t, then we reattach it (this is gross, I know. Sorry.).
Finally, we make sure to Destroy the child when the parent is destroyed.

using UnityEngine;
using UnityEditor;

[ExecuteInEditMode]
public class ParentComponent : MonoBehaviour
{
	private Transform childTransform;

	void Awake()
	{
		GameObject child = new GameObject("Child");
		child.transform.parent = transform;
		child.tag = "DontSave";
		child.hideFlags = HideFlags.DontSave;
		childTransform = child.transform;
	}

	void Update()
	{
		if (childTransform != null && childTransform.parent == null)
		{
			Debug.Log("Setting parent again.");
			childTransform.parent = transform;
		}
	}

	void OnDestroy()
	{
		if (childTransform != null)
		{
			DestroyImmediate(childTransform.gameObject);
		}
	}
}

One downside (of many I’m sure) with this hack is that it immediately dirties your scene after you save. Although, that’s perhaps just an annoyance, which might be considered an improvement over an error.
[1]: Unity - Scripting API: AssetModificationProcessor.OnWillSaveAssets(string[])

Actually this is not really a bug as it is very expected if you do something hacky like that. First of all i’ve heard from a lot people talking about “Editor scripts” when they use ExecuteInEditMode. This is not and editor script. ExecuteInEditMode is mainly a gimmic to execute runtime code at edit time. Think about the ParticleSytem.

The hideflags are part of the serialization system and serialization happens only at edit time in the editor. Setting them in a runtime script doesn’t hurt as nothing is serialized at runtime, but ExecuteInEditMode does execute it also at edit time.

As you might know The serialization system serializes all objects derived from UnityEngine.Object as seperate object. This is obvious when you look at the YAML of a scene or prefab. You set the hideflags of the child gameobject but not of the transform component. However setting it as well won’t change a thing.

References between objects are not verified when the object is serialized. Again, each object lives on it’s own. References to objects which aren’t serialized simply become “null”. If i expand your example above by adding another child to your existing child (let’s call it “child2”) and don’t set the hideflags on this one, the child2 object will be at root level once it get deserialized but the first “child” (which was the parent of child2) doesn’t exist anymore.

I would strongly advise to not use ExecuteInEditMode is possible and to not use hideflags in any runtime code. If you need helper object in an editor script, don’t attach it as child to any persistant content and if so the editor script should make sure to destroy the objects when no longer needed. Your scene contains already quite a few helper objects which are created by Unity.

Finally may i ask why you need this temp gameobject at edit time? And if you need it, why does it has to be marked as dontsave?

I just spent way too long today fighting this problem. I took a step back and noticed that the error doesn’t actually occur if the DontSave GameObject has no parent.

I wrote a small wrapper for instantiating GameObjects with HideFlags:

	/// <summary>
	/// 	HideFlagsGameObjectWrapper provides a wrapper around GameObjects with HideFlags, and is
	/// 	particularly useful when working with DontSave GameObjects.
	/// </summary>
	[Serializable]
	public class HideFlagsGameObjectWrapper
	{
		[SerializeField] private string m_Name;
		[SerializeField] private HideFlags m_HideFlags;
		[SerializeField] private Type[] m_Components;

		#region Properties

		private GameObject m_GameObject;
		/// <summary>
		/// 	Gets the game object.
		/// </summary>
		/// <value>The game object.</value>
		public GameObject gameObject {get {return LazyLoadGameObject();}}

		#endregion

		#region Methods

		/// <summary>
		/// 	Initializes a new instance of the
		/// 	<see cref="Assets.HydraParticles.Scripts.Utils.DontSaveChild"/> class.
		/// </summary>
		/// <param name="name">Name.</param>
		/// <param name="hideFlags">Hide flags.</param>
		/// <param name="components">Components.</param>
		public HideFlagsGameObjectWrapper(string name, HideFlags hideFlags, params Type[] components)
		{
			m_Name = name;
			m_HideFlags = hideFlags;
			m_Components = components;
		}

		/// <summary>
		/// 	Destroys the game object.
		/// </summary>
		/// <returns>The game object.</returns>
		public GameObject DestroyGameObject()
		{
			if (Application.isPlaying)
				GameObject.Destroy(m_GameObject);
			else
				GameObject.DestroyImmediate(m_GameObject);

			m_GameObject = null;
			return m_GameObject;
		}

		#endregion

		/// <summary>
		/// 	Returns existing GameObject or creates a new one.
		/// </summary>
		private GameObject LazyLoadGameObject()
		{
			if (m_GameObject == null)
			{
#if UNITY_EDITOR
				m_GameObject =
					UnityEditor.EditorUtility.CreateGameObjectWithHideFlags(m_Name, m_HideFlags, m_Components);
#else
				m_GameObject = new GameObject(m_Name, m_Components);
#endif
			}

			return m_GameObject;
		}
	}

I encountered the “CheckConsistency: Transform child can’t be loaded” when I’d copied in play mode an InputField whose values I had changed and pasted them into the standard editor outside of play mode.

Interestingly, I found that if I ran the scene again and deleted the offending object IN PLAY MODE, though the object still remained in the standard editor after ending play mode, subsequently running the scene did not have the error. Strange, but maybe useful for someone out there.