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. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice

Question GetComponent() in script no longer returning a value, how do I work out what is going wrong?

Discussion in 'Scripting' started by Wildman537oo, Jul 31, 2023.

  1. Wildman537oo

    Wildman537oo

    Joined:
    Oct 21, 2019
    Posts:
    3
    After saving and commiting to my repo, all of the getcomponents in my script have just stopped working. One of them I fixed after just multiple recompiles but another one just refuses to work.

    Code (CSharp):
    1. public abstract class BaseBar : MonoBehaviour
    2. {
    3.     [HideInInspector] public Slider slider;
    4.     [HideInInspector] public Player.Player player;
    5.  
    6.     private TextMeshProUGUI text;
    7.  
    8.     private void Start()
    9.     {
    10.         this.slider = GetComponent<Slider>();
    11.         this.text = gameObject.transform.Find("ValueText").GetComponent<TextMeshProUGUI>();
    12.         this.player = GameObject.FindWithTag("Player").GetComponent<Player.Player>();
    13.     }
    14.  
    15.     public virtual void SetMaxValue(int value)
    16.     {
    17.         this.slider.maxValue = value;
    18.         this.slider.value = value;
    19.     }
    20.  
    21.     public void SetValue(int value)
    22.     {
    23.         this.slider.value = value;
    24.     }
    25.  
    26.     public void SetTextValues(int current, int max)
    27.     {
    28.         this.text.text = current.ToString() + "/" + max.ToString();
    29.     }
    30. }
    31.  
    However this still causes:

    Code (CSharp):
    1. NullReferenceException: Object reference not set to an instance of an object
    2. BaseBar.SetTextValues (System.Int32 current, System.Int32 max) (at Assets/Scripts/UI/Hud/BaseBar.cs:32)
    3. HpBar.SetInitialValues (System.Int32 maxHp, System.Int32 hp) (at Assets/Scripts/UI/Hud/HpBar.cs:12)
    4. Player.Player.Start () (at Assets/Scripts/Player/Player.cs:31)
    5.  
    I don't understand how this is even happening as I've verified that these are the correct paths to getting the components. However saving the scene and commiting has just broke this.
     
  2. DevDunk

    DevDunk

    Joined:
    Feb 13, 2020
    Posts:
    4,456
    1. Try to avoid .Find, as it's very inaccurate and prone to make bugs.
    2. Debug everything, log the .Find stuff as well to see how that does
     
  3. Wildman537oo

    Wildman537oo

    Joined:
    Oct 21, 2019
    Posts:
    3
    What would be the alternative to .Find?
     
  4. Sluggy

    Sluggy

    Joined:
    Nov 27, 2012
    Posts:
    845
    Typically you would either hard link references in the editor or use/create a more robust dependency injection system that is more reliable and more performant.

    Either way in this situation as DevDunk said you should use the debugger to find out precisely what is happening. More than likely the values assigned in Start are invalid because they didn't find what was needed. Perhaps the objects are missing from the scene? Or the tags are wrong? Or the hierarchy has been changed and your code isn't robust enough to work around that.
     
  5. Ryiah

    Ryiah

    Joined:
    Oct 11, 2012
    Posts:
    20,184
    There are multiple alternatives: (i) direct assignment through the Inspector, (ii) static field referencing the instance of the object, (iii) a service locator, etc. Here's an example of a static field.

    Add a static field to Player:
    Code (csharp):
    1. public static Player Instance;
    Assign it in Awake():
    Code (csharp):
    1. Instance = this;
    Access it in Start() from BaseBar:
    Code (csharp):
    1. this.player = Player.Player.Instance;
     
  6. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,954
    Keep in mind that using GetComponent<T>() and its kin (in Children, in Parent, plural, etc) to try and tease out Components at runtime is definitely deep into super-duper-uber-crazy-Ninja advanced stuff.

    This sort of coding is to be avoided at all costs unless you know exactly what you are doing.

    If you run into an issue with any of these calls, start with the documentation to understand why.

    There is a clear set of extremely-well-defined conditions required for each of these calls to work, as well as definitions of what will and will not be returned.

    In the case of collections of Components, the order will NEVER be guaranteed, even if you happen to notice it is always in a particular order on your machine.

    It is ALWAYS better to go The Unity Way(tm) and make dedicated public fields and drag in the references you want.

    Remember the first rule of GameObject.Find():

    Do not use GameObject.Find();

    More information: https://starmanta.gitbooks.io/unitytipsredux/content/first-question.html

    More information: https://forum.unity.com/threads/why-cant-i-find-the-other-objects.1360192/#post-8581066

    In general, DO NOT use Find-like or GetComponent/AddComponent-like methods unless there truly is no other way, eg, dynamic runtime discovery of arbitrary objects. These mechanisms are for extremely-advanced use ONLY.

    If something is built into your scene or prefab, make a script and drag the reference(s) in. That will let you experience the highest rate of The Unity Way(tm) success of accessing things in your game.