Sometime string comparison is the only way, but is it really???

To start off, I’m talking about caching references to other GameObjects on Start/Awake (whether it be children of the current obj, or a completely different one).

I am really trying to make my code re-usable across projects, and anytime I have to resort to comparing/looking for a GameObject by string I get a sick feeling in my belly!

I try to NEVER use Find(), ever. I try to use FindWithTag() sparingly (can’t have a different tag for every object…). I try not to use drag drop in the inspector, other then when I’m doing quick prototyping. So the next best thing I thought of using is:

// Example, caching references to specific children
private GameObject ref01, ref02, ref03;
foreach( GameObject go in GetComponentsInChildren<GameObject>() )
{
    if( go.name == "myReference01" ) ref01 = go;
    else if( go.name == "myReference02" ) ref02 = go;
    else if( go.name == "myReference03" ) ref03 = go;
}

// I find the above better then (especially when looking for children):
ref01 = GameObject.Find("myReference01");
ref02 = GameObject.Find("myReference02");
ref03 = GameObject.Find("myReference03");

But I still don’t like it. I don’t like using strings.
Now, for a lot of my classes, I use self-reference assignment, which I do like, a lot. But I can’t have a different class for each GameObject I need a reference to! When the object in question has a script already attached this isn’t a problem, but what if it doesn’t? What if I only need a reference to it in order to access its GUITexture or GUIText, which are the only components attached to the GameObject in question?

In those situations, I can’t think of any way other then the ones I mentioned above (drag drop, string compare, ect…).

I’m really curious to find out if you guys have a better solution for these cases?! If you wouldn’t mind sharing your workarounds, tips tricks, please do, you’ll make me that much closer to choosing a solution to use everytime I come across this problem :slight_smile:

Thanks for your time!

Stephane

I know you say you don’t like to use Find(), ever. But have you considered transform.Find()? it is different than GameObject.Find() in that it only searches children, and not the entire scene.

// Example, caching references to specific children
//changed the type to Transform to avoid possible null exceptions below
private Transform ref01, ref02, ref03;

ref01 = transform.Find("myReference01");
ref02 = transform.Find("myReference02");
ref03 = transform.Find("myReference03");

Or if you don’t care about having the references specifically and correctly named:

// Example, caching references to all children
private List<GameObject> references = new List<GameObject>();

foreach( Transform trans in transform )
{
   references.Add(trans.gameObject);
}

or if you do want them named, you could try:

// Example, caching references to all children
private Dictionary<string,GameObject> references = new Dictionary<string,GameObject>();

foreach( Transform trans in transform )
{
   references.Add(trans.gameObject.name, trans.gameObject);
}

Ah! No I have not considered this…never even crossed my mind! But in your comment you say “changed the type to Transform to avoid possible null exceptions below”, and I don’t understand why this would avoid them, won’t it return null if “transform.Find(“myReference01”)” isn’t found?

Thanks for the Dictionary example, this will come in very handy as well.

I watched a few videos from the Unite events, and in one of them, the speaker said that they never use Find() in any of their code, in order the keep it more re-usable across project. When they do come across code that uses Find() they get rid of it and use workarounds. But I can’t see how that’s possible…at one point or another you will need to look for specific game objects by name, tag or ID, unless the object in question has a custom component attached. Are they being extreme?

Stephane

I never need to look for specific game objects by name, tag or id. What situation are you running into?

Ok, for example, consider the following hierarchy:

Managers (parent GameObject)
— ControlsManager
— HUDManager

HUD (parent GameObject)
— laptime
— laps
— hud_controls (has “HUD_Menus” and “SwitchCameras” components attached)
------- switch_camera (only has a GUITexture attached)
------- menu (only has a GUIText attached)

Now, “HUDManager” looks for and caches references to needed GameObjects from HUD, which also looks for and caches it’s own GameObjects/Components…

First let’s cache some references in HUD_Menus, attached to hud_controls GameObject:

// Code from the "HUD_Menus" component attached to "hud_controls" GameObject, a child of "HUD"
private GameObject menu;
private GUIText menuText;

void Awake( )
{
    menu = transform.FindChild("menu");       // Need the name of "menu" gameObject to return the correct GUIText component.
    menuText = menu.GetComponent<GUIText>();
}

Now cache hud objects references in HUDManager, attached to Managers GameObject:

// Code from the "HUDManager" component
private GameObject mainHUD;
private GameObject controlsHUD;
private GameObject menuHUD;
private GameObject camHUD;
private GameObject fpsHUD;
private GameObject debugHUD;

void Awake( )
{
    mainHUD = GameObject.FindWithTag( "HUD" );

    controlsHUD = mainHUD != null ? mainHUD.FindChild( "hud_controls") : GameObject.Find( "hud_controls" );
    menuHUD = controlsHUD != null ? controlsHUD.FindChild("menu") : GameObject.Find( "menu" );

    camHUD = mainHUD != null ? mainHUD.FindChild( "camera-switch") : GameObject.Find( "camera-switch");
    fpsHUD = mainHUD != null ? mainHUD.FindChild( "FPS") : GameObject.Find( "FPS");
    debugHUD = mainHUD != null ? mainHUD.FindChild( "debug") : GameObject.Find( "debug");
}

Keep in mind, none of the objects above - except for HUD and hud_controls - have any scripts attached to them, so I can’t do any self-reference assignment like “HUDManager.Instance.menuHUD = this;”. Also keep in mind that I want to avoid using Drag&Drop in the editor to assign references as much as possible.

I am really looking forward to read what you have to offer as alternate solutions, because I have been conflicted about my methods above to cache references to other GameObjects for a while now :slight_smile: Maybe there’s something I’m not thinking about, or doing this all wrong?

Thanks for your time guys!

Stephane

Yes it will return null if the transform isn’t found. Transform.Find(…) returns Transform objects, and to get the GameObject, you’d have to call .gameObject on them which would throw the null exception. Instead of writing the code to check for null before grabbing the gameObject, I chose to simply change the type to Transform. Granted you would still have to check for null down the line because those could indeed be null references.

You’d have to ask them for more specific reasoning.
I’d guess that “more re-usable across a project” means that when you do find objects by name or id, that puts that specific name or id into that piece of code, and thus can’t be used elsewhere.
Find, GetComponent, and those types of methods are also expensive, and should only be done in start or awake methods, or in other methods that do not get called repeatedly.

Cool, thanks for the explanation Democre!

Why? This appears to be the perfect example of when to use it.

Because everytime you use drag drop, you have to remember to manually re-drag those references when you make a new scene, or when you start a new project and want to use the same classes. When I first started using Unity and learning programming, I was mostly using drag drop in the Editor and loved that feature. But the bigger my code base grew and the more things I had going on in my scene, the more problems I ran into by forgetting to re-drag and drop, and I was getting errors everywhere.

When it’s on the same GameObject, yeah it works pretty good because the Prefab u make out of it keeps the references to the object’s children. But when you drag references from other GameObjects, the Prefab can’t keep track of them, and you have to re-drag those references everytime you drop that prefab into a new scene.

  1. Prefabs.
  2. So remembering to re-drag some instances is complicated, but remembering to create and correctly name the sub-instances is perfectly fine?

I’d rather drag some instances then try to remember a naming system for all my GO’s.

Well, prefabs don’t maintain connections to anything external in the scene. This can be an issue, for example, when creating a grid of buttons for a level-select screen - you need to manually re-connect them to the manager script. Also, having a reference to every sub-object can seriously clutter up your interfaces, and in many cases would be false modularity.

I don’t understand why using GameObject.Find would be inherently any less portable than referencing by name in general though. It is a slow call, so it should be used only once and the references stored, but there’s nothing “unclean” about it.

I also very rarely look for anything by name, and avoid it like plague because resorting to that can so easily lead to bugs or errors, and makes my code dependent on things artists do in the scene and vice versa. In short, it makes life difficult in a production environment, and in that context I can’t think of a case where none of the alternatives are superior.

Firstly, prefabs go a long way. Set up your repeatable GameObject hierarchy and script references once, save the prefab, and be happy.

Secondly, where a prefab isn’t suitable because of variable configurations, finding components in children by type is incredibly useful and means you don’t have to make assumptions about configuration (except where it’s functionally necessary) or naming.

Thirdly, you can create required components through code, which is very useful in some circumstances. If you can procedurally configure something correctly then that removes the possibility of mis-configuring something in the Editor and makes everyone happier.

Fourth, for once-off stuff, hooking it up manually in the Editor works a treat.

Fifth, you don’t always have to have scripts find child components and directly access them. It can work the other way around, or you can have entirely different systems that work for your application’s needs. For instance, components can register to and deregister from a manager with OnEnable and OnDisable and then be accessed indirectly through that. Or you can have them subscribe to an event system and internally react and respond to events as required (this is particularly useful in cases where you don’t want to have to make the object where an event originates aware of every potential object which might be impacted by the events).

Well that’s the whole reason of this thread…are there any other ways to cache references without drag drop and without needing to know the names of those specific game objects.

The self-assign solution is perfect since you never need to drag drop in the editor, and you never need to know the names. But this is only possible when the objects in question have a script attached to them.

Ok let’s forget the drag drop for a minute, and assume that you can’t use it, because you’re creating an object dynamically, and need to cache some references on the object’s Awake()/OnEnable() function. Now obviously you can drag drop, so you HAVE to look them up by name or tag…unless there are any other ways to achieve this that I am not thinking about?

Yep, couldn’t agree more…

Already doing that where it fits, yes they do go a long way and it took me a while to wrap my head around that! :slight_smile:

Yes, but let’s say you have 2 objects, menu1 and menu2, and both objects have a GUIText attached (and nothing else). Now you need to cache 2 references, 1 for each object, from another “Managers” object. The only way to do that would be to:

// From Managers.cs attached to Manager GameObject, separate from menu1 and menu2
myGUIText = GameObject.Find("menu1").GUIText;
// or 
foreach( GUIText gt in FindObjectsOfType( typeof(GUIText)) as GUIText) 
{
    if( gt.gameObject.name == "menu1") myGUIText = gt.GUIText"
}

Am I forgetting something? And since menu1 and menu2 are different objects, dragged references on a Prefab wouldn’t stick.

I think this is probably the best solution out of them all! I never thought of doing it this way, might not work for everything, but I can definitively think of a few places where I could use that…

As long as the Prefab keeps the connection, then yeah I’m all for it :slight_smile:

Yes this is great, and I already started to change my code in multiple places that can benefit from this method. But in the case of menu1 and menu2 - which don’t have scripts attached - I can’t do that.

Just to clarify, when you say “components can register to and deregister from a manager with OnEnable and OnDisable and then be accessed indirectly through that.”, what do you mean exactly? Here’s how I am currently “registering” to my Manager:

public GUITexture menuTexture;

void OnEnable()
{
    Managers.Instance.MenuHUD = this;
    Managers.Instance.MenuGUITexture = menuTexture ? menuTexture : transform.FindChild("menu").GUITexture;
    // ect...
}

void OnDisable()
{
    Managers.Instance.MenuHUD = null;
    Managers.Instance.MenuGUITexture = null;
}

Is this what you’re talking about?

Thanks a bunch for your help!

Steph

If you’re creating the object dynamically then you’re being given a reference to it when you do that - either from “new GameObject()” or from “gameObject.AddComponent…”, depending on what you’re dynamically creating. When you instantiate a prefab you get a reference to its root GameObject in the same way.

All I can really say without knowing what you’re trying to do is to read the Unity documentation about finding GameObjects and Components. It covers the first four of the approaches I mentioned above, and the fifth is really a catchall for “ways you learn to manage objects in general programming outside of Unity” (which are, of course, just as applicable within it, they’re just out of scope of the tutes).

What are you using those GUIText references for? I think this is a fundamental design issue in how you’re doing things. By the looks of it you’re doing different stuff with each anyway (otherwise stash them in an array?), so why not create separate scripts for each and attach them directly? That would solve a lot of your issues, reduce code, and mean that you could look up those components by type rather than look up GameObjects by name and mess with them from a remote script (which is what it appears that you’re doing).

I also don’t get what you mean by dragged refs on prefabs not sticking. Refs in a prefab should only point at other objects or components within that prefab.

What prefab? I said that was for once off things, so there’s no reason to use a prefab. Just set it up in your scene and leave it alone.

All I can suggest here is learning about how event systems work. Perhaps start with the Publish Subscribe Pattern: http://en.wikipedia.org/wiki/Publish–subscribe_pattern. This is the kind of thing generally taught in the 2nd or 3rd year of a computer science program, so if you’re learning as you go it’ll be a bit of a challenge.

Then I have some sort of controllers that are responsible for this. Either the spawner or some global access classes are usual suspects.

Ahh, yes, that’s one I should have added to my list of approaches - having a “factory” object of some kind which creates objects and places references where they’re needed.

A piece of advice, though: be as consistent as you can in a given project. Don’y use a dozen different methods to handle object references, lest you make your life a nightmare later on trying to track things down.

I think you might be right about the design issue. What I am doing is instead of having 2 separate scripts (1 for each object with a GUIText), I have 1 “master” script which is attached to the parent of both menu1 menu2. In my master script, I need references to the GUIText because I do things with them, like for example hide one or the other depending on what’s happening.
It would definitively be easier if like you said, each object had its own script, that way I could lookup by component, or have them register themselves…but I like having a parent object which acts kinda like a “manager” for its children, along with 1 master script on it. Maybe that kind of design doesn’t work well, I’ll have to re-think my approach!

For the refs on prefabs not sticking, it’s because they do not point to other objects or components within that prefab. Sounds like this might be a bad design issue as well.

Thanks for the link, I’ll definitively read through it and learn a thing or 2!

Got it…again this points to a design issue on my part. Thanks for all your help guys, I’ll go apply it and see what I come up with.