I have a script that adds to "ProjectFilesGenerator.ProjectFileGeneration", so it adds to the Assemblies every time they are recompiled. I was hoping to make an editor window with a button to toggle what it adds on and off. Works like a charm, except you have to restart visual studio to force Unity to recompile the assembly files. Is there any way to make my editor button also force a recompile of the assemblies?
-
// force a recompile by touching one asset
-
AssetDatabase.StartAssetEditing();
-
string[] allAssetPaths = AssetDatabase.GetAllAssetPaths();
-
for (int i = 0; i < allAssetPaths.Length; i += 1)
-
{
-
MonoScript script = AssetDatabase.LoadAssetAtPath(allAssetPaths[i], typeof(MonoScript)) as MonoScript;
-
if (script != null)
-
{
-
AssetDatabase.ImportAsset(allAssetPaths[i]);
-
break;
-
}
-
}
-
AssetDatabase.StopAssetEditing();
But that doesn't force the Assemblies to recompile.
Try changing this
AssetDatabase.ImportAsset(allAssetPaths[i])
to this
AssetDatabase.ImportAsset(allAssetPaths[i], ImportAssetOptions.ForceUpdate)
the Assembly still remains unchanged until Visual Studio is restarted.
Bumped into this thread while searching how to force Unity to recompile scripts. I've found solution for that in Unity 2019.3: it's UnityEditor.Compilation.CompilationPipeline.RequestScriptCompilation method (API reference). Tested it with hooking to assembly compilation started/finished events and this method indeed recompiles all assemblies in the project.
-
using UnityEditor;
-
using UnityEditor.Compilation;
-
using UnityEngine;
-
public class CompilationWindow : EditorWindow
-
{
-
[MenuItem("Window/" + nameof(CompilationWindow))]
-
private static void ShowWindow()
-
{
-
GetWindow<CompilationWindow>();
-
}
-
private void OnGUI()
-
{
-
if (GUILayout.Button("Request Script Compilation"))
-
{
-
CompilationPipeline.RequestScriptCompilation();
-
}
-
}
-
}
Unfortunatelly this public method is available only in Unity 2019.3 and above. For previous versions of Unity can try to call UnityEditor.Scripting.ScriptCompilation.EditorCompilationInterface.DirtyAllScripts via reflection.
/// Force Unity to recompile scripts///
Unity 2019.3 introduced public editor API to force scripts recompilation: it's UnityEditor.Compilation.CompilationPipeline.RequestScriptCompilation
method. Tested it with hooking to assembly compilation started/finished events and this method indeed recompiles all scripts in the project.
For Unity versions from Unity 2017.1 to Unity 2019.2 can call UnityEditor.Scripting.ScriptCompilation.EditorCompilationInterface.DirtyAllScripts
via reflection. DirtyAllScripts
is the method Unity 2019.3 calls internally from RequestScriptCompilation
method (see reference). Sadly for Unity versions older than 2017.1 I'm not sure DirtyAllScripts
is present since Unity C# reference has no source code for that Unity versions.
using UnityEditor;
#if UNITY_2019_3_OR_NEWER
using UnityEditor.Compilation;
#elif UNITY_2017_1_OR_NEWER
using System.Reflection;
#endif
using UnityEngine;
namespace PumpEditor
{
public class CompilationWindow : EditorWindow
{
[MenuItem("Window/Pump Editor/Compilation")]
private static void ShowWindow()
{
var window = EditorWindow.GetWindow<CompilationWindow>();
window.titleContent = new GUIContent("Compilation");
window.Show();
}
private void OnGUI()
{
if (GUILayout.Button("Request Script Compilation"))
{
#if UNITY_2019_3_OR_NEWER
CompilationPipeline.RequestScriptCompilation();
#elif UNITY_2017_1_OR_NEWER
var editorAssembly = Assembly.GetAssembly(typeof(Editor));
var editorCompilationInterfaceType = editorAssembly.GetType("UnityEditor.Scripting.ScriptCompilation.EditorCompilationInterface");
var dirtyAllScriptsMethod = editorCompilationInterfaceType.GetMethod("DirtyAllScripts", BindingFlags.Static | BindingFlags.Public);
dirtyAllScriptsMethod.Invoke(editorCompilationInterfaceType, null);
#endif
}
}
}
}
public static void ForceRebuild()
{
string[] rebuildSymbols = { "RebuildToggle1", "RebuildToggle2" };
string definesString = PlayerSettings.GetScriptingDefineSymbolsForGroup(
EditorUserBuildSettings.selectedBuildTargetGroup);
if (definesString.Contains(rebuildSymbols[0]))
{
definesString = definesString.Replace(rebuildSymbols[0], rebuildSymbols[1]);
}
else if (definesString.Contains(rebuildSymbols[1]))
{
definesString = definesString.Replace(rebuildSymbols[1], rebuildSymbols[0]);
}
else
{
definesString += ";" + rebuildSymbols[0];
}
PlayerSettings.SetScriptingDefineSymbolsForGroup(
EditorUserBuildSettings.selectedBuildTargetGroup,
definesString);
}
I made a utility script that allow us to force recompile code, without tweaking any file:
using UnityEngine;
using UnityEditor;
public class Recompiler : ScriptableObject
{
public static void Recompile()
{
// Create a temporary instance of Recompiler
ScriptableObject tmpInstance = CreateInstance<Recompiler>();
// Get the script asset that Recompiler object uses
MonoScript sourceScriptAsset = MonoScript.FromScriptableObject(tmpInstance);
// Get the script asset path
string scriptPath = AssetDatabase.GetAssetPath(sourceScriptAsset);
// Call DestroyImmediate() in order to destroy the temporary instance properly
DestroyImmediate(tmpInstance);
// Reimport the script asset, at the computed path
AssetDatabase.ImportAsset(scriptPath, ImportAssetOptions.ForceUpdate);
}
}
Add this script in an /Editor
directory of your project, and call Recompiler.Recompile()
where you need to force Unity to recompile scripts.
I don't know if the EditorUtility.RequestScriptReload()
suggested by @Raresh works, but it exists only in Unity 2019, and I'm working on a project that uses Unity 2018. So this little utility script did the job! ;)
/ Reload assembly /
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; } } }
/ async 和 await 的使用 ///
using System.Threading.Tasks; using UnityEditor; using UnityEngine; public class Example { [MenuItem( "Tools/Run" )] private static void Run() { RunAsync(); } private static async void RunAsync() { var count = 10; for ( int i = 0; i < count; i++ ) { var num = i + 1; Debug.Log( $"{num}/{count}" ); await Task.Delay( 1000 ); } } }
// Compiler /
#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
// 运行时编译 回调和加锁解锁 /
using UnityEditor;
using UnityEditor.Compilation;
using UnityEngine;
public class AssemblyTest : EditorWindow
{
[MenuItem("Tool/AssemblyTest")]
private static void ShowWindow()
{
EditorWindow.GetWindow<AssemblyTest>("编译测试");
}
private void OnEnable()
{
CompilationPipeline.assemblyCompilationStarted += AssemblyCompilationStartedCallback;
CompilationPipeline.assemblyCompilationFinished += AssemblyCompilationFinishedCallback;
EditorApplication.playModeStateChanged += PlayModeStateChanged;
}
private void OnDestroy()
{
CompilationPipeline.assemblyCompilationStarted -= AssemblyCompilationStartedCallback;
CompilationPipeline.assemblyCompilationFinished -= AssemblyCompilationFinishedCallback;
EditorApplication.playModeStateChanged -= PlayModeStateChanged;
}
private void OnGUI()
{
if (GUILayout.Button("刷新"))
{
Refresh();
}
if (GUILayout.Button("进入游戏"))
{
EditorApplication.isPlaying = true;
}
if (GUILayout.Button("退出游戏"))
{
EditorApplication.isPlaying = false;
}
}
private void AssemblyCompilationStartedCallback(string AssemblyName)
{
Debug.Log(string.Format("当前程序集({0})开始编译", AssemblyName));
}
private void AssemblyCompilationFinishedCallback(string AssemblyName, CompilerMessage[] compilerMessages)
{
Debug.Log(string.Format("当前程序集({0})编译完毕", AssemblyName));
}
private void Lock()
{
EditorApplication.LockReloadAssemblies();
}
private void UnLock()
{
EditorApplication.UnlockReloadAssemblies();
}
private void PlayModeStateChanged(PlayModeStateChange playModeState)
{
if (playModeState == PlayModeStateChange.EnteredPlayMode)
{
Lock();
}
if (playModeState == PlayModeStateChange.ExitingPlayMode)
{
UnLock();
}
}
private void Refresh()
{
AssetDatabase.ImportAsset("Assets/Assembly/Test.cs");
AssetDatabase.ImportAsset("Assets/Assembly/Editor/AssemblyTest.cs");
AssetDatabase.Refresh();
}
}
在编译开始时加锁是可以阻止编译继续,但是这个用起来要小心死锁
//基于上面的脚本修改的测试脚本
private static string name;
private void AssemblyCompilationStartedCallback(string AssemblyName)
{
Debug.Log(string.Format("当前程序集({0})开始编译", AssemblyName));
if (name != AssemblyName)
{
Debug.Log("加锁");
Lock();
Debug.Log("解锁");
UnLock();
name = AssemblyName;
}
}