MonoBehaviorをExecuteInEditMode属性で修飾、シーン保存でリソースリーク
hideFlags設定忘れたというしょうもない備忘録。
「カスタムエディタ系の機能を作りたいけれど、SceneViewに現物を表示させてアレコレ設定もさせたい」
あえてEditorフォルダ系のクラス(UnityEditor系のクラス)を継承せずに、MonoBehaviorをExecuteInEditMode属性で修飾する場合、エディタでシーンを保存するとリソースのメモリリークが報告されてしまうケースがあります(*1)。
[ExecuteInEditMode] public class MyComponent : MonoBehaviour { // 以下のリソースは、Update()やOnGUI()契機でnewしたりInstantiateし、メモリ上だけで利用して保存はせずに、OnDisable()やOnDestroy()で破棄するとする。 [System.NonSerialized] public Mesh myMesh; [System.NonSerialized] public Texture2D myTexture; [System.NonSerialized] public Material myMaterial; private void Update() { : : if (myTexture == null) myTexture = new Texture2D(128, 128); if (myMesh == null) myMesh = new Mesh(); if (myMaterial == null) myMaterial = new Material(Shader.Find("Diffuse")); : : } }
これでシーン保存操作をしちゃうと、きちんとDestroy/DestroyImmediate書いてるにもかかわらずリークが報告されて、はわわ。となります。
正しくはこう。
private void Update() { : : if (myTexture == null) { myTexture = new Texture2D(128, 128); myTexture.hideFlags = HideFlags.DontSave; } if (myMesh == null) { myMesh = new Mesh(); myMesh.hideFlags = HideFlags.DontSave; } if (myMaterial == null) { myMaterial = new Material(Shader.Find("Diffuse")); myMaterial.hideFlags = HideFlags.DontSave; } : : }
あー…「格納する変数がNonSerialized修飾されてても、セットするリソース生成処理がそれを監視してるわけじゃなく、ExecuteInEditModeでは、平常のゲーム実行中と変わりなく暗黙にシーン上のリソースとして永続させようとする設定がデフォルトになってる一方、NonSerialized変数にぶち込まれる事で、エディターのシリアライズ管理からも外れていて、保存契機でオーナー不在リソースと認識される感じ」でしょうか…。(さすがにこんなの想像できなかったわよ!)
するってぇと、ExecuteInEditMode特有のお約束のような気がします(いや…もしかして一時変数としてリソース確保する奴は全部なの?)。
EditorUtility.CreateGameObjectWithHideFlags
というのもあるようですが、同じようにExecuteInEditModeでリークが発生しちゃう時は、とりあえずちまちまhideFlagsを設定してみてはいかがでしょう。
*1: Cleaning up leaked objects in scene since no game object, component or manager is referencing them / Texture2D has been leaked 1 times. てな具合のメッセージ。