Search Unity

  1. Unity Asset Manager is now available in public beta. Try it out now and join the conversation here in the forums.
    Dismiss Notice
  2. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  3. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Configurable Enter Play Mode

Discussion in '2019.3 Beta' started by alexeyzakharov, Oct 29, 2019.

  1. alexeyzakharov

    alexeyzakharov

    Joined:
    Jul 2, 2014
    Posts:
    507
    TL;DR; Try out the experimental Configurable Play Mode option in Editor ProjectSettings to improve iteration time when you don't mind scripts not being fully reset.

    Play Mode is one of the key concepts in game development with Unity. The ability to iterate fast in the Editor is a great advantage which allows developers to prototype faster, fail faster, and create great games more easily. The time it takes to enter PlayMode is a vital metric when it comes to fast iterations. With an empty project it typically takes 1.5-2.5 seconds. However, with the project evolving, time to enter Play Mode might grow significantly to 10-25 seconds, crippling productivity.
    We profiled and analyzed Enter Play Mode to understand where the time is spent. As a result, we would like to offer options to skip the most time consuming parts during Enter Play Mode and let you decide whether accuracy or speed is preferred during development.

    How much time can be saved
    Domain Reload and Scene Reload are the main contributors to the cost to Enter Play Mode - typically their cumulative impact is around 80-90% of Enter Playmode time.
    Domain Reload is a generic way to achieve a clean state of game and Editor C# code (besides its main role of reloading a new scripting code). Similarly Scene Reload is a generic way to get a clean state of GameObjects and associated MonoBehaviors/ScriptableObjects. Generic approaches are simple and allow not to think about behavior differences, but incur huge time costs in some cases and reduce productivity.
    As you can see below, the effect of skipping Domain Reload and Scene Reload is very promising.

    It should be noted that the outcome depends on the project and might vary depending on Scene complexity and used packages and AssetStore plugins.

    How to enable
    You can find the options in the Editor Project Setting.

    ProjectSettings → Editor → Enter Play Mode Settings

    The same settings are configurable through the EditorSettings scripting API that we have added to the UnityEditor namespace:
    1. bool EditorSettings.enterPlayModeOptionsEnabled;
    2. EnterPlayModeOptions EditorSettings.enterPlayModeOptions;
    EnterPlaymodeOptions allows you to disable Domain and/or Scene Reload.

    What exactly is skipped
    From a high-level perspective, Enter Play Mode consists of the following main stages:
    1. [Backup current Scenes]. Allows us to restore state during Exit Play Mode. Is optional - only happens when Scene is dirty.
    2. Reset scripting state (reload scripting domain, aka “Domain Reload”).
    3. Resets Scene state (Scene Reload).
    4. Update scene (2 times - without rendering and with rendering).
    Combination of Domain Reload and Scene Reload is the main job of Enter Play Mode state. It resets the game world and simulates the startup behavior as the game would run in the player.
    To help you understand those options, I would like to provide a scripting events diagram for Enter Play Mode and highlight differences.

    What are the side effects
    Important differences due to skipped Domain Reload
    The main side effect is that the scripting state is not globally reinitialized and it is up to you to decide what has to be reset.
    1. Fields marked with [NonSerialized] attribute keep their values - as script serialization step is skipped completely and scripts remain the same, all data is carried to the Play Mode.
    2. Static variables keep their values - because Domain Reload does not happen types stay initialized.
      • Static events keep their subscribers.
    3. There is no extra OnDisable/OnEnable calls for ExecuteInEditMode scripts.
    Static variables can be reinitialized with the help of the RuntimeInitializeOnLoad (RuntimeInitializeOnLoadMethod) attribute.
    Code (CSharp):
    1. using UnityEngine;
    2. public class StaticCounterExampleFixed : MonoBehaviour
    3. {
    4.     static int counter;
    5.  
    6.     [RuntimeInitializeOnLoadMethod]
    7.     static void Init()
    8.     {
    9.             counter = 0;
    10.     }
    11. }
    Important differences due to skipping Scene Reload
    Avoiding Scene Reload is much more complex to implement and align with the normal loading path, but should have minimal side effects. However, due to its tight connection to Domain Reload, there are a couple of important differences:
    1. ScriptableObject, MonoBehaviour fields which are not serialized into the build ([NonSerialized], private or internal) keep their values - as existing objects are not recreated and constructors are not called.
      Watch out for null private and internal fields of array/List type - they are converted to an empty array/List object during Domain Reload and stay non null for game scripts.
    2. ExecuteInEditMode/ExecuteAlways scripts are not destroyed/awaken - no OnDestroy/Awake calls for those.
      Watch out for Awake/OnEnable methods which check EditorApplication.isPlaying property - Awake is not called and OnEnable is called only when EditorApplication.isPlaying is already true on Play Mode change.
    Nonserialized fields for game scripts should not be an issue, as game scripts are not active in Edit Mode, however, the ones marked with ExecuteInEditMode/ExecuteAlways might change themselves or touch fields of other game scripts. This can be mitigated by initializing affected fields in an OnEnable callback.

    Known issues
    1. https://issuetracker.unity3d.com/is...oad-scene-enter-play-mode-setting-is-disabled - Fixed.
    2. https://issuetracker.unity3d.com/is...tion-when-using-fast-play-mode-editor-setting - Can’t be fixed due to being a Domain Reload issue.
    3. https://issuetracker.unity3d.com/is...-scene-reload-is-disabled-in-project-settings - To be fixed.
    Call for feedback
    We believe that if your project is currently slow to enter Play Mode, this feature will speed things up significantly. However, we understand that the side effects of the feature might be difficult to handle and we’re looking forward to hearing what you think, which issues you encounter in you project/ package/AssetStore plugin and how we can make life easier for you!

    Big thanks to @sinitreo, @chrisk, @Peter77, @Baste, @Hyp-X and others for providing a valuable feedback.
     
    Last edited: Nov 5, 2019
  2. SugoiDev

    SugoiDev

    Joined:
    Mar 27, 2013
    Posts:
    395
    Thank you for this feature. In my opinion, this had one of the largest positive impacts on our workflow so far (and we've been using Unity since the 3.x days).
    Our playmode cycles have never been this fast and a lot less time is spent creating/maintaining a poor-man's pseudo-playmode state for edit-time. We can just enter playmode now!

    I don't disable scene-reloading, as that one caused trouble for us. But I'm already plenty happy with not having to reload the domain.

    It would be awesome to see script recompilation cycles without having domain reloads, but this one is probably not feasible without .NET core.
     
  3. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    3,749
    After selecting "Enter Play Mode Settings" and hitting play for the first time, Unity just crashed to desktop straight away.

    Stacktrace seems to point to an ECS / jobs issue.

    Is this meant to be supported yet? If it is I'll have a look at debugging it over the weekend to figure out if it's something I'm doing or something the package is doing. First look it seems to be crashing in one of my GameObjectConversion systems.

    -edit-

    After a clean restart, re-enabling the feature i can't seem to replicate the crash. Everything seems to work well. That I'm going to have to tweak one small section of code to be compliant.
     
    Last edited: Nov 1, 2019
  4. chrisk

    chrisk

    Joined:
    Jan 23, 2009
    Posts:
    704
    Once released, Fast Enter-to-play should be the default mode going forward. I'm guessing within a year also? Once experienced the fast mode, there is no way you want to go back. In my opinion, it should've been the default mode from the beginning.

    Well, it's better late than never and everyone should start thinking about the new ways of initializing the game. Anyway, big thanks for the initiative and I'm happy that it's a big step forward.

    However, people will not optin unless 2019.3 releases (I find that too many developers say they will not touch beta Unity and I still think that you should've released it on 2019.2 as a preview if you really wanted feedbacks earlier) and don't despair for the lack of feedback. I'll try to push it once 2019.3 release and I'm sure you will have plenty of feedback soon enough. 2019.3 still on-track for mid-Nov?

    I want to point out that we still have to get rid of AppDomain reload for the compilation; it will allow insanely fast compilation as well. Make the fast iteration complete.

    As you already know it can be done by managing Assembly load/unload ONLY the changed assemblies through AssemblyLoadContext. I hope there is a team who is looking into this already.

    One of the big problems AppDomain reload is that not only it takes a really long time to unload/reload whole Assemblies regardless it's changed or not, but Unity Editor will freeze if you are using native DLL with p/invoke, thus we can't use many native frameworks out there.

    Please keep the good work.

    Cheers!
     
  5. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    3,749
    Been using this for a few days. Great feature, but at the moment sadly very unstable at least for me.
    I crash about 1 in 3 times when I make a code change.

    Based off stack trace it appears to be the Burst asynchronous compiler.
     
    KarlKarl2000 and alexeyzakharov like this.
  6. alexeyzakharov

    alexeyzakharov

    Joined:
    Jul 2, 2014
    Posts:
    507
    Could you please file a bugreport - we'll ensure there is no crash.
     
  7. alexeyzakharov

    alexeyzakharov

    Joined:
    Jul 2, 2014
    Posts:
    507
    Yes, com.unity.entities is supported since 0.2.0-preview.
     
  8. M_R

    M_R

    Joined:
    Apr 15, 2015
    Posts:
    559
    nice post (especially the diagram with enter play mode lifecycle. should be put in the docs along with exit play mode and script compilation counterparts).

    @alexeyzakharov could you also share some best practices about how to properly support this feature in code? (e.g. when to initialize serialized state vs non-serialized state vs static state etc...)
     
    alexeyzakharov likes this.
  9. alexeyzakharov

    alexeyzakharov

    Joined:
    Jul 2, 2014
    Posts:
    507
    Thanks! Yes,we'll add the diagram to a manual section here https://docs.unity3d.com/2019.3/Documentation/Manual/ConfigurableEnterPlayModeDetails.html

    When updating internal projects to use the feature I kept those statements in mind:
    1. Game initialization is driven by Awake/OnEnable and shutdown by OnDestroy/OnDisable - this is already a best practice for Unity games.
    2. Editor scripts use OnEnable to alter Editmode/Playmode behavior.
    3. Avoid using global static variables shared between editor and game scripts.
    4. Prefer singletons which are initialized with Awake/RuntimeInitializeOnLoad to having a many scattered static variables.
    5. Do heavy loading in Awake or in a lazy init way.
    6. Add tests to ensure Editor functionality survives domain reload and playmode changes.
    The procedure I've used is quite simple:
    1. Enable the mode.
    2. Enter Play Mode 2-3 time consecutively.
    3. Check that there are no errors in the console.
    4. Check that the game functions properly - requires help from the game team.
    5. If there are errors or different behavior - enable scene reload.
    6. Investigate which static variables might be carried to the next game session - move those to RuntimeInitializeOnLoad helper method.
    I didn't have issues with private fields serialization, but if you experienced those mark private array/lists game script references game script doesn't care about with [NotSerialized] as @Hyp-X mentioned here. (although, I still think we might be able to fix this).
    Generally, skipping the domain reload is a huge win in iteration time and requires minimal code modifications (none in some cases if you were already following general MonoBehaviour lifecycly patterns) and ongoing support. Skipping scene reload saves extra time, but also requires extra fixes which are more workarounds unfortunately, and this is where the most of the work is to get the feature out of the experimental.
     
  10. laessnb

    laessnb

    Joined:
    Jun 10, 2014
    Posts:
    101
    This is by far the biggest potential problem in an established codebase. Is there any possible way you guys could provide diagnostics that would help detect the problems in a mature codebase, without having to run through a game's myriad pathways to ensure some tiny static didn't sneak its way through? At least getting some detection from Unity's side would help us find these things more quickly and in a methodical way rather than hoping we have enough coverage.
     
  11. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    3,749
    I upgraded from burst 1.2.0p8 to 1.2.0p9 and disabled domain reloading again and have not yet been able to crash it in about 30min of use so far. Even trying hard doing some dodgy things.

    The burst changelog for p9 somewhat related to the last exception in the stacktrace I was getting so I'm hoping it's all sorted. I'll continue testing and see how it goes.
     
    alexeyzakharov likes this.
  12. Awarisu

    Awarisu

    Joined:
    May 6, 2019
    Posts:
    215
    It would be nice if first-party Unity packages actually supported this, like the new input system. (case 1192017)
     
    noio, Ramobo, Shinyclef and 12 others like this.
  13. MechEthan

    MechEthan

    Joined:
    Mar 23, 2016
    Posts:
    166
  14. Arkade

    Arkade

    Joined:
    Oct 11, 2012
    Posts:
    654
    FYI: URP's DebugManager can start giving an NRE sometimes when using this feature. To reiterate, it doesn't happen all the time. However once it starts happening, it happens every time one tries to enter play mode.

    In case anyone else ends-up here, you can get it working again by disabling the "Enter Play Mode" feature, starting your game (it'll work fine) then stop and re-enable the feature. HTH

    Skimming the code, I find myself wondering if it's after I do a non-debug build...? I'll keep that in mind. (DebugManager's Lazy-initialized constructor has a check for `Debug.isDebugBuild` which I'd hazard is leaving lying around an instance that /is/ constructed but has no members. (not checked.))

    Code (CSharp):
    1. NullReferenceException: Object reference not set to an instance of an object
    2.   at UnityEngine.Rendering.DebugManager.UpdateActions () [0x0001b] in D:\Users\User\Dev\Unity3D\LevelDesignTest2019\Library\PackageCache\com.unity.render-pipelines.core@7.1.6\Runtime\Debugging\DebugManager.Actions.cs:165
    3.   at UnityEngine.Rendering.DebugUpdater.Update () [0x00001] in D:\Users\User\Dev\Unity3D\LevelDesignTest2019\Library\PackageCache\com.unity.render-pipelines.core@7.1.6\Runtime\Debugging\DebugUpdater.cs:18
    4. (Filename: Library/PackageCache/com.unity.render-pipelines.core@7.1.6/Runtime/Debugging/DebugManager.Actions.cs Line: 165)
     
  15. n-gist

    n-gist

    Joined:
    Mar 7, 2016
    Posts:
    41
    Really nice feature, started using it. Thanks!

    So, we need to manage resetting some static variables and so on when they causing problems. I found that [RuntimeInitializeOnLoad] and [RuntimeInitializeOnLoadMethod] is not all-case usable for this because they firing when you go to playing mode, and when you go back to editing mode - they are not. I needed resetting variables in both cases and I ended up with this, attached to any persistent gameobject on scene:

    Code (CSharp):
    1. #if UNITY_EDITOR
    2. using UnityEngine;
    3. using UnityEditor;
    4.  
    5. [ExecuteAlways]
    6. public class DOMAINKEEPER : MonoBehaviour {
    7.     void OnEnable() {
    8.         if (EditorSettings.enterPlayModeOptionsEnabled &&
    9.             EditorSettings.enterPlayModeOptions.HasFlag(EnterPlayModeOptions.DisableDomainReload)) {
    10.             EditorApplication.playModeStateChanged += PlaymodeStateChange;
    11.         }
    12.     }
    13.     void OnDisable() {
    14.         EditorApplication.playModeStateChanged -= PlaymodeStateChange;
    15.     }
    16.     void PlaymodeStateChange(PlayModeStateChange playModeStateChange) {
    17.         if (playModeStateChange == PlayModeStateChange.ExitingPlayMode ||
    18.             playModeStateChange == PlayModeStateChange.ExitingEditMode) {
    19.             // variables resetting code
    20.         }
    21.     }
    22. }
    23. #endif
    It is working in my case, but did I do it right? Maybe it needs another event or switching from 'exiting' to 'entering' ones? What will be more closer to disabled domain reload moment in time?

    upd: Checked the entering events, they does not fit.
     
    Last edited: Dec 19, 2019
    alexeyzakharov likes this.
  16. snacktime

    snacktime

    Joined:
    Apr 15, 2013
    Posts:
    3,356
    This is probably a better flow for detecting play vs editor mode. Although instead of just setting IsPlaying and EditorModeEnabled you would probably want those to fire events that other code can subscribe to. This was pulled from some code that was specific to handling ECS worlds transitions.

    Why this is all so complex right now is just due to how the flow has evolved over time. Even Unity's own developers can't keep it straight. I've seen their own code where they document the flow inline in some feature just to remind themselves how it works (and comment on how nuts it is). Sort of a clue they should not be just making it more complex without stopping and fixing it.


    Code (csharp):
    1.  
    2.  
    3. #if UNITY_EDITOR
    4. private static void OnPlaymodeChanged(UnityEditor.PlayModeStateChange change)
    5.         {
    6.             switch (change)
    7.             {
    8.                 case UnityEditor.PlayModeStateChange.EnteredEditMode:
    9.                     IsPlaying = false;
    10.                     EditorModeEnabled = true;
    11.                     break;
    12.             }
    13.         }
    14. #endif
    15.  
    16.  [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
    17.         private static void Init()
    18.         {
    19.          
    20.             EditorModeEnabled = false;
    21.             IsPlaying = true;
    22.  
    23. #if UNITY_EDITOR
    24.             UnityEditor.EditorApplication.playModeStateChanged -= OnPlaymodeChanged;
    25.             UnityEditor.EditorApplication.playModeStateChanged += OnPlaymodeChanged;
    26. #endif
    27. }
    28.  
    29.  
     
  17. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    6,203
    those property names are to die for
     
    alexeyzakharov likes this.
  18. Wobbers

    Wobbers

    Joined:
    Dec 31, 2017
    Posts:
    55
    I got frequent crashes on Enter Playmode as well when Reload Domain is disabled. Crashes in EntityDataAccess.AddComponent. Is a little harder to reproduce in a small project, but it seems to happen when quickly entering playmode while there is still some entity reload stuff going on.
    Bug case is 1209152.
     
  19. Peter77

    Peter77

    QA Jesus

    Joined:
    Jun 12, 2013
    Posts:
    6,586
    If a MonoBehaviour is decorated with the [ExecuteInEditMode] attribute, the Component does not receive its Awake() event when entering playmode, which is causing all sorts of issues for me. Tested with Unity 2019.3.0f4.

    I added the following Code/Component to a GameObject and just pressed Play&Stop in the editor and checked the Console Window what events were triggered:

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. //[ExecuteAlways]
    6. [ExecuteInEditMode]
    7. public class TestCode : MonoBehaviour
    8. {
    9.     void Awake()
    10.     {
    11.         Debug.LogFormat("Awake: isPlaying={0}", Application.isPlaying);
    12.     }
    13.  
    14.     void Start()
    15.     {
    16.         Debug.LogFormat("Start: isPlaying={0}", Application.isPlaying);
    17.     }
    18.  
    19.     void OnEnable()
    20.     {
    21.         Debug.LogFormat("OnEnable: isPlaying={0}", Application.isPlaying);
    22.     }
    23.  
    24.     void OnDisable()
    25.     {
    26.         Debug.LogFormat("OnDisable: isPlaying={0}", Application.isPlaying);
    27.     }
    28.  
    29.     void OnDestroy()
    30.     {
    31.         Debug.LogFormat("OnDestroy: isPlaying={0}", Application.isPlaying);
    32.     }
    33. }
    34.  

    First what works. If the Component does NOT use [ExecuteInEditMode], it receives these events, which make perfect sense to me:
    upload_2020-1-10_7-51-14.png


    If the Component does use the [ExecuteInEditMode] attribute, events look like this:
    upload_2020-1-10_7-51-33.png

    What happens when entering play mode, showed as "Press PLAY" here, does not look right to me.

    When the new "Enter Play Mode" feature is enabled, the Component does not receive its Awake event when pressing play. I don't think this is on purpose, because Components without the [ExecuteInEditMode] attribute receive their Awake event, as well as when not using the new Enter Play Mode feature, in which case they receive their Awake event too.

    Not having Awake for "isPlaying" requires me to move various one time initialization code from Awake to OnEnable and then implement more complex code to handle what happens when OnEnable is called multiple times as shown above.

    Another smell when entering play mode is that "OnDisable" isn't called when isPlaying is still false. The problem with that is because OnEnable was called with isPlaying=false, yet when entering play mode we first receive the OnDisable with isPlaying=true event. These do not match, making it difficult to correctly intialize/deinitialize things.

    When the new "Enter Play Mode" feature is disabled, the Component receives multiple OnDisable/OnEnable events when playmode is being entered. Is this on purpose? It does not cause issues for me, but why is Unity doing it? Maybe it makes enter play mode faster when OnDisable/OnEnable events are being sent only once.
     
    SugoiDev and alexeyzakharov like this.
  20. alexeyzakharov

    alexeyzakharov

    Joined:
    Jul 2, 2014
    Posts:
    507
    Hi @Peter77 ,
    Thanks for the good question and detailed test!

    This is a result of disabled "Full Scene Reload".
    I've changed the Awake behavior deliberately to be able to avoid Editor scripts initialization multiple times.
    That is reflected on the diagram above (the skipped Awake part).

    The reasoning behind that was to avoid second loading that is usually done in Awake. And the assumption was that given that script is marked with ExecuteAlways/ExecuteInEditMode it has to be able to use OnDisable/OnEnable to restart in order to be able to survive domain reload correctly. However, in case Awake has checks for playmode and does differrent init for play/edit modes, then yes, I can see the issue. Moreover, the same is applicable to scripts not marked with ExecuteAlways and get Awake on scene loading in EditMode.
    It is quite challenging to provide a fast "reset" path for the scene and not break existing scripts. Basically the benefit from the whole scene reset diminishes then to be only "skip reading scene from disk and deserialization". Which for many of games is not the biggest contributor.

    Do you have different init paths for the editor script in edit and playmode? How much time does it save for you when Scene Reload disabled?

    We usually follow rule "don't do what you don't have to do", and a variant of it "don't do what is later not used" :). But sometimes it is almost impossible to follow it and not to break existing projects. We are discussing how to proceed with this change and the "fast scene reset".

    It is a part of serialization during domain reload. And it would be beneficial to not call those callbacks, yes. But there are use cases when those are used to detect exact moment of scripts unload before domain reload or very first moment after domain is inited (before InitializeOnLoad and Awake). So we decided not to change existing behavior until we provide a clear and consistent lifecycle notifications.
    We are using this feature internally and have a lot of feedback thanks to you and others who tried it and contributed. There are some fundamental questions we need to answer to ensure we have a right vision for a scalable playmode/simulation workflow in the Editor.
     
    MechEthan, Peter77 and SugoiDev like this.
  21. alexeyzakharov

    alexeyzakharov

    Joined:
    Jul 2, 2014
    Posts:
    507
    Thank you for the straight feedback! Yes, it is complicated nowadays. And we are willing to solve it. Let's find a way with the lowest friction.
     
    firstuser and MechEthan like this.
  22. Peter77

    Peter77

    QA Jesus

    Joined:
    Jun 12, 2013
    Posts:
    6,586
    Hey Alex,

    thanks for your reply!

    I see, from that point of view it makes sense how it works then.

    Using OnEnable/OnDisable is the workaround I came up with.

    The problem is when the Component receives the OnDisable event during enter playmode, Application.isPlaying is true, but Application.isPlaying should be false in this case, because during the OnEnable event it received earlier, Application.isPlaying was false too.

    These OnEnable/OnDisable and Application.isPlaying state pairs should match imo, in order to get initialization/shutdown stuff right.

    In my case now, I perform things during OnDisable that shouldn't happen, because their initializatiton didn't occur in OnEnable at edit time.

    upload_2020-1-10_10-29-46.png


    Yes. Take a look at BlobShadow.cs from the project attached to Case 1108597.
    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. [ExecuteInEditMode]
    6. public class BlobShadow : MonoBehaviour, IMessageReceiver<PrefsChangedMessage>
    7. {
    8.     [Tooltip("A reference to the actual shadow prefab that is used as blob-shadow.")]
    9.     [SerializeField] GameObject _Shadow = null;
    10.     [SerializeField] float _Scale = 2.0f;
    11.  
    12.     CullingHandler _Culler = null;
    13.  
    14.     void Awake()
    15.     {
    16. #if UNITY_EDITOR
    17.         if(!Application.isPlaying)
    18.             return;
    19. #endif
    20.  
    21.         _Culler = this.GetComponent<CullingHandler>();
    22.         if(_Culler == null)
    23.         {
    24.             _Culler = this.gameObject.AddComponent<CullingHandler>();
    25.             _Culler.CullingRadius = _Scale;
    26.             _Culler.CullingRange = Prefs.Graphic.ShadowDistance;
    27.         }
    28.     }
    29.  
    30.     void OnEnable()
    31.     {
    32. #if UNITY_EDITOR
    33.         if(!Application.isPlaying)
    34.             return;
    35. #endif
    36.  
    37.         MessageManager<PrefsChangedMessage>.Subscribe(this);
    38.     }
    39.  
    40.     void OnDisable()
    41.     {
    42. #if UNITY_EDITOR
    43.         if(!Application.isPlaying)
    44.             return;
    45. #endif
    46.  
    47.         MessageManager<PrefsChangedMessage>.Unsubscribe(this);
    48.     }
    49.  
    50.     void Start()
    51.     {
    52. #if UNITY_EDITOR
    53.         if(!Application.isPlaying)
    54.             return;
    55. #endif
    56.  
    57.         var obj = ContentManager.Instantiate(_Shadow, transform);
    58.         obj.transform.localScale = Vector3.one * _Scale;
    59.  
    60.         _Culler.Add(obj);
    61.     }
    62.  
    63.     public void OnReceiveMessage(PrefsChangedMessage msg)
    64.     {
    65.         if(_Culler != null)
    66.         {
    67.             _Culler.CullingRange = Prefs.Graphic.ShadowDistance;
    68.         }
    69.     }
    70.  
    71. #if UNITY_EDITOR
    72.     void Update()
    73.     {
    74.         if(Application.isPlaying)
    75.             return;
    76.  
    77.         var shadow = _Shadow;
    78.         if(shadow == null)
    79.             return;
    80.  
    81.         var renderer = shadow.GetComponent<MeshRenderer>();
    82.         if(renderer == null)
    83.             return;
    84.  
    85.         var filter = shadow.GetComponent<MeshFilter>();
    86.         if(filter == null)
    87.             return;
    88.  
    89.         var matrix = Matrix4x4.TRS(transform.position + shadow.transform.localPosition, shadow.transform.localRotation, Vector3.one * _Scale);
    90.         Graphics.DrawMesh(filter.sharedMesh, matrix, renderer.sharedMaterial, shadow.layer);
    91.     }
    92. #endif
    93. }
    94.  

    800ms
     
    Last edited: Jan 10, 2020
    SugoiDev and alexeyzakharov like this.
  23. alexeyzakharov

    alexeyzakharov

    Joined:
    Jul 2, 2014
    Posts:
    507
    It is better to use RuntimeInitializeOnLoadMethod attribute atm - as stated at the diagram EditorApplication.playModeStateChanged fires too late in Play mode - when your scripts already got 2 Update calls.
     
  24. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    6,203
    The lowest friction would be zero code change so let's start there:
    Why do we need to change our code?
     
  25. snacktime

    snacktime

    Joined:
    Apr 15, 2013
    Posts:
    3,356
    Well it's either end users solve it explicitly, or Unity adds a bunch of automatic magic. The latter is likely to lead to even more issues long term I think.

    My thought is fast enter play mode should have it's own api. What's different with it should be explicit and handled by explicit api's. Treat it as a separate concern pretty much.


    In more complex projects treating FEPM as an entirely separate thing is I think likely the only sane approach. We have 2 separate VS projects that produce shared dll's that are used on client and server. We use standard libraries that make use of thread locals and thread statics with explicit initializers/destructors. We have 3 C++ dll's 2 that use attach/detach hooks (both process and thread). We share so much code that I would really have to reason about FEPM on it's own, outside of any of our existing lifecycle hooks for game logic. If I started to mix those concerns I would be worried about edge cases I didn't think of, ones that would potentially result in having to go back and do a lot more refactoring then if I had cleanly separated things up front.
     
    alexeyzakharov likes this.
  26. QFSW

    QFSW

    Joined:
    Mar 24, 2015
    Posts:
    2,906
    I really think a OnEnterPlayMode and OnLeavePlayMode that act like OnEnable and OnDisable really would be the best way to go around it
     
    Miguel_TagaiArts, BTStone and Awarisu like this.
  27. Awarisu

    Awarisu

    Joined:
    May 6, 2019
    Posts:
    215
    Because the old, slow way of entering Play mode comes with a LOT of guarantees for your code, such as static variables being reinitialized or every GameObject being newly created. That can't reasonably be made faster, so the other option is to drop these guarantees, however that necessarily requires code changes from you, e.g., to ensure that you don't depend on statics being reinitialized, Awake being called every time, etc.
     
    alexeyzakharov likes this.
  28. Ramobo

    Ramobo

    Joined:
    Dec 26, 2018
    Posts:
    212
    Ahem. The compilation freeze desperately needs fixing too, you know.
    And don't come with "pausing everything and moving to .NET Core isn't reasonable" — AssemblyLoadContext is far from the only benefit and Unity has set a rather low bar for pretty much everything.
     
    lclemens and goncalo-vasconcelos like this.
  29. Awarisu

    Awarisu

    Joined:
    May 6, 2019
    Posts:
    215
    Unity with CoreCLR is a dream of mine but I've long lost hope of it ever happening. Apparently Unity painted themselves into a corner in the C++ side of things making too many Mono-specific assumptions such as the GC not being compacting that is too costly to fix.
     
    Ramobo likes this.
  30. Peter77

    Peter77

    QA Jesus

    Joined:
    Jun 12, 2013
    Posts:
    6,586
    Hey Alex,

    could you please take (another) look at this post of mine:
    https://forum.unity.com/threads/configurable-enter-play-mode.768689/#post-5356122

    Please take a look at the table image in that post. The left column represents the actual implementation and the right column what I would expect.

    The highlighted cell shows that I expect OnDisable being called while isPlaying=false, because OnEnable was called during isPlaying=false too.

    However, OnDisable is actually called when isPlaying=true already:
    Code (CSharp):
    1. OnEnable: isPlaying=false
    2. Start: isPlaying=false
    3. OnDisable: isPlaying=true <- this should be false
    4. OnEnable: isPlaying=true
    5. Start: isPlaying=true
    This causes issues inside OnDisable that you don't know whether the Component was OnEnable'd while playing or during edit mode.
     
    TextusGames and alexeyzakharov like this.
  31. alexeyzakharov

    alexeyzakharov

    Joined:
    Jul 2, 2014
    Posts:
    507
    This is inconsistent, thanks for pointing it out, I'll fix it.
     
    TextusGames, SugoiDev and Peter77 like this.
  32. alexeyzakharov

    alexeyzakharov

    Joined:
    Jul 2, 2014
    Posts:
    507
    We are going to look into how we can support existing code and allow faster path for a new one.
    Similar thread here https://forum.unity.com/threads/cas...r-called-for-some-monobehaviour-types.803631/
     
    firstuser and Peter77 like this.
  33. madnesslabo

    madnesslabo

    Joined:
    Nov 8, 2012
    Posts:
    59
    Hello. I am a Japanese and asset store publisher.
    I'm sorry if my English is wrong.

    https://blogs.unity3d.com/jp/2019/11/05/enter-play-mode-faster-in-unity-2019-3/
    Does this sentence mean that asset store developers get support from Unity for debugging, testing and coding checks?
    Users who read this may think that it is easy for asset developers to do.
    But this is a breaking change and it's very hard.

    I am updating my assets now.
    For me, the static part worked with a few changes.
    But the private field needs a big change.
    As for ExecuteInEditMode, there is no hope for a solution.

    For private fields, there is a problem only immediately after the script is changed.
    I suggest adding a "Reload after recompilation" option.
    Differences between normal and recompiled results may cause bugs. That is so bad.

    I also hope these options are user local settings, not ProjectSettings.
    It is only the programmer that recompiles frequently in team development.
    The required options will vary depending on who uses them.
     
    CodeRonnie and alexeyzakharov like this.
  34. Peter77

    Peter77

    QA Jesus

    Joined:
    Jun 12, 2013
    Posts:
    6,586
    It's bound to the project, more specifically it's stored in the
    ProjectSettings/EditorSettings.asset
    file.
     
  35. RunninglVlan

    RunninglVlan

    Joined:
    Nov 6, 2018
    Posts:
    180
    Hi, found a bug related to this feature.
    I have it enabled without any Reload ticks.
    Then onValueChanged, e.g., on Toggles/Sliders that are set in Inspector, stop working, although onClick on Buttons still work.
    If I add onValueChanged listeners in script, then they start working again.
    If I have callbacks in Inspector and disable Enter Play Mode feature, then they start working again.
     
  36. QFSW

    QFSW

    Joined:
    Mar 24, 2015
    Posts:
    2,906
    I've already reported this for toggles but not for sliders, I assume its the same bug causing both
     
    alexeyzakharov and RunninglVlan like this.
  37. Ghat-Smith

    Ghat-Smith

    Joined:
    Aug 16, 2016
    Posts:
    48
    Hi.
    I've enabled the fast enter play mode options and I noticed something that looks like a bug, or that at least I can't understand. I talk about it here without doing a bug report as it may be the expected behavior ?

    Inside a C# class (which is serialized inside a MonoBehaviour script), I had an auto property (type: C# class) initialized at null. The value was not correctly initialized after recompiling. I changed to a complete property with System.NonSerialized attribute on the field and it solved the problem. Is it intended ?

    The auto property was null before compilation, and got an obscure value after recompilation (looks like a C# class instance which was replaced during serialization). The strangest thing is that I don't have any line of code changing the value during the recompilation (nothing in OnEnable, Awake etc) and it happens when recompiling out of play mode o_O.

    upload_2020-1-30_17-35-7.png
     
  38. Awarisu

    Awarisu

    Joined:
    May 6, 2019
    Posts:
    215
    Backing fields of auto-properties are private with no properties of them so this sounds like a bug.
     
  39. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,292
    @alexeyzakharov

    upload_2020-2-1_11-24-25.png

    The feature is great, but could you please stop telling me that it's on every time I enter play mode?
     
  40. Quatum1000

    Quatum1000

    Joined:
    Oct 5, 2014
    Posts:
    889
    + My first Start takes in 2019.3f without enabled Configurable Play Mode about ~67 Seconds. Second Start ~0.5 seconds.

    + The first start with the same tiny project in 2019.2f about ~3 Seconds. Second start about the same.

    But ~67 Seconds for the first start after running Unity 2019.3f is strange..
    Anyway its a real great feature.
     
    Last edited: Feb 2, 2020
    phobos2077 and Awarisu like this.
  41. Awarisu

    Awarisu

    Joined:
    May 6, 2019
    Posts:
    215
    There's been a massive regression with old-school Play mode but they released with it anyway.
     
    phobos2077 likes this.
  42. EddieC

    EddieC

    Joined:
    Mar 11, 2014
    Posts:
    6
    I don't know where else to put this, but FYI, RuntimeInitializeOnLoad does not work for methods on generic classes. I just spent an hour trying to make my singleton implementation work before realising my reinit method just wasn't being called.
    After realising this, it makes sense (how would you decide which versions of the generic class to call the method on?), but a note in the docs would be useful!

    Btw, my workaround was to just make a non-generic base class to handle the initialisation, but this might not always be an easy option.
     
  43. alexeyzakharov

    alexeyzakharov

    Joined:
    Jul 2, 2014
    Posts:
    507
    Noted, we'll keep only log output.
     
    phobos2077 and TextusGames like this.
  44. LazloBonin

    LazloBonin

    Joined:
    Mar 6, 2015
    Posts:
    809
    Hi, could you describe exactly what "simulate destroy and reset" scene does when scene reload is skipped?
     
    phobos2077 likes this.
  45. alexeyzakharov

    alexeyzakharov

    Joined:
    Jul 2, 2014
    Posts:
    507
    When the enter play mode happens the scene loading part corresponds to:
    1) The current scene(s) content is serialized into a temp scene files.
    2) All scenes, objects are unloaded/destroyed (with some exceptions).
    3) The scene is loaded from the backup (active scene as new first, other additively).
    The "simulation" for this process aimed at the step 3) where construct/read/deserialize is the most expensive part.
    What it does is that instead of destroying and loading objects from disk we allow to reuse the existing editmode instance and save time that would be spent for construction/read/deserialization. In Step 2 we deactivate objects (OnDisable), in Step 3 we activate those (OnEnable). If they were not initialized before, Step 3 also calls Awake. The order is equivalent to the default order. (Events wise see the diagram above ^.)

    Basic assumptions is that the editmode state is equivalent to what you get from backup (except of private/NonSerializedFields!). And the motivation is to figure out the way where we can evolve the playmode workflow into a something scalable which doesn't depend on everything in the project and editor, but only on changed code/assets (O(N^2)->O(1) kind of).
     
    LazloBonin and TextusGames like this.
  46. LazloBonin

    LazloBonin

    Joined:
    Mar 6, 2015
    Posts:
    809
    Thanks for the precision!

    Just rephrasing to make sure I understand:

    The two expected differences are that:

    1. OnDestroy will not be called. Only OnDisable.

    2. Non-serialized fields will not reset to their default initializers like they would have had the skipping option been disabled?

    If so, that's very reassuring :)
     
    alexeyzakharov likes this.
  47. Player7

    Player7

    Joined:
    Oct 21, 2015
    Posts:
    1,533
    I mean never thought I'd see hitting the play button and having things playable in seconds.. its become over minute later before anything is playable ... unbearable really, so I'm all about using this feature as much as possible now.

    Mainly it's the usage of a lot of third party assets, which aren't always getting used in the scene being tested, so for those third party assets will there some better feature to set certain asset folders up so they are reloaded like before, bit like . ~ being used on asset folders to ignore them etc ... and maybe option on packages for similar? For a start 'Mirror' doesn't work at all with this enabled, and well that doesn't help :)
     
    firstuser likes this.
  48. QFSW

    QFSW

    Joined:
    Mar 24, 2015
    Posts:
    2,906
    Since partial domain reloads aren't possible with Mono I don't think this can be done
     
    Last edited: Feb 29, 2020
    TextusGames likes this.
  49. methusalah999

    methusalah999

    Joined:
    May 22, 2017
    Posts:
    640
    I'm having the exact same problem. I'm using this famous singleton class and it won't work with Configurable Enter PlayMode if domain is not reloaded.

    The static variable m_ShuttingDown is set to true when application is quitting or singleton is destroyed, but never set back to false.

    Adding a RuntimeInitializeOnLoadMethod won't work as it is never called.

    @alexeyzakharov can you think of a workaround?
     
  50. alexeyzakharov

    alexeyzakharov

    Joined:
    Jul 2, 2014
    Posts:
    507
    Good question! TBH I think the singleton deinit should be only tied to OnDestroy if you drive it with MonoBehaviour lifecycle as this is the point where the object destruction actually happens.
    In case you really want to avoid singleton usage at shutdown, would this Singleton implementation work for your use case?
    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. public class Singleton<T> : MonoBehaviour where T : MonoBehaviour
    4. {
    5.     // Check to see if we're about to be destroyed.
    6.     private static object s_Lock = new object();
    7.     private static T s_Instance;
    8.     /// <summary>
    9.     /// Access singleton instance through this propriety.
    10.     /// </summary>
    11.     public static T Instance
    12.     {
    13.         get
    14.         {
    15.             lock (s_Lock)
    16.             {
    17.                 if (s_Instance != null)
    18.                     return s_Instance;
    19.  
    20.                 // Search for existing instance.
    21.                 s_Instance = (T)FindObjectOfType(typeof(T));
    22.                 // Create new instance if one doesn't already exist.
    23.                 if (s_Instance == null)
    24.                 {
    25.                     // Need to create a new GameObject to attach the singleton to.
    26.                     var singletonObject = new GameObject();
    27.                     s_Instance = singletonObject.AddComponent<T>();
    28.                     singletonObject.name = typeof(T).ToString() + " (Singleton)";
    29.                     // Make instance persistent.
    30.                     DontDestroyOnLoad(singletonObject);
    31.                 }
    32.  
    33.                 return s_Instance;
    34.             }
    35.         }
    36.     }
    37.     private void OnDestroy()
    38.     {
    39.         lock (s_Lock)
    40.         {
    41.             s_Instance = null;
    42.         }
    43.     }
    44. }
    The "destroyed" warning message in this case would be replaced by Unity's
    Some objects were not cleaned up when closing the scene. (Did you spawn new GameObjects from OnDestroy?)
    error if you use the instance inside other script's OnDestroy. Which makes its usage equivalent to the original usecase if I understand it correctly
     
    glenneroo, firstuser and CodeRonnie like this.