本ページにはプロモーションが含まれます
Unity

【Unity】Projectビューのフォルダに個別に色を付ける

この記事は約21分で読めます。

はじめに

Projectビューのフォルダに個別で色を付けられる Editor 拡張コードを作成したよ。
これまで Rainbow Folders 2 などを使っていましたが、正直そこまでの高機能は不要で、超シンプルで不具合が出ないものを探していました。

MIT ライセンスで配布されているシンプルなものも試したのですが、それでも自分の用途にはまだ多機能すぎて「ちがうな…」という結論に。
結果として、必要な機能だけに絞って自作することにしました。

やりたいこと

Projectフォルダが多くて、ディレクトリ間を行ったり来たりすることが多く、プレハブどのディレクトリだっけ?となるたびにフラストレーションがたまっていきます。
シンプルにディレクトリにマーキングできてそこかっ!」って一瞬で分かれば、それで十分。
余計な機能はいらなくて、迷わないための目印として使えれば何でもいい。

これが

こうなる

コード

using UnityEngine;
using UnityEditor;

namespace Assets.MyProject.Editor
{
    /// <summary>
    /// Projectビューのフォルダに色を付けるEditor拡張。
    /// 右クリックメニュー [Assets/Color/...] から色を設定できます。
    /// 設定はEditorPrefsに保存されるため、作業者の環境にのみ適用されます。
    /// </summary>
    [InitializeOnLoad]
    public class FolderColorizer
    {
        static FolderColorizer()
        {
            EditorApplication.projectWindowItemOnGUI += OnProjectWindowItemGUI;
        }

        private static void OnProjectWindowItemGUI(string guid, Rect selectionRect)
        {
            // 有効なフォルダかチェック
            string path = AssetDatabase.GUIDToAssetPath(guid);
            if (string.IsNullOrEmpty(path) || !AssetDatabase.IsValidFolder(path))
            {
                return;
            }

            // 色情報を取得
            string colorKey = GetColorKey(guid);
            if (!string.IsNullOrEmpty(colorKey))
            {
                Color color = GetColorFromKey(colorKey);
                if (color != Color.clear)
                {
                    // 背景を描画 (アルファ値を下げて文字が見えるように)
                    Color drawColor = color;
                    drawColor.a = 0.2f;

                    // 描画範囲の調整
                    Rect rect = selectionRect;
                    if (rect.height > 20) // アイコン表示
                    {
                        // そのまま
                    }
                    else // リスト表示
                    {
                        rect.x = 0;
                        rect.width += selectionRect.x;
                    }

                    EditorGUI.DrawRect(rect, drawColor);
                }
            }
        }

        private static string GetColorKey(string guid)
        {
            return EditorPrefs.GetString($"FolderColor_{guid}", "");
        }

        private static void SetColorKey(string guid, string colorNameOrHex)
        {
            if (string.IsNullOrEmpty(colorNameOrHex))
            {
                EditorPrefs.DeleteKey($"FolderColor_{guid}");
            }
            else
            {
                EditorPrefs.SetString($"FolderColor_{guid}", colorNameOrHex);
            }
            EditorApplication.RepaintProjectWindow();
        }

        private static Color GetColorFromKey(string key)
        {
            switch (key)
            {
                case "Red": return new Color(1f, 0.4f, 0.4f);
                case "Green": return new Color(0.4f, 1f, 0.4f);
                case "Blue": return new Color(0.4f, 0.6f, 1f);
                case "Yellow": return new Color(1f, 0.9f, 0.4f);
                case "Orange": return new Color(1f, 0.65f, 0.3f);
                case "Purple": return new Color(0.8f, 0.6f, 1f);
                default:
                    if (ColorUtility.TryParseHtmlString(key, out Color color))
                    {
                        return color;
                    }
                    return Color.clear;
            }
        }

        // --- Context Menu Items ---

        [MenuItem("Assets/Color/Red")]
        private static void SetRed() => SetColorForSelection("Red");

        [MenuItem("Assets/Color/Green")]
        private static void SetGreen() => SetColorForSelection("Green");

        [MenuItem("Assets/Color/Blue")]
        private static void SetBlue() => SetColorForSelection("Blue");

        [MenuItem("Assets/Color/Yellow")]
        private static void SetYellow() => SetColorForSelection("Yellow");

        [MenuItem("Assets/Color/Orange")]
        private static void SetOrange() => SetColorForSelection("Orange");

        [MenuItem("Assets/Color/Purple")]
        private static void SetPurple() => SetColorForSelection("Purple");

        [MenuItem("Assets/Color/Reset")]
        private static void SetReset() => SetColorForSelection("");

        [MenuItem("Assets/Color/Reset All", false, 20)]
        private static void SetResetAll()
        {
            if (EditorUtility.DisplayDialog("Reset All Colors", 
                "プロジェクト内のすべてのフォルダの色設定をリセットしますか?\n(この操作は元に戻せません)", 
                "Reset", "Cancel"))
            {
                string[] allGuids = AssetDatabase.FindAssets("t:Folder");
                foreach (string guid in allGuids)
                {
                    EditorPrefs.DeleteKey($"FolderColor_{guid}");
                }
                EditorApplication.RepaintProjectWindow();
            }
        }

        [MenuItem("Assets/Color/Custom...", false, 100)]
        private static void SetCustom()
        {
            FolderColorPickerWindow.ShowKey(SetColorForSelection);
        }

        private static void SetColorForSelection(string colorNameOrHex)
        {
            foreach (string guid in Selection.assetGUIDs)
            {
                string path = AssetDatabase.GUIDToAssetPath(guid);
                if (AssetDatabase.IsValidFolder(path))
                {
                    SetColorKey(guid, colorNameOrHex);
                }
            }
        }
    }

    /// <summary>
    /// 色選択用の小さなポップアップウィンドウ
    /// </summary>
    public class FolderColorPickerWindow : EditorWindow
    {
        private Color _selectedColor = Color.white;
        private System.Action<string> _onApply;

        public static void ShowKey(System.Action<string> onApply)
        {
            var window = GetWindow<FolderColorPickerWindow>("Color Picker");
            window._onApply = onApply;
            window.minSize = new Vector2(250, 100);
            window.maxSize = new Vector2(250, 100);
            window.Show();
        }

        private void OnGUI()
        {
            EditorGUILayout.Space();
            _selectedColor = EditorGUILayout.ColorField("Color", _selectedColor);

            GUILayout.Space(10);

            if (GUILayout.Button("Apply"))
            {
                string hex = "#" + ColorUtility.ToHtmlStringRGBA(_selectedColor);
                _onApply?.Invoke(hex);
                Close();
            }
        }
    }
}

namespaceを変えてご自由にどうぞ。
ディレクトリ個別、複数選択して Assets > Color から色を選択。戻す時は Reset か Reset All で。

おわりに

有料のものやオープンソースのものでも、意図しないワーニングやエラーが出たりすることはよくあって、
開発中のイライラの原因にもなっていました。最小構成なので自身でもメンテできよいのではという感じに落ち着きました。