Unity 5: a clean way to manage dynamically created GameObjects

In Unity 5, what is a β€œclean” way to control dynamically created game objects?

I wrote a component ( MonoBehavior ) that creates / destroys several GameObjects . Objects are downloaded as part of a customization system that selects parts of the character - hair / clothes, etc. This means that they are visible to the player, visible in the editor, but should not be edited in the editor. Downloadable objects are skeletal grids.

The script behaves as follows:

  • Loads GameObjects from resources (the exact object is defined in the script, they are not prefabs)
  • Attaches them to some part of the scene (not necessarily to its own node)
  • It is deleted upon destruction.

Removal:

 protected void unloadLastResource(){ if (lastResourceInstance){ if (Application.isEditor){ GameObject.DestroyImmediate(lastResourceInstance); } else GameObject.Destroy(lastResourceInstance); Resources.UnloadUnusedAssets(); lastResourceInstance = null; } } 

Creature:

  GameObject target = getEffectiveTargetNode(); Object resource = Resources.Load(newResourceName); instance = Instantiate(resource) as GameObject; instance.hideFlags = HideFlags.HideAndDontSave; instance.transform.parent = target.transform; instance.transform.localPosition = Vector3.zero; instance.transform.localRotation = Quaternion.identity; instance.transform.localScale = Vector3.one; 

Destruction Handler:

 void OnDestroy(){ unloadLastResource(); } 

It seems to work fine in the editor, but when I switch from game mode back to edit mode, I get a lot of warnings:

 Destroying object multiple times. Don't use DestroyImmediate on the same object in OnDisable or OnDestroy. UnityEngine.Object:DestroyImmediate(Object) 

And I get a bunch of new object trees (those that need to be deleted - the trees come from the object that was loaded with the original "resources" and was attached) at the top level of the scene hierarchy.

So, how can I handle dynamically created game objects?

I need to know what flags I need to set, and what steps I must take to ensure that the object does not "flow" into the scene and is correctly destroyed when the component that created it is deleted.

Tip


Full base class used by "ResourceLoader"

 public class BaseResourceLoader : MonoBehaviour { public GameObject targetNode = null; protected GameObject lastTargetNode{ get{return lastTargetNodeInternal;} } private GameObject lastTargetNodeInternal = null; protected bool targetNodeChanged(){ return targetNode != lastTargetNode; } protected string lastResourceName{ get{return lastResourceNameInternal;} } private string lastResourceNameInternal = ""; //private Object lastResource; private GameObject lastResourceInstance; protected GameObject getEffectiveTargetNode(){ if (targetNode == null) return this.gameObject; return targetNode; } public void reloadResource(){ loadNewResource(lastResourceNameInternal, true); } protected void unloadLastResource(){ if (lastResourceInstance){ if (Application.isEditor){ GameObject.DestroyImmediate(lastResourceInstance); } else GameObject.Destroy(lastResourceInstance); Resources.UnloadUnusedAssets(); lastResourceInstance = null; } lastResourceNameInternal = ""; } protected void loadNewResource(string newResourceName, bool forceReload){ if ((newResourceName == lastResourceNameInternal) && !forceReload) return; GameObject instance = null; if (newResourceName != ""){ GameObject target = getEffectiveTargetNode(); Object resource = Resources.Load(newResourceName); instance = Instantiate(resource) as GameObject; instance.hideFlags = HideFlags.HideAndDontSave; instance.transform.parent = target.transform; instance.transform.localPosition = Vector3.zero; instance.transform.localRotation = Quaternion.identity; instance.transform.localScale = Vector3.one; } unloadLastResource (); lastResourceInstance = instance; lastResourceNameInternal = newResourceName; lastTargetNodeInternal = targetNode; } void OnDestroy(){ unloadLastResource(); } } 
+5
source share
2 answers

I get it.

The problem is caused by this line:

 instance.hideFlags = HideFlags.HideAndDontSave; 

In a hierarchy, this flag affects only one object and does not affect child objects. As a result, if you set this flag for the root object in the hierarchy and the scene is saved, root will be destroyed, but its children will be serialized (which means that they will appear in the inspector after the scene is reloaded). And you will get tons of warnings about the destruction of the same object many times.

To solve this problem, you must go through the entire hierarchy and set a flag for all objects in the hierarchy. Which fixes the problem and fixes all errors.

 void makeHierarchyHidden(GameObject obj){ obj.gameObject.hideFlags = HideFlags.HideAndDontSave; foreach(Transform child in obj.transform){ makeHierarchyHidden(child.gameObject); } } ..... makeHierarchyHidden (newObject); ..... 

It is unclear whether this happens only when loading non-files from disk or simply in the general case with all hierarchical objects.

+4
source

The error message means that your code will create infinite recursion because you are destroying an object from OnDestroy() , which will - again - call OnDestroy() on that object, etc ...

Let me share this piece of code that I use as the base class for all my Behaviors:

 using UnityEngine; using System.Collections.Generic; namespace de.softfun.drawntogether { public class EnhancedBehavior : MonoBehaviour { private List<GameObject> linkedObjects = new List<GameObject>(); protected void destroy(params GameObject[] objects) { foreach (GameObject o in objects) { try { Destroy(o); } catch { continue; } } } public void LinkObjects(params GameObject[] objects) { foreach (GameObject o in objects) { linkedObjects.Add(o); } } void OnDestroy() { foreach (GameObject o in linkedObjects) { destroy(o); } } protected T instantiate<T>(T prefab, bool addLink = true) where T : Object { T o = (T)Instantiate(prefab); if (addLink && (o is GameObject)) { linkedObjects.add(o); } return o; } } } 

The trick here is that I am collecting List of GameObjects that are tied to this MonoBehaviour and must be destroyed if the "parent" is destroyed. When using instantiate this class, the created object is automatically added to the List , unless addLink is set to false in this method.

Perhaps you can use this or something similar.

+2
source

Source: https://habr.com/ru/post/1216485/


All Articles