Search Unity

Using GameObject.Find()

Discussion in 'Scripting' started by Jason210, Mar 22, 2018.

  1. Jason210

    Jason210

    Joined:
    Oct 14, 2012
    Posts:
    131
    I'm doing some exercises (that I've created for myself) with script communication and need a little advice. I understand that Find() is quite heavy and not something that should be called every frame, so I wanted to set all these up and cache them before the program ran. Firstly, I tried assigning Find() as fields in the class, but this apparently is not allowed in C#.

    Code (CSharp):
    1. public GameObject cylinder = GameObject.Find("Cylinder");
    The problem is that if I declare them in Awake(), then they are only available within the scope of Awake(), so the only way I can see to do this is to declare the objects first as fields in the public class, and then assign them using Find() in Awake().

    Code (CSharp):
    1. // field
    2. public GameObject cylinder;
    3.  
    4. void Awake()
    5.     {
    6.         cylinder = GameObject.Find("Cylinder");
    7.     }

    Is this the correct approach, or is there another approach?
     
  2. methos5k

    methos5k

    Joined:
    Aug 3, 2015
    Posts:
    8,712
    It will only find active game objects.

    While technically correct, it would be even better to avoid 'Find' altogether, if you can. That can be a new fun exercise for later :)
     
    Jason210 likes this.
  3. Jason210

    Jason210

    Joined:
    Oct 14, 2012
    Posts:
    131
    Thanks. Why is it better to avoid Find()? I see there is a method called FindWithTag,(), but other than that how else might I approach this problem?
     
  4. methos5k

    methos5k

    Joined:
    Aug 3, 2015
    Posts:
    8,712
    It's slow -- it checks every game object in the scene. It can't find inactive game objects. It's error prone (using strings), harder to update if you change the game object name..etc.

    You can create references to game objects and drag and drop them in the inspector. For game objects that are instantiated, you can pass along needed references to them, and there are more ways, too.
     
    Jason210 likes this.
  5. Nigey

    Nigey

    Joined:
    Sep 29, 2013
    Posts:
    1,129
    One potential ruleset is:

    1. When objects are already in the scene and want to communicate between each other, set the fields as 'public' or '[SerializeField] private/protected' to make them visible in the Component's inspector window. From there you can drag and drop them in. When you do that the scene will already have the reference at the start of play and then you can use as you will.
    2. When objects are created dynamically after the beginning of play, a good way, rather than using GameObject.Find("SomeString"), is to make sure the object that's instantiating the GameObject is also passing the reference to whichever class needs it. Something like:

      Code (CSharp):
      1.     public class ExampleNeedingReference : MonoBehaviour
      2.     {
      3.         public GameObject somethingItNeeds;
      4.         public ExampleCreator creator;
      5.  
      6.         private void Awake()
      7.         {
      8.             creator.CreateObject();
      9.         }
      10.     }
      11.  
      12.     public class ExampleCreator : MonoBehaviour
      13.     {
      14.         public ExampleNeedingReference needyClass;
      15.         public GameObject prefabToCreate;
      16.        
      17.         public void CreateObject()
      18.         {
      19.             needyClass.somethingItNeeds = Instantiate(prefabToCreate);
      20.         }
      21.     }

      Generally you'll find the classes instantiating these objects will naturally have some relationship with those needing reference to the classes.
    3. From there you can look into have a overall structure of things like the Composite Design Pattern, but progress not perfection, right?
     
    Jason210 likes this.
  6. Jason210

    Jason210

    Joined:
    Oct 14, 2012
    Posts:
    131
    When you say "drag and drop", do mean clicking on the circle with the dot in the middle to select an object?
     
  7. StarManta

    StarManta

    Joined:
    Oct 23, 2006
    Posts:
    8,775
    There is never a time when GameObject.Find is the best solution, with the possible exception of an occasional debug or editor tool function (though even in those cases it's been years since I've seen a legit use for it). Literally the only reason to use GO.F is because you haven't yet learned the better way to get a reference to the object you want.

    So I wrote this article, which should help you learn when to use what technique.
     
    Ryiah and Jason210 like this.
  8. Joe-Censored

    Joe-Censored

    Joined:
    Mar 26, 2013
    Posts:
    11,847
    Outside of performance, another reason GameObject.Find is just bad is because it depends on the name of the GameObject. You may change the name of the GameObject and break your code, and when objects are instantiated of the same name as an existing object they get renamed with (1) after them, so again break a Find call. You might think it is silly that you would rename the gameobject when you're using its name in code, but it is common to forget why something like a gameobject name is important 6 months after you wrote code for it.

    Finding an object based on tag instead of name still has performance issues (though is somewhat better still than simply Find), but is less prone to accidental breakage as you would rarely accidentally rename a tag and multiple objects can be in the same level of the scene hierarchy with the same tag. A tag is also far more obvious that it is an important identifier for finding the object in comparison to the gameobject's name. .
     
    Ryiah and Jason210 like this.
  9. Jason210

    Jason210

    Joined:
    Oct 14, 2012
    Posts:
    131
    Thanks - great article.
     
    omgdorian likes this.
  10. Nigey

    Nigey

    Joined:
    Sep 29, 2013
    Posts:
    1,129
    That's one way of selecting it, however you can actually click+drag any object from the hierarchy window to the inspector as well.
     
    Jason210 likes this.
  11. ThermalFusion

    ThermalFusion

    Joined:
    May 1, 2011
    Posts:
    906
    There is also some nice 'hidden' features with drag and drop references.
    Say you have an object with some random script on it. This script has a "public BoxCollider verySpecificBox" field. It also has 3 BoxCollider components on it. You know the middle one is your special one, but how do you drag the correct one into the verySpecialBox field of your script?
    You simply drag and drop the BoxCollider header of your special box in the inspector!
    If your script is on another object, you'll have a hard time doing this, because the inspector will change if you select another object. How do you solve this?
    Simply open a new Inspector and lock it on one object then select the other object to view inspector of both objects at once, then just drag and drop from one to the other.
     
    Nigey and Jason210 like this.