diary

日記です

UnityでEditor拡張から高速にC#コードを書いたり消したりする時のお作法

TL;DR

  • 書いてる最中からコンパイルされてEditor拡張が走ってるアセンブリが猛然と破棄されたりする
  • EditorApplication.LockReloadAssemblies, EditorApplication.UnlockReloadAssembliesで囲め
  • AssetDatabase.StartAssetEditing, AssetDatabase.StopAssetEditingで囲め

どういうことか

Unityを使ってるとそのだるさから自動生成によるコード生成を行ったり、パッケージ管理がだるすぎて思わずパッケージマネージャを自作してしまうことがあると思う。

そのような場合には高速にスクリプトからC#コードを生成したり削除したりするわけだが、Unityはせっかちなので生成してるそばからコンパイルを始めて、実行中のコードがアセンブリごと破棄されたり、不完全なコンパイルがされたり、AssetDatabaseが一時的にぶっ壊れたりする。死ね

そのような場合には適切なAPIを各位やっていく必要がある

対処

AssetDatabaseの更新をスクリプトのコード編集中だけロックしてあげればいい。ついでにアセンブリリロードもロックしておくと安心できる。ちなみに超ステートフルなAPIとして実装されてるので、きっちり対応させて呼ばないとUnityが実質固まったり、いくらまってもあたらしいアセンブリがロードされなくなったりする。死ね

我々には文明があるので、IDisposableを実装する形で実装して、usingで適切にやっていけばよい

実装

以下のようなコードを書いた上で、高速にC#コードを書いたり消したりするコードブロックをusingで囲んでいく

using System;
public class LockReloadAssembliesScope : IDisposable
{
  public bool IsDisposed { get; private set; }
  public LockReloadAssembliesScope()
  {
    EditorApplication.LockReloadAssemblies();
    IsDisposed = false;
  }
  public void Dispose()
  {
    if (!IsDisposed)
    {
      EditorApplication.UnlockReloadAssemblies();
      IsDisposed = true;
    }
  }
}

public class AssetEditingScope : IDisposable
{
  public bool IsDisposed { get; private set; }
  public AssetEditingScope()
  {
    AssetDatabase.StartAssetEditing();
    IsDisposed = false;
  }
  public void Dispose()
  {
    if (!IsDisposed)
    {
      AssetDatabase.StopAssetEditing();
      IsDisposed = true;
    }
  }
}