Search Unity

Configurable Enter Play Mode

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

  1. alexeyzakharov

    alexeyzakharov

    Unity Technologies

    Joined:
    Jul 2, 2014
    Posts:
    339
    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:
    287
    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:
    1,949
    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:
    614
    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:
    1,949
    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.
     
    alexeyzakharov likes this.
  6. alexeyzakharov

    alexeyzakharov

    Unity Technologies

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

    alexeyzakharov

    Unity Technologies

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

    M_R

    Joined:
    Apr 15, 2015
    Posts:
    528
    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

    Unity Technologies

    Joined:
    Jul 2, 2014
    Posts:
    339
    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. laeusnb

    laeusnb

    Joined:
    Jun 10, 2014
    Posts:
    57
    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:
    1,949
    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. Lorash

    Lorash

    Joined:
    May 6, 2019
    Posts:
    65
    It would be nice if first-party Unity packages actually supported this, like the new input system. (case 1192017)
     
  13. Ethan_VisualVocal

    Ethan_VisualVocal

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

    Arkade

    Joined:
    Oct 11, 2012
    Posts:
    604
    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. nick_gist

    nick_gist

    Joined:
    Mar 7, 2016
    Posts:
    17
    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:
    2,515
    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:
    2,251
    those property names are to die for
     
    alexeyzakharov likes this.
  18. Wobbers

    Wobbers

    Joined:
    Dec 31, 2017
    Posts:
    19
    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:
    4,412
    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

    Unity Technologies

    Joined:
    Jul 2, 2014
    Posts:
    339
    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.
     
  21. alexeyzakharov

    alexeyzakharov

    Unity Technologies

    Joined:
    Jul 2, 2014
    Posts:
    339
    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.
     
    Ethan_VisualVocal likes this.
  22. Peter77

    Peter77

    QA Jesus

    Joined:
    Jun 12, 2013
    Posts:
    4,412
    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

    Unity Technologies

    Joined:
    Jul 2, 2014
    Posts:
    339
    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:
    2,251
    The lowest friction would be zero code change so let's start there:
    Why do we need to change our code?
     
    alexeyzakharov likes this.
  25. snacktime

    snacktime

    Joined:
    Apr 15, 2013
    Posts:
    2,515
    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,529
    I really think a OnEnterPlayMode and OnLeavePlayMode that act like OnEnable and OnDisable really would be the best way to go around it
     
    Miguel_LZ, BTStone and Lorash like this.
  27. Lorash

    Lorash

    Joined:
    May 6, 2019
    Posts:
    65
    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:
    82
    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.
     
    goncalo-vasconcelos likes this.
  29. Lorash

    Lorash

    Joined:
    May 6, 2019
    Posts:
    65
    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.
     
  30. Peter77

    Peter77

    QA Jesus

    Joined:
    Jun 12, 2013
    Posts:
    4,412
    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

    Unity Technologies

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

    alexeyzakharov

    Unity Technologies

    Joined:
    Jul 2, 2014
    Posts:
    339
    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/
     
    Peter77 likes this.
  33. goodtime

    goodtime

    Joined:
    Nov 8, 2012
    Posts:
    54
    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.
     
  34. Peter77

    Peter77

    QA Jesus

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