2015-03-30 5 views
5

In Unity 5, was ist eine "saubere" Art, dynamisch erstellte Spielobjekte zu verwalten?Unity 5: Saubere Art, dynamisch erstellte GameObjects zu verwalten

Ich habe eine Komponente (MonoBehavior) geschrieben, die mehrere GameObjects erstellt/zerstört. Die Objekte werden als Teil des benutzerdefinierten Anpassungssystems geladen, das Teile des Charakters - Haare/Kleidung usw. - auswählt. Dies bedeutet, dass sie für den Spieler sichtbar sind, im Editor sichtbar sind, aber im Editor nicht editierbar sein sollen. Die geladenen Objekte sind Maschen mit Skeletten.

Das Skript verhält sich in dieser Art und Weise:

  1. Lasten Gameobjects von Ressourcen (genaues Objekt in Skript bestimmt wird, sind sie nicht prefabs)
  2. Hängt sie bis zu einem gewissen Teil der Szene (nicht unbedingt zu seinem eigener Knoten)
  3. Löscht, wenn zerstört.

Löschung:

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

Creation:

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; 

Zerstörung Handler:

void OnDestroy(){ 
    unloadLastResource(); 
} 

, die im Editor zu funktionieren scheint, aber wenn ich wechseln von Gaming-Modus zurück Im Editiermodus erhalte ich viele Warnungen:

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

Und ich bekomme Haufen neuer Objektbäume (diejenigen, die gelöscht werden sollen - Bäume von Objekt stammen, die zusammen mit original „Ressourcen“ und wurde angebracht geladen wurde) auf der obersten Ebene der Szene hieararchy.

Also, wie handhabe ich dynamisch dynamisch erstellte Spielobjekte?

Ich muss wissen, welche Flags ich setzen muss und welche Schritte ich tun sollte, um sicherzustellen, dass das Objekt nicht in die Szene "leckt" und ordnungsgemäß zerstört wird, wenn ich die Komponente lösche, die es erstellt hat.

Beratung?


Vollbasisklasse, die von „Resource“

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(); 
    } 
} 

Antwort

4

Ich habe es herausgefunden.

Das Problem wird durch diese Linie verursacht:

instance.hideFlags = HideFlags.HideAndDontSave; 

In Hierarchie, dieses Flag nur ein Objekt wirkt und wirkt sich nicht auf Objekt. Wenn Sie also dieses Flag für das Root-Objekt in der Hierarchie setzen und die Szene gespeichert wird, wird root zerstört, aber die untergeordneten Objekte werden serialisiert (was bedeutet, dass sie im Inspector nach dem Laden der Szene angezeigt werden) UND Sie werden Tonnen davon erhalten Warnungen darüber, dasselbe Objekt mehrmals zu zerstören.

Um das Problem zu beheben, müssen Sie die gesamte Hierarchie durchlaufen und das Flag auf alle Objekte in der Hierarchie setzen. Das behebt das Problem und beseitigt alle Fehler.

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

..... 
       makeHierarchyHidden (newObject); 
..... 

Es ist unklar, ob dies geschieht nur, wenn nicht-prefabs von der Festplatte geladen wird, oder nur in allgemeinem Fall mit allen hierarchischen Objekten.

2

Die Fehlermeldung bedeutet, dass der Code eine unendliche Rekursion schaffen würde, weil Sie ein Objekt destorying aus OnDestroy() und die - wieder - Aufruf OnDestroy() auf dieses Objekt und so weiter ...

mir dieses Stück Code Lassen Sie teilen, die ich als Basisklasse verwenden, für alle meine 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; 
     } 

    } 
} 

Der hier Trick ist, dass ich eine List von GameObjects sammeln, die zu diesem MonoBehaviour gebunden sind, und sollten zerstört werden, wenn die ‚Eltern‘ zerstört wird.Wenn Sie instantiate dieser Klasse verwenden, wird das erstellte Objekt automatisch zu List hinzugefügt, es sei denn, addLink ist in dieser Methode auf false eingestellt.

Vielleicht können Sie dies oder etwas ähnliches verwenden.

+0

Danke für das Schnipsel. Ich analysiere es gerade und suche nach Unterschieden im Vergleich zu meinem Code (ich speichere auch Objektinstanzen in Variablen, damit sie dasselbe tun) – SigTerm

+0

Ich habe dein Skript untersucht und herausgefunden, dass es genauso funktioniert Bergwerk. Ich habe das Problem herausgefunden, wenn Sie interessiert sind. Siehe die Antwort. – SigTerm

Verwandte Themen