When Unity detects a change in a script or DLL file, an assembly reload will trigger. This is even so if the game is running in play mode.
An Assembly reload will clear out all NonSerialized and static fields, call OnDisable/OnEnable (sometimes in a broken way) and do generally weird things to the whole runtime.
I never ever saw any Unity game more complex than a simple tutorial that could meaningful survive an assembly reload during playmode. Even Unity’s own Unity UI will dump out tons of warnings and generally do not support assembly reloads on many of its functions (i.e. dynamically added event listeners will be silently unregistered).
Worse than that, this assembly reload takes a lot of time during gameplay and will usually result in a very broken editor state (especially if gameobject’s with hideFlags set to DontSave are involved)
In most bigger projects I saw, it is way faster to kill the unity.exe process and reload the whole editor than to “wait it out” and hope that you even can press the “Stop” button after you accidentally triggered an assembly reload during play.
Please change the behavior of Unity to first exit playmode and THEN do an assembly reload if changes to script files or DLL’s are detected.
With a quick google search, I found a way to do this with a simple editor script.
Tested and works.
Here is the script, it must be put it in a folder called Editor
using UnityEditor;
[InitializeOnLoad]
public class StopPlayingOnRecompile
{
static StopPlayingOnRecompile()
{
//Since InitializeOnLoad is called when unity starts AND every time you hit play, we will unsubscribe and resubscribe to avoid duplicates.
//Might not be needed to do since EditorApplication.update might be cleared on every InitializeOnLoad call?
EditorApplication.update -= StopPlayingIfRecompiling;
EditorApplication.update += StopPlayingIfRecompiling;
}
static void StopPlayingIfRecompiling()
{
if(EditorApplication.isCompiling && EditorApplication.isPlaying)
{
EditorApplication.isPlaying = false;
}
}
}
As it is with the current Unity “feature” of becoming overall broken and sometimes crashing, this is a terrible idea. Hence my suggestion to change it.
This works, if there is any update call during compilation. It does not work in all cases, for example sometimes Unity decides to do a synchronous compilation - you see a popup, no editor update is ever called until its too late. It does this every time you change a DLL.
Yes, that is what we have to do now. Very annoying. If you forget this… bam.
Yes, this request here to Unity isn’t a big whopping uber thing. Its just an annoyance that is constantly there. This little annoyance adds up to my frustration-meter every other day. Everytime, I have to wait 2 minutes until Unity finally finishes reporting 1000+ error log messages after an in-game assembly reload… Or I have to kill the Unity process, probably loosing 10 minutes of unsaved work, I think: WHY?
Yea… The problem you are encountering is 100% user error. Exit play mode before editing a dll. If you are having trouble remembering set the play mode tint to a bright color so it is obvious. The ability to make minor edits to scripts is a nice feature. Asking to have that removed for everyone else because of your memory challenges isn’t really a fair ask.
It might be a nice feature if it would work. But since it does not work (in any real-life project more complex than toy examples or simple test cases), it is a problem - not a feature.
And trying to maintain this broken feature needs developers that could have done other and better things.
The above hack doesn’t seem to work for me in 2017.1, but this one does (also doesn’t rely on an update poll):
[InitializeOnLoad]
public static class StopEditorOnRecompile
{
static StopEditorOnRecompile()
{
// Rely on the fact that recompiling causes an assembly reload and that, in turn,
// causes our static constructor to be called again.
if( EditorApplication.isPlaying )
{
Debug.LogWarning("Stopping Editor because of AssemblyReload.");
EditorApplication.isPlaying = false;
}
}
}
It is a very reasonable request, even if just an option in settings for those of us who do want it. New users would think it is a problem with Unity when it crashes or throw these errors, giving Unity a bad image.
I too iterate so fast between my compiled code/ scripts and Unity that I sometimes forget to turn play mode off when switching back to and from VS to make a change.
I found this thread while looking for a way I could hack in what should be a feature.
Calling LockReloadAssemblies will prevent the assemblies from reloading, but does not stop the recompile so the isCompiling flag still gets set to true and we can exit play mode when we detect it. Alternatively you can remove the Update event handler and this will prevent assemblies from reloading until you exit play mode.
Sorry if it’s a bit late, but I found a simple script that also play sound on beginning and ending of compile process, and I think this will help a bit for the “stop play” part on the beginnig.
using System;
using System.Reflection;
using UnityEditor;
using UnityEngine;
// I recommend dropping this script in an Editor folder.
// You should have two audio clips somewhere in the project.
// You'll need to edit-in the paths of those clips (from your project root folder) in the static initializer below.
// Example path: "Assets/Editor/CompileIndicator/start.mp3"
namespace Assets.Editor {
/// <summary>
/// Plays a sound effect when script compiling starts and ends.
/// </summary>
[InitializeOnLoad]
public static class CompileIndicator {
private const string CompileStatePrefsKey = "CompileIndicator.WasCompiling";
private static readonly AudioClip StartClip;
private static readonly AudioClip EndClip;
static CompileIndicator() {
EditorApplication.update = OnUpdate;
StartClip = AssetDatabase.LoadAssetAtPath<AudioClip>("REPLACE_WITH_PATH_TO_YOUR_START_CLIP");
EndClip = AssetDatabase.LoadAssetAtPath<AudioClip>("REPLACE_WITH_PATH_TO_YOUR_END_CLIP");
}
private static void OnUpdate() {
var wasCompiling = EditorPrefs.GetBool(CompileStatePrefsKey);
var isCompiling = EditorApplication.isCompiling;
// Return early if compile status hasn't changed.
if (wasCompiling == isCompiling)
return;
if (isCompiling)
OnStartCompiling();
else
OnEndCompiling();
EditorPrefs.SetBool(CompileStatePrefsKey, isCompiling);
}
private static void OnStartCompiling() {
PlayClip(StartClip);
}
private static void OnEndCompiling() {
PlayClip(EndClip);
}
private static void PlayClip(AudioClip clip) {
Assembly unityEditorAssembly = typeof(AudioImporter).Assembly;
Type audioUtilClass = unityEditorAssembly.GetType("UnityEditor.AudioUtil");
MethodInfo method = audioUtilClass.GetMethod(
"PlayClip",
BindingFlags.Static | BindingFlags.Public,
null,
new []{typeof(AudioClip)},
null
);
method.Invoke(null, new object[]{clip});
}
}
}
Neither of these preferences have worked for me in any recent version of Unity, on multiple platforms. It always tries to recompile while playing, no matter what.
Using Unity 2018.3.0, this doesn’t work when changing dlls, only when changing scripts.
Using the ConsoleE asset for this works, but breaks the VS project if you have assembly definitions.
ATM, there’s no perfect solution for my case.
Update: I solved my issue (it seems) by closing unity, removing the Library, obj, Temp folders , removing all VS-related files (.csproj, .sln etc) and letting unity regenerate them on next launch.
Still using ConsoleE’s “Compile on stop” in conjunction with Unity’s own “Recompile After Finished Playing”. Only using this combination I can both change dlls and scripts and have them compiled after I exit play mode & also keep the VS project correctly synced