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

The issue of Instantiation and inactive objects; Just looking for confirmation.

Discussion in 'Scripting' started by dreamtocode77, Apr 9, 2020.

  1. dreamtocode77

    dreamtocode77

    Joined:
    Nov 10, 2019
    Posts:
    46
    I have a U.I panel with a text component child saved as prefab. I want to instantiate said prefab but apparently I cannot use GameObject.Find for in inactive objects(?) so it is returning a null.

    * Yes, I know if I made it a public field it would solve the problem but I would like to use public fields only when necessary.

    * I have seen some workarounds like keeping the original panel in the hierarchy then switching it to inactive upon starting.

    Before I submit to public fields or workarounds; Is there no way to store a reference of an inactive object?
     
  2. Yoreki

    Yoreki

    Joined:
    Apr 10, 2019
    Posts:
    2,590
    Instantiate() returns you the reference to the instantiated object. You store references to inactive objects in the same way you store references to active objects. Both are just normal (C#) references after all. Being active or inactive is a property of the referenced object, which is a Unity specific thing.
    Also, try to stay away from GameObject.Find() in the first place. It's slow and basically only intended for quick prototyping. It never has to be used, so really it never should unless you know about that and still decide to use it.
     
  3. dreamtocode77

    dreamtocode77

    Joined:
    Nov 10, 2019
    Posts:
    46
    Ok. So then how do you make a reference to a object without finding it (and public fields)? I only know:

    GameObject gameObject = Game.Find("cube"); Thats how I make a reference and this doesn't work with inactive Objects.

    Show me how to do that without the GameObject.Find part.
     
  4. Yoreki

    Yoreki

    Joined:
    Apr 10, 2019
    Posts:
    2,590
    As i already described, if you use Instantiate(), it returns you the reference to the instantiated object.
    https://docs.unity3d.com/ScriptReference/Object.Instantiate.html

    Other than that, you can manually assign references through the inspector if the object exists outside runtime. By the way, such a field does not have to be public. It has to be serialized. Public just serializes a field by default, but from an architectural viewpoint, you'd most often want to use [SerializeField] private, instead of public (if all you need it for is for it to show up in the inspector, and not actually need it to be public).

    One other way to get a reference is to get it from another object which knows the reference.

    So basically you can get a reference in these ways:
    • Create the object yourself,
    • get the reference from some other object that has it, and to which you have a reference,
    • or assign the reference manually.
     
  5. ianswerquestions20

    ianswerquestions20

    Joined:
    Apr 7, 2020
    Posts:
    22
    If the inactive game object has a parent, you can use GameObject.Find. Example:

    GameObject gameObject = parentObject.transform.Find("cube");

    Which I believe this is that same thing as what @Yoreki pointed out:

     
  6. dreamtocode77

    dreamtocode77

    Joined:
    Nov 10, 2019
    Posts:
    46
    When I say i don't want use public fields, I really mean I do not want to use any of the "drag n drop in the inspector" routes unless necessary. What does the bold mean?I cant instantiate the object because its not stored, its not stored because I cant make a reference to it... because its inactive, ignoring using public and serialized fields...which I don't want to


    In this case the Canvas would be the parent when it is instantiated.

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using UnityEngine.UI;
    5.  
    6. public class ChangeName : MonoBehaviour
    7. {
    8.     Text text;
    9.     GameObject panel;
    10.     Canvas canvas;
    11.  
    12.     // Start is called before the first frame update
    13.     void Start()
    14.     {
    15.        
    16.     }
    17.  
    18.     // Update is called once per frame
    19.     void Update()
    20.     {
    21.        
    22.     }
    23.  
    24.     public void NameChanger()
    25.     {
    26.         canvas = GetComponent<Canvas>();
    27.         if (canvas != null)
    28.         {
    29.             panel = canvas.transform.Find("NameTag");
    30.             text = panel.GetComponentInChildren<Text>();
    31.             text.text = "hi";
    32.         }
    33.     }
    34. }
    Gives me I cant convert unityengine. transfrom to unityengine.gameobject error.
     
    Last edited: Apr 9, 2020
  7. ianswerquestions20

    ianswerquestions20

    Joined:
    Apr 7, 2020
    Posts:
    22
    You need to make the panel a Transform not a GameObject.

    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.UI;
    3.  
    4. public class ChangeName : MonoBehaviour {
    5.  
    6.     Text text;
    7.     Transform panel; // MAKE THIS Transform INSTEAD OF GameObject
    8.     GameObject canvas;
    9.  
    10.     // Start is called before the first frame update
    11.     void Start() {
    12.    
    13.     }
    14.  
    15.     void Update (){
    16.    
    17.     }
    18.     public void NameChanger() {
    19.    
    20.         canvas = GameObject.Find("Canvas");
    21.         if (canvas != null) {
    22.        
    23.             panel = canvas.transform.Find("NameTag");
    24.             text = panel.GetComponentInChildren<Text>();
    25.             text.text = "hi";
    26.         }
    27.     }
    28. }
    Your code is overly complex though. Do this instead:

    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.UI;
    3.  
    4. public class ChangeName : MonoBehaviour {
    5.  
    6.     // Make this public and assign it in the editor
    7.     // ALSO: You don't event have to disable the text object at all. Just set text.text = ""
    8.     public Text text;
    9.  
    10.     public void NameChanger() {
    11.    
    12.         text.text = "hi";
    13.     }
    14. }
     
  8. dreamtocode77

    dreamtocode77

    Joined:
    Nov 10, 2019
    Posts:
    46
    Lololololol. Guuuuys! I am well that If I assigned them the inspector this thread would not even be here. I want to learn to program using the "drag into inspector" only when necessary.
     
    swelker likes this.
  9. ianswerquestions20

    ianswerquestions20

    Joined:
    Apr 7, 2020
    Posts:
    22
    Note: the "drag into inspector" is a huge part of Unity. Use that over .Find where possible.

    Regardless, changing the GameObject to Transform like I stated above should solve your issue.

    Edit: Looked above and saw that you are already familiar with inspector variables.
     
  10. dreamtocode77

    dreamtocode77

    Joined:
    Nov 10, 2019
    Posts:
    46
    Currently showing no errors!

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using UnityEngine.UI;
    5.  
    6. public class ChangeName : MonoBehaviour
    7. {
    8.     Text text;
    9.     public Transform panel;
    10.     Canvas canvas;
    11.  
    12.     // Start is called before the first frame update
    13.     void Start()
    14.     {
    15.      
    16.     }
    17.  
    18.     // Update is called once per frame
    19.     void Update()
    20.     {
    21.      
    22.     }
    23.  
    24.     public void NameChanger()
    25.     {
    26.         GameObject holder = GameObject.Find("Canvas");
    27.         if (holder != null)
    28.         {
    29.             canvas = holder.GetComponent<Canvas>();
    30.             panel = canvas.transform.Find("NameTag");
    31.             text = panel.GetComponentInChildren<Text>();
    32.             text.text = "hi";
    33.         }
    34.     }
    35. }
    Now here is the code that is doing the instantiating:

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using System.Threading;
    4. using UnityEngine;
    5. using UnityEngine.UI;
    6.  
    7. public class Spawn : MonoBehaviour
    8. {
    9.     ChangeName changeName;
    10.     Transform bar;
    11.     GameObject spot;
    12.     Transform Position;
    13.  
    14.  
    15.     // Start is called before the first frame update
    16.     void Start()
    17.     {
    18.        
    19.         changeName = GetComponent<ChangeName>();
    20.         //bar = changeName.panel;
    21.         spot = GameObject.Find("Spot");
    22.         MakeThing();
    23.     }
    24.    
    25.     // Update is called once per frame
    26.     void Update()
    27.     {
    28.        
    29.        
    30.     }
    31.  
    32.     public void MakeThing()
    33.     {
    34.         bar = changeName.panel;
    35.         Transform Position = spot.transform;
    36.         Transform HUD = Instantiate(bar, Position.position, Position.rotation);
    37.         HUD.transform.SetParent(GameObject.Find("Canvas").transform,false);
    38.         HUD.transform.position = Position.position;
    39.         changeName.NameChanger();
    40.     }
    41. }
    42.  
    its saying the bar = changeName.panel is is not set to instance of object. Again its tring to instantiate a object that is not being referenced. Its wants to know what panel is, panel is going to be the currently inactive prefab.
     
  11. ianswerquestions20

    ianswerquestions20

    Joined:
    Apr 7, 2020
    Posts:
    22
    You're calling changeName.panel before panel is set. Panel is set when you call changeName.NameChanger() . Call changeName.NameChanger() before calling bar = changeName.panel

    Code (CSharp):
    1.  
    2. changeName.NameChanger();
    3. bar = changeName.panel;
    4. Transform Position = spot.transform;
    5. Transform HUD = Instantiate(bar, Position.position, Position.rotation);
    6. HUD.transform.SetParent(GameObject.Find("Canvas").transform,false);
    7. HUD.transform.position = Position.position;
    8.  
    Try to follow the methods in your code manually in your head so you can better understand what order things are happening in.
     
  12. dreamtocode77

    dreamtocode77

    Joined:
    Nov 10, 2019
    Posts:
    46
    I know my code can be very convoluted:

    Iline 5 that is where the UI panel is instantiated
    6. it is childed to canvas
    7. put into positon
    then i want to change text with Namechanger()

    If I do namechanger() first it tries to change the name of an object not yet instantiated... that would not be an issue if I could access the text component of panel, but I cant... because I cant make a reference to it, because it is currently inactive.
     
  13. Yoreki

    Yoreki

    Joined:
    Apr 10, 2019
    Posts:
    2,590
    If you want to work without it, go look for another engine. Sorry if this seems rude, but you are effectively asking how to do something without using a tool, in a forum dedicated to answering questions about said tool.

    You can always write code without Unity. There you get references only by either directly creating the object, or passing it around between objects. It also took me some time to get used to the Unity workflow, but that's how it is.
    If you want to instantiate a prefab, you need a reference to the prefab. You can get this either by dragging the prefab in through the inspector, or load the ressource based on its name. The latter is what's actually bad design, since the programm breaks as soon as the file gets renamed.

    Honestly tho, just try to see the inspector inputs as the launch parameters of your program. It's not that different from normal programms, where you can give it launch parameters to alter its behavior through the console, or possibly some settings. You are just determining the looks and relations of the software you write, by altering its input parameters.

    So for all intents and purposes, the 'drag into inspector' part is necessary for referencing the prefab. I have the feeling you think about this as 'cheating' somehow, and believe it makes you a worse programmer. If that's the case, improve your confidence by writing a normal object oriented application outside of Unity or any other engine. From scratch up. Then you know you can do it 'the normal way'. Afterwards ask yourself: does it make you a better or worse programmer to intentionally use a slower, worse way (loading with Find() or from ressources for example), simply because you do not want to use the intended fast way.
     
  14. swelker

    swelker

    Joined:
    Sep 22, 2015
    Posts:
    6
    Working with the editor is very difficult when you are working in a big team and your code has to go through code review (you cannot easily code review Scenes.) Also usually you do not have all the references in the scene yet, but they are being instantiated later when the whole application is brought up. The way we do this is to instantiate most of the Scene components from code and giving each system the needed references from there, this also helps with your references not going too crazy. although in some cases, there are static classes which can be referenced on the fly when an object is instantiated dynamically.

    I think working in the editor is acceptable as long as the code is contained in one prefab, but outside of a prefab i would not use scene links for references.