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): 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): // field public GameObject cylinder; void Awake() { cylinder = GameObject.Find("Cylinder"); } Is this the correct approach, or is there another approach?
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
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?
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.
One potential ruleset is: 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. 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): public class ExampleNeedingReference : MonoBehaviour { public GameObject somethingItNeeds; public ExampleCreator creator; private void Awake() { creator.CreateObject(); } } public class ExampleCreator : MonoBehaviour { public ExampleNeedingReference needyClass; public GameObject prefabToCreate; public void CreateObject() { needyClass.somethingItNeeds = Instantiate(prefabToCreate); } } Generally you'll find the classes instantiating these objects will naturally have some relationship with those needing reference to the classes. From there you can look into have a overall structure of things like the Composite Design Pattern, but progress not perfection, right?
When you say "drag and drop", do mean clicking on the circle with the dot in the middle to select an object?
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.
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. .
That's one way of selecting it, however you can actually click+drag any object from the hierarchy window to the inspector as well.
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.