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

How to get a reference to a script in a scene from a DontDestroyOnLoad object script?

Discussion in 'Scripting' started by Bazz_boyy, Oct 2, 2020.

  1. Bazz_boyy

    Bazz_boyy

    Joined:
    May 22, 2013
    Posts:
    192
    Hello,

    I have a script in a scene that I need to get a reference to from a script that persists across scenes. What is a good way of doing this?

    I know I can use FindObjectOfType, but from my understanding, this can be a bit unperformant. I also know I could use Singletons, however I am not entirely fond of them and would rather not have to use global objects.

    Any other solutions?

    Cheers
     
  2. WarmedxMints

    WarmedxMints

    Joined:
    Feb 6, 2017
    Posts:
    1,035
    You could use a scriptable object. If it is placed in the Resources folder, you can then load it from there or if your objects are already in the scene before building, you could simply drag a reference to the scriptable object in the inspector.

    The SO could then hold a reference to the object you want a reference for.
     
  3. adehm

    adehm

    Joined:
    May 3, 2017
    Posts:
    369
    If the script is attached to an object you can find it in Start of the script in the new scene with find by name and clean garbage at the end.
    Code (CSharp):
    1. aScript myScript;
    2. void Start()
    3. {
    4.     myScript = GameObject.FindByName("object name holding the script").GetComponent<TheScript>();
    5.     System.GC.Collect();
    6. }
    7.  
     
  4. Bazz_boyy

    Bazz_boyy

    Joined:
    May 22, 2013
    Posts:
    192
    Thanks for the reply. I don't believe you can reference scene objects with assets such as scriptableObjects. However there is a work around using ExposedReferences which I could try I suppose... https://docs.unity3d.com/ScriptRefe...1.1885255618.1601610599-1888491936.1601610599

    Yeah this solution is probably the easiest. The problem I have with this kind of solution is that comparing strings is very slow, which is what this method would be doing. It's probably okay if you have a low amount of objects, but as the number of objects you have grows, so does the time.

    I think I might have a go at using ExposedReferences, because then I can just reference scene components/objects through scriptableobjects as if everything was in the same scene.
     
  5. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,754
    Here's my super-simple singleton implementation, usable in pretty much any MonoBehavior context you want.

    Simple Singleton (UnitySingleton):

    Some super-simple Singleton examples to take and modify:

    Simple Unity3D Singleton (no predefined data):

    https://pastebin.com/SuvBWCpJ

    Unity3D Singleton with Prefab used for predefined data:

    https://pastebin.com/cv1vtS6G

    These are pure-code solutions, do not put anything into any scene, just access it via .Instance!
     
    Joe-Censored likes this.
  6. Joe-Censored

    Joe-Censored

    Joined:
    Mar 26, 2013
    Posts:
    11,847
    I'd just give the GameObject the script is attached to a specific tag. Then just use FindWithTag if there is only ever 1 of these active at the same time, or FindGameObjectsWithTag if you sometimes have more than 1 of them. Then if the result is not null, you just GetComponent for the component you are looking for. You take the result of GetComponent and cache it. If that cached result is ever null, that means you've probably changed scenes which caused the old component to get destroyed so if you need a reference to a new one in a new scene you just repeat the process.

    FindWithTag isn't exactly fast (though faster than FindObjectOfType), but calling it once on scene load is pretty trivial. GetComponent is pretty fast.

    Code (csharp):
    1. private ScriptInScene cachedScriptInScene = null;
    2.  
    3. void Update()
    4. {
    5.     if (cachedScriptInScene == null)
    6.     {
    7.         GameObject scriptInSceneGO = GameObject.FindWithTag("ScriptInSceneTag");
    8.         if (scriptInSceneGo != null)
    9.         {
    10.             cachedScriptInScene = scriptInSceneGO.GetComponent<ScriptInScene>();
    11.         }
    12.     }
    13. }
    The above if part of your script which persists between scenes, should capture a reference to the script which exists in each scene at some point in the first frame. Just check if the reference is null before using it since it is possible for it to be null earlier in the same first frame.
     
  7. WarmedxMints

    WarmedxMints

    Joined:
    Feb 6, 2017
    Posts:
    1,035
    They can hold a reference to what you like. You just need to store the reference at runtime if that reference is in a scene and not a prefab or asset. So in the object you wish to reference, you have it store a reference to itself in its awake or start method and can code it to set the reference to null if you wish it its destroy or OnDisable method.
     
  8. adehm

    adehm

    Joined:
    May 3, 2017
    Posts:
    369
    But the scene is still loading at that point so shouldn't really matter. If you need to reference hundreds of objects then you're likely better to do it in more organized way then blindly rummaging for object names. Like creating the scripts inside the script that 'needs' reference to them.
     
  9. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,754
    Joe-Censored likes this.
  10. Bazz_boyy

    Bazz_boyy

    Joined:
    May 22, 2013
    Posts:
    192
  11. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    3,528
    While profiling and testing is always the right way to go if you worry about performance or when you want to find bottle necks, you should be careful with such benchmarks. Many only scratch at the surface and overlook important details in their testing environment. In this specific case using DateTime for such time measurements is a bad choice. Also keep in mind that comparing those variables is pretty pointless:

    Code (CSharp):
    1. string value0 = "AAAAAAAAAAAAAAAAAAAAAA";
    2. string value1 = "AAAAAAAAAAAAAAAAAAAAAA";
    because both will be references to the same interned string literal and comparing them would be O(1) because they would be detected as equal just based on their reference. However strings which do not represent the same string object have to be compared character by character and the comparison has a runtime of O(n).

    Of course all this is in the range of "sub micro optimisations" :) A single integer comparison is so fast we don't even try to give it an actual duration as it's almost impossible to measure because the CPU cache is 100 or 1000 times greater bottle neck than the actual comparison. The strings we talk about have 22 character and the comparison scales linearly. So compared to an int - int comparison it would be "22 times slower". However since strings are stored in continuous memory a single integer comparsion probably won't be much faster than a single string comparsion.

    People in general tend to make too many assumptions about how the underlying code will be executed. Especially with C# and the intermediate language in between and different target hardware most assumptions could easily be completely wrong. Therefore you should simply test your actual code in production if you think you have performance issues. Having several people or "intelligences" trying to optimise code could easily go horribly wrong. We already have the CPU and cache controller at the lowest level. The operating system maybe in between. The jit compiler on top and the C# compiler at the other end. For platforms like Android we may have things like the dalvik Java VM in between the OS and your code.

    Micro optimisations may only be considered if you run some code in a loop that runs 10 million times+