Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Can't instantiate a prefab and access it's child objects both in the Start() method

Discussion in 'Scripting' started by gmfbrown, Dec 18, 2020.

  1. gmfbrown

    gmfbrown

    Joined:
    Jan 9, 2019
    Posts:
    25
    My current set up involves a level building script called PuzzleBuilder.cs attached to the Main Camera. In the Start() method I instantiate a prefab called Epicyclopod. The Epicyclopod has a script attached called Epicyclopod.cs and its Start() method instantiates another prefab called Cyclopod and attached it as a child object of the Epicyclopod. I'm trying to get the PuzzleBuilder script to do things with the Cyclopod child object but, when I do this, it produces a NullReferenceException error.

    On debugging the problem seems to be that the Cyclopod child object isn't being instantiated until the Start() method in the PuzzleBuilder.cs script is done. This doesn't make sense, since I instantiated the Cyclopod in the Start() method of the Epicyclopod.cs script, and the Epicyclopod was instantiated in the Start() method of the PuzzleBuilder.cs.

    Relevant Code:

    PuzzleBuilder.cs

    Code (CSharp):
    1.     void Start()
    2.     {
    3.         // Instantiate epicyclopod
    4.         epicyclopod = Instantiate(epicyclopodPrefab).GetComponent<Epicyclopod>();
    5.  
    6.         // other code cut because it's irrelevant to the bug
    7.  
    8.         // Get cyclopod from epicyclopod
    9.         // This is the line of code that throws the NullReferenceException
    10.         cyclopod = epicyclopod.transform.Find("Cyclopod").gameObject.GetComponent<Cyclopod>();
    11.  
    12.         // other code cut because it's irrelevant to the bug
    13.     }
    Epicyclopod.cs

    Code (CSharp):
    1.     void Start()
    2.     {
    3.         // other code cut because it's irrelevant to the bug
    4.        
    5.         // Instantiate cyclopod and set it as child object
    6.         cyclopod = Instantiate(cyclopodPrefab).GetComponent<Cyclopod>();
    7.         cyclopod.transform.SetParent(this.transform);
    8.  
    9.         // other code cut because it's irrelevant to the bug
    10.     }
    When debugging, it seems that, by the time line 10 in the PuzzleBuilder.cs script is called, line 7 hasn't yet been called in the Epicyclopod.cs script, so the child object isn't recognized as existing. This seems odd since I would thought the entire Start() method of Epicyclopod.cs would be called once the Epicyclopod prefab is instantiated in line 4 of the PuzzleBuilder.cs script.

    What's going on and how can I access the child object in the level-building script itself?
     
  2. Antistone

    Antistone

    Joined:
    Feb 22, 2014
    Posts:
    2,833
    IIRC, when you Instantiate a new object at runtime, Start() won't interrupt the caller, but Awake() will. Moving your code from Start to Awake will probably solve your issue.

    You can also always stick it in an ordinary public function and call it explicitly right after instantiation.
     
    Joe-Censored and PraetorBlue like this.
  3. PraetorBlue

    PraetorBlue

    Joined:
    Dec 13, 2012
    Posts:
    7,722
    Yep. Awake() and OnEnable() will be called before Instantiate returns. Start() won't be called until the next frame.
     
  4. gmfbrown

    gmfbrown

    Joined:
    Jan 9, 2019
    Posts:
    25
    Moving to Awake() doesn't seem to be working. I've tried moving all the code to the scripts' Awake() methods. And I've tried moving the Epicyclopod.cs code and the beginning of the PuzzleBuilder.cs code to the Awake() methods while keeping the line 10 of PuzzleBuilder.cs in the Start() method. Either way, I keep on getting the same NullReferenceException error.

    The only difference I can see is that, when everything was in the Start() method, it said epicyclopod.transform.childCount was equal to 0, where as, now it says it's equal to 1 but still can't find the actual child object.
     
  5. PraetorBlue

    PraetorBlue

    Joined:
    Dec 13, 2012
    Posts:
    7,722
    Have you tried printing out the name of that 1 child object? I'd guess the name is just not what you're expecting.
     
    Antistone likes this.
  6. gmfbrown

    gmfbrown

    Joined:
    Jan 9, 2019
    Posts:
    25
    That took care of it. Thanks! The child object was called "Cyclopod(Clone)" instead of "Cyclopod". For future reference, what is the underlying logic behind when Unity chooses to add "(Clone)" at the end of an object's name?
     
  7. Antistone

    Antistone

    Joined:
    Feb 22, 2014
    Posts:
    2,833
    If you are only expecting one child, then it might be better to write your code in a way that doesn't care about its name, anyway. You could use GetComponentInChildren<>, or just take the first child, or create some public variable or property in Epicyclopod that references the Cyclopod and is set as soon as the child is instantiated.

    It also occurs to me that you could modify the prefab for Epicyclopod so that it just contains the Cyclopod already and doesn't need to instantiate it upon its own creation. (It used to be that Unity didn't support nested prefabs, but it does now.)
     
    PraetorBlue likes this.