先看效果:

image.png

制表符的一个冷门使用场景。制表符的特点就是字符是与字符边界对齐的,四边边距是0,所以在没有行间距、字符间距的条件下是可以连接起来的,完全可以当作连线来使用。

实现原理其实还是比较简单的,在 Hierarchy GUI 对每一个节点显示的时候,获取节点的 Rect,根据节点的深度,计算出对应位置及要显示的制表符,同时根据深度遍历前置节点绘制竖向连线。需要注意每一个层级的 Indent 和是否有 Expander。

完整代码如下:

using UnityEditor;
using UnityEngine;

[InitializeOnLoad]
public static class HierarchyCascadeViewer
{
    private static GUIStyle _tabStyle = null;

    static int GetTransformDepth(Transform transform)
    {
        int depth = 0;
        while (transform.parent != null)
        {
            depth++;
            transform = transform.parent;
            depth += GetTransformDepth(transform);
        }
        return depth;
    }

    [InitializeOnLoadMethod]
    static void InitializeOnLoadMethod()
    {
        EditorApplication.hierarchyWindowItemOnGUI += (instanceId, rect) =>
        {
            GameObject go = EditorUtility.InstanceIDToObject(instanceId) as GameObject;
            if (go == null)
                return;

            Rect selectionRect = rect;
            if (_tabStyle == null)
            {
                _tabStyle = new();
                _tabStyle.fontSize = 14;
                _tabStyle.normal.textColor = Color.gray;
            }

            Transform transform = go.transform;
            int depth = GetTransformDepth(transform);
            if (depth > 0)
            {
                Rect tabRect = new Rect(selectionRect);
                tabRect.x -= 27;
                int siblingIndex = transform.GetSiblingIndex();
                int totalSibling = transform.parent.childCount;
                if (siblingIndex + 1 == totalSibling)
                {
                    if (transform.childCount > 0)   //避开有子节点的箭头
                    {
                        EditorGUI.LabelField(tabRect, "└", _tabStyle);
                    }
                    else
                    {
                        EditorGUI.LabelField(tabRect, "└─", _tabStyle);
                    }
                }
                else
                {
                    if (transform.childCount > 0)   //避开有子节点的箭头
                    {
                        EditorGUI.LabelField(tabRect, "├", _tabStyle);
                    }
                    else
                    {
                        EditorGUI.LabelField(tabRect, "├─", _tabStyle);
                    }
                }

                Transform parent = transform.parent;
                for (int i = 1; i <= depth; i++)
                {
                    if (parent == null || parent.parent == null) break;
                    tabRect.x -= 14;
                    if (parent.GetSiblingIndex() + 1 < parent.parent.childCount)
                    {
                        EditorGUI.LabelField(tabRect, "│", _tabStyle);
                    }
                    parent = parent.parent;
                }
            }
        };
    }
}