[Unity] MaterialPropertyBlockを用いて、オブジェクトを半透明化にする

# はじめに
Unity Asset Storeで入手した[assets](https://assetstore.unity.com/packages/3d/environments/urban/polygon-city-low-poly-3d-art-by-synty-95214)のGameObjectの透明度を変更しようとしたとき、1つのオブジェクトのマテリアル変更が他のオブジェクトにも反映される現象に遭遇しました。これは、複数のオブジェクトが1つのマテリアルを共有していたことが原因です。

このような場合に使えるのが、[MaterialPropetyBlock](https://docs.unity3d.com/ScriptReference/MaterialPropertyBlock.html)です。
これを用いることで、同じマテリアルを使用している複数のオブジェクトに対して、それぞれ独自のプロパティ値を設定することができます。

# 実装
今回は、半透明にしたいオブジェクトに適用するスクリプト名をSampleMaterialとしました。

ゲームが開始するとき、親オブジェクトとその子オブジェクトのMeshRendererを取得します。
```c#
private MeshRenderer[] meshRenderers;//親 子オブジェクトを格納。

void Awake()
{
    meshRenderers = this.GetComponentsInChildren<MeshRenderer>();
}
```

### MaterialPropertyBlockの準備
オブジェクトの色を保持するためにColor変数を設定します。今回は透明度のみを変更するので、初期値は白色にします。
```c#
private Color color = Color.white;
```
次に、MaterialPropertyBlockを初期化します。MaterialPropertyBlockは複数のオブジェクトに対して個別のプロパティ値を設定するために使用します。これにより、複数のオブジェクトが1つのマテリアルを共有しながらも、個々のオブジェクトに異なるプロパティを持たせることができます。
```c#
private MaterialPropertyBlock m_mpb;

public MaterialPropertyBlock mpb
{
    // m_mpbがnullの場合にのみ新しいMaterialPropertyBlockを作成します。
    get { return m_mpb ?? (m_mpb = new MaterialPropertyBlock()); }
}

```

### オブジェクトを透明にする
まず、Colorのアルファ値を任意の値(今回は0.15f)に設定し、オブジェクトを半透明にします。
```c#
color.a = 0.15f;
```
その後、この色をMaterialPropertyBlockに設定します。ここではShaderの"_Color"プロパティを設定しています。
```c#
mpb.SetColor(Shader.PropertyToID("_Color"), color);
```
そして、取得したすべてのMeshRendererに対して、このMaterialPropertyBlockを適用します。
```c#
for (int i = 0; i < meshRenderers.Length; i++)
{
    meshRenderers[i].GetComponent<Renderer>().material.shader = Shader.Find("Transparent/Diffuse ZWrite");
    meshRenderers[i].SetPropertyBlock(mpb);
}
```
"Transparent/Diffuse ZWrite"を選んだ理由は、透明度を変更するプロパティーがあるからです。

### オブジェクトを不透明に戻す
オブジェクトを不透明に戻すためには、Colorのアルファ値を1に戻すだけです。その後、上記と同様にMaterialPropertyBlockを更新し、MeshRendererに適用します。
```c#
color.b = 1f;
mpb.SetColor(Shader.PropertyToID("_Color"), color);
for (int i = 0; i < meshRenderers.Length; i++)
{
    meshRenderers[i].GetComponent<Renderer>().material.shader = Shader.Find("Mobile/Diffuse");
    meshRenderers[i].SetPropertyBlock(mpb);
}
```

### ソースコード
以下に全体のソースコードを示します。
```C#
public class SampleMaterial : MonoBehaviour
{

    private Color color = Color.white;

    //親 子オブジェクトを格納。
    private MeshRenderer[] meshRenderers;
    private MaterialPropertyBlock m_mpb;

    public MaterialPropertyBlock mpb
    {
        get { return m_mpb ?? (m_mpb = new MaterialPropertyBlock()); }
    }

    void Awake()
    {
        //子オブジェクトと親オブジェクトのmeshrendererを取得
        meshRenderers = this.GetComponentsInChildren<MeshRenderer>();
    }
    public void ClearMaterialInvoke()
    {
        color.a = 0.15f;
        
        mpb.SetColor(Shader.PropertyToID("_Color"), color);
        for (int i = 0; i < meshRenderers.Length; i++)
        {
            meshRenderers[i].GetComponent<Renderer>().material.shader = Shader.Find("Transparent/Diffuse ZWrite");
            meshRenderers[i].SetPropertyBlock(mpb);
        }
    }
    public void NotClearMaterialInvoke()
    {
        color.b = 1f;
        mpb.SetColor(Shader.PropertyToID("_Color"), color);
        for (int i = 0; i < meshRenderers.Length; i++)
        {
            meshRenderers[i].GetComponent<Renderer>().material.shader = Shader.Find("Mobile/Diffuse");
            meshRenderers[i].SetPropertyBlock(mpb);
        }
    }
}
```

# 関連記事
https://qiita.com/susasaki/items/24c71a6023c20ae4b547

## GitHub
https://github.com/subarunrun0812/CityEater