Search Unity

Stop play-mode in editor when script changes.

Discussion in 'Editor & General Support' started by Immanuel-Scholz, May 12, 2016.

Thread Status:
Not open for further replies.
  1. Immanuel-Scholz

    Immanuel-Scholz

    Joined:
    Jun 8, 2013
    Posts:
    221
    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.


    If you think that's a good idea, please vote for this suggestion.
     
    marcospgp, synthc and neonblitzer like this.
  2. Robert9552

    Robert9552

    Joined:
    Feb 26, 2014
    Posts:
    13
    Why would it be a good idea to change code or external libraries during execution?
     
    Joe-Censored likes this.
  3. HiddenMonk

    HiddenMonk

    Joined:
    Dec 19, 2014
    Posts:
    987
    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
    Code (CSharp):
    1.  
    2. using UnityEditor;
    3.  
    4. [InitializeOnLoad]
    5. public class StopPlayingOnRecompile
    6. {
    7.     static StopPlayingOnRecompile()
    8.     {
    9.         //Since InitializeOnLoad is called when unity starts AND every time you hit play, we will unsubscribe and resubscribe to avoid duplicates.
    10.         //Might not be needed to do since EditorApplication.update might be cleared on every InitializeOnLoad call?
    11.         EditorApplication.update -= StopPlayingIfRecompiling;
    12.         EditorApplication.update += StopPlayingIfRecompiling;
    13.     }
    14.  
    15.     static void StopPlayingIfRecompiling()
    16.     {
    17.         if(EditorApplication.isCompiling && EditorApplication.isPlaying)
    18.         {
    19.             EditorApplication.isPlaying = false;
    20.         }
    21.     }
    22. }
    Source - http://www.rebz.org/2014/05/gist-stop-playing-on-recompile/
     
  4. zombiegorilla

    zombiegorilla

    Moderator

    Joined:
    May 8, 2012
    Posts:
    9,052
    Alternatively, you could just exit play mode before making changes.
     
    JasonC_ likes this.
  5. Immanuel-Scholz

    Immanuel-Scholz

    Joined:
    Jun 8, 2013
    Posts:
    221
    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?
     
    ZergatuL likes this.
  6. zombiegorilla

    zombiegorilla

    Moderator

    Joined:
    May 8, 2012
    Posts:
    9,052
    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.
     
    Joe-Censored likes this.
  7. Immanuel-Scholz

    Immanuel-Scholz

    Joined:
    Jun 8, 2013
    Posts:
    221
    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.
     
    ZergatuL, neonblitzer and Menion-Leah like this.
  8. ckerr

    ckerr

    Joined:
    Oct 3, 2016
    Posts:
    1
    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):
    Code (csharp):
    1.  
    2.     [InitializeOnLoad]
    3.     public static class StopEditorOnRecompile
    4.     {
    5.         static StopEditorOnRecompile()
    6.         {
    7.             // Rely on the fact that recompiling causes an assembly reload and that, in turn,
    8.             // causes our static constructor to be called again.
    9.             if( EditorApplication.isPlaying )
    10.             {
    11.                 Debug.LogWarning("Stopping Editor because of AssemblyReload.");
    12.                 EditorApplication.isPlaying = false;
    13.             }
    14.         }
    15.     }
    16.  
     
    synthc, sarahnorthway and xVergilx like this.
  9. Leslie-Young

    Leslie-Young

    Joined:
    Dec 24, 2008
    Posts:
    1,148
    Wow.. for a moderator to reply that snarky.

    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.

    Thanks to the people who gave an actual answer.
     
  10. apushak

    apushak

    Joined:
    Jan 13, 2015
    Posts:
    5
    The polling method should work in all cases if you also prevent reloading assemblies while in play mode like this:

    Code (CSharp):
    1. using UnityEditor;
    2.  
    3. [InitializeOnLoad]
    4. class StopPlayOnRecompile
    5. {
    6.     static StopPlayOnRecompile()
    7.     {
    8.         EditorApplication.update += Update;
    9.         EditorApplication.playModeStateChanged += OnPlayModeChange;
    10.     }
    11.  
    12.     private static void Update()
    13.     {
    14.         if (EditorApplication.isCompiling && EditorApplication.isPlaying)
    15.         {
    16.             EditorApplication.isPlaying = false;
    17.         }
    18.     }
    19.  
    20.     private static void OnPlayModeChange(PlayModeStateChange state)
    21.     {
    22.         if (state == PlayModeStateChange.EnteredPlayMode)
    23.         {
    24.             EditorApplication.LockReloadAssemblies();
    25.         }
    26.         else if (state == PlayModeStateChange.EnteredEditMode)
    27.         {
    28.             EditorApplication.UnlockReloadAssemblies();
    29.         }
    30.     }
    31. }
    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.
     
    NibbleByte3 likes this.
  11. alk-dafi

    alk-dafi

    Joined:
    Jun 6, 2017
    Posts:
    1
    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.

    Code (CSharp):
    1. using System;
    2. using System.Reflection;
    3. using UnityEditor;
    4. using UnityEngine;
    5.  
    6. // I recommend dropping this script in an Editor folder.
    7. // You should have two audio clips somewhere in the project.
    8. // You'll need to edit-in the paths of those clips (from your project root folder) in the static initializer below.
    9. // Example path: "Assets/Editor/CompileIndicator/start.mp3"
    10.  
    11. namespace Assets.Editor {
    12.  
    13.     /// <summary>
    14.     /// Plays a sound effect when script compiling starts and ends.
    15.     /// </summary>
    16.     [InitializeOnLoad]
    17.     public static class CompileIndicator {
    18.  
    19.         private const string CompileStatePrefsKey = "CompileIndicator.WasCompiling";
    20.         private static readonly AudioClip StartClip;
    21.         private static readonly AudioClip EndClip;
    22.  
    23.         static CompileIndicator() {
    24.             EditorApplication.update = OnUpdate;
    25.             StartClip = AssetDatabase.LoadAssetAtPath<AudioClip>("REPLACE_WITH_PATH_TO_YOUR_START_CLIP");
    26.             EndClip = AssetDatabase.LoadAssetAtPath<AudioClip>("REPLACE_WITH_PATH_TO_YOUR_END_CLIP");
    27.         }
    28.  
    29.         private static void OnUpdate() {
    30.             var wasCompiling = EditorPrefs.GetBool(CompileStatePrefsKey);
    31.             var isCompiling = EditorApplication.isCompiling;
    32.  
    33.             // Return early if compile status hasn't changed.
    34.             if (wasCompiling == isCompiling)
    35.                 return;
    36.  
    37.             if (isCompiling)
    38.                 OnStartCompiling();
    39.             else
    40.                 OnEndCompiling();
    41.  
    42.             EditorPrefs.SetBool(CompileStatePrefsKey, isCompiling);
    43.         }
    44.  
    45.         private static void OnStartCompiling() {
    46.             PlayClip(StartClip);
    47.         }
    48.  
    49.         private static void OnEndCompiling() {
    50.             PlayClip(EndClip);
    51.         }
    52.  
    53.         private static void PlayClip(AudioClip clip) {
    54.             Assembly unityEditorAssembly = typeof(AudioImporter).Assembly;
    55.             Type audioUtilClass = unityEditorAssembly.GetType("UnityEditor.AudioUtil");
    56.             MethodInfo method = audioUtilClass.GetMethod(
    57.                 "PlayClip",
    58.                 BindingFlags.Static | BindingFlags.Public,
    59.                 null,
    60.                 new []{typeof(AudioClip)},
    61.                 null
    62.             );
    63.             method.Invoke(null, new object[]{clip});
    64.         }
    65.     }
    66. }

    Source: https://www.reddit.com/r/Unity3D/comments/52pf19/play_a_sound_when_script_compilation_starts_and/
     
  12. Deleted User

    Deleted User

    Guest

    The above feature was introduced to Unity 2018. you can set it at

    Code (CSharp):
    1. Edit > Preferences > General >. Script Changes While Playing (Stop Playing and Recompile)
     

    Attached Files:

    Last edited by a moderator: Sep 13, 2018
  13. Summit_Peak

    Summit_Peak

    Joined:
    Nov 15, 2014
    Posts:
    3
    (Stop Playing and Recompile) didn't work for me in Unity 2018.2.8f1, but
    (Recompile After Finished Playing) did work. What a life saver.

    Thanks for the pointer.
     
  14. aroOon

    aroOon

    Joined:
    Mar 19, 2013
    Posts:
    5
    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.
     
    neonblitzer, xucian and esaniemi like this.
  15. xucian

    xucian

    Joined:
    Mar 7, 2016
    Posts:
    846
    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
     
    Last edited: Mar 28, 2019
    Mark-Currie likes this.
Thread Status:
Not open for further replies.