見出し画像

AIにUnityエディタ拡張作ってもらった

自己紹介

Luraです。
普段はVRChat向けのワールドを制作している3DCGデザイナーです。

自宅用に使えるワールドをBoothで販売してたりします。
UnityBlenderをバリバリ使ってフルスクラッチのワールドを制作をするので、その補助になるツールAI先生に作ってもらおうと思った次第です。

ChatGPTすごい

皆さんご存知ChatGPT、アイディア出しとか大喜利とか人生相談とか色々使えて楽しいですが、実用的なとこではプログラムも書いてくれます

以前Blenderのアドオンを作ってもらってみたりもしてました。

私はあくまでもデザイナーなので、プログラミングはメインではなく、あまりそちらに時間をかけたくない性分です。(モデリングしてたい)
一度必要に迫られてC#で個人用にエディタ拡張を作ったことはありますが、それ以降は特に触ってませんでした。

なので作り方の作法も何もかも忘れてしまっているので、AIに丸投げしてしまおう作戦!
ということでちょうどほしい機能をAIにお願いしてみることに。

作って欲しいもの

制作中のワールド

そもそもエディタ拡張で何がしたかったかというと、マテリアルの入れ替えです。
ワールドをPCVR向けに作っていたので、MetaQuestで快適に動くようにするには軽量なシェーダーを使っているマテリアルに入れ替える必要があるのです。
そして、標準機能だと使われているマテリアルを検索することができず、1つ1つ使われているマテリアルを目視で確認して入れ替え…という大変非効率なことをしていました。抜けもれが出てしまう可能性もあります。
実は何か知らないだけでやり方があるかもしれませんが、せっかくなので、AIに頼んでみましょう
ということで、やってみます。

実際にやってみた

質問内容

Unityで、開いているシーン内に存在するオブジェクトを探索して、MeshRendererに適用されているマテリアルの中から指定した名前と同じマテリアルを選ぶことができる、UnityのスクリプトをC#で作成して下さい。
また、それをEditorWindowから使えるボタンにして下さい。

ChatGPTに聞いてみた

なるべくやってほしいことを具体的に書くのがコツみたいです。
でも、ほんとにそれだけです。

using UnityEngine;
using UnityEditor;

public class FindMaterial : EditorWindow
{
    string materialName = "";

    [MenuItem("Window/Find Material")]
    public static void ShowWindow()
    {
        GetWindow<FindMaterial>("Find Material");
    }

    private void OnGUI()
    {
        materialName = EditorGUILayout.TextField("Material Name:", materialName);

        if (GUILayout.Button("Find Material"))
        {
            FindMaterialInScene();
        }
    }

    void FindMaterialInScene()
    {
        MeshRenderer[] renderers = Resources.FindObjectsOfTypeAll<MeshRenderer>();

        foreach (MeshRenderer renderer in renderers)
        {
            foreach (Material material in renderer.sharedMaterials)
            {
                if (material.name == materialName)
                {
                    Debug.Log("Found Material: " + material.name + " on object: " + renderer.gameObject.name);
                    Selection.activeGameObject = renderer.gameObject;
                    return;
                }
            }
        }
        Debug.Log("Material not found in scene");
    }
}

出てきたコードがこちら。
解説↓

このスクリプトは、EditorWindowから使用することができる、シーン内のオブジェクトを探索し、指定された名前のマテリアルを持つMeshRendererを見つけることができます。
また、それをEditorWindowから使えるボタンにしています。

上記のスクリプトをUnityのEditorフォルダに入れ、Unityを再生し、Window>Find Materialを選択してください。
これにより、Find Materialという名前のEditor Windowが開かれ、そこにはMaterial Nameというテキストボックスと、Find Materialという名前のボタンがあります。
テキストボックスにマテリアルの名前を入力し、Find Materialボタンをクリックして、その名前のマテリアルを持つオブジェクトを見つけることができます。

ChatGPT

出てきた。

半信半疑でC#スクリプトにコピペして、保存すると…

エディタ拡張が追加されてる!

起動すると…

ウィンドウが出てきた

個人的にはこれだけで感動ですが、試しに使ってみることに。
おそらくこのMaterial Name に入れた名前が検索されるのでしょう。
(それすら指定してないのにいい感じに作ってくれるってすごくない…??)

とりあえず白い玉に使われているマテリアルがTestAという名前なので、選択してもらいましょう。

ぽちっとな
1つだけ選択された!

おおお!やった!!
本当に選択された!……ってあれ、1つだけしか選択されてないね?
これだと目的に沿わないので、修正してもらいます。

修正

修正要望

同じマテリアルが複数ある場合は同時に選択できるようにして下さい

回答

using UnityEngine;
using UnityEditor;

public class FindMaterial : EditorWindow
{
    string materialName = "";

    [MenuItem("Window/Find Material")]
    public static void ShowWindow()
    {
        GetWindow<FindMaterial>("Find Material");
    }

    private void OnGUI()
    {
        materialName = EditorGUILayout.TextField("Material Name:", materialName);

        if (GUILayout.Button("Find Material"))
        {
            FindMaterialInScene();
        }
    }

    void FindMaterialInScene()
    {
        MeshRenderer[] renderers = Resources.FindObjectsOfTypeAll<MeshRenderer>();
        GameObject[] selectedObjects = new GameObject[renderers.Length];
        int foundObjectsCount = 0;

        foreach (MeshRenderer renderer in renderers)
        {
            foreach (Material material in renderer.sharedMaterials)
            {
                if (material.name == materialName)
                {
                    selectedObjects[foundObjectsCount] = renderer.gameObject;
                    foundObjectsCount++;
                    break;
                }
            }
        }
        if (foundObjectsCount > 0)
        {
            Selection.objects = selectedObjects.Take(foundObjectsCount).ToArray();
            Debug.Log("Found " + foundObjectsCount + " objects with Material: " + materialName);
        }
        else
        {
            Debug.Log("Material not found in scene");
        }
    }
}

上記のスクリプトは、シーン内のオブジェクトを探索し、指定された名前のマテリアルを持つMeshRendererを見つけることができます。
そして、同じ名前のマテリアルを持つ複数のオブジェクトを同時に選択することができます。

特に、同じ名前のマテリアルが複数ある場合に対応するために、配列を使用し、選択したオブジェクトの数をカウントしました。
そして、最終的に、Selection.objectsに選択したオブジェクトを設定します。
これにより、Editor上で選択された複数のオブジェクトを同時に操作することができるようになります。

ChatGPT

らしいです。
ということで何も考えずにコピペして保存!!!!

エラー

あれー、さっきまで動いてたのにエラーが出てしまった…
原因もよくわからないので聞いてみることに。

エラーを直してもらうように依頼する

以下のエラーが発生しました。修正して下さい。
error CS1061: 'GameObject[]' does not contain a definition for 'Take' and no accessible extension method 'Take' accepting a first argument of type 'GameObject[]' could be found (are you missing a using directive or an assembly reference?)

回答

using UnityEngine;
using UnityEditor;
using System.Linq;

public class FindMaterial : EditorWindow
{
    string materialName = "";

    [MenuItem("Window/Find Material")]
    public static void ShowWindow()
    {
        GetWindow<FindMaterial>("Find Material");
    }

    private void OnGUI()
    {
        materialName = EditorGUILayout.TextField("Material Name:", materialName);

        if (GUILayout.Button("Find Material"))
        {
            FindMaterialInScene();
        }
    }

    void FindMaterialInScene()
    {
        MeshRenderer[] renderers = Resources.FindObjectsOfTypeAll<MeshRenderer>();
        GameObject[] selectedObjects = new GameObject[renderers.Length];
        int foundObjectsCount = 0;

        foreach (MeshRenderer renderer in renderers)
        {
            foreach (Material material in renderer.sharedMaterials)
            {
                if (material.name == materialName)
                {
                    selectedObjects[foundObjectsCount] = renderer.gameObject;
                    foundObjectsCount++;
                    break;
                }
            }
        }
        if (foundObjectsCount > 0)
        {
            Selection.objects = selectedObjects.Take(foundObjectsCount).ToArray();
            Debug.Log("Found " + foundObjectsCount + " objects with Material: " + materialName);
        }
        else
        {
            Debug.Log("Material not found in scene");
        }
    }
}

上記のスクリプトは、System.Linqを使用していないため、'GameObject[]' does not contain a definition for 'Take' errorが発生します。

ChatGPT

ほうほう。(よくわからない)
ということでこれもコピペして保存。
エラーは消えました!

よくわからないエラーを聞いたら原因教えてくれて直してくれるのめっちゃ便利じゃん…

ヨシ!

さっきと同じようにTestAのマテリアルを検索すると…

複数選択された!!

うごいたぁぁぁぁ!!!
やった!!!!これで作業が楽になる!!!!!
ありがとうChatGPT!!!!!

使える!!

まとめ

ChatGPTに頼んだ場合、必ずしも一発で動くスクリプトを作ってくれるわけではないみたいなので、状況に応じて修正をお願いする感じが良さそうです。
1発で出してくれないなど多少面倒なところもありますが、だとしても!
本来わからないものをいい感じにやってくれちゃうのは本当に助かります!
特にプログラミングよりもモデリングなどのアート方面に集中したいデザイナーにとっては救世主です。

もっといろいろなことを余裕でできるとは思うのですが、私にとって便利な道具を1つ作ってくれたのが嬉しくて記事にしてしまいました。
読んでいただきありがとうございました~

この記事が気に入ったらサポートをしてみませんか?