Materialが増える操作を特定する
色を変える処理やオブジェクトの生成・破棄を繰り返すとMaterial数が増える場合は、Renderer.material、Renderer.materials、new Materialを使う場所を調べます。表示確認用のログでmaterialを取得する処理も確認対象です。この記事はUnity 2022.3 LTSを基準にしています。
1. material取得時の専用コピーを理解する
Renderer.materialは、そのRendererが使うインスタンスを返すAPIです。共有Materialを使っているRendererから取得すると、そのRenderer用のコピーが作られる場合があります。図の例ではRenderer 1だけがコピーA′へ切り替わり、Renderer 2は元のAを使い続けます。
すでに専用インスタンスを保持している同じRendererで、getterを読むたびに必ず新しいMaterialが1つ増える、という仕組みではありません。新しいRendererへの初回アクセス、Materialの再割り当て、明示的な複製と解放を追ってください。Renderer.materialsもインスタンス化を伴うため、複数スロットの参照確認にはsharedMaterialsを使う経路を検討します。
2. 参照の変更と、共有Materialの値変更を分ける
renderer.sharedMaterialへ既存Assetを割り当てる操作は、そのRendererが参照するMaterialを変更します。一方、取得したsharedMaterialにSetColorなどを呼ぶ操作は、共有するMaterial自体の値を変更し、同じMaterialを使うほかのRendererへも影響します。プロジェクトのAssetを意図せず変更しないよう、参照の取得と値の書き換えを区別します。
色違いが少数で固定なら、あらかじめ用意したMaterial Assetを使い分ける方法もあります。多数のRendererを同じMaterialへ戻す際も、すでに作った専用コピーが未解放で残らないか確認します。
3. 必要な違いに合わせて手段を選ぶ
- 既存Assetを使う:固定された見た目なら共有参照を割り当てます。個別表示のために共有Assetの色を直接書き換えないようにします。
- 値だけを上書きする:色やFloatなどの対応プロパティならMaterialPropertyBlockが候補です。ブロックを再利用し、実際のShaderが使うプロパティ名を確認します。URP/Litの基本色なら_BaseColorなど、Shaderに合う名前が必要です。
- 専用Materialを持つ:固有の設定や描画状態が必要なら、コピーを作る所有者を決め、参照を保持して再利用し、不要時にDestroyします。共有Assetはこの解放対象に含めません。
MaterialPropertyBlockでは描画状態の変更は扱えません。またUnity 2022.3のSRP Batcherとは非互換です。Material数を減らせたことだけで高速化と判断せず、描画構成とCPU時間を比較します。
4. 上書きの範囲と、解放の責任を確認する
PropertyBlockを複数の処理が書く場合は、更新する所有者と対象スロットを整理します。Renderer全体とMaterialスロット別の両方にブロックがあると、そのスロットではMaterial別のブロックが使われます。色が戻る場合は、別の書き込みやスロットの優先関係も調べます。
専用コピーは所有者が不要になる時点で、参照を外すか適切なMaterialへ戻してから解放します。ObjectPoolで保持しているオブジェクトなら、無効化のたびに作り直す設計より、再利用中は同じコピーを保ち、プールの最終破棄時に解放する方針も検討します。ほかのRendererが使うコピーを先に壊さないようにします。
修正後の確認
- 対象Playerの同じ場面で、生成前・生成後・破棄後のMaterial数とメモリを比較します。
- 同じ生成・破棄を複数回行い、保持数が安定するか確認します。Destroy直後だけで判断せず、破棄が処理された後を比較します。
- プールが意図的に保持する分と、失われた参照のまま増える分を分けます。Editorの一時資源やAsset数も混ぜて、すべてリークと判断しないでください。
- Frame DebuggerとProfilerで、SRP Batcherの状態・CPU時間・見た目を確認します。
Materialの個別インスタンスがあること自体は不具合ではありません。作成数、共有範囲、寿命が設計どおりかを確認します。
参考資料
- Renderer.material: instantiated materials
- Renderer.materials: instance array and cleanup
- Renderer.sharedMaterial: shared state
- MaterialPropertyBlock: supported uses
- Renderer.SetPropertyBlock: scope and precedence
- Unity 2022.3: SRP Batcher compatibility
- Object.Destroy: destruction timing
- URP Lit source: _BaseColor property