[Unity] modelのmaterialをmask用のmaterialに切り替える

# 初めに
Unityを使ったアニメーション制作が最近でもチラホラ記事になる事があります。
そこで出力した絵にレタッチする際にマスク画像が必要になる事があります。

## イメージはこんな感じ

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/37068/73fa38f0-8d55-d46a-3f47-3574596634b6.png)


Unityちゃんの元に設定されているmaterialからスクリプトで切り替えることで、マスクみたいな
モデルにします。

### 機能説明

機能として、モードを２つ用意しました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/37068/4536e859-524f-8455-f28e-24b7d460824a.png)


※Colorモード
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/37068/17d0a9cd-206a-ebd7-a2b5-4440292d7e6e.png)

ChangerMaterialsに設定される、Colorはランダムで指定されています。
そのカラーを使って、設定されます。
MaterialShaderとMaterialShaderColorNameはランダムでスクリプトで
指定するShader名とColorのプロパティ名です。
Materialをプログラムで生成するので、マテリアルに適用するshderと色を指定する
プロパティ名を指定してあげます。


※Nameモード
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/37068/5aa87833-e726-dea1-5340-383c1778811c.png)

ChangeSearchNameにマテリアル名の後ろに指定されているマテリアルを
探し、設定します。


#### スクリプト

```c#
using UnityEditor;
using UnityEngine;
using System.Linq;
using System.Collections.Generic;

[System.Serializable]
public class ChangerData
{
    public SkinnedMeshRenderer ren;
    public Material mat;
    public int matIndex;
    public Color col;
}

public class MaterialChanger : EditorWindow
{
    [MenuItem("Tool/MaterialChanger")]
    public static void Open()
    {
        var window = GetWindow<MaterialChanger>();
        window.Show();
    }

    public enum eSelect
    {
        Color,
        Name,
    };
    private GameObject _gameObject = null;
    //スクロール位置
    private Vector2 _scrollPosition = Vector2.zero;
    private bool _changeToggle = true;
    private readonly int _directoryIndex = 1;
    private eSelect _select = eSelect.Color;
    private string _changeMaterialShader = "Unlit/Color";
    private string _changeMaterialShaderColorName = "_Color";
    private string _changeSearchName = "_change";

    [SerializeField]
    private List<ChangerData> _changerMaterials = new List<ChangerData>();

    /// <summary>
    /// 
    /// </summary>
    void OnGUI()
    {
        // 自身のSerializedObjectを取得
        var so = new SerializedObject(this);
        //描画範囲が足りなければスクロール出来るように
        _scrollPosition = EditorGUILayout.BeginScrollView(_scrollPosition);

        EditorGUI.BeginChangeCheck();

        _gameObject = EditorGUILayout.ObjectField("root object", _gameObject, typeof(GameObject), true) as GameObject;
        if (EditorGUI.EndChangeCheck() && _gameObject)
        {
            Init();
        }

        if (_gameObject)
        {
            _select= (eSelect)EditorGUILayout.EnumPopup( "select", _select);
            switch (_select)
            {
                case eSelect.Name:
                    _changeSearchName = EditorGUILayout.TextField("ChangeSearchName", _changeSearchName);
                    break;
                case eSelect.Color:
                    _changeMaterialShader = EditorGUILayout.TextField("MaterialShader", _changeMaterialShader);
                    _changeMaterialShaderColorName = EditorGUILayout.TextField("MaterialShaderColorName", _changeMaterialShaderColorName);
                    break;
            }
            _changeToggle = EditorGUILayout.Toggle("ChangeToggle", _changeToggle);
            
            //切り替え
            if (GUILayout.Button("MaterialChang"))
            {
                MaterialChang();
            }
            EditorGUILayout.PropertyField(so.FindProperty("_changerMaterials"), true);

            
        }
        
        so.Update();

        so.ApplyModifiedProperties();
        //スクロール箇所終了
        EditorGUILayout.EndScrollView();
    }

    /// <summary>
    /// 
    /// </summary>
    private void Init()
	{
        // Listクリア
        _changerMaterials.Clear();
        // 指定オブジェクト以下のレンダラーを取得
        var rens = _gameObject.GetComponentsInChildren<SkinnedMeshRenderer>();

        foreach( var ren in rens)
        {
            var mats = ren.sharedMaterials;
            var i = 0;
            foreach (var mat in mats)
            {
                ChangerData data = new ChangerData();
                data.mat = mat;
                data.matIndex = i;
                data.ren = ren;
                data.col = new  Color(Random.value, Random.value, Random.value, 1.0f);
                _changerMaterials.Add(data);
                i++;
            }
        }
    }
    /// <summary>
    /// 切り替え用のデータからマテリアル取得
    /// </summary>
    /// <param name="data"></param>
    /// <returns></returns>
    private Material GetNameToMaterial( ChangerData data )
    {
        var name = data.mat.name;
        if (_changeToggle)
        {
            name += _changeSearchName;
        }
        
        //名前から切り替え用のマテリアルを探してロードする
        var guids = AssetDatabase.FindAssets(name);
        if (guids.Length == 0)
            return null;
        var paths = guids.Where(_ => AssetDatabase.GUIDToAssetPath(_).Contains(".mat")).Select(guid => AssetDatabase.GUIDToAssetPath(guid)).ToArray();
        if (paths.Length == 0)
            return null;
        var objects = AssetDatabase.LoadAllAssetsAtPath(paths[0]);
        if (objects.Length == 0)
            return null;

        // LoadAllAssetsAtPathでロードしたオブジェクトのどこにマテリアル設定されているか調べる
        var objIndex = 0;
        for (var i = 0; i < objects.Length; i++)
        {
            if (objects[i] is Material)
            {
                objIndex = i;
                break;
            }
        }
        return objects[objIndex] as Material;
    }
    /// <summary>
    /// 
    /// </summary>
    private void MaterialChang()
    {
        //Debug.Log("MaterialChang");

        foreach (var changerMaterial in _changerMaterials)
        {
            // マテリアル作成
            var mat = new Material(Shader.Find(_changeMaterialShader));
            //元に戻すために、マテリアル名を指定する
            var mats = changerMaterial.ren.sharedMaterials;
            
            //ランダムカラーを指定するか、名前でスワップするか
            switch (_select)
            {
                case eSelect.Color:
                    mat.name = changerMaterial.mat.name;
                    break;
                case eSelect.Name:
                    mat = GetNameToMaterial(changerMaterial);
                    break;
            }
            // 設定するマテリアルがnullだった場合は、切り替えをしないでcontinue
            if (mat == null)
                continue;
            // マテリアルをスワップするか、切り替え前のマテリアルを指定するか
            mats[changerMaterial.matIndex] = (_changeToggle ?  mat: changerMaterial.mat);

            if (_changeToggle && eSelect.Color == _select)
            {
                mat.color = changerMaterial.col;
            }
            else
            {
                mat.color = Color.white;
            }

            //render Materialにセット
            changerMaterial.ren.sharedMaterials = mats;
        }
        AssetDatabase.Refresh();
    }
}

```
##### スクリプトの説明


LoadAllAssetsAtPathどうやらロードしたデータに適応されている、コンポーネント全部引っ張ってきてくれる
結構優秀なやつでした。ただ、欲しいコンポーネントなのかは、中身全部調べて見るしか無いでした。

https://kan-kikuchi.hatenablog.com/entry/AssetDatabase_LoadAsset


プロジェクトの中のオブジェクトを探して参照する手段として
AssetDatabase.FindAssetsでオブジェクトのGUIDを調べて
AssetDatabase.GUIDToAssetPathでGUIDからオブジェクトのパスしらべて
AssetDatabase.LoadAllAssetsAtPathパスからアセットをロードします。

https://docs.unity3d.com/jp/current/ScriptReference/AssetDatabase.FindAssets.html

https://docs.unity3d.com/jp/current/ScriptReference/AssetDatabase.GUIDToAssetPath.html

https://docs.unity3d.com/jp/current/ScriptReference/AssetDatabase.LoadAllAssetsAtPath.html



##### 最後に
プロジェクトの中からロードするのをもっといい方法がないのかなって思いました。
アニメのレタッチするように必要と言われて作ったのですが、これがどんな風に利用されるかは。。
まだわかってませんので、もしこんな機能が欲しいって言ってくれたらアップデートします。