【Unity】SceneView上のカーソル位置のオブジェクトのマテリアルを変えるEditor拡張

# 概要
SceneView上のカーソル位置のオブジェクトを取得し、そのオブジェクトのマテリアルを複数の任意のマテリアルに変更できるようにする。
ショートカットをいくつか用意し、それぞれにマテリアルを割り当てて、そのマテリアルに変えれるようにする。
![MaterialChanger.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3686460/94ff8c64-6438-b327-9c3f-585131389f40.gif)
# Windowの作成
変更したいマテリアルを新しいタブから設定できるようにします。
```MaterialChanger.cs
using UnityEditor;
using UnityEngine;

public class MaterialChanger : EditorWindow
{
    static Material _materialQ;
    static Material _materialE;
    static Material _materialR;

    [MenuItem("Tools/MaterialChanger/MaterialChanger Window")]
    private static void Open()
    {
        MaterialChanger materialChanger = GetWindow<MaterialChanger>();
        materialChanger.Show();
    }

    private void OnGUI()
    {
        _materialQ = EditorGUILayout.ObjectField("Material Q", _materialQ, typeof(Material), false) as Material;
        _materialE = EditorGUILayout.ObjectField("Material E", _materialE, typeof(Material), false) as Material;
        _materialR = EditorGUILayout.ObjectField("Material R", _materialR, typeof(Material), false) as Material;
    }
}
```
新しいWindowを開く関数`Open`を定義し、Unityのツールバーから実行できるようにします。
`OnGUI`でWindowを編集します。
`EditorGUILayout.ObjectField`でオブジェクトの取得、表示をします。
QERをショートカットにしたのでこの3つの変数名になっている。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3686460/7849a350-cb17-7ecb-d1f0-d3adb3e1de8b.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3686460/49d3fcb1-68ae-9de8-02cc-115d039e1691.png)
ツールバーにメニューが表示され、Windowが開ける。
# マテリアル変更処理
### SceneView上のカーソル位置のオブジェクトを取得
`[InitializeOnLoadMethod]`でSceneView上のOnGUIを登録することが出来る。
引数SceneViewの関数を登録し、Eventからマウスの位置を取得する。
```cs
        static Vector2 _mousePos;

        [InitializeOnLoadMethod]
        private static void Initialize()
        {
            SceneView.duringSceneGui += SceneViewOnGUI;
        }

        static void SceneViewOnGUI(SceneView sceneView)
        {
            Event e = Event.current;
            _mousePos = e.mousePosition;
        }
```
### マテリアル変更
`HandleUtility.PickGameObject`でマウス位置のオブジェクト取得。
`Renderer`の`sharedMaterial`からマテリアルの変更
```cs
        static void ChangingMaterialQ()
        {
            var picked = HandleUtility.PickGameObject(_mousePos, false);

            if (picked && picked.TryGetComponent(out Renderer renderer))
            {
                renderer.sharedMaterial = _materialQ;
            }
        }
```
### ショートカットで呼び出す
```cs
        [MenuItem("Tools/MaterialChanger/Change Material on E #e")] // Shift+E
        static void ChangingMaterialE()
        {
            // 中略
        }
        
        [MenuItem("Tools/MaterialChanger/Change Material on R #&r")] // Shift+Alt+R
        static void ChangingMaterialR()
        {
            // 中略
        }
```
新しいアイテムメニューを追加します。追加する項目の文字列の最後の記号でショートカットを設定できます。
%でCtrl、#でShift、&でAltを指定できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3686460/457b5320-383d-4844-3fe0-9c57d7c4614f.png)
アイテムメニューに追加することでEditorからもショートカットを変更できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3686460/927e0d20-322c-3ec4-a6bf-9e8d65ef8647.png)
これで使うことが出来ます。
# 注意点
### エラーが出る
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3686460/a316862c-2870-058d-5d5b-e19e35ac414b.png)
詳しい理由はわからなかったのですがおそらく`HandleUtility.PickGameObject`をOnGUI外で使ったからだと思います。`SceneViewOnGUI`内でキー判定して呼び出したらエラーはなくなりましたが、Unityのショートカットとして使用できなくなります。エラーが出ても問題なく使用できるので僕は無視して使いました。
```cs
        static void SceneViewOnGUI(SceneView sceneView)
        {
            Event e = Event.current;
            _mousePos = e.mousePosition;

            if (e.keyCode == KeyCode.Q)
            {
                ChangingMaterialQ(); // ここで呼び出す
            }
        }
```
### 変更が保存されない
これを使って変更を加えても変更として認識されず、保存されません。これは現在のシーンを右クリックなどで直接SaveSceneすると保存されます。また、プレハブの変更はApplyAllでも保存できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3686460/9be2f165-7861-b37f-f354-82fbf84dd85c.png)
# 参考
https://kan-kikuchi.hatenablog.com/entry/Hotkey

https://redhologerbera.hatenablog.com/entry/2022/09/10/214552