【Unity】エディタ拡張でシーン上の条件に合うゲームオブジェクトのリストを表示する

投稿者: | 2023-04-08

シーンにゲームオブジェクトが大量にあると、少しの修正にも時間がかかります。そこで、エディタ拡張で、条件にあったヒエラルキーにあるゲームオブジェトを素早く選択できるようにしてみました。

今回は、シーンの中からオーディオミキサーの設定されていないAudioSourceコンポーネントを持ったゲームオブジェクトだけをエディタウィンドウに表示してみます。

Editorウィンドウを作る

まず、プロジェクトウィンドウで「Editor」という名前のフォルダを作り、その中にC#スクリプトを新規作成しました。このクラスはMonoBehaviourではなく、EditorWindowクラスを継承させます。

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

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

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

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

            // すべてのゲームオブジェクト
            foreach (GameObject go in Resources.FindObjectsOfTypeAll(typeof(GameObject)) as GameObject[])
            {               
                // ヒエラルキーにあるゲームオブジェクト
                if (!EditorUtility.IsPersistent(go.transform.root.gameObject) && !(go.hideFlags == HideFlags.NotEditable || go.hideFlags == HideFlags.HideAndDontSave))
                {

                    // AudioSourceを取得
                    var a = go.GetComponent<AudioSource>();

                    // AudioSourceがあって、オーディオミキサーが未設定の場合
                    if (a != null && a.outputAudioMixerGroup == null)
                    {
                        // リストの先頭に追加
                        objectsInScene.Insert(0, go);
                    }
                }
            }

        }
        // クリアボタンを押す
        else if(GUILayout.Button("クリア"))
        {
            // リストをクリア
            objectsInScene.Clear();
        }

        // リストにゲームオブジェクトがあるとき
        if (objectsInScene.Count > 0)
        {
            // 縦にレイアウト
            using (new EditorGUILayout.VerticalScope())
            {
                // リストを一つずつ見る
                for(int i = 0; i < objectsInScene.Count; i++)
                {
                    // ボタンを表示する。ボタン上にゲームオブジェクトの名前を表示。
                    if (GUILayout.Button(objectsInScene[i].name, EditorStyles.textField))
                    {
                        // ボタンをクリックすると、そのゲームオブジェクトを選択する。
                        Selection.activeGameObject = objectsInScene[i];
                    }
                }
            }
        }
 
    }
}

まずグローバル変数として、検索したゲームオブジェクトを入れるリストを作ります。

List<GameObject> objectsInScene = new List<GameObject>();

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

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

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

検索ボタンをクリックすると、まずリストをクリアし、ヒエラルキーウィンドウにあるゲームオブジェクトをすべて検索します。

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

            // すべてのゲームオブジェクト
            foreach (GameObject go in Resources.FindObjectsOfTypeAll(typeof(GameObject)) as GameObject[])
            {               
                // ヒエラルキーにあるゲームオブジェクト
                if (!EditorUtility.IsPersistent(go.transform.root.gameObject) && !(go.hideFlags == HideFlags.NotEditable || go.hideFlags == HideFlags.HideAndDontSave))
                {
                    // ...
                }
            }
        }

Resources.FindObjectsOfTypeAllメソッドでは、指定した型のオブジェクトをすべて取得できます。Object.FindObjectsOfTypeメソッドとは違って、無効なオブジェクトも検索できます。

その内、シーンにあるゲームオブジェクトだけを処理します。そのために、まずルートのオブジェクトをEditorUtility.IsPersistentメソッドの引数に渡しています。オブジェクトがシーンにある場合はfalseを返します。

さらに、ハイドフラッグを使って、インスペクターで編集不可のものと、ヒエラルキーに表示されずシーンに保存されないものを除外しています。

シーンにある場合、AudioSourceコンポーネントを取得します。それがnullでなく、オーディオミキサーがnullのときにリストに追加します。ヒエラルキーウィンドウの順番と揃うように、先頭に追加しています。

                {
                    // AudioSourceを取得
                    var a = go.GetComponent<AudioSource>();

                    // AudioSourceがあって、オーディオミキサーが未設定の場合
                    if (a != null && a.outputAudioMixerGroup == null)
                    {
                        // リストの先頭に追加
                        objectsInScene.Insert(0, go);
                    }

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

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

そして、リストにゲームオブジェクトがあるときは、その数だけボタンを縦に並べます。ボタン上には各ゲームオブジェクトの名前を表示し、スタイルも変更しています。

        if (objectsInScene.Count > 0)
        {
            // 縦にレイアウト
            using (new EditorGUILayout.VerticalScope())
            {
                // リストを一つずつ見る
                for(int i = 0; i < objectsInScene.Count; i++)
                {
                    // ボタンを表示する。ボタン上にゲームオブジェクトの名前を表示。
                    if (GUILayout.Button(objectsInScene[i].name, EditorStyles.textField))
                    {
                        // ボタンをクリックすると、そのゲームオブジェクトを選択する。
                        Selection.activeGameObject = objectsInScene[i];
                    }
                }
            }
        }

ボタンをクリックすると、対応するゲームオブジェクトがヒエラルキーウィンドウで選択されます。

使ってみる

シーンにAudioSourceゲームオブジェクトを複数新規作成しました。シーンには他にカメラとディレクショナルライトがあります。

検索ボタンを押すと、オーディオソースだけがリストアップされます。エディタウィンドウ上でクリックすると、ヒエラルキーウィンドウで選択されます。

オーディオソースの条件がない場合、カメラなども表示されます。ヒエラルキーウィンドウに表示されない、InternalIdentityTransformは操作しないほうが良さそうです。

ハイドフラッグやEditorUtility.IsPersistentを使わないと、リストの要素がさらに増えました。

5つあるオーディオソースのうち、「Audio Source (2)」と「Audio Source (4)」にオーディオミキサーを設定してみます。

検索ボタンをクリックすると、オーディオミキサーを設定したゲームオブジェクトがリストから除外されました。

これで条件にあったゲームオブジェクトを素早く選択できるようになりました。

参考:
https://docs.unity3d.com/ja/2019.4/ScriptReference/Resources.FindObjectsOfTypeAll.html

コメントを残す

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