(:3[kanのメモ帳]

個人ゲーム開発者kan.kikuchiのメモ的技術ブログ。月木更新でUnity関連がメイン。

アセンブリごとのコンパイル時間を計測したり、一定時間を越えたら警告を出したり【Unity】【エディタ拡張】


このエントリーをはてなブックマークに追加


この記事でのバージョン
Unity 2018.2.17f1


はじめに

Unityではコードを変更するたびに自動でコンパイルがされます。

(コンパイルしている時は右下にグルグルが出ている)


f:id:kan_kikuchi:20181127050810j:plain


当然、コンパイル時間はコードが多いほど増していくので、

プロジェクトが大きくなっていくと無視できない時間になります。


そんな時はStandard Assetsという特殊なディレクトリやAssembly Definition Filesを使って、

アセンブリ(コンパイルする範囲)を分けて、コンパイル時間を短縮するのが常套手段です。




しかし、具体的なコンパイル時間が分かるわけではないので、

どのタイミングでコンパイル時間の短縮を図ればいいのかというのは難しい問題です。


ということで今回は、アセンブリごとのコンパイル時間を計測したり、

一定時間を越えたら警告を出したりという事をしようという記事です。


イメージとしては以下のような感じ


f:id:kan_kikuchi:20181127050927j:plain


AssemblyCompileTimeWindow

早速ですが、アセンブリごとのコンパイル時間を計測するためのウィンドウ

AssemblyCompileTimeWindowのプログラムです。



これをEditorディレクトリに作れば、


f:id:kan_kikuchi:20181127051116j:plain


Window -> AssemblyCompileTimeWindowからウィンドウが開けるようになります。


f:id:kan_kikuchi:20181127051347j:plain


ウィンドウを開いていれば、それだけで直近のコンパイル時間を計測し、表示するようになります。


f:id:kan_kikuchi:20181127051444j:plain


また、ログ表示のチェックボックスを有効にすれば、

コンパイルされる度にコンパイル時間をログで表示するようになります。


f:id:kan_kikuchi:20181127051608j:plain


さらに警告表示のチェックボックスを有効にし、時間を設定すれば、

コンパイル時間がその時間を越えた時だけ警告を表示するようになります。


f:id:kan_kikuchi:20181127051659j:plain


なお、設定や直近のコンパイル時間は保存されているので、

ウィンドウを閉じたり、Unityエディタを再起動したりしても消える事はありません。


実装方法

AssemblyCompileTimeWindowの実装について、要点だけを簡単に紹介しておきます。


まず、コンパイルの開始と終了の検知はCompilationPipelineを使っています。

//各アセンブリのコンパイル開始、終了時のイベントにメソッド登録
CompilationPipeline.assemblyCompilationStarted  += OnAssemblyCompilationStarted;
CompilationPipeline.assemblyCompilationFinished += OnAssemblyCompilationFinished;


次に、時間計測にはStopwatchというUnityではなく.NET Frameworkのクラスを使っています。

//時間を計るためのクラス
private static Stopwatch _stopwatch = new Stopwatch();
//計測時間を初期化し、計測開始
_stopwatch.Reset();
_stopwatch.Start();
//計測を終了時、コンパイル時間を取得
_stopwatch.Stop();
float compileTime = (float)_stopwatch.Elapsed.TotalSeconds;


さらに、設定データの保存は保存用のクラスを作成し、JsonにしてEditorUserSettingsで保存しています。

//設定情報と保存時のKey
private AssemblyCompileTimeWindowSettingData _settingData = null;
private const string SETTING_DATA_SAVE_KEY = "AssemblyCompileTimeWindowSettingDataKey";
//設定情報のロード
_settingData = JsonUtility.FromJson<AssemblyCompileTimeWindowSettingData>(EditorUserSettings.GetConfigValue(SETTING_DATA_SAVE_KEY));
if (_settingData == null) {
  settingData = new AssemblyCompileTimeWindowSettingData();
}
//設定情報の保存
EditorUserSettings.SetConfigValue(SETTING_DATA_SAVE_KEY, JsonUtility.ToJson(_settingData));



また、直近のコンパイル時間をアセンブリ名と紐づけて管理&保存がしたかったのですが、

Dictionaryはそのままシリアライズ出来ない(保存出来ない)ので

保存用のクラスを作って、そのListを使って代用しています。

/// <summary>
/// 名前と時間をペアで管理するクラス(シリアライズするために、Listで使ってDictの代わりに)
/// </summary>
[Serializable]
public class NameAndTimePair {

  [SerializeField]
  private string _name = "";
  public  string  Name { get { return _name; } }

  [SerializeField]
  private float _time = 0;
  public  float  Time { get { return _time; } set { _time = value; } }

  public NameAndTimePair(string name, float time) {
    _name = name;
    _time = time;
  }

}
//各アセンブリの直近のコンパイル時間
[SerializeField]
private List<NameAndTimePair> _currentCompileTimeList = new List<NameAndTimePair>();


なお、多次元Listも似たような感じでシリアライズが行えます。





おわりに

何でもそうですが、後から一気に直すのは大変です。

なので直すための仕組みづくりが大事になってくるので、今回紹介した方法を使って

コンパイル時間が長くなりすぎないように監視するのは我ながら便利だと思います。