LoginSignup
2
0

More than 1 year has passed since last update.

【Unity】Editモードでマテリアルのプロパティをスクリプトから変更する

Posted at

UnityエディターのEditモードでマテリアルのプロパティをスクリプトから変更したい場合、少し工夫がいります。

まず、良くないパターンです。以下のコードはMeshRenderer#materialで取得したマテリアルの_BaseColorプロパティをUpdate内で設定しています。[ExecuteAlways]属性を付与しているので、EditモードでもUpdateが呼ばれます。

using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif

[ExecuteAlways]
[RequireComponent(typeof(MeshRenderer))]
public class MaterialPropertyChanger01 : MonoBehaviour
{
    [SerializeField] Color color = Color.white;
    MeshRenderer meshRenderer;
    Material material;

    void Start()
    {
        meshRenderer = GetComponent<MeshRenderer>();
    }

    void Update()
    {
        if (material == null)
        {
            material = meshRenderer.material;
        }
        material.SetColor("_BaseColor", color);
    }

    void OnDestroy()
    {
        if (material != null)
        {
#if UNITY_EDITOR
            if (EditorApplication.isPlaying)
            {
                Destroy(material);
            }
            else
            {
                DestroyImmediate(material);
            }
#else
            Destroy(material);
#endif
        }
    }
}

今回の記事とは直接関係ないですが、MeshRenderer.materialを取得した場合は自分で破棄する必要があり、またEditモードではDestroyではなくDestroyImmediateを使わなければいけないのでコードが冗長になっています。
【Unity】Renderer.materialで取得したマテリアルは自分で破棄しないとリークする話 - LIGHT11

このスクリプトでもマテリアルのプロパティを更新できますが、コンソールに以下のエラーが出るようにメモリリークが発生します。

Instantiating material due to calling renderer.material during edit mode. This will leak materials into the scene. You most likely want to use renderer.sharedMaterial instead.

エラーの指示に従って、MeshRenderer#sharedMaterialを使ってみます。

using UnityEngine;

[ExecuteAlways]
[RequireComponent(typeof(MeshRenderer))]
public class MaterialPropertyChanger02 : MonoBehaviour
{
    [SerializeField] Color color = Color.white;
    MeshRenderer meshRenderer;

    void Start()
    {
        meshRenderer = GetComponent<MeshRenderer>();
    }

    void Update()
    {
        meshRenderer.sharedMaterial.SetColor("_BaseColor", color);
    }
}

この場合エラーはでませんが、マテリアルアセット自体のプロパティが書き換わってしまい、同じマテリアルを複数のオブジェクトで使いまわすことが難しくなります。

エラーを出さずに、かつマテリアルアセット自体のプロパティを書き換えたくない場合は次のようにします。全体的に冗長ですが見てほしいのはUpdateelse内で、自分でMeshRenderer#sharedMaterialからマテリアルのインスタンスを作成して、それを新たなMeshRenderer#sharedMaterialに設定しています。

using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif

[ExecuteAlways]
[RequireComponent(typeof(MeshRenderer))]
public class MaterialPropertyChanger03 : MonoBehaviour
{
    [SerializeField] Color color = Color.white;
    MeshRenderer meshRenderer;
    Material material;

    void Start()
    {
        meshRenderer = GetComponent<MeshRenderer>();
    }

    void Update()
    {
        if (material == null)
        {
#if UNITY_EDITOR
            if (EditorApplication.isPlaying)
            {
                material = meshRenderer.material;
            }
            else
            {
                material = new Material(meshRenderer.sharedMaterial);
                meshRenderer.sharedMaterial = material;
            }
#else
            material = meshRenderer.material;
#endif
        }
        material.SetColor("_BaseColor", color);
    }

    void OnDestroy()
    {
        if (material != null)
        {
#if UNITY_EDITOR
            if (EditorApplication.isPlaying)
            {
                Destroy(material);
            }
            else
            {
                DestroyImmediate(material);
            }
#else
            Destroy(material);
#endif
        }
    }
}

以下の記事を参考にしました。

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0