- Home /
Best practice for finding game objects,Best practice for finding gameobjects
Hi Team,
I have a UI I'm setting up that I want to programmatically change content. See attached picture illustrating the hierarchy of objects.
I have a script on the BackgroundPanel which searches for individual items, for example, the textbox "heading", the textbox "LeftTextBox", and the object "ObjectRight" which may have a video player, a render image, or some other content (currently to be defined, which will be created later).
I currently have the following code to find each item by name - which obviously is totally static. It works, but what I'd like to know is - is there a "best practice" or better way to do this?
public class UIContentController : MonoBehaviour
{
Text leftText;
Text linkBox;
GameObject objectRight;
GameObject heading;
// Use this for initialization
void Start () {
heading = transform.Find("Heading").gameObject;
leftText = transform.Find("LeftTextBox").GetComponent<Text>();
linkBox = transform.Find("GeniusLink").GetComponent<Text>();
objectRight = transform.Find("ObjectRight").gameObject;
}
}
If I use the code
leftText = GetComponentInChildren<Text>();
Then I find the first text component, without finding the correct text component.
Thanks in advance for any assistance.
,Hi Team,
I have a UI I'm setting up that I want to programmatically change content. See attached picture illustrating the hierarchy of objects.
I have a script on the BackgroundPanel which searches for individual items, for example, the textbox "heading", the textbox "LeftTextBox", and the object "ObjectRight" which may have a video player, a render image, or some other content (currently to be defined, which will be created later).
I currently have the following code to find each item by name - which obviously is totally static. It works, but what I'd like to know is - is there a "best practice" or better way to do this?
public class UIContentController : MonoBehaviour
{
Text leftText;
Text linkBox;
GameObject objectRight;
GameObject heading;
// Use this for initialization
void Start () {
heading = transform.Find("Heading").gameObject;
leftText = transform.Find("LeftTextBox").GetComponent<Text>();
linkBox = transform.Find("GeniusLink").GetComponent<Text>();
objectRight = transform.Find("ObjectRight").gameObject;
}
}
If I use the code
leftText = GetComponentInChildren<Text>();
Then I find the first text component, without finding the correct text component.
Thanks in advance for any assistance.
Answer by JVene · Jul 11, 2018 at 05:01 AM
You've basically figured out what "Find" does, and that it's not particularly thrilling.
You've also identified what I find all but useless about GetComponentInChildren.
Find also doesn't descend into children, so you have to manually iterate through a hierarchy when that becomes an issue.
However, not all is lost if you're looking for something better, which does depend on your use case.
To preface, the assumption I'm making with the suggestion that follows is that UIContentController is a singleton (there's only 1, and it could be considered a "global" entity, either available to all, or supervisory such that lots of objects look up to it for centralized control).
I'd have the kids call home.
Instead of this parental, supervisory object searching the children for the components, I would consider having the kids do that. For this to work any better, you'd need some quick, simpler means for the kids to find this parent script (UIContentController). It is not practical to assume the order in which start is called among objects, but it seems to me (and this might be documented, but I've not read it) that object starts are called from the lowest children upwards. It is likely UIConentController's start function won't be called before the children's start script executes (so you can't rely upon UIContentController's start script to initialize the static UICC for them). The script, UIContentController, will exist, however, and can be found.
So, I'd use "lazy initialization" of a global reference to UIContentController. As odd as this may seem, put a:
static private UIContentController UICC = null;
In your class. This is, basically, a globally accessible value, but it starts out null (you don't have an instance to one at this point - the very early initialization of the code, and remember I said the assumption is that there will only be 1).
It is private in this example so that only UIContentController can assign it (that keeps it under control).
Next, I'd suggest a static public member function like:
static public UIContentController GetUICC() {...}
The purpose of this function is simply to return UICC, but before doing that check to see if it is ==null. If it is, this is a call from the first child trying to register. In the chilren's start function, they can:
UIContentController uicc = UIContentController.GetUICC();
Then use the uicc to register themselves (more in a moment).
For the first call made by a child to GetUICC, the static UICC will be null. The function will have to search the hierarchy for the object that owns the UIContentController script component, get the component and set UICC to that (so that all subsequent children will find it much faster).
Since I have no idea where your UIContentController is located, I can't write a precise example, but it likely starts at the root, checks it's children (and if you know for certain the owner is at the top level, that might be all that needs to happen). If it is owned by a subobject (child of a top level object), you might already know where that is, so you might be able to tune a "find" command to go right to it.
This approach means a "find" will only have to happen once. Subsequent calls from children will use the cached static UICC.
So, when the "heading" object start function executes, it can call the parent with something like:
UIContentController.GetUICC().SetHeading( transform.gameObject );
The "ObjectRight" can do something similar.
LinkBox might:
UIContentController.GetUICC().SetGeniusLink( GetCompoment<Text>() );
In this way, the kids call home when they start, the parent is only "found" once, and all is a bit faster.
Of course, I'm using member functions in UIContentController as an example method, but you can choose as you prefer (properties, direct manipulation of UIContentController members even though that's considered naive).
This approach has one significant advantage. If you move the children or rename them, you don't have to change anything. With the approach you're using, where UIContentController finds the kids, if, say, GeniusLink were later made the child of a child, you'd have to change UIContentController's start function to find it in the new location. With this approach, the kid calls home no matter where it goes.
Hi JVene,
Thank you for your response, that is far more comprehensive and detailed than I was expecting (having read some other Q&A posts here!)
Reversing my perspective of "children registering themselves" rather than "parent finding children" is something I hadn't thought about, but can see why you've raised it!
I currently haven't set up the UICC script as a unique, single only script (I was intending on adding the script to all the prefab UI objects I've created, probably 10ish) but I can see that your way is going to work better (and be more modular) in the long run (when I add more UIs and anything else in the future)
Thank you so much!
You're welcome ;)
If you're are going to have about 10 of these, then to keep the idea intact, I'd suggest you consider UIContentController as a base class, an create 10 small classes that derive from it, each named to identify what makes each unique among the 10, where the "GetUICC" is perhaps an abstract function in the base, and the static member is held by the most derived (and therefore a unique name for each static 'UICCxxx', so they're separate.
I was playing around with different data structures last night and thought inheriting from a base class (the UIContentController) would be a better way - I'm glad I was on the right thought path there!
However as all the Unity classes inherit from the $$anonymous$$onoBehaviour class, am I right in assu$$anonymous$$g I'd have to create UIContentController as an interface? So I can inherit both $$anonymous$$onoBehaviour AND UIContentController?
So for example, I'd have the UIContentController as an interface, then on each of the top level UI game objects, I'd have a UICCExample script which inherits the interface, that the child game objects would register to?
EDIT: Never $$anonymous$$d! reading a bit more on inheritance, I can see how this will work (As I said, I'm a beginner here!) Thanks!
Your answer
Follow this Question
Related Questions
Design Best Practice Question 1 Answer
Best practice : Image.enable vs go.SetAactive() 1 Answer
using Contains(gameObject) to find and destroy a gameObject from a list 2 Answers
Projectile not 'projecting' because i have to use "var xxx : GameObject = Instantiate" 2 Answers
Position and rotation of a game object acting wired 0 Answers