【Unity】Assetsフォルダの条件にあうプレハブをエディタウィンドウに表示する

投稿者: | 2023-04-10

Assetsフォルダ内のたくさんのプレハブを修正するのは時間がかかるので、エディタ拡張で条件に合うプレハブをエディタウィンドウに表示して、簡単に選択できるようにしてみました。

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

まずEditorという名前をつけたフォルダにC#スクリプトを新規作成しました。このクラスはEditorWindowクラスを継承させます。

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

public class FindPrefabs : EditorWindow
{
    // 条件に合うゲームオブジェクトのリスト
    List<GameObject> objs = new List<GameObject>();
   
    Vector2 scrollPosition;

    // メインメニューからウィンドウを表示できるようにする。
    [MenuItem("Window/Test/FindPrefabs")]
    static public void CreateWindow()
    {
       EditorWindow.GetWindow<FindPrefabs>();
    }

    private void OnGUI()
    { 
        // 検索ボタンを押す
        if (GUILayout.Button("検索"))
        {
            // リストをクリアする
            objs.Clear();

            // Assetsフォルダの中にある全てのプレハブを検索してパスを返す
            var paths = AssetDatabase.FindAssets("t:Prefab", new string[] { "Assets/"}).Select(AssetDatabase.GUIDToAssetPath);

            // なければ終了
            if (paths.Count() == 0)
            {
                return;
            }

            // パスを一つずつ処理
            for(int i = 0; i < paths.Count(); i++)
            {
                // プレハブを読み込む
                var obj = AssetDatabase.LoadAssetAtPath<aGmeObject>(paths.ElementAt(i));

                // すべての子オブジェクトのレンダラーコンポーネントを取得
                var renderers = obj.GetComponentsInChildren<Renderer>();
                
                // レンダラーコンポーネントがあるとき
                if (renderers != null)
                {                  
                    foreach (var r in renderers)
                    {
                        // 影を落とす場合、プレハブをリストに入れて、次のプレハブを処理。
                        if (r.shadowCastingMode == UnityEngine.Rendering.ShadowCastingMode.On)
                        {
                            objs.Insert(0, obj);
                            break;
                        }
                    }
                }
            }
        }
        // クリアボタンを押す
        else if(GUILayout.Button("クリア"))
        {
            // リストをクリアする
            objs.Clear();
        }

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

                // 縦にレイアウト
                using (new EditorGUILayout.VerticalScope())
                {
                    foreach (var o in objs)
                    {
                        // ボタンを表示する。ボタン上にプレハブの名前を表示。
                        if (GUILayout.Button(o.name, EditorStyles.textField))
                        {
                            // ボタンをクリックすると、プロジェクトウィンドウでそのプレハブを選択する。
                            Selection.activeObject = o;
                        }
                    }
                }
            }
        }
    }
}

検索したゲームオブジェクトのリストとスクロールバーを表示するために使うグローバル変数を宣言しています。

public class FindPrefabs : EditorWindow
{
    // 条件に合うゲームオブジェクトのリスト
    List<GameObject> objs = new List<GameObject>();
   
    Vector2 scrollPosition;

このウィンドウを作成するための静的メソッドには、MenuItem属性を付けて、メインメニューから実行できるようにします。

    // メインメニューからウィンドウを表示できるようにする。
    [MenuItem("Window/Test/FindPrefabs")]
    static public void CreateWindow()
    {
       EditorWindow.GetWindow<FindPrefabs>();
    }

ウィンドウの内容をOnGUIに書きます。このウィンドウには「検索」ボタンと「クリア」ボタンを表示します。

検索ボタンを押すと、まずリストをクリアして、アセットデータベースを検索します。AssetDatabase.FindAssetsメソッドの引数で、検索するアセットのタイプや検索するフォルダを指定できます。今回はAssetsフォルダにあるプレハブを検索しています。

    private void OnGUI()
    { 
        // 検索ボタンを押す
        if (GUILayout.Button("検索"))
        {
            // リストをクリアする
            objs.Clear();

            // Assetsフォルダの中にある全てのプレハブを検索してパスを返す
            var paths = AssetDatabase.FindAssets("t:Prefab", new string[] { "Assets/"}).Select(AssetDatabase.GUIDToAssetPath);

            // なければ終了
            if (paths.Count() == 0)
            {
                return;
            }

AssetDatabase.FindAssetsメソッドは、文字列であるGUIDの配列を返しますが、LinqのSelectメソッドを使って、各要素をそのゲームオブジェクトの場所を表すパスにしています。

プレハブが見つからなければ処理を終了します。プレハブがあれば、AssetDatabase.LoadAssetAtPathメソッドを使って、パスからゲームオブジェクトを読み込みます。

            for(int i = 0; i < paths.Count(); i++)
            {
                // プレハブを読み込む
                var obj = AssetDatabase.LoadAssetAtPath<GameObject>(paths.ElementAt(i));

そして、GameObject.GetComponentsInChildrenを使って、そのゲームオブジェクトの子オブジェクトのレンダラーコンポーネントをすべて取得して、一つでもshadowCastingModeがオンになっているものがあれば、親ゲームオブジェクトをリストに入れています。

                var renderers = obj.GetComponentsInChildren<Renderer>();
                
                // レンダラーコンポーネントがあるとき
                if (renderers != null)
                {                  
                    foreach (var r in renderers)
                    {
                        // 影を落とす場合、プレハブをリストに入れて、次のプレハブを処理。
                        if (r.shadowCastingMode == UnityEngine.Rendering.ShadowCastingMode.On)
                        {
                            objs.Insert(0, obj);
                            break;
                        }
                    }
                }
            }
        }

クリアボタンを押したら、リストをクリアして要素数を0にします。

        else if(GUILayout.Button("クリア"))
        {
            // リストをクリアする
            objs.Clear();
        }

リストにプレハブがある場合は、ボタンの下に並べて表示します。リストがウィンドウよりも長くなる場合のために、まずはスクロールビューを表示しています。

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

EditorGUILayout.ScrollViewScopeメソッドの引数にグローバル変数の値を渡して、戻り値から得られる値を変数に渡すことでスクロールさせることができます。

その中でリストの内容を縦にレイアウトします。要素の数だけボタンを表示して、ゲームオブジェクトの名前をボタン上に表示しています。ボタンをクリックすると、対応するゲームオブジェクトがプロジェクトウィンドウで選択されます。

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

使ってみる

検索ボタンをクリックすると、プレハブのリストが表示されます。クリックするとプロジェクトウィンドウで選択されていることがわかります。

選択したプレハブは、インスペクタでも表示されます。

インスペクタの右上の「Open」からプレハブを開いて、子オブジェクトを選択してみます。

インスペクタを見ると、メッシュレンダラーの「Cast Shadows」がオンになっているのがわかります。

これをオフに変更しました。

再度検索ボタンを押すと、このプレハブがリストからなくなりました。

Cast Shadows: オン
Cast Shadows: オフ

これで条件にあったプレハブを素早く選択することができました。

コメントを残す

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