Unity HierarchyとProjectの階層構造をラインで見やすくする。

## できるようになること
Hierarchy
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2294598/f8a63dfe-1746-1771-80b8-e559ad4d3bbe.png)
Project
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2294598/832adb76-354e-a671-3ec2-8f013845e76e.png)

フォルダ構造をラインで表して、ファイルがどこの階層にあるのかわかりやすくなる。

## 前置き
有料アセットにもあるが自分で作れば無料なので自分でつくった。

## 使い方
以下のコードをEditorフォルダに入れる。表示が変化しなときはUnity再起動。

```ProjectFolderLines.cs
using UnityEditor;
using UnityEngine;
using System.Linq;

[InitializeOnLoad]
public static class ProjectFolderLines
{
    static ProjectFolderLines()
    {
        EditorApplication.projectWindowItemOnGUI += OnProjectWindowItemOnGUI;
    }

    private static void OnProjectWindowItemOnGUI(string guid, Rect selectionRect)
    {
        string path = AssetDatabase.GUIDToAssetPath(guid);
        bool isFolder = AssetDatabase.IsValidFolder(path);
        int depth = path.Count(f => f == '/') - "Assets".Count(f => f == '/');

        // スプライトシートからのスプライトであるかどうかを判断
        bool isMultiSprite = false;
        TextureImporter importer = AssetImporter.GetAtPath(path) as TextureImporter;
        if (importer != null && importer.spriteImportMode == SpriteImportMode.Multiple)
        {
            isMultiSprite = true;
        }

        if (depth > 0)
        {
            DrawVerticalLines(selectionRect, depth, isFolder, isMultiSprite);
        }
    }

    private static void DrawVerticalLines(Rect selectionRect, int depth, bool isFolder, bool isMultiSprite)
    {
        float indent = 14; // Unityのデフォルトのインデントサイズ
        Handles.BeginGUI();

        // フォルダとファイル、スプライトシートからのスプライトでオフセットを調整
        float baseOffset = isFolder ? 7 : 7; // 通常のファイルやフォルダに対するオフセット
        if (isMultiSprite)
        {
            baseOffset = 7; // スプライトシートからのスプライトに対するオフセット
        }

        for (int i = 1; i <= depth; i++)
        {
            float lineX = selectionRect.x - indent * i - baseOffset;

            Handles.color = GetColorByDepth(depth - i + 1);

            Vector2 startPoint = new Vector2(lineX, selectionRect.y);
            Vector2 endPoint = new Vector2(lineX, selectionRect.yMax);

            Handles.DrawLine(startPoint, endPoint);
        }

        Handles.EndGUI();
    }

    // 階層の深さに応じた色を取得するメソッド
    private static Color GetColorByDepth(int depth)
    {
        switch (depth)
        {
            case 1:
                return Color.red;
            case 2:
                return Color.green;
            case 3:
                return Color.blue;
            case 4:
                return Color.yellow;
            case 5:
                return Color.magenta;
            case 6:
                return Color.cyan;
            case 7:
                return Color.grey;
            case 8:
                return new Color(1f, 0.5f, 0f); // オレンジ
            case 9:
                return new Color(0.5f, 0f, 0.5f); // パープル
            case 10:
                return new Color(0f, 0.5f, 0f); // ダークグリーン
            case 11:
                return new Color(0.5f, 0.5f, 0f); // オリーブ
                                                  // 以下、必要に応じて他の階層の色を追加
            default:
                return new Color(0.5f, 0.5f, 0.5f, 0.2f); // デフォルトの色
        }
    }

}


```

```HierarchyFolderLines.cs
using UnityEditor;
using UnityEngine;

[InitializeOnLoad]
public static class HierarchyFolderLines
{
    static HierarchyFolderLines()
    {
        EditorApplication.hierarchyWindowItemOnGUI += OnHierarchyWindowItemOnGUI;
    }

    private static void OnHierarchyWindowItemOnGUI(int instanceID, Rect selectionRect)
    {
        GameObject obj = EditorUtility.InstanceIDToObject(instanceID) as GameObject;
        if (obj != null)
        {
            int depth = GetDepth(obj.transform);
            DrawVerticalLines(selectionRect, depth);
        }
    }

    private static int GetDepth(Transform transform)
    {
        int depth = 0;
        while (transform.parent != null)
        {
            depth++;
            transform = transform.parent;
        }
        return depth; // "Assets"の階層を含まないので、ここでは調整不要
    }

    private static void DrawVerticalLines(Rect selectionRect, int depth)
    {
        float indentWidth = 14f; // Unityのデフォルトのインデントサイズ
        float baseIndent = 14f; // 基準となるインデント
        float lineWidth = 1f; // 線の幅

        Handles.BeginGUI();

        for (int i = 0; i < depth; i++)
        {
            float indent = baseIndent + (indentWidth * i) + (indentWidth / 2f); // インデントの調節
            float lineX = selectionRect.x - indent;

            // 色を親から決定する
            Color lineColor = GetColorByDepth(depth - i,0.2f);
            Handles.DrawSolidRectangleWithOutline(new Rect(lineX, selectionRect.y, lineWidth, selectionRect.height), lineColor, lineColor);
        }

        Handles.EndGUI();
    }
    // 階層の深さに応じた色を取得するメソッド（アルファ値付き）
    private static Color GetColorByDepth(int depth, float alpha = 1.0f)
    {
        Color color;
        switch (depth)
        {
            case 1:
                color = Color.red;
                break;
            case 2:
                color = Color.green;
                break;
            case 3:
                color = Color.blue;
                break;
            case 4:
                color = Color.yellow;
                break;
            case 5:
                color = Color.magenta;
                break;
            case 6:
                color = Color.cyan;
                break;
            case 7:
                color = Color.grey;
                break;
            case 8:
                color = new Color(1f, 0.5f, 0f); // オレンジ
                break;
            case 9:
                color = new Color(0.5f, 0f, 0.5f); // パープル
                break;
            case 10:
                color = new Color(0f, 0.5f, 0f); // ダークグリーン
                break;
            case 11:
                color = new Color(0.5f, 0.5f, 0f); // オリーブ
                break;
            default:
                color = new Color(0.5f, 0.5f, 0.5f); // デフォルトの色
                break;
        }
        return new Color(color.r, color.g, color.b, alpha);
    }

}



```


これで見やすくなるね。やったね。


## 202406 追記

HierarchyとProjectの名前が逆になっていたので修正

もっとみやすくProjectフォルダにラインと同様の色を設定できるスクリプト追加
適応後
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2294598/a9e54275-2812-a324-2101-fcfde2fb3e03.png)


```ProjectFolderColors.cs
#if UNITY_EDITOR
using UnityEditor;
using UnityEngine;

public static class ColoredProjectView
{
    const string MenuPath = "NXLab/ColoredProjectView";
    const string PrefKey = "NXLab_ColoredProjectView_Enabled";

    [MenuItem(MenuPath)]
    static void ToggleEnabled()
    {
        bool isEnabled = !Menu.GetChecked(MenuPath);
        Menu.SetChecked(MenuPath, isEnabled);
        EditorPrefs.SetBool(PrefKey, isEnabled);
        EditorApplication.RepaintProjectWindow();
    }

    [InitializeOnLoadMethod]
    static void Initialize()
    {
        SetEvent();
        bool isEnabled = EditorPrefs.GetBool(PrefKey, true);  // 初期値をtrueに設定
        Menu.SetChecked(MenuPath, isEnabled);
    }

    static void SetEvent()
    {
        EditorApplication.projectWindowItemOnGUI += OnGUI;
    }

    static void OnGUI(string guid, Rect selectionRect)
    {
        if (!Menu.GetChecked(MenuPath))
        {
            return;
        }

        string assetPath = AssetDatabase.GUIDToAssetPath(guid);
        int depth = assetPath.Split('/').Length - 1;
        Color colorToUse = GetColorByDepth(depth);

        // アルファ値を設定して色を適用
        colorToUse.a = GetAlphaByDepth(depth);
        EditorGUI.DrawRect(selectionRect, colorToUse);
    }

    private static Color GetColorByDepth(int depth)
    {
// 指定された階層に基づいて色を返す
        switch (depth)
        {
            case 1: return Color.red;
            case 2: return Color.green;
            case 3: return Color.blue;
            case 4: return Color.yellow;
            case 5: return Color.magenta;
            case 6: return Color.cyan;
            case 7: return Color.grey;
            case 8: return new Color(1f, 0.5f, 0f); // オレンジ
            case 9: return new Color(0.5f, 0f, 0.5f); // パープル
            case 10: return new Color(0f, 0.5f, 0f); // ダークグリーン
            case 11: return new Color(0.5f, 0.5f, 0f); // オリーブ
            default: return new Color(0.5f, 0.5f, 0.5f); // デフォルトの色
        }
    }

    private static float GetAlphaByDepth(int depth)
    {
        // 階層の深さに応じてアルファ値を補間する
        // 例：最深部でアルファ値を最も低くする
        const float maxDepth = 10f;  // この深さ以降はアルファ値が最小になる
        const float minAlpha = 0.01f; // アルファ値の最小値
        const float maxAlpha = 0.05f; // アルファ値の最大値

        // 階層の深さに基づいてアルファ値を計算する
        float alpha = maxAlpha - ((depth - 1) / maxDepth) * (maxAlpha - minAlpha);
        return Mathf.Clamp(alpha, minAlpha, maxAlpha);
    }
}
#endif

```