Search Unity

Insanely Fast Enter-to-Play mode

Discussion in '2019.3 Beta' started by chrisk, Jun 27, 2019.

  1. chrisk

    chrisk

    Joined:
    Jan 23, 2009
    Posts:
    560
    The Fast Enter-to-Play mode is probably the most exciting feature in years and it will probably save months of waiting time during the project development cycle on our team.

    It should've been the part of the Unity from the beginning but I'm still happy that Unity finally recognizes.
    Unity routinely is asking for feedback but it really makes me sick whenever I hear them.
    It's not like we didn't give Unity enough feedbacks but Unity does not listen and take actions from the user's perspective.

    It's late nonetheless it's a good start and I hope it continues.

    The doc ( https://docs.google.com/document/d/...j-Pd9M71Uz9dc-dg4/edit#heading=h.2gazcsgmxkub ) is really good explaining how thing will change. However, it still lacks explaining how to work around it. If there is one asset causing a problem, this feature will not be usable, therefore I would like Unity to help the asset developers to adopt the new way of entering the play mode as fast/easily as possible. Having some use-cases where it can cause problems and how to work around it would be useful. And in order to do that, it will help to have a new API/Attributes, such as InitializeOnPlayToEnter(and perhaps ExitOnPlayToEnter) and etc.

    This is the best thing ever happened in a long time and I can't wait until 2019.3 releases. It can immediately save many hours, but adopting the new style will take a while, therefore we need to move as soon as we can.

    I really hope this feature still be part of 2019.2 preview package. It will make the adoption 3-4 months earlier. This is still an optional preview that won't break if not opt-in, right? Yeah, it will require some core changes but if users do not opt-in, just make the code path to take the old. I don't think it's that hard and I don't see any harm doing it.

    I'll really appreciate in advance and it will make me think twice about Unity if you can push it. Look how many canceled project or delayed projects we had in the past? Can you please make it something to happen earlier just for once?

    Cheers!
     
    Last edited: Jun 28, 2019
  2. Lurking-Ninja

    Lurking-Ninja

    Joined:
    Jan 20, 2015
    Posts:
    4,635
    alexeyzakharov likes this.
  3. alexeyzakharov

    alexeyzakharov

    Unity Technologies

    Joined:
    Jul 2, 2014
    Posts:
    308
    Thank you for taking time to read and the suggestions! I'll add examples with issues to the doc.

    We've been considering and are still considering adding an attribute. Having
    [InitializeOnEnteredPlayMode]
    which executes method
    static void MyPlaymodeSetup(EnterPlayModeOptions options)
    totally makes sense.
    However, as mentioned above we already have https://docs.unity3d.com/ScriptReference/EditorApplication-playModeStateChanged.html event which can be used to clear the state. In some cases is has to be used in combination with other APIs which makes detection not simple (at all). Partially it is because PlayModeStateChange.EnteredPlayMode is fired too late after 2 updates when game code is technically already in playmode. I would rather fix this behavior first - we are looking if it is safe - and if not add an attribute.
     
    Alverik, Yozaro, Peter77 and 2 others like this.
  4. Peter77

    Peter77

    Joined:
    Jun 12, 2013
    Posts:
    4,282
    In this case, the boiler-plate code would be more right?

    Rather than being able to just do:
    Code (CSharp):
    1. class Cat : MonoBehaviour
    2. {
    3.     static int s_TotalMeows;
    4.    
    5.     [InitializeOnEnteredPlayMode]
    6.     static void OnEnteredPlayMode()
    7.     {
    8.         s_TotalMeows = 0;
    9.     }
    10. }
    We would need to have something like:
    Code (CSharp):
    1. class Cat : MonoBehaviour
    2. {
    3.     static int s_TotalMeows;
    4.  
    5. #if UNITY_EDITOR
    6.     static void OnEnteredPlayMode(UnityEditor.PlayModeStateChange value)
    7.     {
    8.         if (value == UnityEditor.PlayModeStateChange.ExitingEditMode)
    9.         {
    10.             s_TotalMeows = 0;
    11.         }
    12.     }
    13.  
    14.     [UnityEditor.InitializeOnLoadMethod]
    15.     static void RegisterCallback()
    16.     {
    17.         UnityEditor.EditorApplication.playModeStateChanged += OnEnteredPlayMode;
    18.     }
    19. #endif
    20. }
    Would I also need to unregister the playModeStateChanged callback with this new feature?
     
  5. Prodigga

    Prodigga

    Joined:
    Apr 13, 2011
    Posts:
    794
    I noticed according to the document the scene reset does not re-deserialise the objects in the scene. It just invokes awake and on enable. Consider the scenario where I have an object in my scene that has a behaviour that references another object. I hit play, and while the game is running I change the reference on that behaviour to a 3rd game object. When I hit stop, my behaviour isn't "reset"? IE it'll be referencing the 3rd object? Am I understanding this right?
     
  6. brunocoimbra

    brunocoimbra

    Joined:
    Sep 2, 2015
    Posts:
    54
    If I understood correctly, when stopping it will still reset, as it backups the scene before playing, what does not occurs is a reload when entering play mode, so if you change a field of an object while in edit mode (for example, using the ExecuteInEditMode attribute) this field will not reset automatically when entering play mode.
     
    alexeyzakharov likes this.
  7. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    4,393
    When you say that you "backup current scenes" - what does that mean? If none of the current scenes are dirty, this should take zero time, since they can be loaded from file, right?
     
    alexeyzakharov likes this.
  8. Hyp-X

    Hyp-X

    Joined:
    Jun 24, 2015
    Posts:
    199
    I submitted a bug: (Case 1168733) Fast Enter Playmode compatibility issue

    The issue is that the default values can change in some circumstances.
    This is happening in playmode only scripts so this is not an issue of something remaining from editor mode user code.

    Here's a test script:

    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. public class PlayModeTest : MonoBehaviour
    4. {
    5.     private Vector3[] testVector;
    6.  
    7.     private void Start()
    8.     {
    9.         if (testVector == null)
    10.             Debug.Log("Start: testVector is null");
    11.         else
    12.             Debug.Log("Start: testVector is not null");
    13.     }
    14. }
    15.  
    If you trigger a recompile, THEN enter playmode it will report the variable as not null.
    Subsequent playmode entries, or if you manually reload the scene than the variable will be null.

    Found it in real production code - was quite unexpected even after reading the entire doc.
     
  9. brunocoimbra

    brunocoimbra

    Joined:
    Sep 2, 2015
    Posts:
    54
    Not sure if the reply was to me, but if yes, I meant exactly that, as scenes with modifications needs to be backed up. If is doesn't have modification it will not take "zero time" as it still requires to load from file, but it should be back to a "before play mode" state.
     
  10. alexeyzakharov

    alexeyzakharov

    Unity Technologies

    Joined:
    Jul 2, 2014
    Posts:
    308
    Thanks for the bug!
    It is more likely scene reset fault when we soft reset alive scene objects. Could you please enable normal scene reload as a workaround.
     
  11. alexeyzakharov

    alexeyzakharov

    Unity Technologies

    Joined:
    Jul 2, 2014
    Posts:
    308
    InitializeOnLoadMethod is called only with the domain reload, so no, there is no need to unregister because there will be no second registration for the domain lifetime.
    But yes, I like personally attributes way - less boilerplate code - I'll add it.
     
    brunocoimbra and Peter77 like this.
  12. alexeyzakharov

    alexeyzakharov

    Unity Technologies

    Joined:
    Jul 2, 2014
    Posts:
    308
    When you exit playmode the scene is destroyed and reloaded from the backup, so you get unaltered scene state which was before you hit play. The exit playmode behavior stays exactly the same.
     
    brunocoimbra likes this.
  13. alexeyzakharov

    alexeyzakharov

    Unity Technologies

    Joined:
    Jul 2, 2014
    Posts:
    308
    Yes, you are right. If scene is not dirty backup is pointing to the original scene file.
    (I'll add it to the doc, thanks for pointing this out)
     
  14. alexeyzakharov

    alexeyzakharov

    Unity Technologies

    Joined:
    Jul 2, 2014
    Posts:
    308
    Would you use such boilerplate to catch EnterPlaymode and do reset or other Editor work for game code?
    Code (CSharp):
    1.  
    2. using UnityEngine;
    3. #if UNITY_EDITOR
    4. using UnityEditor;
    5. #endif // UNITY_EDITOR
    6. public class InitializeOnEnterPlayModeScript : MonoBehaviour
    7. {
    8.     public static int GameStaticValue = 42;
    9. #if UNITY_EDITOR
    10.     [InitializeOnEnterPlayMode]
    11.     static void InitializeOnEnterPlayModeGameMethod(EnterPlayModeOptions options)
    12.     {
    13.         if (!options.HasFlag(EnterPlayModeOptions.DisableDomainReload))
    14.             return;
    15.         GameStaticValue = 42;
    16.     }
    17. #endif // UNITY_EDITOR
    18. }
     
  15. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    4,393
    In that case you don't want the editor code to be different from the play mode code. We'd need an attribute that fires either when we enter play mode, or when the build is launched.

    In your example, and in @Peter77's, having that code in builds would be harmless. For something like a must-be-initialized-at-startup-singleton, it's necessary for the code to run in builds.

    I think the #if boilerplate should be reserved for instances where you need different things to happen from builds to play mode.
     
    Griz, Peter77 and Lars-Steenhoff like this.
  16. Peter77

    Peter77

    Joined:
    Jun 12, 2013
    Posts:
    4,282
    I think the initialization code is often the same, whether you press play in the editor or if you actually start a player. At least in my code base. Thus I would prefer if we can use the same initialization method in both cases, to avoid having different initialization behaviour.

    I think of something along the lines:
    Code (CSharp):
    1. public class InitializeOnEnterPlayModeScript : MonoBehaviour
    2. {
    3.     public static int GameStaticValue;
    4.  
    5.     [RuntimeInitializeOnLoadMethod]
    6.     [UnityEngine.InitializeOnEnterPlayMode]
    7.     static void InitializeOnEnterPlayModeGameMethod()
    8.     {
    9.         GameStaticValue = 42;
    10.     }
    11. }
     
    alexeyzakharov likes this.
  17. alexeyzakharov

    alexeyzakharov

    Unity Technologies

    Joined:
    Jul 2, 2014
    Posts:
    308
    Thanks for the replies!
    Yes, agree. For this we have
    RuntimeInitializeOnLoadMethod
    attribute which is executed both in the Player and Editor during the scene load and doesn't depend on domain reload happened or not.
    And now that @Peter77 mentioned it in the example I understand that there is no need to use InitializeOnEnterPlayMode for game scripts and thus complement
    RuntimeInitializeOnLoadMethod
    with
    InitializeOnEnterPlayMode
    attribute as it is called anyway. (Although
    [RuntimeInitializeOnLoadMethod([URL='https://docs.unity3d.com/ScriptReference/RuntimeInitializeLoadType.BeforeSceneLoad.html']RuntimeInitializeLoadType.BeforeSceneLoad[/URL])]
    should be used to make sure init happens before any Awake or OnEnable of game scripts).
    I shouldn't have used #if probably, sorry for confusion - that was a copy paste from a test script :)

    The main reason for
    InitializeOnEnterPlayMode
    to exist is to provide a better entry point for resetting Editor scripts.
    During Enter Play Mode there is a specific order in which magic callbacks are called for Editor scripts. And there is no way to reset singletons before the first Update happens due to
    EditorApplication.playModeStateChanged
    callback being called after the second Update - you basically need to use boilerplate in a form of Editor script with the lowest order index.
    There is
    InitializeOnLoad
    attribute which does custom initialization for the Editor code, unfortunately we can't use it in the path without domain reload as it breaks scripting state in 100% of cases we looked at.
    InitializeOnEnterPlayMode
    could be used to compliment
    InitializeOnLoad
    . However it is flexibility vs usability question (and somewhat performance) :)
    Option 1:
    Code (CSharp):
    1. public class MayEditorClass :
    2. {
    3.     public static int StaticValue;
    4.     [InitializeOnLoadMethod]
    5.     static void MyInitializeOnLoadMethod()
    6.     {
    7.         StaticValue = 42;
    8.     }
    9.     [InitializeOnEnterPlayMode]
    10.     static void MyInitializeOnEnterPlayModeMethod(EnterPlayModeOptions options)
    11.     {
    12.         if (options.HasFlag(EnterPlayModeOptions.DisableDomainReload))
    13.             MyInitializeOnLoadMethod();
    14.     }
    15. }
    Option 2:
    Code (CSharp):
    1. public class MayEditorClass :
    2. {
    3.     public static int StaticValue;
    4.  
    5.     [InitializeOnLoadMethod]
    6.     [InitializeOnEnterPlayMode]
    7.     static void MyInitializeMethod()
    8.     {
    9.         StaticValue = 42;
    10.     }
    11. }
    In the last example InitializeOnLoadMethod is called after domain reload, so if we choose it we have to make sure that a set of methods marked with InitializeOnLoadMethod is not called during processing of InitializeOnEnterPlayMode methods to be able to use InitializeOnEnterPlayMode universally for any Play Mode configuration.
     
  18. AlkisFortuneFish

    AlkisFortuneFish

    Joined:
    Apr 26, 2013
    Posts:
    699
    Been trying this out and found Addressables throwing a bit of a wobbly with it. Submitted a bug report with a repro, as well as a thread here.
     
  19. chrisk

    chrisk

    Joined:
    Jan 23, 2009
    Posts:
    560
    I'm still waiting for 2019.3 to release asap to push other asset developers but I'm getting a bit worried.

    Since my wish wasn't granted (i.g., release this mode with 2019.2 as a preview so that we can test early on), I expect Unity will do thorough testing that all of Unity packages work out-of-box when it's released in 2019.3, so that Unity itself is not the problem for the adoption.
    I hope it's not too much to ask and I would be very disappointed if Unity packages themselves are not working properly.
     
    andreiagmu likes this.
  20. Hyp-X

    Hyp-X

    Joined:
    Jun 24, 2015
    Posts:
    199
    I got a reply from QA that
    Now let me be clear:
    - I know why the issue appears.
    - I know how to work around it, so I don't need more help on that
    - I think the behavior is anything but expected.
    - It will cause a lot of headaches and random bugs for a lot of developers
    - Most people will not understand why it is happening and will see it as random failure.

    So in this case I filed the bug to be helpful, not just for ourselves.

    Just so you know, the generic workaround that WE found is:
    If you have a
    private
    or
    internal
    field, that is a reference type (arrays, List<>, or a class marked [Serializable]), starting from 2019.3 you should mark them [NonSerialized]
    Otherwise you might see seemingly random issues, that didn't happen in previous Unity versions.
     
  21. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    4,393
    When you get an "our developers have decided that this behaviour is expected" reply, it seems like 9 times out of 10 it's QA not understanding your issue, and passing on a really bad explanation to the devs.

    Even if it is expected, at least insist that they properly document it. It's pretty annoying that every time I get back a "is expected" reply, I have to nag them to at least write that down somewhere, as it's not very useful to us end-users if something's expected if that's a secret.
     
    SugoiDev, JoNax97 and hippocoder like this.
  22. AlkisFortuneFish

    AlkisFortuneFish

    Joined:
    Apr 26, 2013
    Posts:
    699
    The only way what is described there is to be "expected" is if they inverted their understanding of what the problem is, something being non-null the second time around.
     
  23. alexeyzakharov

    alexeyzakharov

    Unity Technologies

    Joined:
    Jul 2, 2014
    Posts:
    308
    I looked at the issue as well and think that resolution notes describe the reason very well.
    Basically this happens due to domain reload serializing list/array references as empty arrays. And thus keeping same objects alive on enter playmode keeps same field values which are non-null anymore.

    I've tired to reconstruct the C# class instances, but that led to necessity of restoring values of other serialized fields which can be done only by reading those from a backup and defeats the purpose of "soft" reset of already loaded scene.
    So in my opinion the right fix would be to fix unexpected null->empty array conversion during domain reload. And that is quite risky to do - from overall stability perspective change in settled behavior to accommodate experimental feature needs is no go.

    For now I can only consider the following scenarios 1) update domain reload serialization behavior for arrays/lists if the feature is on, 2) if skipping scene reload uncovers other fundamental issues remove it.
     
    SugoiDev and brunocoimbra like this.
  24. alexeyzakharov

    alexeyzakharov

    Unity Technologies

    Joined:
    Jul 2, 2014
    Posts:
    308
    Did you also get a resolution notes with the QA reply? If not there might be some process issue - the developer wrote why and it is displayed on issuestracker.
    I really appreciate reporting the issue you've found and for now I've mentioned it in the forum post and it will appear in manuals soonish.
     
  25. AlkisFortuneFish

    AlkisFortuneFish

    Joined:
    Apr 26, 2013
    Posts:
    699
    Yeah, but this seems to have an extra bit that is rather unexpected:

    * Reload the domain
    * Restores array as non-null
    * Enter play mode
    * Array is not null
    * Exit play mode
    * Enter play mode
    * Array is null

    What is unexpected here is the inconsistency.

    From doing a lot of editor tooling, I fully expect private arrays to be serialized and deserialized on assembly reload, hence them being non-null after reload, but what is setting the array instance to null on exiting and re-entering play mode with scene reload and assembly reload off?
     
    alexeyzakharov likes this.
  26. rastlin

    rastlin

    Joined:
    Jun 5, 2017
    Posts:
    114
    Please stop advocating for more static classes/methods, this is really bad design.

    If you want to take advantage of this feature, you should construct your dependency tree on Awake() of a single entry point to your game, this way once you hit enter play mode, your dependency tree will be re-generated.
     
    alexeyzakharov likes this.
  27. alexeyzakharov

    alexeyzakharov

    Unity Technologies

    Joined:
    Jul 2, 2014
    Posts:
    308
    On exiting play mode we load scene from backup and that is what restoring the GO/components state.
    Scene load (non additive) consist of:
    1. Destroy current scene GO - C# script instances are destroyed.
    2. Read scene from disk.
    3. Create new C# script instances for MB/SO - ctor is called here.
    4. Deserialize fields.
    5. OnValidate, Awake, OnEnable,....
    #3 is what "fixes" the default script values.
    Yes, this is unexpected for game scripts. However, it was always the case for editor scripts which have to mitigate null->empty array conversion after domain reload in OnEnable or other ways. Perhaps, as @rastlin said if you have another init entry point based on OnEnable/Awake the soft scene reset option should work.
     
  28. Peter77

    Peter77

    Joined:
    Jun 12, 2013
    Posts:
    4,282
    I think that's not the issue he is describing. The problem is that the null->empty array conversion does not work the second time you enter playmode, in which case it stays null rather than being converted to an empty array.
     
  29. AlkisFortuneFish

    AlkisFortuneFish

    Joined:
    Apr 26, 2013
    Posts:
    699
    Ah, I see. Yeah, I can see the issue.
     
    alexeyzakharov likes this.