- Home /
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.
Just a note that Unity 4.5 adds ISerializationCallbackReceiver (http://docs.unity3d.com/ScriptReference/ISerializationCallbackReceiver.html) which might be useful in this scenario. I haven't tested it yet though.
@$$anonymous$$Williams Nope, ISerializationCallbackReceiver won't work. The callback is called when the object is being serialized, so 1.) it's too late, 2.) it's often in separate thread, so you can't change hierarchy.
Answer by Guts · Jan 05, 2014 at 01:05 PM
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]: http://docs.unity3d.com/Documentation/ScriptReference/AssetModificationProcessor.OnWillSaveAssets.html
Thanks for your answer, it's nice to know I'm not the only person struggling with this.
Your use of Asset$$anonymous$$odificationProcessor.OnWillSaveAssets() is a nice partial solution to the problem, but actually it also occurs when switching between edit mode and play mode (because Unity saves and then reloads the scene). OnWillSaveAssets() does not seem to be called in this case and I haven't found an alternative.
The other option I have is to simply accept the saving of GameObjects I don't want, and to then just discard them one the scene is loaded. Ugly and wasteful but I guess it should work
Putting these two together, I can make my wasteful approach the default (used when switching between edit mode and play mode) but also set up the OnWillSaveAssets() callback to avoid saving the unwanted game objects when properly saving the scene. At least, that's my current plan.
Also, I'm wondering about deleting the objects rather than setting their parent to null... They can be regenerated on demand so this might avoid the need to track their transform separately and to dirty the scene.
Anyway, I have some ideas to try now so I'll see how it goes.
P.S. Actually I think OnWillSaveAssets() should return void (according to JetBrains' dotPeek tool). I don't know whether this actually affects anything.
P.P.S I'll give a day or two to see if anyone else has ideas, otherwise I'll accept your answer.
Answer by Bunny83 · Jul 14, 2014 at 11:00 AM
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?
Naturally there are going to be cases you can't predict where being able to hide and/or discard objects in editor is important.
In my situation I am developing a toolset for artists. I don't want my artists to need to worry about how the tools work, so I want to keep hierarchy complexity to a $$anonymous$$imum.
$$anonymous$$y specific tool generates meshes procedurally and renders them via a $$anonymous$$eshRenderer. Ins$$anonymous$$d of requiring a $$anonymous$$eshRenderer and $$anonymous$$eshFilter on every GameObject I'm choosing to make a hidden GameObject to reduce clutter.
Furthermore, we do NOT want our meshes to inherit the GameObject Transform, so it's a happy accident that this "bug" has forced me to place my hidden GameObjects at the root.
Thanks for the feedback. I do accept that this could be done 'by design', and that the problem I am encountering is a result of trying to trying to bend the system in an inappropriate way. However, I don't see a better solution at the moment and I think some context will help to illustrate the problem.
$$anonymous$$y system is a terrain editor, and for simplicity lets say I'm trying to exactly mimic the behaviour of the built in Unity terrain editor. $$anonymous$$y base representation is therefore a heightmap, and it should be possible to edit this both in the editor and in-game. A mesh representation will be constructed and should be syncronized to the heightmap as it changes, but we don't want the overhead of storing this mesh as it can be generated from the heightmap on demand (therefore we set the 'DontSave' flag). Actually we have a whole hierarchy of these meshes (each with a game object) to form a quad-tree, but this is probably just a detail. $$anonymous$$ultiple terrains can exist in a scene and have different transformations applied, which is why they don't just sit at the root.
I hope this explains why we want to run game code in the editor (for syncronization of mesh/heightmap, handling of LOD, etc), and also why we don't want to save the mesh data. That said, you have made me question whether I can factor out the relevant code and call it differently/separately from edit an play mode. I'll have to think about that.
So how does Unity implement it's own terrain editor? Well, I guess it's likely that they have access to their own internal API which gives a bit more flexibility. Or perhaps I shouldn't be building out of GameObjects/$$anonymous$$eshes but should be using the raw Graphics API. I'm not really sure, but my current solution works well apart from this bug described here.
@$$anonymous$$Williams: You just talked about your $$anonymous$$esh you created on the fly. Why don't you just set the hideflags of the mesh? If the gameobject is needed anyways, why shouldn't it be part of your scene? Using scripts with ExecuteInEdit$$anonymous$$ode as terrain editor is not a good idea. It kills the performance in the editor if you have much of such scripts. Inside the editor it's better to create editor scripts (either a custom inspector or an editorwindow) to get the edit functionality. Unity's terrain editor is an editor script, actually just the Terrain custom inspector which does all the magic. It also creates temp stuff, but it destroys it when it's not needed anymore. If you need the edit functionality at runtime as well, you can create a class which does most of your magic which is shared by both, the ingame editor and the edit-time-editor.
@VesuvianPrime: That's fine. You can create as much hidden objects as you want. If you look at an empty scene there's still a hidden sceneview camera for each sceneview that is open and a directional light for the sceneview rendering. Unity creates a lot hidden, not-saved objects and they are all at root level. Helper objects don't belong to the actual gamedata, so keep them seperated. If you need a helper to move along with a gameobject, just let the editor script move it manually. The editor is purely event driven, there are many ways to keep things in sync.
@Bunny83: Ok, thanks a lot for this information. I think I tried setting the 'DontSave' flag on just the meshes and that did work, but I was still concerned about serializing the quad-tree of game objects even if there were no meshes attached. Technically the structure of this tree changes at runtime so that it is only subdivided as much as required for the current camera position (which may be different going from edit to play mode). Serializing it seemed wasteful, but practically speaking it may not be a problem.
Your thoughts on true 'editor scripts' vs [ExecuteInEdit$$anonymous$$ode] are also appreciated as I'd never really considered the difference. I may well refactor the code in the way you describe, but actually I do have a largely working solution now (this question is several months old) so it might not happen immediately.
Answer by VesuvianPrime · Jul 10, 2014 at 08:26 PM
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;
}
}
...the error doesn't actually occur if the DontSave GameObject has no parent.
Indeed, I noticed this as well. At one point I therefore used a solution where I maintained a separate 'hidden ghost object' at the root of the scene, attached components to this ins$$anonymous$$d of my real game object, and synchronized the transform, etc as required. This kind of worked but had it's own set of problems. Perhaps your solution is better :-)
Answer by Noah Dyer · Mar 20, 2015 at 08:49 PM
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.