[Unity] Instantiate 传入父对象实参与 Instantiate 不传入父对象实参,之后再用 SetParent 设置父对象之间存在差异

386 阅读1分钟

问题

例如我在对象池中需要从 Perfab 创建物体,然后我需要这个物体的父对象是某个特定物体

具体到我的场景中,我需要这个物体的父对象拥有 Canvas,因为这个物体的脚本中的 Start 需要获取 Canvas

因此我希望把这个对象池放到 Canvas 下面,然后希望这个物体刚生成的时候就以对象池作为父级

如果在 Instantiate 函数中不指定父对象,那么新创建的物体默认会放在当前激活场景的根目录

我使用的第一种更改父对象的方法会报错,使用的第二种不会报错,因此记录一下

脚本

对象池的脚本

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace Universal
{
    public class ObjectsPool : MonoBehaviour
    {

        [SerializeField] private GameObject prefab;

        private Queue<GameObject> pooledInstanceQueue = new Queue<GameObject>();

        public GameObject GetInstance()
        {
            if (pooledInstanceQueue.Count > 0)
            {
                GameObject instanceToReuse = pooledInstanceQueue.Dequeue();
                instanceToReuse.SetActive(true);
                return instanceToReuse;
            }
            else
            {
                // 默认父对象为 Pool
                GameObject instance = Instantiate(prefab, transform);
                // 默认父对象为 Pool
                // instance.transform.SetParent(gameObject.transform);
                return instance;
            }
        }

        public void ReturnInstance(GameObject gameObjectToPool)
        {
            pooledInstanceQueue.Enqueue(gameObjectToPool);
            gameObjectToPool.SetActive(false);
        }
    }
}

其中存在差异的是

// 默认父对象为 Pool
GameObject instance = Instantiate(prefab, transform);
// 默认父对象为 Pool
// instance.transform.SetParent(gameObject.transform);
return instance;

第一种方法是 Instantiate(prefab, transform); 这个是生成的时候就设置了父对象是对象池

第二种方法是先以当前激活场景的根节点为父对象,然后再移动到对象池为父对象

实践中发现,第二种方法中,被创建的物体的 Start 会先于 instance.transform.SetParent(gameObject.transform); 调用,那么新创建的物体在进行 Start 方法时父对象还是当前激活场景的根节点,那么 Start 方法中具体的逻辑在获取一些物体,比如获取 Canvas 时就会报错