0からスタート!ゲーム開発ブログ

ゲーム開発に関する様々な記事を更新します

【Unity基礎テクニック】スクリプトを使ってマテリアルを細かく制御しよう

1. はじめに

こんにちは、azarashin です。 3D-CGのモデリングデータやゲームを初めて作成する時、のっぺりとした無地のオブジェクトを作成したことがあるかと思います。実際に遊んだことがあるゲームでは派手な見た目なのに対し、自分がこれから作成しようとしているコンテンツが無地なのをみて「全然ゲームっぽくない」と感じられたこともあるのではないでしょうか?

モデリングデータに色や模様をつけて表示するためにはマテリアルというものを作成し、モデリングデータに割り当てる必要があります。また、より見た目を派手にして画面に動きを出すにはマテリアル自体を細かく制御することもあります。

本記事ではUnity を用いてマテリアルを細かく制御する方法についてご紹介致します。

関連記事【Unityゲーム開発の基本】マテリアルを使ってオブジェクトの表面を変化させようはこちら

wizardia.hateblo.jp

2. マテリアルを作成する

まずはマテリアルを作成します。Project ウィンドウの適当な場所で右クリックし、Create -> Material を選択すると新しいマテリアルが生成されます。

マテリアルを作成する

生成されたマテリアルには何かしら適当な名前を付けておきましょう。

作成されたマテリアル

マテリアルを作成したら、そのマテリアルをレンダリングに反映させたいメッシュへと割り当てます。

マテリアルをメッシュに割り当てる

このままでは色の設定しかできませんので、適当なテクスチャ画像をマテリアルに割り当て、見た目をリッチにしてみます。

テクスチャをマテリアルに設定する

この時、画面右上のShader の欄が「Standard(Specular Setup)」になっていることに注意してください。

マテリアルには必ずShader が関連付けられます。 このShader とは、ライティング(光の当たり方)やテクスチャ・その他各種パラメータからメッシュの表面がどのように見えるかを計算するための一種のプログラム(シェーダプログラム)です。 今回の記事ではこのシェーダプログラムの詳細には触れませんが、ひとまず見た目を計算するための特殊なプログラムであることだけ覚えておいてください。 ここで選択されている「Standard(Specular Setup)」はUnity に標準搭載されているシェーダプログラムの一つです。

3. 透過させる

レンダリング処理を実行する際、マテリアルを割り当てたメッシュが透過する可能性のあるものかどうかは明確に区別されます。ここでは詳細を省きますが、以下の2点だけここではおさえておきましょう。

  • 透過処理を有効にすると表現力が向上すること
  • その代わりにレンダリングの負荷が上昇すること

透過処理を多用するとレンダリング負荷の上昇により操作性や見た目に影響がでますので、使用する場合は必要最小限にとどめておきましょう。

さて、Standard(Specular Setup)シェーダを使用している場合、Albedo パラメータの色値を変更すると割り当てたテクスチャの色味を変更できるのですが、Rendering Mode がOpaque になっているとα値を変更しても半透明になりません。

α値を設定しても半透明にならない

そこで、Rendering Mode をTransparent に変更します。これでAlbedo の色値のα値を変更することで、対象のメッシュが半透明になります。

Rendering Mode をTransparentにすると半透明になる

4. マテリアルのパラメータを動的に変更する

今度はAlbedo の色値のα値を時間の経過とともに変化させ、メッシュの透明度が変化するような仕掛けを入れてみます。 先ほどお話ししたシェーダプログラムには設定可能なパラメータがいくつか定義されています。まずはこれらのパラメータについて調べてみましょう。Shader 欄横のEdit ボタンをクリックします。

使用可能な変数を調べる

すると、シェーダプログラムの情報が表示されます。このうち、Properties の部分にシェーダプログラムのパラメータが表示されます。 「Standard(Specular Setup)」シェーダの場合、テクスチャの色味を変更する時は_Color パラメータを操作します。

使用可能な変数(Properties)

シェーダのパラメータを操作するにはMaterial クラスのSetColor メソッドを使用します。

using UnityEngine;

public class VariableMaterial : MonoBehaviour
{
    [SerializeField]
    Material _material;

    void Update()
    {
        float speed = 1.0f;
        float centerSize = 0.5f;
        float deltaSize = 0.5f;
        float alpha = centerSize + Mathf.Cos(Time.time * Mathf.PI * 2.0f * speed) * deltaSize;
        _material.SetColor("_Color", new Color(1.0f, 1.0f, 1.0f, alpha));
    }
}

上記のようなソースコードを記述し、Hierarchy 上に適当なGameObject を生成してこのソースコードコンポーネントとしてアタッチします。

スクリプトを割り当て

そして、このコンポーネントの中の_material フィールドに操作したいマテリアルをドラッグ&ドロップします。

マテリアルをスクリプトに割り当て

これでエディタを再生すると、マテリアルの色値パラメータが変化し、メッシュの透明度が変化するのがわかると思います。

スクリプト制御の確認

5. メッシュごとに異なるマテリアル変化をさせる

さて、先ほどのマテリアルを複数のメッシュに割り当てるとどうなるでしょう?

同じマテリアルが割り当てられているメッシュすべてに更新が反映されてしまう

これまでの操作でマテリアルをメッシュに関連付けると一つのマテリアルを複数のメッシュに関連付けてしまいます。 すると、マテリアルのパラメータを変化させたときにすべてのメッシュの見た目が同じように変化してしまいます。

同じマテリアルが複数のメッシュに作用する

さて、場合によってはメッシュごとに異なる変化をさせたいこともあると思います。 このような場合にはマテリアルを複製することでメッシュごとに独立した変化をさせることができるようになります。

マテリアルをメッシュごとに複製する

マテリアルを複製するには、MeshRenderer のmaterial メンバに対してSetColorを呼び出し、マテリアルを更新します。

        _meshRenderer.material.SetColor("_Color", new Color(1.0f, 1.0f, 1.0f, alpha));

Unity の場合、そのマテリアルが他のRenderer で使用されているとそのマテリアルを複製してくれます。

この挙動については本家のスクリプトリファレンスにも記載されています。

If the material is used by any other renderers, this will clone the shared material and start using it from now on.

これを踏まえてVariableMaterial クラスを見直してみましょう。

using UnityEngine;

public class VariableMaterial : MonoBehaviour
{
    [SerializeField]
    Material _originalMaterial;

    [SerializeField]
    float _speed = 1.0f;

    MeshRenderer _meshRenderer;


    private void Start()
    {
        // オリジナルのマテリアルをこのオブジェクト用に複製し、メッシュに割り当てる
        _meshRenderer = GetComponent<MeshRenderer>();
        _meshRenderer.material = _originalMaterial;
    }


    void Update()
    {
        float centerSize = 0.5f;
        float deltaSize = 0.5f;
        float alpha = centerSize + Mathf.Cos(Time.time * Mathf.PI * 2.0f * _speed) * deltaSize;

        // ある_meshRenderer.material に対してSetColor を最初に呼び出した時、パラメータの更新が他のマテリアルから独立するようマテリアルが複製される。
        _meshRenderer.material.SetColor("_Color", new Color(1.0f, 1.0f, 1.0f, alpha));
    }
}

メッシュごとに見た目が独立して変化することがわかるように、メッシュに割り当てたスクリプトのパラメータをそれぞれ別のものに設定しておきましょう。

複数のメッシュにスクリプトを割り当てる(パラメータは変える)

これで、複数のメッシュの見た目が独立して変化するようになります。

メッシュごとに独立したマテリアルアニメーションをさせた時の例

マテリアルをメッシュごとに複製することで表現力が向上しますが、実はマテリアルを複製するとレンダリング速度が低下するという副作用があります。 マテリアルの複製が本当に必要なのかどうかよく吟味して複製を行うようにしましょう。 もしマテリアルを複製すべきでない場合、意図せずマテリアルが複製されてしまわないよう material メンバの代わりにsharedMaterial の使用を検討してみましょう。

        _meshRenderer.sharedMaterial.SetColor("_Color", new Color(1.0f, 1.0f, 1.0f, alpha));

6. おわりに

本記事ではスクリプトを用いてマテリアルのパラメータを変化させ、時間の経過とともに見た目を変化させる例についてご紹介しました。 文中にて触れさせて頂いたのですが、マテリアルを使う際には「半透明にするかどうか」や「マテリアルを複製するかどうか」のように、 表現力とレンダリング速度とを天秤にかけなければならないことがあります。 表現力のみを追求しすぎていると、気づいたときには動きがカクカクのゲームになってしまうこともありますので、 マテリアルの設定をこまめに見直し、表現力とレンダリング速度のバランスがとれていることを確認しながらゲーム開発をすすめていきましょう。