transform.root and empty root game objects don't play nice

A handful of my scripts need to use transform.root due to nested colliders in their parent object. This is not a problem when the base object contains the component I am trying to retrieve, typically code like below after a succesful raycast hit.

EnemyHealth e;
e = hitInfo.collider.transform.root.GetComponent<EnemyHealth>();
e.SomeMethod(value);

But now I’ve decided I have too many objects in my scene and I would like to group them into empty game object ‘folders’ as per most of the community’s agreement. Subsequently the above code is broken because root now references an empty game object. I tried doing this,

EnemyHealth e; 
e = hitInfo.collider.transform.GetComponentInChildren<EnemyHealth>();
e.SomeMethod(value);

but it doesn’t always find the component. Is there a way to use transform.root and empty root game objects correctly? If not, is there an alternative to using transform.root in my case?

Sure, use your own “root”

Here are some extension methods i just wrote from scratch:

public static Transform GetRoot(this Transform aObj, int aLevel)
{
    var tmp = aObj;
    var list = new List<Transform>();
    while(tmp != null)
    {
        list.Add(tmp);
        tmp = tmp.parent;
    }
    list.Reverse();
    return list[Mathf.Clamp(aLevel,0,list.Count)];
}

public static T GetNextParentComponent<T>(this Transform aObj) where T : Component
{
    var tmp = aObj;
    while(tmp != null)
    {
        var comp = tmp.GetComponent<T>();
        if (comp != null)
            return comp;
        tmp = tmp.parent;
    }
    return null;
}

public static Transform FindParent(this Transform aObj, string aName)
{
    var tmp = aObj;
    while(tmp != null)
    {
        if (tmp.name == aName)
            return tmp;
        tmp = tmp.parent;
    }
    return null;
}

Just place them in a static class somewhere. The usage should be clear, but just in case:

hitInfo.collider.transform.GetRoot(0);  // returns the same as .root
hitInfo.collider.transform.GetRoot(1);  // returns one object before the actual root
hitInfo.collider.transform.GetRoot(2);  // and so on...

// If you have a certain script on the "root" object use
hitInfo.collider.transform.GetNextParentComponent<MyScript>();

// Find the nearest parent that is named "MyParent"
hitInfo.collider.transform.FindParent("MyParent");

edit
Fixed the generic parameters which aren’t displayed since the last update. Replaced them with lt / gt