Unity 5:管理动态创建的GameObjects的简洁方法

Sig*_*erm 5 c# unity-game-engine unity3d-editor

在Unity 5中,管理动态创建的游戏对象的"干净"方式是什么?

我写了一个MonoBehavior创建/销毁几个的组件()GameObjects.对象作为自定义自定义系统的一部分加载,选择部分字符 - 头发/衣服等.这意味着它们对于播放器是可见的,在编辑器中可见,但不应该在编辑器中可编辑.正在加载的对象是带有骨架的网格.

脚本以这种方式运行:

  1. 从Resources中加载GameObjects(确切的对象在脚本中确定,它们不是预制件)
  2. 将它们附加到场景的某些部分(不一定是它自己的节点)
  3. 销毁时删除.

删除:

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

创建:

    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;
Run Code Online (Sandbox Code Playgroud)

销毁处理程序:

void OnDestroy(){
    unloadLastResource();
}
Run Code Online (Sandbox Code Playgroud)

这似乎在编辑器中工作正常,但是当我从游戏模式切换回edito模式时,我收到很多警告:

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

我得到了一堆新的对象树(那些应该被删除的树 - 树来自于与原始"资源"一起被加载的对象并被附加)在场景hieararchy的顶层.

那么,我如何干净地处理动态创建的游戏对象?

我需要知道我需要设置哪些标志,以及我应该采取哪些步骤来确保对象不会"泄漏"到场景中并在删除创建它的组件时被正确销毁.

建议吗?


"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();
    }
}
Run Code Online (Sandbox Code Playgroud)

Sig*_*erm 5

我已经弄清楚了。

问题是由这一行引起的:

instance.hideFlags = HideFlags.HideAndDontSave;
Run Code Online (Sandbox Code Playgroud)

在层次结构中,此标志仅影响一个对象,不影响对象的子对象。因此,如果您为层次结构中的根对象设置此标志并且场景被保存,根将被销毁,但其子对象将被序列化(意味着它们将在场景重新加载时出现在检查器中)并且您将获得大量关于多次销毁同一对象的警告。

为了解决这个问题,有必要遍历整个层次结构并为层次结构中的所有对象设置标志。这解决了问题并消除了所有错误。

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

.....
                makeHierarchyHidden (newObject);
.....
Run Code Online (Sandbox Code Playgroud)

目前尚不清楚这种情况是否仅在从磁盘加载非预制件时发生,或者仅在所有分层对象的一般情况下发生。