見出し画像

Unity : Changing material from script

はじめに

Unity上で、Dynamicにmaterialを変更する方法として、以前、Klak syphonを使った方法を紹介しました。
これは、openframeworksなどで複雑なtextureを作成し、これをRealtimeにUnity側へ送る方法です。

一方、もっとシンプルに、ただmaterialの色をDynamicに変更したいケースがあります。

今回は、これを実現するためのtipsです。

環境

* MacBook Pro(Retina, 15-inch Mid 2015)
* Unity2018.2.2f1
* XCode v7.2

materialのPropertyをscriptから変更

例えばマテリアルの色を変える場合、以下のコードで行えます。

Color color = new Color(1.0f, 0, 0, 1.0f);
GetComponent<Renderer>().material.color = color;

ただしこれは、_Color という名前でプロパティが設定されているシェーダにのみ有効です。
より一般的には、以下のようなCodeとなります

Color color = new Color(1.0f, 0, 0, 1.0f);
GetComponent<Renderer>().material.SetColor("_Color", color);

第一引数はPropertiesの名前です。
また、SetFloatやSetVectorなどを使う事で他のプロパティも変更できます。

Propertyの確認

プロパティは、マテリアル右上の歯車からSelect Shaderを選択し、

画像1

下の方にあるPropertiesから確認できます。

左側がプロパティの名前、右側が型になります。

画像2

プロパティが存在するか

プロパティの存在確認を行いたい場合はHasPropertyを使います。

if(GetComponent<Renderer>().material.HasProperty("_TintColor")){
    GetComponent<Renderer>().material.SetColor("_TintColor", Color.red);
}

sharedMaterial

Renderer.materialを変更すると、内部でマテリアルをコピーして作成し、その値を変更します。
同じマテリアルを使用しているObjectを全て変更したい場合、sharedMaterialを使います。

GetComponent<Renderer>().sharedMaterial.color = Color.red;

sharedMaterialはマテリアル自体の設定を変更しているので、シーンの実行を終了しても元に戻りません。

ご注意ください。

まとめ

 Renderer.sharedMaterialは、元のmaterial自体を操作する。
よって、同materialをapplyされた全てのObjectが同時に変更される。
また、scriptによって変更された"元のmaterial"は、実行後も変更されたままである点に注意。

Renderer.materialを操作すると、このObject専用のmaterialが、内部で自動複製され、これが操作される。

GameObjectを破棄しても、materialは破棄されないので、leakには注意すること。

official pageの使い方に乗っ取り、Start()で取得してこれを操作。OnDestroy()でmaterialを破棄すること。DestroyImmediateの方がいいのかな、と思ったけど、DestroyImmediateは非推奨のようです。official pageもDestroy( )で対応しているので、こちらがいいですね。

もっと考察

Renderer.materialのmemory leakが心配だったので、さらに考察した。
具体的には、"Resources.FindObjectsOfTypeAll"を使ってmaterialのResourceを表示しながらtestした

> 結果と考察

* Renderer.materialを使っても、実行中に"Resources.FindObjectsOfTypeAll(typeof(Material)).Length"が増えていくことはなかった(leakしてない)。

* "Resources.FindObjectsOfTypeAll(typeof(Material)).Length"の絶対値については、Scene中に配置したmaterialの数と合わず、不明

* unity editor上では、Start/Stop/Start...を繰り返す度、"Resources.FindObjectsOfTypeAll(typeof(Material)).Length"の初期値が増えていった(実行中には増えないが).

* 試しにexeを書き出し、これを実行してみたが、この時は、何度起動し直しても、毎回 同じ初期値(つまりeditorのみの問題)。Material意外のitem(e.g. GameObject)は、起動の度、増えるようなことは、ない。   


また、 "Resources.FindObjectsOfTypeAll(typeof(Material)).Length"について、
* Unity2017.4.11f1(Latest of 2017)までは、起動の度、同じ値(相変わらず、絶対値は不明)
*Unity2018.1.0f2(1st of 2018)から、起動の度に絶対値が1ずつ増える現象が発生

であることがわかった。

...Unity2018から発生したbug? or editor上で、Gabage Collectionをするタイミングや条件を最適化したのだろうか?            

結論

Unity2017を使おうかとも考えたが、以下の理由から最新(Unity2018.2.2f1)を使うこととしよう。

* 少なくとも、書き出されたexeは問題ない
* Klak Syphonが"Unity 2018.1"以降対応

「editor上の最適化が異なるだけ」であると信じるとしよう。
どなたか、知っている人がいたら教えてください。

Source Code

Source Codeは、以下にupしてあります。
https://github.com/SJ-magic-study-unity/MaterialCol_x_script
から、"ControlMaterialColor.cs"をダウンロードしてください。

編集後記

今、以前つくっていた音楽連動Systemをzeroからつくりなおしています。新Systemでは、Unity上に配置したLight用Objectを音楽に合わせて光らせようと思っています。このためには、materialの色だけでなく、Bloom効果や、emissionのDynamicな変更が必要になりますが、これらは次回、ご紹介します。

参考文献/ URL

* マテリアルのプロパティをスクリプトから変更【Unity】
* Color (unity official documentation)
* Unity でスクリプトから Renderer の Material を操作するとリークする件について
* Renderer.material(公式page:English)

もしよろしければ、サポートをお願いします! 頂いたサポートは、Creatorとしての活動費に充てさせて頂きます。