このScriptついているオブジェクトってどれだっけ?を解決するための拡張。UnityEditor拡張その2

こんなことってありませんか?

このMonoBeheivior継承のスクリプトってどれだけのオブジェクトについているんだっけ?
一個一個数えようにも、すごい数のオブジェクトが配置されているからわからないよー

といった具合に、MonoBeheiviorというのはUnityを使う上で便利だけど、
普通のクラスと違うから、生成のタイミングとかをVisualStudioから追いづらい。

それらを解決する為のものです。

こんな事を解決してくれます。

Hieralchyの中から指定したスクリプトのついているオブジェクトを探しだしてくれる。
※今回はシーン内で探している状態ですが、どこから探すかを選択できるようにすれば、リソースの中からでも探索は可能です。

使用環境

Unity2017.1.1f1
VisulaStudio2017Community (Pro版がほしい)

やりたい事の考え方

ひとまず、作成していく上では、どういった要件が必要か?を考えなければいけません。
なので要件として、以下の通り

1.Scene内のオブジェクトを取得する。
2.指定したスクリプトを設定して、取得できるように行う。この話の肝。
3.そのScriptが基底クラスとかの継承とかみれたらいいな

の3点とします。

というのは、以下のアプローチでの解決を図るためです。

1-オブジェクトを見る
2-指定したコンポーネントがついているかを判断する。
3-ついていたら表示を行う。

具体的な内容

1.Scene内のオブジェクトを取得する。

なにはともあれ、調べる為のゲームオブジェクトを取得しなければいけません。

//現在のシーンを取得
Scene _scene = SceneManager.GetActiveScene();


//そのシーン内にあるオブジェクトを取得
foreach(GameObject _obj in _scene.GetRootGameObjects())
{
///処理内容
}

これはあくまでも、現在のScene内でのGameObjectを取得しているので
Resourceのみとかする場合は、取得するGameObjectの先を変更してください。


2.指定したスクリプト名を設定して、取得できるように行う。

対象のGameObjectに対して、指定したスクリプトのComponentがついているかを判断しなければいけません。
そこで、そのゲームオブジェクトに指定したスクリプトがついているかで判断を行っていこうと思います。

取得したいスクリプトの指定の仕方はどうするか?

MonoBeheiviorも一種のクラスだから、こうすればいいんじゃないの?

※以下EditorGUILayout. をEGLとします。

sample.cs
格納する変数名  = EGL.OBjectField(格納する変数名,typeof(MonoBeheibioer),false,レイアウト設定);

しかしながら、このままだとある問題が立ちふさがります。

取得してほしいコンポ―ネント名を取得できない(´;Д;`)

MonoBehevior型では、
 .nameだとついているオブジェクト名が
 .ToString()だとMonoBehebior
の値が文字列として返却されます。
そのため、指定したコンポーネントをうまく取得してくれません。

ではコンポーネント名を取得するにはどうするか?
こうしましょう。

samaple2.cs
格納する変数名= EGL.ObjectFiled(格納する変数名,typeof(Object),false,レイアウト設定);

ただし、これだとスクリプト以外のPrefabとか、GameObjectとかもセットできてしまいます。
困りましたねー。どうしましょうー?

こうしましょう!

var _temp= EGL.ObjectFiled(格納する変数名,typeof(Object),false,レイアウト設定);

if(GUI.change)
{
    if(null != _setting_script as MonoScript)
    {
         if(格納する変数名!= null)
         {
             捜索する対象が変わった際の処理
         }
         格納する変数名= _temp;
    }
}

重要な部分はここ

if(null != _setting_script as MonoScript)

これによって、その対象がMonoScript型に変換できるかを確かめています。
これによってnullになったものはMonoScriptではないという事になるので
捜索する対象として受け付けない仕組みになっています。

もし、他の人も使用する場合は、ここにどうして値が入らないかなどの
ダイアログなどを出す等を行うとかなり親切なツールになります。

(要検証)
なお、MonoScriptでチェックしているのは、スクリプトの扱いが、TextAsset?の扱いになっているっぽく
MonoBeheiviorでの判定をしてしまうと、扱ってくれないため、こういう作りになっています。
なので、MonoBeheiviorにすると取得が出来なくなってしまいます。


3.そのScriptが基底クラスとかの継承とかみれたらいいな

必要な情報としては、指定したComponentを取得しておかなければいけません。

※前提として以下のスクリプトの取得したコンポーネントは、指定し

bool _result = 取得したコンポーネント.GetType().BaseType != typeof(MonoBehevior).GetType()?true:false

if(_result)
{
 表示したい文字列  = 取得したコンポーネント.GetType().BaseType.ToString();
}

とすることで、基底クラスの名前を取得する事が出来ます。
判定をMonoBeheiviorにしているのは、MonoBeheiviorはGameObjectにつくうえで多くの場合MonoBeheiviorを継承した形をとるため、それを判定に行う事で、MonoBeheiviorを継承した以降のクラスで、基底に当たっている形になっている状態のものを表示するためです。

例えば以下のような場合
A:MonoBeheivior
B:A

Aを検索対象にした際に、Bがついているオブジェクトもヒットします。
BがついているオブジェクトはAという基底が使われているので、表示はしたいですが。
AがついているオブジェクトはMonoBeheiviorがついているのはわかっているので
表示したくないわけです。
イメージでいうと以下の感じです。
例)

Obj AddComponent A
      表示は A
Obj AddComponent B
      表示はB:A

終わりに

他の職場はどうかしりませんが、個人小規模開発だと、Hierarchyに対して、GameObjectにスクリプトをつけることが多くなると思います。(手軽だし)

その際に、そのまま小規模だといいのですが、Game部分などになるとどうしても、色々なスクリプトなどが付属されるので、どんどんどこにあるんだっけ?といったものが増えてきます。
その際に使用すると便利かもしれません。

あと処理的に全オブジェクトに対してGetComponentをするので、かなり重くなりがちです。

一応以下サンプルコード(一部抜粋)を乗っけますが、これだけじゃ動きません。
Buttonなどは、GUI.Buttonなどを使いやすい形にしているだけなので、そこはうまく工夫をしてください。

サンプルコード
 //表示部分
   void OnGUI()
    {
        var _setting_script = EditorGUILayout.ObjectField(script, typeof(Object),false, GUILayout.ExpandWidth(true));
        Button("状態クリア",clear_status, GUILayout.ExpandWidth(true));
        Button("探索開始!",find_object, GUILayout.ExpandWidth(true));
        ScrollVirticalBlock(ref scroll_pos, show_pickup_gameobjs, GUILayout.ExpandWidth(true));

        if(GUI.changed)
        {
            if(null != _setting_script as MonoScript)
            {
                if(script != null)
                {

                }
                script = _setting_script;
            }
        }
    }

   //取得したものを表示する部分
   private void show_pickup_gameobjs()
    {
     for (int _index = 0;_index < _count;_index++)
        {
            GameObject _obj = pick_up_g_obj[_index];
            EditorGUILayout.ObjectField(_obj, typeof(GameObject),false, GUILayout.ExpandWidth(true));
            //FIXME:同数じゃない場合は考慮してないので、注意
            EditorGUILayout.BeginHorizontal();
            {
                Space();
                string _display_name = pick_up_comp[_index].ToString();
                string _base_name = pick_up_comp[_index].GetType().BaseType != typeof(MonoBehaviour).GetType() ? ":" + pick_up_comp[_index].GetType().BaseType.ToString() : "";
                if (_base_name.Length > 0)
                {
                    _display_name = _display_name + _base_name;
                }
                Label(_display_name, GUILayout.ExpandWidth(true));


            }
            EditorGUILayout.EndHorizontal();
        }
     }

/// <summary>
    /// ヒエラルキーの中から探す
    /// </summary>
    private void get_game_object_all_in_hierarchy()
    {
        Scene _scene = SceneManager.GetActiveScene();
        Debug.Log("serch_name " + script.name);
        //取得したゲームオブジェクトを全て拾ってきてGetComponetしてあるか調べる
        //foreach (Object _obj in Resources.FindObjectsOfTypeAll(typeof(Object)))
        foreach(GameObject _obj in _scene.GetRootGameObjects())
        {
            //ゲームオブジェクトじゃなかったら無視
            GameObject _g_obj = _obj as GameObject;
            if(null == _g_obj)
            {
                continue;
            }

            Component _component = _g_obj.GetComponent(script.name);
            if (null == _component)
            {
                continue;
            }
       
      //リストに保存していく。
            pick_up_g_obj.Add(_component.gameObject);
            pick_up_comp.Add(_component);
        }
    }

参考サイト

Hierarchy上のゲームオブジェクトを全て取得。
http://buravo46.hatenablog.com/entry/2014/12/31/064658

型、インスタンスが派生クラスかどうかを調べる。
http://smdn.jp/programming/netfx/tips/check_type_isassignablefrom_issubclassof/

Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account log in.