Unity、たまに意図的にコンパイルそのものを止めたくなる時があるんだけど、何か良い手無いかな...。(主に複数のフォルダ整理やADF整理など)
— mao (@TEST_H_) August 6, 2020
ちなみに自分は速攻でコンパイルエラーになるコードを仕込むことで、完了時間を短くした状態で整理すると言う力技で対処したことならある。
はじめに
新規スクリプトの追加や移動でいちいちコンパイルが走ると開発のイテレーション速度が落ちるので
- Unityの自動コンパイルを抑制
- ショートカットキー or メニューから実行することで明示的にコンパイルする
という挙動を実現するエディタ拡張を書いてみた。
ちなみにこれまで無駄なコンパイルが走ってほしくないときは自分も@mao_さんと同じやり方でやっていた。
2021/1/2 更新
- スクリプトの変更が検知されない不具合修正
- 再生後に
Tools/Compile Locker/Compile
でコンパイルが走らない不具合修正
更新ここまで
動作環境
- macOS Catalina(10.15.6)
- Unity2019.4.16
にて動作確認。
ソースコード
#if UNITY_EDITOR
using System;
using System.Threading.Tasks;
using UnityEditor;
using UnityEditor.Compilation;
using UnityEngine;
[InitializeOnLoad]
public class CompileLocker
{
private const string MenuItemPath = "Tools/Compile Locker/";
public class CompileLockerData : ScriptableSingleton<CompileLockerData>
{
public bool isInitialized;
public bool isLocking;
}
static CompileLocker()
{
Debug.Log("Assemblies reloaded.");
Lock();
if (isInitialized) return;
isInitialized = true;
Debug.LogWarning($"Disabled auto compiling. For manual compiling, execute \"{MenuItemPath}Compile\".");
}
private static bool isInitialized
{
get => CompileLockerData.instance.isInitialized;
set => CompileLockerData.instance.isInitialized = value;
}
private static bool isLocking
{
get => CompileLockerData.instance.isLocking;
set => CompileLockerData.instance.isLocking = value;
}
private static void Lock()
{
if (isLocking) return;
EditorApplication.LockReloadAssemblies();
isLocking = true;
}
private static void Unlock()
{
if (isLocking == false) return;
EditorApplication.UnlockReloadAssemblies();
isLocking = false;
}
private static Task _compileTask = null;
[MenuItem(MenuItemPath + "Compile %r", false)]
private static void ReloadAssemblies()
{
if (_compileTask != null) return;
_compileTask = CompileTask().ContinueWith(_ => _compileTask = null);
}
private static async Task CompileTask()
{
AssetDatabase.Refresh();
Unlock();
if (EditorApplication.isCompiling)
{
Debug.Log("Compiling...");
// 連打防止
while (EditorApplication.isCompiling)
{
await Task.Delay(TimeSpan.FromSeconds(0.5f));
}
}
else
{
Debug.Log("Nothing to compile.");
Lock();
}
}
[MenuItem(MenuItemPath + "Force Compile %#r")]
private static void ForceCompile()
{
AssetDatabase.Refresh();
Unlock();
Debug.Log("Compiling...");
CompilationPipeline.RequestScriptCompilation();
// NOTE: 必ずアセンブリのリロードが走るのでstaticコンストラクタでロックされる
}
}
#endif
挙動
- プロジェクトに入れるだけでUnityの自動コンパイルを抑制する
-
⌘ + R
もしくはTools/Compile Locker/Compile
- →必要に応じて通常のコンパイルを走らせる
-
⌘ + shift + R
もしくはTools/Compile Locker/Force Compile
- →プロジェクト内のスクリプトをすべて強制的にコンパイルする
Windosの場合は ⌘
を Ctrl
に読み替えてください。
ショートカットキーが被る
-
⌘ + R
:Assets/Reimport
と被る -
⌘ + shift + R
:Extenject(Zenject)のValidate Then Run
と被る
ので、適当に変更したほうがいいです。「被ったら両方実行」とかできないんだろうか。
ハマったポイント
コンパイルが走らない1
EditorApplication.UnlockReloadAssemblies();
EditorApplication.LockReloadAssemblies();
みたいなコードだとコンパイルが走らないので EditorApplicaion.delayCall
を使って再ロックをちょっと遅らせたり EditorApplication.update
を1回待ったりしたけどうまくいかなかった。
コンパイルが走らない2
CompileChecker.cs
を編集した場合は ⌘ + R
でコンパイルしようとしてもなぜかコンパイルが走らず、 CompileChecker.cs
を先に Reimport
してから ⌘ + R
する必要がある。謎
→ AssetDatabase.Refresh()
で解決しました。
注意
当然再生中もコンパイルが走らなくなります。あと自動コンパイルが走らない挙動はUnityがおかしくなったと勘違いされると思うので、チーム開発ではこのスクリプトを .gitignore
に登録するのを忘れずに。
所感
Assembly Definition( .asmdef
)ファイルと合わせて使うとさらに無駄なコンパイルを減らせそう。
...記事を書き終わってから「Rider上でファイル操作すればいいのでは?🤔」と思った。
参考
-
【小技】Unityのコンパイルを無効化する【Unity】
- 単純に自動コンパイルをオンオフできるやつ
-
decoc/CompileLocker.cs(Gist)
- 同上
- Force Assembly Recompile(Unityフォーラム)
- 【Unity】エディタ拡張で async / await を使用する(コガネブログ)