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

Editor拡張すげええ！！簡単！！やっべええ！！（※深夜テンション）
ということで、**シーン内のゲームオブジェクトをタグで検索するエディタ拡張**を作ってみました。
![Editor.png](https://qiita-image-store.s3.amazonaws.com/0/68811/d17c65ab-8d8e-40fe-39f6-ad9fe48d2e57.png)

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

[タグでシーン内のゲームオブジェクトを検索するエディタ拡張 - Qiita](http://qiita.com/yodaka16/items/77c831d20ffb40e4222c)を元に改良しています。ありがとうございます。

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

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

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

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

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

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

```csharp: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の初期化その１：未登録のタグがあればそのリストを作成
		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の初期化その２：タグが消去されていればそのリストを削除		
		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](http://qiita.com/yodaka16/items/77c831d20ffb40e4222c)
[Unity EditorGUILayout - Scripting API](http://docs.unity3d.com/ScriptReference/EditorGUILayout.html)
[【エディタ拡張徹底解説】初級編③：いろいろなGUI(EditorGUILayout編)【Unity】](http://caitsithware.com/wordpress/archives/1454)