Access object hierarchy members

I am trying to access an ancestor object in my scene hierarchy. The target was created with a prefab (e.g. named Person). It is not the root of the hierarchy, and there will be multiple instances of the prefab (e.g. Dick, Jane). In a true “object oriented” object model, using my example, I’d find the ancestor object of type Person. However, it seems that Unity does not have such a strong sense of an object model. Is there some way to achieve this that I have overlooked?

BTW: I know I can do this with “tags”, but that would be classifying the object twice: once with the tag (Person) and once by its prefab “class” (Person). And, of course, we all want to practice the DRY principle (Don’t Repeat Yourself).

Thanks
–jon

A game object is just a container of components. It’s the components you care about. If you need a component of a child game object, you use GetComponentInChildren. If you expect there to be multiple, components of that type, use GetComponentsInChildren instead and run through for the correct instance.

This of course implies you put a component on your prefabs which contain the necessary information to be able to pick out the right one.

2 Likes

Thanks, but i am concerned with accessing members of the game object tree, not the components in a given game object.

You access the parent and children through the Transform component.

I just wanted to rant how much I hate the fact that the scene doesn’t have a true root element. It’s a forest instead (aka multi-root system), and to get to the top-level objects you have to do

SceneManagement.SceneManager.GetActiveScene().GetRootGameObjects()

which is such a horrible thing that just breaks the recursive tree beauty apparently out of spite.

OP:
The actual hierarchy is encoded through transforms, not GameObjects, as explained above. It’s a bit weird, given that no GameObjects ever exist without a transform, but you’ll get used to it. I wouldn’t have a problem with it, if it wasn’t for the above. Unity is full of illogical design decisions like that. Iterative design by committee.

I mean don’t get me wrong, if you need to traverse the hierarchy trees, you’re probably doing something wrong(*), and that’s probably why no one seems to have an issue with the non-extant root object (don’t be fooled by Transform.root btw, that just gives you a topmost parent). I also didn’t need this ever until recently.

But if you need this then you have to do something like this

static public GameObject FindInScene(string path) {
  if(string.IsNullOrEmpty(path)) throw new System.ArgumentException($"{nameof(path)} is empty or null");

  // pops the first item
  if(!path.SplitFromStart('/', out var name, out path)) { // returns false if '/' wasn't encountered
    name = path;
    path = null;
  }

  var rootObjs = getRootObjects();

  foreach(var go in rootObjs) {
    if(match(go.name, name)) {
      if(string.IsNullOrEmpty(path)) return go;
      var nested = go.transform.Find(path); // the rest of the path works as is
      if(nested != null) return nested.gameObject;
    }
  }

  return null;

  static GameObject[] getRootObjects() => UnityEngine.SceneManagement.SceneManager.GetActiveScene().GetRootGameObjects();
  static bool match(string a, string b) => System.String.Equals(a, b, System.StringComparison.InvariantCultureIgnoreCase);
}

I just find it crazy that it’s so complicated to have a fully qualified path to some object, and simply do this.transform.root.Get(path);

There should be a 1 to 1 between a scene and a root transform. I bet it would simplify things on their end as well. Or I dunno, call me spoiled.

Edit:

  • The reason is why this is wrong is because it’s slow (especially when used on a hot path). There are half a dozen of smarter ways to latch onto an object in your scene without ever having to look for it via hierarchy, because you really want to store a direct reference to it in the runtime, instead of mulling the strings, producing garbage etc. If you must search for something, do it exactly once (i.e. in Awake) then store the result for later.

If you frequently access children or parents of an object (which isn’t bad per se, it’s commonly used), make sure to at least cache the OG transform locally, and work from there. And think of GameObjects just like Spiney suggested, as containers for components, they are literally just the main access point for all of the components (custom or otherwise), but usually not the main thing that you’re after.

I like to think of it more as a mosh pit… it includes everything hanging around the scene AND everything else not even in the scene, and it’s kinda up to you to … well, not get thrown out by the mosh pit bouncers!!

Yes, you might get stabbed on the mosh pit floor but that’s a small price to pay for such great dance music.

Also, if there was some kind of top level root, it might begin to impair Unity’s amazing ability to just graft any multiple number scenes (or partial scenes) together to make a composite or more-involved game.

2 Likes

Hahaha you know what, I’ll keep this awesome analogy in my mind for the next time I get angry at it.

Well, I’m not sure about that. You can do all kinds of things with a top level root transform, and I never said it’s a game object, just a transform. For example you can merge the children, and you’d pretty much get the same thing, it’s just a continuity thing, where each object’s Transform.root simply refers to this one and only singleton root.

But they most definitely won’t change it any time soon, that’s for sure.

1 Like

Yeah, I am definitely getting a design by committee vibe in playing around with unity. Seems that there is no real answer to my original concern about being able to navigate the tree by find the nearest ancestor/descendant by prefab “class”. Oh well. I ended up using tags on my game objects that name the “class” of my game objects (as I would if these were OO objects), and then implementing recursive utility functions for findParentByTag() and findChildByTag().

Right, but components are what let you discern one game object or another. Otherwise the only thing you have to discern one game object from another is their name, layer (which is for physics), or tag (which are effectively a legacy feature this point). So if you want a certain game object, put a component on it, and look for that component. When you find the component, you have the game object you care about. It’s the standard Unity way of doing things.

Because there is no such thing as a “prefab class”. Prefabs are just game objects, nothing more. If you want to find a specific object, you do it via it’s components.

1 Like

You could place an “artificial” component to prefab’s root, like

public class PrefabRoot : MonoBehaviour {}

Then use it like marker

PrefabRoot getPrefabRoot() {
  var xf = this.transform;
  while(xf != null) {
    if(xf.TryGetComponent<PrefabRoot>(out var found)) return found;
    xf = xf.parent;
  }
  return null;
}
// in some child object
var root = getPrefabRoot(); // store once
var rootObject = root.gameObject; // ofc check for null
var rootTransform = root.transform;
// or something along these lines

However, you could try to make a component that autoregisters the prefab to some collection on Awake. Depends on what you’re doing exactly.

There are many ways to do this automatically or semi-automatically, but I would always prefer anything over tags, because tags aren’t set through code so it’s easy to oversee the relationships in some cases.

2 Likes

OK. That makes sense and seems a reasonable alternative to “tags”, although tags do not require the creation of “extra” object tree nodes and scripts.

In general I find the twilight zone existence between Unity programming and Unity editor to be rather ad hoc and unsettling. I find myself wanting to avoid dragging and dropping “references” between objects in the editor and then, later, not being able to readily find out what is connected to what (and I’m just writing little baby game pieces to learn the platform). So, I am exploring doing the referencing programmatically, hence to need to navigate the game object tree. But, since tree “objects” are not always real instances of some user defined class, with a user specified name, it seems that “tags” are the simplest surrogate.

It’s just a composition approach as opposed to the bog standard ‘OO’ approach. Your thinking is more in terms of how Unreal and Godot work, where you directly inherit from types, and use said types directly in your scenes.

In Unity, GameObject is a sealed type. We can’t inherit from it. Instead, we compose as we need via components. In the end, you can get the same result via a slightly different pattern.

And honestly you can very easily design a simple system that only requires one component and one scriptable object:

[CreateAssetMenu]
public class ObjectType : ScriptableObject { }

public class ObjectTypeCollection : MonoBehaviour
{
    [SerializeField]
    private List<ObjectType> _objects = new();
   
    public bool ContainsType(ObjectType objectType)
    {
        return _objects.Contains(objectType);
    }
}
2 Likes

Nah you don’t have to create anything, you just attach PrefabRoot component to the already made root object of every prefab. You can also remove that component from the object during runtime once you store the reference somewhere. The component is really just a marker, it does nothing, but if you have many prefab instances you can reclaim some memory by removing them.

This is a semi-automatic way to do this, but you could as well make a component (or a Scriptable Object which is even more rudimentary) that registers itself to a list automatically (or manually, as shown in Spiney’s post above).

Or you can dragndrop all instantiable prefabs to a script that acts as a catalog and provides a singleton access, and then you proceed to instantiate objects in code only from this catalog.

Or you can adopt a different standard, i.e. perhaps you name the actual root object in a specific way, with some suffix or something. This is in general a bad practice, however in this case it’s not, because you make it so that it fires a clear error if something goes wrong (for example no parent was found with such and such suffix), as soon as the prefab is instantiated.

There are many possibilities, some are better than others, obviously if you can tailor your specific use case, it can be even better suited to your project, so that it stays well communicated, consistent, extensible, and intuitive in the future. The largest problem in Unity is that the most of the groundwork is open ended, there are no straight standards, it’s a mosh pit, as Kurt described. But you can still, thankfully, invent your own standards.

All good info and food for thought. Thanks!