【Unity】エディタウィンドウでフォルダを検索する #1

投稿者: | 2023-04-13

Assetsフォルダの条件にあうプレハブをエディタウィンドウに表示するでは、プレハブを検索するフォルダがAssetsフォルダに固定されていましたが、エディタウィンドウで検索するフォルダを指定できるようにしてみます。

今回は、Assetsフォルダにあるフォルダを検索して、ダイアログに表示し、トグルで選択したフォルダのパスを元のエディタウィンドウに渡します。

ダイアログを作る

まず、Editorという名前のフォルダにC#スクリプトを新規作成して、フォルダを一覧表示するダイアログを作りました。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;

public class FindFolderDialog : EditorWindow
{
    // 検索したフォルダのパス
    string[] allFolderPaths;

    // フォルダの選択/非選択
    bool[] selectedFolderIndexes;

    // 元のエディタウィンドウ
    SelectPrefabs selectPrefabs;

    // ダイアログを表示する
    public static FindFolderDialog ShowDialog(SelectPrefabs selectPrefabs)
    {
        var w = EditorWindow.GetWindow<FindFolderDialog>();

        // 元のエディタウィンドウを持つ
        w.selectPrefabs = selectPrefabs;

        // パスを検索
        w.FindPaths();

        return w;
    }

    // パスを検索
    void FindPaths()
    {
        // Assetsフォルダの一つ下のフォルダのパスをすべて取得
        allFolderPaths = AssetDatabase.GetSubFolders("Assets");

        // パスと同じ数のbool配列
        selectedFolderIndexes = new bool[allFolderPaths.Length];
    }

    private void OnGUI()
    {
        // フォルダが見つかったとき
        if (allFolderPaths != null && allFolderPaths.Length > 0)
        {
            // トグルを縦に並べる。対応するパスを表示。
            for (int i = 0; i < allFolderPaths.Length; i++)
            {
                selectedFolderIndexes[i] = GUILayout.Toggle(selectedFolderIndexes[i], allFolderPaths[i]);
            }

            // 決定ボタンを押す
            if (GUILayout.Button("決定"))
            {
                if (selectPrefabs != null)
                {
                    // 選択されたトグルに表示されているパスのリスト
                    var selectedFolderPaths = new List<string>();

                    // トグルがオンなら、リストにパスを追加。
                    for (int i = 0; i < selectedFolderIndexes.Length; i++)
                    {
                        if (selectedFolderIndexes[i])
                        {
                            selectedFolderPaths.Add(allFolderPaths[i]);
                        }
                    }

                    // リストを元のエディタウィンドウにわたす。
                    selectPrefabs.SetFolderPaths(selectedFolderPaths.ToArray());
                }

                // ダイアログを閉じる
                Close();
            }
        }
        // キャンセルボタンを押す
        if (GUILayout.Button("キャンセル"))
        {
            // ダイアログを閉じる
            Close();
        }
    }
}

このクラスはEditorWindowクラスを継承させます。検索したフォルダのパスやフォルダを選択したかどうかを表すbool値の配列、元のエディタウィンドウのインスタンスを持ちます。

また、このエディタウィンドウを表示するための静的メソッドも定義しています。引数に元のウィンドウのインスタンスが渡されます。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;

public class FindFolderDialog : EditorWindow
{
    // 検索したフォルダのパス
    string[] allFolderPaths;

    // フォルダの選択/非選択
    bool[] selectedFolderIndexes;

    // 元のエディタウィンドウ
    SelectPrefabs selectPrefabs;

    // ダイアログを表示する
    public static FindFolderDialog ShowDialog(SelectPrefabs selectPrefabs)
    {
        var w = EditorWindow.GetWindow<FindFolderDialog>();

        // 元のエディタウィンドウを持つ
        w.selectPrefabs = selectPrefabs;

        // パスを検索
        w.FindPaths();

        return w;
    }

ウィンドウを取得したら、フォルダのパスを検索して配列にいれます。この配列と同じ数のbool型の配列も作ります。今回は、Assetsフォルダ直下のフォルダだけを検索しています。

    void FindPaths()
    {
        // Assetsフォルダの一つ下のフォルダのパスをすべて取得
        allFolderPaths = AssetDatabase.GetSubFolders("Assets");

        // パスと同じ数のbool配列
        selectedFolderIndexes = new bool[allFolderPaths.Length];
    }

ウィンドウには検索したフォルダと同じ数のトグルを縦に並べます。トグルのテキストにはパスを表示します。

    private void OnGUI()
    {
        // フォルダが見つかったとき
        if (allFolderPaths != null && allFolderPaths.Length > 0)
        {
            // トグルを縦に並べる。対応するパスを表示。
            for (int i = 0; i < allFolderPaths.Length; i++)
            {
                selectedFolderIndexes[i] = GUILayout.Toggle(selectedFolderIndexes[i], allFolderPaths[i]);
            }

また、「検索」ボタンと「キャンセル」ボタンも表示します。検索ボタンを押すと、トグルがオンになっているパスだけの配列を作って、元のエディタウィンドウに渡します。

            if (GUILayout.Button("決定"))
            {
                if (selectPrefabs != null)
                {
                    // 選択されたトグルに表示されているパスのリスト
                    var selectedFolderPaths = new List<string>();

                    // トグルがオンなら、リストにパスを追加。
                    for (int i = 0; i < selectedFolderIndexes.Length; i++)
                    {
                        if (selectedFolderIndexes[i])
                        {
                            selectedFolderPaths.Add(allFolderPaths[i]);
                        }
                    }

                    // リストを元のエディタウィンドウにわたす。
                    selectPrefabs.SetFolderPaths(selectedFolderPaths.ToArray());
                }

                // ダイアログを閉じる
                Close();
            }
        }

キャンセルボタンを押すと、このエディタウィンドウを閉じます。

        if (GUILayout.Button("キャンセル"))
        {
            // ダイアログを閉じる
            Close();
        }
    }
}

元のエディタウィンドウを作る

上のダイアログで指定したフォルダからプレハブを検索するエディタウィンドウを作ります。このクラスにもEditorWindowを継承させています。検索したフォルダのパスの配列や、プレハブのリストを定義しています。

using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using System.Linq;

public class SelectPrefabs : EditorWindow
{
    // 検索するフォルダのパス
    string[] folderPaths = new string[0];

    // 見つかったプレハブのリスト
    List<GameObject> objs = new List<GameObject>();
    
    Vector2 scrollPosition;
    
    [MenuItem("Window/Test/SelectPrefabs")]
    static public void CreateWindow()
    {
        EditorWindow.GetWindow<SelectPrefabs>();
    }

    // ダイアログからフォルダのパスを受け取る
    public void SetFolderPaths(string[] folderPaths)
    {
        this.folderPaths = folderPaths;
    }

    private void OnGUI()
    {
        // 「フォルダを選択」ボタンを押す
        if (GUILayout.Button("フォルダを選択"))
        {
            // ダイアログを表示する
            FindFolderDialog.ShowDialog(this);
        }
        //クリアボタンを押す
        else if(GUILayout.Button("クリア"))
        {
            // 検索するフォルダのパスをクリア
            folderPaths = new string[0];
        }
        // 検索するパスがあれば
        if (folderPaths != null || folderPaths.Length > 0)
        {
            // パスを縦に並べて表示
            foreach (var p in folderPaths)
            {
                GUILayout.Label(p);
            }
        }

        // 検索ボタンを押す
        if (GUILayout.Button("検索"))
        {
            // 検索するフォルダがなければ何もしない
            if (folderPaths == null || folderPaths.Length <= 0) return;

            // プレハブのリストをクリア
            objs.Clear();

            // 検索するフォルダにある全てのプレハブのパスを取得。
            var paths = AssetDatabase.FindAssets("t:Prefab", folderPaths).Select(AssetDatabase.GUIDToAssetPath);

            // プレハブがなければ終了
            if (paths.Count() == 0)
            {
                return;
            }

            // プレハブがあれば一つずつ処理
            for (int i = 0; i < paths.Count(); i++)
            {
                // パスからゲームオブジェクトを読み込む
                var obj = AssetDatabase.LoadAssetAtPath<GameObject>(paths.ElementAt(i));
                
                // リストに追加。
                objs.Insert(0, obj);
            }

        }
        // クリアボタンを押す
        else if (GUILayout.Button("クリア"))
        {
            // プレハブのリストをクリア
            objs.Clear();
        }

        // プレハブがリストにあれば
        if (objs.Count > 0)
        {
            // スクロールビューを表示
            using (var scroll = new EditorGUILayout.ScrollViewScope(scrollPosition))
            {
                scrollPosition = scroll.scrollPosition;

                foreach (var o in objs)
                {
                    // ボタンを表示する。ボタン上にプレハブの名前を表示。
                    if (GUILayout.Button(o.name, EditorStyles.textField))
                    {
                        // ボタンをクリックすると、そのプレハブを選択する。
                        Selection.activeObject = o;
                    }
                }

            }
        }
    }

}

このエディタウィンドウはメインメニューから表示します。そのために、静的メソッドにMenuItem属性を付けています。ダイアログからパスの配列を受け取るためのメソッドも定義しています。

    [MenuItem("Window/Test/SelectPrefabs")]
    static public void CreateWindow()
    {
        EditorWindow.GetWindow<SelectPrefabs>();
    }

    // ダイアログからフォルダのパスを受け取る
    public void SetFolderPaths(string[] folderPaths)
    {
        this.folderPaths = folderPaths;
    }

このウィンドウには、まずフォルダを指定/クリアするための「フォルダを選択」ボタンと「クリア」ボタン、そしてプレハブを検索/クリアするための「検索」ボタンと「クリア」ボタンを表示します。

フォルダを選択ボタンを押すと、ダイアログのクラスで定義した静的メソッドでダイアログを表示します。クリアボタンを押すと、フォルダのパスの配列の要素数を0にします。

    private void OnGUI()
    {
        // 「フォルダを選択」ボタンを押す
        if (GUILayout.Button("フォルダを選択"))
        {
            // ダイアログを表示する
            FindFolderDialog.ShowDialog(this);
        }
        //クリアボタンを押す
        else if(GUILayout.Button("クリア"))
        {
            // 検索するフォルダのパスをクリア
            folderPaths = new string[0];
        }
        

フォルダが指定されると、そのフォルダのパスの文字列をクリアボタンの下に縦に並べて表示します。

        if (folderPaths != null || folderPaths.Length > 0)
        {
            // パスを縦に並べて表示
            foreach (var p in folderPaths)
            {
                GUILayout.Label(p);
            }
        }

フォルダを選択でダイアログが表示され、ダイアログのトグルで選択したフォルダのパスが元のエディタウィンドウに表示されているのがわかります。クリアボタンを押すと表示が消えます。

検索ボタンを押すと、リストをクリアして、指定のフォルダパスの配列からプレハブを検索し、パスを取得します。AssetDatabase.FindAssetsメソッドでタイプとフォルダを指定して、アセットを検索できます。このメソッドはGUIDの配列を返しますが、LinqのSelectメソッドを使ってパスにしています。

        // 検索ボタンを押す
        if (GUILayout.Button("検索"))
        {
            // 検索するフォルダがなければ何もしない
            if (folderPaths == null || folderPaths.Length <= 0) return;

            // プレハブのリストをクリア
            objs.Clear();

            // 検索するフォルダにある全てのプレハブのパスを取得。
            var paths = AssetDatabase.FindAssets("t:Prefab", folderPaths).Select(AssetDatabase.GUIDToAssetPath);

            // プレハブがなければ終了
            if (paths.Count() == 0)
            {
                return;
            }

プレハブが見つからなければ終了し、見つかれば、AssetDatabase.LoadAssetAtPathメソッドでゲームオブジェクトを読み込んで、プレハブのリストに追加します。

            // プレハブがなければ終了
            if (paths.Count() == 0)
            {
                return;
            }

            // プレハブがあれば一つずつ処理
            for (int i = 0; i < paths.Count(); i++)
            {
                // パスからゲームオブジェクトを読み込む
                var obj = AssetDatabase.LoadAssetAtPath<GameObject>(paths.ElementAt(i));
                
                // リストに追加。
                objs.Insert(0, obj);
            }

        }

クリアボタンを押すと、リストをクリアします。

        else if (GUILayout.Button("クリア"))
        {
            // プレハブのリストをクリア
            objs.Clear();
        }

リストにゲームオブジェクトがあれば、スクロールビューを表示して、ボタンを縦に並べます。ボタン上にプレハブの名前を表示し、ボタンがクリックされるとプロジェクトウィンドウで選択されるようにしています。

        if (objs.Count > 0)
        {
            // スクロールビューを表示
            using (var scroll = new EditorGUILayout.ScrollViewScope(scrollPosition))
            {
                scrollPosition = scroll.scrollPosition;

                foreach (var o in objs)
                {
                    // ボタンを表示する。ボタン上にプレハブの名前を表示。
                    if (GUILayout.Button(o.name, EditorStyles.textField))
                    {
                        // ボタンをクリックすると、そのプレハブを選択する。
                        Selection.activeObject = o;
                    }
                }

            }
        }
    }

プレハブを検索する

ダイアログでフォルダを指定して、検索すると元のエディタウィンドウの一番下にゲームオブジェクトの名前が並べて表示されます。

指定するフォルダを増やすと、プレハブの数も増えました。

表示されたボタンをクリックすると、プロジェクトウィンドウでそのプレハブが選択されます。クリアボタンを押すとプレハブの表示がなくなります。

これでフォルダを指定して素早くプレハブを検索することができました。次は、Assetsフォルダ直下のフォルダ以外も階層表示して指定できるようにしてみます。

コメントを残す

メールアドレスが公開されることはありません。