Search Unity

Warning: GetComponentInParent works differently between scene and asset objects

Discussion in 'Prefabs' started by RakNet, Nov 28, 2019.

  1. RakNet

    RakNet

    Joined:
    Oct 9, 2013
    Posts:
    315
    I wanted to share with the community a huge bug I found. I filed a case for it but I don't expect to be fixed because it is so fundamental it would affect existing projects. I'm sharing it here so others will know to be on the lookout.

    According to the documentation https://docs.unity3d.com/ScriptReference/Component.GetComponentInParent.html
    "Returns the component of Type type in the GameObject or any of its parents."

    While this IS the case with scene objects, it is NOT the case with assets. GetComponentInParent will fail to find the component if it is on the same object when it is assets.

    I've attached a script that shows the issue. If you add a Rigidbody to an object, the script will pass if the object is in the scene, but fail as an asset.

    Code (CSharp):
    1.  
    2. using System.Collections;
    3. using System.Collections.Generic;
    4. using UnityEngine;
    5. using UnityEditor;
    6.  
    7. static public class GetComponentInParentBug
    8. {
    9.     [MenuItem("GameObject/3D Object/GetComponentInParentTest/Test")]
    10.     private static void TestFromScene()
    11.     {
    12.         GetComponentInParentBugTest();
    13.     }
    14.  
    15.  
    16.     [MenuItem("Assets/GetComponentInParentTest/Test", false)]
    17.     private static void TestFromProject()
    18.     {
    19.         GetComponentInParentBugTest();
    20.     }
    21.     private static void GetComponentInParentBugTest()
    22.     {
    23.         UnityEngine.Object[] selectedObjects =
    24.             Selection.GetFiltered(typeof(UnityEngine.GameObject), SelectionMode.Assets);
    25.  
    26.         foreach (GameObject selectedObject in selectedObjects)
    27.         {
    28.             Rigidbody parentRb = selectedObject.GetComponentInParent<Rigidbody>();
    29.             Rigidbody thisRb = selectedObject.GetComponent<Rigidbody>();
    30.  
    31.             if (parentRb == null && thisRb != null && thisRb.gameObject == selectedObject)
    32.             {
    33.                 Debug.LogError("GetComponentInParent<Rigidbody>() failed but GetComponent<Rigidbody>() succeeded on the same object!", selectedObject);
    34.             }
    35.             else
    36.             {
    37.                 Debug.Log("All is good", selectedObject);
    38.             }
    39.         }
    40.     }
    41. }
    42.  
    43.  
     
  2. runevision

    runevision

    Joined:
    Nov 28, 2007
    Posts:
    1,892
    GameObject Assets are treated as inactive, so
    GetComponentInParent
    behaves the same way on an Asset as it does on a inactive GameObject in the scene. (I'm not quite sure where this is mentioned in the manual, and I do understand the confusion.)

    If you deactivate a GameObject in the scene, you'll find the behavior is the same there.

    Or, you can pass true to the
    GetComponentInParent
    method to make it also consider inactive objects, and you'll find it works fine for Assets.
     
  3. Peter77

    Peter77

    QA Jesus

    Joined:
    Jun 12, 2013
    Posts:
    6,619
    nzhangaudio and Bastienre4 like this.
  4. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,338
    Why? That seems unexpected.
     
  5. SteenLund

    SteenLund

    Unity Technologies

    Joined:
    Jan 20, 2011
    Posts:
    639
    Because active objects would add stuff to the physics scene if they have physics components and get picked up by the rendering if the have a Renderer and so on.
     
  6. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,338
    ... What.

    You're saying that we have to pretend that prefabs can meaningfully be active/not active, because otherwise Unity would just dump them into the game?

    Prefab assets shouldn't have an active flag! Why can stuff be added to the physics scene or the renderer without being in a scene to begin with?

    I mean I'm a fan of most of what you did with nested prefabs, but it does sound like what you should have done instead was to burn everything relating to prefabs to the ground and made it from scratch, because right now you're describing a pile of ancient, leaky abstractions and bad ideas.
     
  7. SteenLund

    SteenLund

    Unity Technologies

    Joined:
    Jan 20, 2011
    Posts:
    639
    No, you don't have to pretend they can be active. Objects that are persistent are inactive no matter the state of the active flag.

    Since prefabs are just a collection of GameObjects they have all the same serialized state as regular GameObjects including the active flag, so burning existings prefabs to the ground and starting over would have changing a lot more than just how prefabs works.