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

GetComponent vs. static public variables

Discussion in 'Scripting' started by Indie_Dev, Jan 7, 2018.

  1. Indie_Dev

    Indie_Dev

    Joined:
    Dec 29, 2017
    Posts:
    35
    I've just been watching videos on data persistence and singleton method to track data.

    In the most recent video, it was discussed that the "Find Object" family of methods should be avoided at all cost due to performance cost, in favor of the singleton method and globally accessible "static public" variables.

    Does GetComponent<T> suffer the same performance hit? Should these be replaced with static variables as well when possible?

    My early OOP learning (years ago, C & C++) taught me that global variables as a general rule should be avoided like the plague because they broke the rules of encapsulation.. is this thinking incorrect or outdated?
     
    KarlKarl2000 likes this.
  2. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,196
    When people talk about the Find Object family being bad for performance reasons, it's generally the static GameObject.Find and FindObject/sOfType methods, which looks through every single object in the scene to find a match.

    There's a non-static transform.Find method, which looks for a child with the name you're asking for, which isn't as bad.

    GetComponent<T> just searches through the a GameObjects's components, to find one that matches the type. That's fast, and you shouldn't have any worries about using it.


    Now, another problem with Find, which transform.Find also suffers, is that it's based around names, rather than types. This makes it pretty brittle, as it assumes that the names of your gameobjects doesn't change. If those names change, you also won't get compile errors, but runtime exceptions. My advice is generally to never do something with strings that you can do with enums, and never do something with enums that you can do with the type system.


    For singletons, you'll get into flamewars over if they're great or okay or the devil. If you have a specific problem that you're trying to solve, it's much easier to give advice on "is the singleton pattern applicable here". The general answer to that question is "meh, it depends"
     
  3. DaDonik

    DaDonik

    Joined:
    Jun 17, 2013
    Posts:
    258
    All of those Find() methods are very slow, because they have to search the whole scene hierarchy.
    It's fine to use them after loading a level, in order to set everything up and cache references to whatever you need.

    In my opinion there are basically two ways you can handle all the stuff you have in your game world:
    1) Have a MonoBehaviour class that uses Awake() to find all the objects in your scene and store them in lists, dictionaries, arrays or whatever else floats your boat. This means you don't need that class to be static or using the singleton pattern. You could just slap the script onto a commonly known game object and use the Start() methods on other objects to find it and store a reference to it.
    The largest downside with this is that you will not be able to access your 'object manager' from the Awake() methods of other objects.
    2) Make a static class or singleton that does not reference MonoBehaviour and use it's static constructor to Find() the objects in your game world. This will make it accessible from everywhere without further effort and allow all objects to access it as early as in their Awake() methods.

    GetComponent<T> should really only be called once for each component on your objects. Just cache a reference to it, which will be much faster than GetComponent<T> every time you need it. I normally do that in Awake() and store the references in private variables.

    You may want to consider throwing out some of those 'good OOP' habits. I know it's hard to do something that breaks encapsulation, but sometimes it's just a neccessity to make your life easier, or your game faster. Just be careful and probably document possible pitfalls, so you remember them.
     
    diliupg and Indie_Dev like this.
  4. smallstep

    smallstep

    Joined:
    Jan 25, 2016
    Posts:
    34
    @DaDonik
    2: How can you use Find() or other MB methods, without making this class a MonoBehaviour? o_O
     
  5. dontdiedevelop

    dontdiedevelop

    Joined:
    Sep 18, 2018
    Posts:
    68
    static variables cause some memory problems because program allocate it from beginning to end
    getcomponent isn't cause memory problems but it's a little expensive for cpu
     
    smallstep likes this.
  6. DaDonik

    DaDonik

    Joined:
    Jun 17, 2013
    Posts:
    258
  7. smallstep

    smallstep

    Joined:
    Jan 25, 2016
    Posts:
    34
    @DaDonik
    You have right, I see...

    However:

    1) with a static Find<> one can only get the *enabled* objects, so I don't understand how you use this locator from the Awake methods of other scripts

    2) by pulling the objects' refs with Find<> from a locator, if some objects are destroyed you end up keeping a fault ref resulting to a nullreference (at least this should happen, as things get worse in practice because of 3)

    3) keeping a ref to a locator prevents garbage collector from cleaning your destroyed objects, but you can keep necro-posting to their ref corpse, until some call eventually results to a nullreference.

    I'm currently studying how to also implement a central reference keeper, as the one you describe above, but I think that an inversion of control should be applied, with the obects registering/unregistering themselves (on their lifecycle events) to the refs keeper instead of the keeper finding them once on beginning of the session.
    I'm still experimenting with this idea, but it seems to work without problems.
     
  8. DaDonik

    DaDonik

    Joined:
    Jun 17, 2013
    Posts:
    258
    @smallstep

    1) You are right, just ignore that i said you can do it in Awake(). Clearly a brainfart on my part =)
    2+3)
    Normally you wouldn't want to destroy your objects, but pool them. So the reference will be valid throughout the applications lifetime and never be null. You just need some way to send an object back to the pool and 'reinstantiate' it if you need it again.
    There are so many ways to do this...in the end the 'correct' solution is the one that works for you =)
     
  9. smallstep

    smallstep

    Joined:
    Jan 25, 2016
    Posts:
    34
    Yes, I guess what each of us believe as advantageous or right is eventually what better fits to his exact situation and the needs of his current project! :)
     
    Joe-Censored and DaDonik like this.