父物体禁用导致子物体组件未注册的Bug

问题描述

在业务中遇见了这样一个Bug,对象池创建小球,并且唤起小球的随机设置颜色功能。实际运行时,前两次或者第二次小球总是默认的颜色。

进一步细化Bug:

对象池没有命中的时候,小球的颜色总是随机的,比如:超出池中现有对象数量。但是只要没有超出对象池即对象池命中的时候,小球的颜色每次都没有变。

问题原因

通过打断点发现,在前两次创建小球的时候,拿不到小球的颜色组件,所以小球颜色没有更改。等拿到组件的时候,是小球的父物体被 SetActive 的时候。

简化原因: 小球本该在 Awake 的时候设置颜色,但是由于父物体被禁用,所以直到父物体启用,才会走小球的 Awake 周期,进而注册组件。

简化模型,做一个Demo

搭建了这样一个场景: 有一个 CubeSample(右),就待在右边。 有一个 CubeTest(左),挂了一个 CubeExpand.cs 脚本,并制作成了 Prefab,会在 Awake 的时候把自身 LocalScale 设置为 2,也就是放大 2 倍。

// CubeExpand.cs
using UnityEngine;
 
public class CubeExpand : MonoBehaviour
{
    private void Awake()
    {
        transform.localScale = Vector3.one * 2;
        SLog.DebugLog($"[CubeExpand] Awake frame:{Time.frameCount}");
    }
}

有一个空物体 ScriptObj,挂了一个 AwakeTest.cs,引用了 CubeTest Prefab 和 Root,用来在 Root 下实例化 CubeTest Prefab。 shouldActiveRoot 用来控制在 Awake 的时候是否要把 Root 启用。

// AwakeTest.cs
using UnityEngine;
 
public class AwakeTest : MonoBehaviour
{
    public GameObject Root;
    public GameObject CubeTestPrefab;
    private GameObject cubeInstance;
    public bool shouldActiveRoot = true;
    private void Awake()
    {
        SLog.DebugLog($"[Test Awake] frame:{Time.frameCount}");
        Root.SetActive(shouldActiveRoot);
        cubeInstance = Instantiate(CubeTestPrefab, Root.transform);
    }
    void OnEnable()
    {
        SLog.DebugLog($"[Test OnEnable] frame:{Time.frameCount}");
    }
 
    private void Start()
    {
        SLog.DebugLog($"[Test Start] frame:{Time.frameCount}");
        Root.SetActive(true);
    }
}

预期结果 与 Log Output:

shouldActiveRoot = true,AwakeTest 在 Awake 的时候,会显示出 Cube 并把自己放大两倍。 shouldActiveRoot = false,AwakeTest 在 Start 的时候,才会显示出 Cube 并把自己放大两倍。

结论

所以,在父物体被禁用的时候,子物体就算实例化了也不会走Awake,并且此时可以拿到子物体的引用(通过 Instantiate)。直到父物体启用的时候,此时子物体也同时被启用,走一遍子物体的生命周期。

相关链接


标题:父物体禁用导致子物体组件未注册的Bug
作者:LeonYew
日期:2025-10-16