3D を表示するにあたり、マテリアルは切っても切れない関係ですが、よくわからず使ってると膨大にメモリを使ってしまったり、メモリリークを起こしてしまう危険な存在(らしい)です。
どういう時にマテリアルが(意図せず)増えてしまうのか、理由とその対処方法について考えてみます。
マテリアルの色を変えたい
色はマテリアルが持っている要素です。
例えば 3 つの球体の色を赤、緑、青にしようとしても、同じマテリアルを使っている限り 1 つの色になってしまいます。
別の色にしたい場合、毎回マテリアルファイルを作るのは面倒です。
例のように 3 つだけならいいですが、100 ファイルとかになると…考えたくありません。
なお、同じ属性のマテリアルを量産したい場合、コピーしたいマテリアルを選択し、インスペクタ右上の3点 > Create Material Preset
で量産することができます。
スクリプトでマテリアルをコピーする
スクリプトであれば、簡単にマテリアルを複製できます。
using UnityEngine; public class MaterialColor : MonoBehaviour { [SerializeField] Color Color; private void Awake() { var renderer = GetComponent<Renderer>(); renderer.material.color = Color; } }
ちょっと怖いのが、コードを見る限り、どこで複製しているのかすらわからないこと。
注目するのはこの行。
renderer.material.color = Color;
なにげなく色を入れているだけに見えますが、この時元のマテリアルが自動的にコピーされ、別のマテリアルが作られています。そのため、色を変更すると別々の色になるわけです。
たしかに便利ですが、知らない場合混乱をもたらします。
コピーされたマテリアルは自分で明示的に解放しなかった場合、メモリリークします。
つまり、このまま何もせずシーンを解放すると、コピーされたマテリアルはそのまま居座ってしまいます。
これを避けるには次のようなコードに修正する必要があります。OnDestroy() でマテリアルも一緒に解放しています。
using UnityEngine; public class MaterialColor : MonoBehaviour { [SerializeField] Color Color; Material material; private void Awake() { material = GetComponent<Renderer>().material; material.color = Color; } private void OnDestroy() { if (material != null) { Destroy(material); material = null; } } }
あらゆる箇所で使っちゃってて、全部に OnDestroy() なんて無理! というエンジョイライト勢の方には Resources.UnloadUnusedAssets() という魔法の解放手段も用意されています。
これを呼び出せば使われていないマテリアル(厳密に言えば、使われていなければなんでも)は全てクリアされます。
ただし、重たい処理なので、シーン切り替え時など、影響のない場所を選びましょう。
コピーせずマテリアルを切り替えたい場合
処理の都合でマテリアルを切り替えたい、でもマテリアルが複製されてほしくない場合は sharedMaterial で設定します。
private void Awake() { material = GetComponent<Renderer>().sharedMaterial; material.color = Color; }
複製されていないので、当然このマテリアルを使っている全てのオブジェクトに影響します。
先ほどの3つの球だと、最後に設定されたオブジェクトのカラーに統一されます。
こちらの場合はマテリアルファイルも書き換えられることに注意してください。unity を終了すると、おそらく最後に実行していたマテリアルのステータスが .mat ファイルに上書きされるようです。