强制Unity编译 Recompile

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?

  1. // force a recompile by touching one asset

  2. AssetDatabase.StartAssetEditing();

  3. string[] allAssetPaths = AssetDatabase.GetAllAssetPaths();

  4. for (int i = 0; i < allAssetPaths.Length; i += 1)

  5. {

  6.     MonoScript script = AssetDatabase.LoadAssetAtPath(allAssetPaths[i], typeof(MonoScript)) as MonoScript;

  7.     if (script != null)

  8.     {

  9.         AssetDatabase.ImportAsset(allAssetPaths[i]);

  10.         break;

  11.     }

  12. }

  13. 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.

  1. using UnityEditor;

  2. using UnityEditor.Compilation;

  3. using UnityEngine;

  4. public class CompilationWindow : EditorWindow

  5. {

  6.     [MenuItem("Window/" + nameof(CompilationWindow))]

  7.     private static void ShowWindow()

  8.     {

  9.         GetWindow<CompilationWindow>();

  10.     }

  11.     private void OnGUI()

  12.     {

  13.         if (GUILayout.Button("Request Script Compilation"))

  14.         {

  15.             CompilationPipeline.RequestScriptCompilation();

  16.         }

  17.     }

  18. }

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.

  1. using UnityEditor;
  2. #if UNITY_2019_3_OR_NEWER
  3. using UnityEditor.Compilation;
  4. #elif UNITY_2017_1_OR_NEWER
  5. using System.Reflection;
  6. #endif
  7. using UnityEngine;
  8. namespace PumpEditor
  9. {
  10. public class CompilationWindow : EditorWindow
  11. {
  12. [MenuItem("Window/Pump Editor/Compilation")]
  13. private static void ShowWindow()
  14. {
  15. var window = EditorWindow.GetWindow<CompilationWindow>();
  16. window.titleContent = new GUIContent("Compilation");
  17. window.Show();
  18. }
  19. private void OnGUI()
  20. {
  21. if (GUILayout.Button("Request Script Compilation"))
  22. {
  23. #if UNITY_2019_3_OR_NEWER
  24. CompilationPipeline.RequestScriptCompilation();
  25. #elif UNITY_2017_1_OR_NEWER
  26. var editorAssembly = Assembly.GetAssembly(typeof(Editor));
  27. var editorCompilationInterfaceType = editorAssembly.GetType("UnityEditor.Scripting.ScriptCompilation.EditorCompilationInterface");
  28. var dirtyAllScriptsMethod = editorCompilationInterfaceType.GetMethod("DirtyAllScripts", BindingFlags.Static | BindingFlags.Public);
  29. dirtyAllScriptsMethod.Invoke(editorCompilationInterfaceType, null);
  30. #endif
  31. }
  32. }
  33. }
  34. }

  1. public static void ForceRebuild()
  2. {
  3. string[] rebuildSymbols = { "RebuildToggle1", "RebuildToggle2" };
  4. string definesString = PlayerSettings.GetScriptingDefineSymbolsForGroup(
  5. EditorUserBuildSettings.selectedBuildTargetGroup);
  6. if (definesString.Contains(rebuildSymbols[0]))
  7. {
  8. definesString = definesString.Replace(rebuildSymbols[0], rebuildSymbols[1]);
  9. }
  10. else if (definesString.Contains(rebuildSymbols[1]))
  11. {
  12. definesString = definesString.Replace(rebuildSymbols[1], rebuildSymbols[0]);
  13. }
  14. else
  15. {
  16. definesString += ";" + rebuildSymbols[0];
  17. }
  18. PlayerSettings.SetScriptingDefineSymbolsForGroup(
  19. EditorUserBuildSettings.selectedBuildTargetGroup,
  20. definesString);
  21. }

 I made a utility script that allow us to force recompile code, without tweaking any file:

 
  1. using UnityEngine;
  2. using UnityEditor;
  3. public class Recompiler : ScriptableObject
  4. {
  5. public static void Recompile()
  6. {
  7. // Create a temporary instance of Recompiler
  8. ScriptableObject tmpInstance = CreateInstance<Recompiler>();
  9. // Get the script asset that Recompiler object uses
  10. MonoScript sourceScriptAsset = MonoScript.FromScriptableObject(tmpInstance);
  11. // Get the script asset path
  12. string scriptPath = AssetDatabase.GetAssetPath(sourceScriptAsset);
  13. // Call DestroyImmediate() in order to destroy the temporary instance properly
  14. DestroyImmediate(tmpInstance);
  15. // Reimport the script asset, at the computed path
  16. AssetDatabase.ImportAsset(scriptPath, ImportAssetOptions.ForceUpdate);
  17. }
  18. }

Add this script in an /Editordirectory 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;
        }
    }
 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值