チリペヂィア

リンクフリー。サンプルコードなどは関連記事内でライセンスについて明示されない限り商用利用なども自由に行って構いませんが、自己責任でお願いします。またこれら日記内容の著作権自体は放棄していません。引用部分については引用元の権利に従ってください。

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. てな具合のメッセージ。