Unityでインディゲームを作る!

Unityでのゲーム制作を目指し、それに関わる話題についてのブログ

Unity ECS: Material Propertyの使い方。DOTSでマテリアルの値をコントロールしてヴィジュアル効果を作る!

 Unity ECSにおいては色々な機能がありますが、その中のひとつにEntityごとにマテリアルのプロパティを操作してヴィジュアル効果を得るというものがあります。そのためにあるのがMaterial Propertyです。マテリアルのプロパティをComponent DataとすることでECS上でその値をコントロールできるようになります。

MaterialProperty_FreezeBallSystem

Material Propertyでボールを凍らせたように色を変えてます。

 その使い方は簡単で、Shader Graph上でプロパティを作成し、Hybrid Per Instanceに設定し、そのプロパティのReferenceに対応した、Material Property属性を持つComponent Dataを作成する、というものです。

 これは公式ページに書かれている説明の通りなのですが、実は重要な工程が抜け落ちてしまっています。(一応、フィードバックでこの件について送りはしました)

 

 今回は、このMaterial Propertyの説明を補足しつつ、私が実験として作ったボールを凍らせるシステムを実例に、実際の使い方を解説していきたいと思います。

※記事内で使われる、Authoring Componentとは要するにBakerクラスを内部に持つMonoBehaviourクラスの事で、ゲームオブジェクトをEntityに変換する際の処理を行うクラスのことです。

 

プロパティを設定し、Component Dataを作る。

 まずはShader Graphにて、ECS上で操作したい値をプロパティとして作成します。今回は氷結具合を0.0-1.0までのIntensityとして扱うので、floatプロパティを追加します。

MaterialProperty_FreezeIntensity

 そして、Graph Inspector上の設定でScopeHybrid Per Instanceに設定します。Node Settingsを開いて、上から四番目の項目です。

MaterialProperty_HybridPerInstance

 そして、このプロパティのReference"_FreezeIntensity"を入力したMaterial Property属性を持つComponent Dataスクリプトを書きます。

MaterialProperty_FreezeIntensityData

 これらは公式マニュアルにも書かれていることですが、この三つの手順がまず必要になります。要するにDOTSに対応したマテリアルをShader Graphで作り、そのプロパティと結びついたComponent Dataを作成し、ECS System上でそのコンポーネントの値を操作してマテリアルの見た目を変えよう!ということなのです。

 

Material PropertyをAddComponentせよ!

 公式マニュアルにも書かれている、これら基本設定だけでは不十分で、ここからが重要ですが、MaterialMeshInfoを持つEntityにAddComponentで追加する必要があります。この説明が無いんですよね、なので本記事でやります!

 そのためにまずAuthoring Componentを作成し、目標のゲームオブジェクトに追加しましょう。

MaterialProperty_JumpBallInspector

目的のゲームオブジェクトにFreezeBallAuthoringを追加

 MaterialMeshInfoは(Skinned)Mesh Rendererを持つGameObjectが、Entityに変換された際に自動で追加されるComponentです。このコンポーネントを持ったEntityにMaterial Property componentが追加されていなければなりません。

MaterialProperty_FreezeBallBaker

Authoring Component内にあるBakerクラスを抜粋

 つまり、私の例で言えば、FreezeBallAuthoringを作り、目標のゲームオブジェクト(ここではボール)に取り付けてBakeメソッド内で、

AddComponent(entity, new FreezeIntensityData{ Intensity = 0.0f });

という風に追加する必要があります。これでシステム上でこの値(この例ではIntensity)を操作すれば、それに伴って該当するマテリアルのプロパティの値が変化するということです。

 

MaterialMeshInfoは何処にある?

 ここで一つの落とし穴があります。今回の例のような単純なオブジェクト(物理ボール)だと階層構造がなく、ルートに全てのコンポーネントがあり、つまりMeshRendererもルートにあるので、MaterialMeshInfoもルートに追加されるので問題ありません。

 しかし、階層構造がある複雑なゲームオブジェクトをEntityに変換されると、当然階層構造を持つEntityとなるので、(Skinned)MeshRendererがルートのゲームオブジェクトに無い場合は、MaterialMeshInfoを持つことになるEntityにMaterial Property componentを追加する必要があります。

 

 具体的な方法としては、Material Property用のAuthoring Componentを作って直接アタッチして、GetEntityでそのEntityを取得し直接AddComponentで追加する、というものです。

 ただし、そうなると今度はそのMaterial Propertyにシステムからどのようにアクセスし値を変更するか、という問題になるのですが今回は省略します。

 

ボールを凍らせる!

 今回の実例はトップのgif動画を見れば分かりますが、バウンドしているボールをクリックして、凍らせて動きを止めるというものです。凍った時のビジュアル表現として、ボールの色を変えるという処理をマテリアル上のプロパティを変更することで得ています。

MaterialProperty_NodeGraph

FreezeIntensityを使って、通常時と氷結時の色をブレンド

 そして、時間経過でどんどん氷が溶けていき、それに伴って元の色に戻っていく、という効果です。これをボール毎に行うことが出来るという、まさにECSにおける大量のオブジェクトへのマテリアル個別操作、Material Propertyの有効活用だと言えると思います。

 

 Entitiesが正式版となってからは今回の例で私がやった、World.DefaultGameObjectInjectionWorld.EntityManagerでEntityManagerを取得してMonoBehaviourからEntityを操作する、というのはバッドデザインとなっていますが、これはあくまで実験的プロジェクトなのでご了承ください!

MaterialProperty_FreezeBallControllerScript

MonoBehaviourからEntityへアクセスするスクリプトの一部

 クリックしたボールからFreezeBallDataにアクセスするのですが、内容については省略。システム内(FreezeBallSystem)では、溶けるまでの残り時間の処理と同時に、割合も計算して、それをIntensityとしてFreezeIntensityDataのIntensityに入力します。該当するマテリアル内部で、それをLerpノードのT(0.0 - 1.0)として使うことで、通常時の色と氷結時の色の補完を計算します。

MaterialProperty_PartOfFreezeBallSystem

FreezeIntesityなどを操作するシステムの一部

 これによりボールが凍り、そして溶けていくという効果を表現をしています。凍ったボールは空中で静止していますが、これはPhysicsMassOverrideを利用したものです。

 

まとめ

 以上でMaterial Propertyについての解説を終わります。DOTS上の大量のオブジェクトのマテリアルを個別に操作できるという夢のような機能であり、活用しない手は無いと思います。

 

 Shader Graph上でHybrid Per Instanceに設定、Material Property属性(プロパティのReference Nameを入力)を持つComponent Dataを作り、なんらかのAuthoring Component上でそのComponentをMaterialMeshInfoを持つEntityに追加する。システム上で、Material Propertyの値を操作する。

 これでECS(DOTS)上において、マテリアルによるビジュアル効果を得ることができる、という内容でした!