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

Question Can someone explain why Unity only TEMPORARILY sets the reference on Start() instead of keeping it?

Discussion in 'Editor & General Support' started by LastFractal, Aug 12, 2023.

  1. LastFractal

    LastFractal

    Joined:
    Mar 6, 2022
    Posts:
    25
    This whole thing makes no sense to me.

    When I make the variable public and assign the reference script in the editor itself, the script reference stays and it doesn't return null, as it should.

    So, if I wanted to set the reference from the script itself, I would find and set the reference, for example, by doing:

    referenceScript = GameObject.FindGameObjectWithTag("TagExample").GetComponent<ReferenceScript>();


    This should actually SET the value right? Well it does, but KIND OF. That's exactly where I don't understand what is going on.

    If I put the code on Start(), it does set the value, but THEN when I want to do something with the ReferenceScript, in a method for example, I get the old fashioned error:

    "NullReferenceException: Object reference not set to an instance of an object."

    Why? Didn't you just SET the value? Why you first SET it, and right after that immediately return null?

    So because of this, I have to continuously set the value every time that specific method is called. This is the only way I can get it to work.

    This also only happens for script references, it has no problem with, say for example, Rigidbody or WheelCollider, etc.

    Why it does this? Is it some kind of thing to save memory, so it removes it instead of keeping it?
     
    Last edited: Aug 12, 2023
  2. CodeSmile

    CodeSmile

    Joined:
    Apr 10, 2014
    Posts:
    3,899
    Please post your entire code. It is pointless to talk about code issues in an abstract way. There is no „it kind of works but doesn‘t“ when it comes to code. Either you have a bug, or you make incorrect assumptions. Without the code, none of us can tell.
     
  3. LastFractal

    LastFractal

    Joined:
    Mar 6, 2022
    Posts:
    25
    I'm not doing a bug report here. It's a known thing and everyone recommends you to set the value every time your method is called, it just how it behaves, I just want to learn why.
     
  4. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,563
    Nobody here knows how your program runs. That's for you to find out through debugging.

    Time to start debugging! Here is how you can begin your exciting new debugging adventures:

    You must find a way to get the information you need in order to reason about what the problem is.

    Once you understand what the problem is, you may begin to reason about a solution to the problem.

    What is often happening in these cases is one of the following:

    - the code you think is executing is not actually executing at all
    - the code is executing far EARLIER or LATER than you think
    - the code is executing far LESS OFTEN than you think
    - the code is executing far MORE OFTEN than you think
    - the code is executing on another GameObject than you think it is
    - you're getting an error or warning and you haven't noticed it in the console window

    To help gain more insight into your problem, I recommend liberally sprinkling
    Debug.Log()
    statements through your code to display information in realtime.

    Doing this should help you answer these types of questions:

    - is this code even running? which parts are running? how often does it run? what order does it run in?
    - what are the names of the GameObjects or Components involved?
    - what are the values of the variables involved? Are they initialized? Are the values reasonable?
    - are you meeting ALL the requirements to receive callbacks such as triggers / colliders (review the documentation)

    Knowing this information will help you reason about the behavior you are seeing.

    You can also supply a second argument to Debug.Log() and when you click the message, it will highlight the object in scene, such as
    Debug.Log("Problem!",this);


    If your problem would benefit from in-scene or in-game visualization, Debug.DrawRay() or Debug.DrawLine() can help you visualize things like rays (used in raycasting) or distances.

    You can also call Debug.Break() to pause the Editor when certain interesting pieces of code run, and then study the scene manually, looking for all the parts, where they are, what scripts are on them, etc.

    You can also call GameObject.CreatePrimitive() to emplace debug-marker-ish objects in the scene at runtime.

    You could also just display various important quantities in UI Text elements to watch them change as you play the game.

    Visit Google for how to see console output from builds. If you are running a mobile device you can also view the console output. Google for how on your particular mobile target, such as this answer for iOS: https://forum.unity.com/threads/how-to-capturing-device-logs-on-ios.529920/ or this answer for Android: https://forum.unity.com/threads/how-to-capturing-device-logs-on-android.528680/

    If you are working in VR, it might be useful to make your on onscreen log output, or integrate one from the asset store, so you can see what is happening as you operate your software.

    Another useful approach is to temporarily strip out everything besides what is necessary to prove your issue. This can simplify and isolate compounding effects of other items in your scene or prefab.

    If your problem is with OnCollision-type functions, print the name of what is passed in!

    Here's an example of putting in a laser-focused Debug.Log() and how that can save you a TON of time wallowing around speculating what might be going wrong:

    https://forum.unity.com/threads/coroutine-missing-hint-and-error.1103197/#post-7100494

    "When in doubt, print it out!(tm)" - Kurt Dekker (and many others)

    Note: the
    print()
    function is an alias for Debug.Log() provided by the MonoBehaviour class.

    Finally, for fixing the actual problem:

    The answer is always the same... ALWAYS!

    How to fix a NullReferenceException error

    https://forum.unity.com/threads/how-to-fix-a-nullreferenceexception-error.1230297/

    Three steps to success:
    - Identify what is null <-- any other action taken before this step is WASTED TIME
    - Identify why it is null
    - Fix that
     
  5. tsibiski

    tsibiski

    Joined:
    Jul 11, 2016
    Posts:
    569
    No, everyone does not recommend that. What you just described is very much NOT how this normally behaves. I am making a game and have zero problems with setting references to scripts, and then using those references elsewhere.

    Either, the referenced script is being deleted (along with the game object it is on), or you have multiple of the same script attached to objects, and only some of them are setting the reference, and some are not. We need to see the code or we can't actually help determine what the cause is.
     
  6. LastFractal

    LastFractal

    Joined:
    Mar 6, 2022
    Posts:
    25
    There's no reason for the script/gameobject being deleted, as I said in my post that it DOES reference when I call it within the method.

    There's no script to provide, as it always happens no matter which script it's in. But the code is simply:

    referenceScript = GameObject.Find("GameObjectExample").GetComponent<ReferenceScript>();


    I saw a post related to finding an object and getting a script component from it on Stack Overflow, they said that the script had to be referenced again WITHIN the method that reads/modifies the Reference Script, and not on Start().

    The GameObject IS stored, but you have to get the script component again. For example;


    void Start()
    {
    referenceObject = GameObject.Find("Object");
    }

    ...
    void ExampleMethod()
    {
    referenceScript = referenceObject.GetComponent<ReferenceScript>();
    referenceScript.exampleValue = 1;
    referenceScript.MethodName();
    ...
    }


    This is the only way it works, however if you do THIS:


    void Start()
    {
    referenceScript = GameObject.Find("Object").GetComponent<ReferenceScript>();
    }

    ...
    void ExampleMethod()
    {
    referenceScript.exampleValue = 1;
    referenceScript.MethodName();
    ...
    }


    Then it gives you an NullReferenceException error when you call the ExampleMethod().
     
  7. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,563
    Well, there's your problem! Right in front of you!!

    You are blatantly violating the first rule of GameObject.Find():

    Remember the first rule of GameObject.Find():

    Do not use GameObject.Find();

    Even if you REALLY REALLY REALLY want to use 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.


    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.

    ^ ^ ^ This is exactly what happens when you use GameObject.Find(). Things make no sense whatsoever. Your mind WANTS it to work, you desperately want it to have a place in your palette of tools... but it doesn't. GameObject.Find() is NOT TO BE USED.
     
    Last edited: Aug 12, 2023
    bugfinders likes this.
  8. LastFractal

    LastFractal

    Joined:
    Mar 6, 2022
    Posts:
    25
    I wouldn't be using it if I didn't need it, I'm not dumb.

    While I agree that it's better to assign it within Unity's serialized field, the reason I'm using it is because what I'm trying to reference is not always there.

    Say I have multiple prefabs, and only one of them is being loaded, which is either randomly chosen or by player. Every single of them has the exact same script. If I drag (reference) the one on the Prefab A, and player chooses Prefab B instead, then it won't simply work because it's referencing to the ExampleScript on Prefab A, and not the one on B.

    So, rather than manually assigning all of them to the list (which isn't quite practical), you want to use GameObject.Find(), or in my case, GameObject.FindGameObjectWithTag("TagName"), so when the scene and the selected Prefab is loaded, it finds and references that one instead.
     
  9. tsibiski

    tsibiski

    Joined:
    Jul 11, 2016
    Posts:
    569
    Keep one additional thing in mind. You probably know this, but I will say it anyway. If you make ANY code changes and save them while the editor is running, the default behavior of Unity is to recompile, while allowing the editor to continue playing. When this happens, tons of references to objects on the Heap are lost, so values that should be set are suddenly null references. If you can tell that no code compilation is occurring in play mode before this issue occurs, then this obviously isn't the cause of your problems. Still worth a mention, especially if someone finds this thread in the future because they are having mysterious null ref issues.
     
    LastFractal likes this.
  10. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,563
    It just sounds like you're trying to get at it when it's not there.

    This stuff isn't hard, don't make it hard on yourself. Seriously. Move up the value chain. Spend your effort where it benefits you: make your dream game, play your dream game.

    Don't be wrestling with all the filthy dirty pigs down in the nullreference slop farm. That's the LAST place you wanna be.

    Use one of the simple locator patterns that StarManta posts above.

    Here's the one I always use for transient services:

    And finally there's always just a simple "static locator" pattern you can use on MonoBehaviour-derived classes, just to give global access to them during their lifecycle.

    WARNING: this does NOT control their uniqueness.

    WARNING: this does NOT control their lifecycle.

    Code (csharp):
    1. public static MyClass Instance { get; private set; }
    2.  
    3. void OnEnable()
    4. {
    5.   Instance = this;
    6. }
    7. void OnDisable()
    8. {
    9.   Instance = null;     // keep everybody honest when we're not around
    10. }
    Anyone can get at it via
    MyClass.Instance.
    , but only while it exists.

    If you want this service to just appear when needed:

    Simple Singleton (UnitySingleton):

    Some super-simple Singleton examples to take and modify:

    Simple Unity3D Singleton (no predefined data):

    https://gist.github.com/kurtdekker/775bb97614047072f7004d6fb9ccce30

    Unity3D Singleton with a Prefab (or a ScriptableObject) used for predefined data:

    https://gist.github.com/kurtdekker/2f07be6f6a844cf82110fc42a774a625

    These are pure-code solutions, DO NOT put anything into any scene, just access it via .Instance
     
    LastFractal likes this.
  11. bugfinders

    bugfinders

    Joined:
    Jul 5, 2018
    Posts:
    711
    what value do you get if you do a debug.log on that referencescript - it could easily be null if the object didnt exist at the time of start running.
     
  12. LastFractal

    LastFractal

    Joined:
    Mar 6, 2022
    Posts:
    25
    I once thought the same thing, but it does print the object name.
    upload_2023-8-12_21-29-38.png
     
  13. bugfinders

    bugfinders

    Joined:
    Jul 5, 2018
    Posts:
    711
    and theres no way that got destroyed since? or theres another copy of the script? this one, not the referenced script