2015-03-30 42 views
5

在Unity 5中,什麼是「乾淨」的方式來管理動態創建的遊戲對象?統一5:清理方式來管理動態創建的GameObjects

我寫了一個組件(MonoBehavior),它創建/銷燬幾個GameObjects。這些對象作爲自定義定製系統的一部分加載,用於選擇角色的部分 - 頭髮/衣服等。這些對象對播放器是可見的,在編輯器中可見,但不應在編輯器中編輯。正在加載的對象與骨架網格。

腳本的行爲以這種方式:

    從資源
  1. 負載GameObjects(確切的對象是在腳本中確定,他們不是預製件)
  2. 它們附着在現場的某些部分(不一定是其自己的節點)
  3. 刪除時被刪除。

刪除:

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

創作:

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; 

銷燬處理:

void OnDestroy(){ 
    unloadLastResource(); 
} 

這似乎在編輯器工作正常,但是當我從遊戲模式切換回以edito模式,我收到很多警告:

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

我也得到許多新對象樹(本應在那些被刪除 - 樹木,將其與原來的「資源」一起加載,並附着對象起源)在現場hieararchy的頂層。

那麼,我該如何幹淨地處理動態創建的gameobjects?

我需要知道我需要設置哪些標誌,以及我應該採取哪些步驟來確保對象不會「泄漏」到場景中,並在刪除創建它的組件時正確銷燬。

建議?通過「資源加載」使用


全部基類

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

回答

4

我已經想通了。

問題是由這一行造成的:

instance.hideFlags = HideFlags.HideAndDontSave; 

在層次結構,這個標誌隻影響一個對象,並不會影響對象的孩子。因此,如果您爲層級結構中的根對象設置此標誌並且場景被保存,則根將被銷燬,但其子元素將被序列化(意味着它們將在場景重新加載時出現在檢查器中),並且您將獲得大量的有關摧毀同一物體的警告很多次。

要解決這個問題,有必要遍歷整個層次並將標誌設置爲層次結構中的所有對象。它修復了問題並消除了所有錯誤。

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

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

目前還不清楚如果從磁盤加載非預製件的時候,或者只是與所有層次的對象一般情況下,這只是發生。

2

該錯誤消息意味着你的代碼將創建一個無限遞歸,因爲你是從內OnDestroy()這將destorying對象 - 再 - 電話OnDestroy()此對象等等......

讓我分享這一段代碼,我作爲基類爲我所有的行爲使用:

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

    } 
} 

這裏的詭計是,我收集的GameObjects這是綁定到這個MonoBehaviour,應該銷燬,如果'父'被破壞。當使用此類的instantiate時,創建的對象會自動添加到List,除非addLink在此方法中設置爲false。也許你可以使用此功能或類似的東西。

+0

感謝您的摘錄。我正在分析它並尋找與我的代碼片段相比的差異(我也將對象實例存儲在變量中,所以它們應該做同樣的事情) – SigTerm 2015-03-31 04:58:19

+0

我調查了你的腳本,發現它的工作原理與礦。不過,如果你有興趣,我已經解決了這個問題。看到答案。 – SigTerm 2015-03-31 14:31:20