Search Unity

Does Unity Need a Generic version of GameObject.Find()?

Discussion in 'Scripting' started by FBones, Aug 25, 2018.

  1. FBones

    FBones

    Joined:
    Aug 28, 2016
    Posts:
    73
    I frequently see GameObject.Find() used in tutorials, etc. as a means to find GameObjects. My problem with this is that GameObject.Find() returns a GameObject that cannot be cast to a specific type. This means you cannot access type-specific members.

    For example, if you had a camera manager, and you wanted to do something simple like modify the background for Camera 3, you could not do this:

    Code (CSharp):
    1. Camera camera3 = GameObject.Find("Camera 3") as Camera
    2. camera3.backgroundColor = newColor
    It seems to me that Unity would benefit from a generic version of GameObject.Find() to allow casting to the specific type to access type-specific members.

    Then you could do:

    Code (CSharp):
    1. Camera camera3 = GameObject<Camera>.Find("Camera3");
    2. camera3.backgroundColor = newColor;
    This would be far superior than the other available methods:
    • Creating public referenced in the inspector is only a solution if you know beforehand all the gameObjects of interest, plus it forces public references.
    • Using Object.FindObjectsOfType<T>() is even slower than GameObject.Find() and does not allow direct searching by name.

    Am I missing some simple method for referencing GameObjects in a way that allows casting to specific types?
     
  2. Antypodish

    Antypodish

    Joined:
    Apr 29, 2014
    Posts:
    10,780
    Are you familiar with GetComponent ?
    Code (CSharp):
    1. Camera myCamera = myCameraGameObject.GetComponent <Camera> () ;
     
    Joe-Censored and FBones like this.
  3. Madgvox

    Madgvox

    Joined:
    Apr 13, 2014
    Posts:
    1,317
    Alternatively, I architect my projects in a way that means that I never have to use GameObject.Find. Besides the speed issue, being reliant on string keys is a fast track to Error Town, with additional exits to Sphaghettiville and the city of Unexpected Behaviour.
     
  4. CodeSmile

    CodeSmile

    Joined:
    Apr 10, 2014
    Posts:
    6,005
    Agree with Madgvox. The use of Find*() methods is bad practice. Use inspector references. Where you can‘t (ie prefab that doesn‘t contain the needed reference) you can use dependency injection.
     
  5. Suddoha

    Suddoha

    Joined:
    Nov 9, 2013
    Posts:
    2,824
    First of all, the generic version of GameObject.Find would not be
    Code (csharp):
    1. GameObject<TComponent>.Find("name");
    but
    Code (csharp):
    1. GameObject.Find<TComponent>("name");
    Now, if you think about it, how would that be any superior (or even far superior) to the alternatives you mentioned?
    In order to be more efficient with that, you'll need additional caches which increase memory costs and maintenance/update costs per frame.

    Without additional caching, the suggestion wouldn't be any faster than the API that is already available to you, because it will be a combination of different methods.

    That said, if you still want to search by name (which I also do not recommend for a variety of reasons), you can simply use GameObject.Find and do what @Antypodish has suggested:
    That's effectively what you're looking for, and you can also write a utility method for your convenience:

    Code (CSharp):
    1. public static class Utils
    2. {
    3.     // generic constraint can also be UnityEngine.Component, though that wouldn't allow interfaces
    4.     public static TComponent FindComponentByObjectName<TComponent>(string name) where TComponent : class
    5.     {
    6.         var go = GameObject.Find(name);
    7.         return go == null ? null : go.GetComponent<TComponent>();
    8.     }
    9. }
    Anyway, instantiation of an object has to be done somewhere. In order to be type-safe and to prevent name-errors, you should simply save the reference to the object when it's instantiated. Alternatively, if you want to have even less reference management, you can also try out dependency injection, as suggested by @SteffenItterheim.
     
  6. FBones

    FBones

    Joined:
    Aug 28, 2016
    Posts:
    73
    I didn't realize you could use GetComponent to get GameObjects [as opposed to Components attached to GameObjects], but it would be nice to have a way to find GameObjects that you may not know the location of in the hierarchy, and do it in a way that lets you use their actual type.
     
  7. FBones

    FBones

    Joined:
    Aug 28, 2016
    Posts:
    73
    Thanks!
     
  8. FBones

    FBones

    Joined:
    Aug 28, 2016
    Posts:
    73
    Object.FindObjectsOfType<T>() polls all objects, so it should be slower than an analogous version that only searches GameObjects.
     
  9. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,537
    No, it gets components.

    'Camera' is a component attached to a GameObject.

    What do you mean find a GameObject of its actual type? It's type is GameObject. I'm unaware of any types that inherit from GameObject. I am aware of components (like 'Camera') which are ATTACHED to GameObjects.

    The idea of finding a GameObject as its actual type doesn't make sense. It is its actual type... it's a GameObject.
     
  10. kdgalla

    kdgalla

    Joined:
    Mar 15, 2013
    Posts:
    4,639
    No, you use both methods together. first you find the game object, then you find component.
     
  11. Suddoha

    Suddoha

    Joined:
    Nov 9, 2013
    Posts:
    2,824
    Well, I can only redirect to my post.
    The feature you were talking about would either find all objects of type and then check names, or the one that I had already described earlier using the utility method, unless you avoid it altogether and keep track of your objects on instantiation.
     
  12. nilsdr

    nilsdr

    Joined:
    Oct 24, 2017
    Posts:
    374
    I am familiar with the concept of dependency injection, but what would that look like in a unity context? Can you point me to an example?
     
  13. Fido789

    Fido789

    Joined:
    Feb 26, 2013
    Posts:
    343
    Look at Zenject.
     
  14. FBones

    FBones

    Joined:
    Aug 28, 2016
    Posts:
    73
    The Unity documentation said that "Components" are things that attach to GameObjects, since the Camera (as a hierarchy item) has things attached to it, I thought it was a Game Object.

    I now see the source of my confusion is that many of the GameObjects that you can create (using the GameObject menu) have Components of the same name. (Example, you create a GameObject Canvas by using the GameObject Menu, and it adds an element called "Canvas" to the Hierarchy... and I hadn't caught that it also had a Canvas Component attached. Same thing for Cameras, Images, etc.)

    Now that you have set me straight, things are more straightforward. I don't need GameObjects of "the correct type," I just need Components... and GetComponent<T> does that already.
     
  15. AndersMalmgren

    AndersMalmgren

    Joined:
    Aug 31, 2014
    Posts:
    5,358
    We have written an entire game without the use of any Find or Tags and other fragile stuff. Its very possible. Use dictionary lookups, Communicate with a event bus, etc, etc