Help us understand the problem. What is going on with this article?

【UnityEditor拡張】FindSceneObjectsWithTag:シーン内のゲームオブジェクトをタグ検索するエディタ拡張

More than 3 years have passed since last update.

Editor拡張すげええ!!簡単!!やっべええ!!(※深夜テンション)
ということで、シーン内のゲームオブジェクトをタグで検索するエディタ拡張を作ってみました。
Editor.png

開発環境
OS:Windows7 64bit
Unity: Unity5.1.0f3 Personal

タグでシーン内のゲームオブジェクトを検索するエディタ拡張 - Qiitaを元に改良しています。ありがとうございます。

仕組みは単純で、Resources.FindObjectsOfTypeAll ()ですべてのオブジェクトを取得してプレハブなど余計なものを除いたあとに、InternalEditorUtility.tagsを使ってタグ別にリスト化しています。

機能

  • シーン内のすべてのゲームオブジェクトをタグ別にツリーで一覧表示します
  • アクティブ/非アクティブ/両方を指定してゲームオブジェクトの絞り込みができます
  • 特定のタグだけを検索して表示する検索機能も(一応)あります

(GameObject.FindGameObjectsWithTag は非アクティブなゲームオブジェクトを取得することができませんが、この拡張ではアクティブ/非アクティブを問わずすべてのゲームオブジェクトを確認することができます。)

使い方

下のスクリプトを「FindSceneObjectsWithTag.cs」として保存し、Unityインストールディレクトリ内のEditorディレクトリに置きます。
僕の場合(Windows)は「C:\Program Files\Unity5.1.0f3\Editor」でした。

訂正します→プロジェクトのAsset内にEditorディレクトリを作ってそこに置いてください
Unityを再起動し、メニューバーのTool->Find GameObjects In Scene With Tagから開きます。

スクリプト

意見質問ありましたらコメントお願いします。
改善のご意見もお待ちしております。

FindSceneObjectsWithTag.cs
using UnityEngine;
using UnityEditor;
using System.Collections.Generic;
using UnityEditorInternal;


public class FindSceneObjectsWithTag : EditorWindow {
    private string oldTagToSearchFor = "";
    private string tagToSearchFor = "";

    private Vector2 scrollPos;
    private Vector2 resultScrollPos;
    private Vector2 objectsScrollPos;

    private Dictionary<string, Pair<bool, List<GameObject>>> sceneObjectsWithTag;
    private List<GameObject> results;

    private bool searchFlag = false;
    private bool listFlag = true;

    enum ActiveOption {
        ALL,
        ACTIVE_ONLY,
        INACTIVE_ONLY
    };
    private ActiveOption oldActiveOption = ActiveOption.ALL;
    private ActiveOption activeOption = ActiveOption.ALL;


    void OnGUI () {
        EditorGUILayout.BeginVertical ();
        {
            EditorGUILayout.Space ();

            EditorGUILayout.LabelField ("Find GameObjects In Scene With Tag", EditorStyles.boldLabel);

            oldActiveOption = activeOption;
            activeOption = (ActiveOption) EditorGUILayout.EnumPopup ("Select Active or Inactive", (System.Enum) activeOption);

            if (GUILayout.Button ("Reload") || activeOption != oldActiveOption) {
                LoadSceneObjectsWithTag ();
                Search ();
            }

            scrollPos = EditorGUILayout.BeginScrollView (scrollPos);


            //タグ名を指定することで絞り込む////////////////////////////////////////////////////////////
            if (searchFlag = EditorGUILayout.Foldout (searchFlag, "Search By Tag")) {
                EditorGUI.indentLevel++;

                oldTagToSearchFor = tagToSearchFor;
                tagToSearchFor = EditorGUILayout.TagField ("Tag To Search:", tagToSearchFor);

                if (tagToSearchFor != oldTagToSearchFor)
                    Search ();

                if (results != null) {
                    EditorGUILayout.LabelField ("Scene Objects Found:", results.Count.ToString (), EditorStyles.boldLabel);
                    EditorGUI.indentLevel++;

                    foreach (GameObject go in results) {
                        EditorGUILayout.ObjectField (go, typeof (GameObject), false);
                    }

                    EditorGUI.indentLevel--;
                }

                EditorGUI.indentLevel--;
            }
            ////////////////////////////////////////////////////////////////////////////////////////////

            EditorGUILayout.Space ();

            //すべてのシーン内オブジェクトをタグ別にリストアップ////////////////////////////////////////
            if (listFlag = EditorGUILayout.Foldout (listFlag, "List By Tags")) {
                if (sceneObjectsWithTag != null) {
                    EditorGUI.indentLevel++;

                    foreach (var item in sceneObjectsWithTag) {

                        if (item.Value.First = EditorGUILayout.Foldout (item.Value.First, item.Key + ":" + item.Value.Second.Count.ToString ())) {
                            EditorGUI.indentLevel++;
                            foreach (GameObject go in item.Value.Second) {
                                EditorGUILayout.ObjectField (go, typeof (GameObject), false);
                            }
                            EditorGUI.indentLevel--;
                        }
                    }

                    EditorGUI.indentLevel--;
                }
            }
            ////////////////////////////////////////////////////////////////////////////////////////////

            EditorGUILayout.EndScrollView ();
        }
        EditorGUILayout.EndVertical ();
    }


    // すべてのオブジェクトを取得しタグ別にリストに格納する////////////////////////////////////////////////////////////
    void LoadSceneObjectsWithTag () {

        // DictionaryのKeyはタグ、ValueはPairクラス.Pairはツリー表示非表示フラグとGameObjectリスト.
        if (sceneObjectsWithTag == null)
            sceneObjectsWithTag = new Dictionary<string, Pair<bool, List<GameObject>>> ();

        // オブジェクトDictionalyの初期化その1:未登録のタグがあればそのリストを作成
        foreach (string tag in InternalEditorUtility.tags) {
            if (!sceneObjectsWithTag.ContainsKey (tag)) {
                sceneObjectsWithTag.Add (tag, new Pair<bool, List<GameObject>> ());
            }
            sceneObjectsWithTag[tag].Second = new List<GameObject> ();
        }

        // オブジェクトDictionalyの初期化その2:タグが消去されていればそのリストを削除        
        bool existTag = false;
        foreach (string key in sceneObjectsWithTag.Keys) {
            foreach (string tag in InternalEditorUtility.tags) {
                if (key == tag) {
                    existTag = true;
                    break;
                } else {
                    existTag = false;
                }
            }

            if (!existTag) {
                sceneObjectsWithTag.Remove (key);
            }
        }

        // 全てのオブジェクトを配列で取得し順に処理する
        foreach (GameObject obj in Resources.FindObjectsOfTypeAll<GameObject> ()) {

            // アセットからパスを取得.シーン上に存在するオブジェクトの場合,シーンファイル(.unity)のパスを取得
            string path = AssetDatabase.GetAssetOrScenePath (obj);

            // シーン上に存在するオブジェクトかどうか拡張子で判定
            bool isScene = path.Contains (".unity");
            if (isScene && sceneObjectsWithTag.ContainsKey (obj.tag)) {
                if (activeOption == ActiveOption.ALL || obj.activeInHierarchy == (activeOption == ActiveOption.ACTIVE_ONLY)) {  // Activeに関するオプションに一致するかチェック
                    sceneObjectsWithTag[obj.tag].Second.Add (obj);      // タグに一致したオブジェクトはリストに追加
                }
            }
        }
    }
    ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    void Search () {

        if (tagToSearchFor != null && tagToSearchFor != "") {
            results = sceneObjectsWithTag[tagToSearchFor].Second;
        }

    }


    // エディターのメニューバーに追加
    [MenuItem ("Tool/Find GameObjects In Scene With Tag...")]
    static void Init () {
        FindSceneObjectsWithTag window = EditorWindow.GetWindow<FindSceneObjectsWithTag> ("Find With Tag");
        window.LoadSceneObjectsWithTag ();
        window.ShowPopup ();
    }


    //自作Pairクラス
    private class Pair<T, U> {
        public Pair () {
        }

        public Pair (T first, U second) {
            this.First = first;
            this.Second = second;
        }

        public T First { get; set; }
        public U Second { get; set; }
    };
}

※不具合:タグの追加削除を繰り返しているとReloadボタンを押したときにエラーが出る不具合がありますが、使用には特に差し支えありません。

雑談

Editor拡張楽しい!!
はじめてEditor拡張に挑戦してみましたが、思いのほか簡単かつ綺麗にできました。
これはゲーム制作ほったらかしてEditor拡張に打ち込む自分の姿が容易に想像できる・・・!(本末転倒クライシス)
とりあえず次はドミニオンタブでも作ろうかな

これももうちょいやったら色々便利になりそうなので、誰か宜しくお願いします
→既にいろいろありそう
→あった:http://wiki.unity3d.com/index.php/FindGameObjects

参考:

タグでシーン内のゲームオブジェクトを検索するエディタ拡張 - Qiita
Unity EditorGUILayout - Scripting API
【エディタ拡張徹底解説】初級編③:いろいろなGUI(EditorGUILayout編)【Unity】

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした