Search Unity

using allowSceneActivation

Discussion in 'Scripting' started by MichaelTaylor3d, Jan 13, 2013.

  1. MichaelTaylor3d

    MichaelTaylor3d

    Joined:
    Jul 17, 2012
    Posts:
    9
    i need to temporarily set allowSceneActivation = false

    so after my level finishes loading async, it has a chance to parse some additional data into the scene before the scene is activated.

    however once I set this flag to false, I cant figure out how to activate the scene when I'm ready. Setting the flag to true doesnt seem to work.

    Does anyone know how to correctly utilize delayed scene activation?
     
    FingerClicks likes this.
  2. florinh

    florinh

    Joined:
    Oct 16, 2012
    Posts:
    2
    I tried using it like below...

    Code (csharp):
    1.  
    2.     private void StartNow () {
    3.         AsyncOperation async = Application.LoadLevelAsync (1);
    4.        
    5.         // Set this false to wait changing the scene
    6.         async.allowSceneActivation = false;
    7.  
    8.         StartCoroutine (LoadLevelProgress (async));
    9.     }
    10.    
    11.    
    12.     IEnumerator LoadLevelProgress (AsyncOperation async) {
    13.         while (!async.isDone) {
    14.             Debug.Log ("Loading " + async.progress);
    15.             yield return null;
    16.         }
    17.        
    18.         Debug.Log ("Loading complete");
    19.        
    20.         // This is where I'm actually changing the scene
    21.         async.allowSceneActivation = true;
    22.     }
    23.  

    ... but it stops at 0.9 and I can't close Unity3d 4.0 after this. If anyone knows how to do this right please reply. Thanks.
     
  3. Arnleif

    Arnleif

    Joined:
    Sep 27, 2012
    Posts:
    10
    I've got this running. It seems as the scene activation is part of the async operation, so when I reach 90% (A bit of a 'magic number' here..), I say we are close enough and allow scene activation to start.

    And don't stop unity while the AsyncOperation has not completed. Unity will hang.

    I'm not sure how 'safe' the 90% is and it would be nice to get some confirmation from someone at Unity, that this is a valid wait of doing tings. The progress does not update smoothly, but rather jumps through some numbers now and then. But it always seems to end at 90% when 'ready to be activated'.

    Code (csharp):
    1. {
    2.     AsyncOperation status = Application.LoadLevelAsync(level);
    3.     status.allowSceneActivation = false;
    4.  
    5.     // Wait until done and collect progress as we go.
    6.     while( !status.isDone )
    7.     {
    8.         loadProgress = status.progress;
    9.                
    10.         if( loadProgress >= 0.9f )
    11.         {
    12.             // Almost done.
    13.             break;
    14.         }
    15.  
    16.         yield return null;
    17.     }
    18.            
    19.     // Allow new scene to start.
    20.     status.allowSceneActivation = true;
    21.     yield return status;
    22.  
    23. }
     
  4. Gibbonator

    Gibbonator

    Joined:
    Jul 27, 2012
    Posts:
    204
    Have you tried checking the value of Application.isLoadingLevel to see if it's done?

    Edit: I did a quick test and this doesn't help. Application.isLoadingLevel stays true until after activation. If it helps I also find it stops at 0.9 progress.
     
    Last edited: Apr 19, 2013
  5. Martin W

    Martin W

    Joined:
    Nov 21, 2012
    Posts:
    13
    The check for progress < 0.9f seems to work for me as well. I'm using this at the moment:

    Code (csharp):
    1. while (!status.isDone  status.progress < 0.9f)
    2.     yield return null;
    However, make sure you actually use 0.9f, not 0.9. Because of the conversion from float to double the progress never reaches double 0.9 which is a slight bit more than the float 0.9f.
     
    Harinezumi likes this.
  6. sirfaty

    sirfaty

    Joined:
    Apr 26, 2013
    Posts:
    1

    Even though you are delaying scene activation, it doesn't prevent the previous level from being unloaded. I suspect the gameObject that is running the coroutine is being destroyed before level progress reaches %100. Use DontDestroyOnLoad on your go to have it persist across scene boundaries, and then destroy itself after setting allowSceneActivation = true.

    EDIT: If you already are using DontDestroyOnLoad, then perhaps the problem is a Unity bug in the version of Unity you're using...? I'm on 4.1.2 and the following works for me on PC:

    Code (csharp):
    1.  
    2. public class SceneMgr : MonoBehaviour
    3. {
    4.     private static SceneMgr s_instance = null;
    5.  
    6.     private AsyncOperation m_asop = null;
    7.     private bool m_quitAfterCurrentLoad = false;
    8.  
    9.     public static SceneMgr Get()
    10.     {
    11.         return s_instance;
    12.     }
    13.  
    14.     public SceneMgr()
    15.     {
    16.         s_instance = this;
    17.     }
    18.  
    19.     public void RestartCurrentLevel()
    20.     {
    21.         LoadLevelImmediate(Application.loadedLevel);
    22.     }
    23.  
    24.     // Pass either the level name as a string or the level index as an int to "level" param
    25.     public void LoadLevel(object level, bool manualActivation = false)
    26.     {
    27.         if (Application.isLoadingLevel)
    28.         {
    29.             Debug.LogError("Call attempted to LoadLevel while a level is already in the process of loading; ignoring the load request...");
    30.         }
    31.         else
    32.         {
    33.             m_asop = _LoadLevelAsyncProxy(level);
    34.             if (null != m_asop)
    35.             {
    36.                 m_asop.allowSceneActivation = !manualActivation;
    37.                 Application.backgroundLoadingPriority = ThreadPriority.Low;
    38.             }
    39.         }
    40.     }
    41.  
    42.     public void LoadLevelImmediate(object level)
    43.     {
    44.         if (Application.isLoadingLevel)
    45.         {
    46.             Debug.LogError("Call attempted to LoadLevel while a level is already in the process of loading; ignoring the load request...");
    47.         }
    48.         else
    49.         {
    50.             _LoadLevelImmediateProxy(level);
    51.         }
    52.     }
    53.  
    54.     public void ActivateLoadedLevel()
    55.     {
    56.         // This will immediately activate the currently loaded level if it hasn't been activated.
    57.         // If the level hasn't finished loading, it will be activated immediately after finishing.
    58.         if (null != m_asop)
    59.         {
    60.             m_asop.allowSceneActivation = true;
    61.         }
    62.         else
    63.         {
    64.             Debug.LogWarning("SceneMgr::ActivateLoadedLevel was called, but there is no inactive scene to activate!");
    65.         }
    66.     }
    67.  
    68.     private static AsyncOperation _LoadLevelAsyncProxy(object level)
    69.     {
    70.         if (level.GetType() == typeof(int))
    71.         {
    72.             return Application.LoadLevelAsync((int)level);
    73.         }
    74.         else if (level.GetType() == typeof(string))
    75.         {
    76.             return Application.LoadLevelAsync((string)level);
    77.         }
    78.         else
    79.         {
    80.             Debug.LogError("SceneMgr.LoadLevel was called with the wrong parameter type " + level.GetType() + "; must be int or string.");
    81.         }
    82.         return null;
    83.     }
    84.  
    85.     private static void _LoadLevelImmediateProxy(object level)
    86.     {
    87.         if (level.GetType() == typeof(int))
    88.         {
    89.             Application.LoadLevel((int)level);
    90.         }
    91.         else if (level.GetType() == typeof(string))
    92.         {
    93.             Application.LoadLevel((string)level);
    94.         }
    95.         else
    96.         {
    97.             Debug.LogError("SceneMgr.LoadLevelImmediate was called with the wrong parameter type " + level.GetType() + "; must be int or string.");
    98.         }
    99.     }
    100.  
    101.     //////////////////////////////////////////////////////////////////////////
    102.     /// UNITY CALLBACKS
    103.  
    104.     void Awake()
    105.     {
    106.         // Persistent object
    107.         DontDestroyOnLoad(gameObject);
    108.     }
    109.  
    110.     void OnEnable()
    111.     {
    112.     }
    113.  
    114.     IEnumerator OnLevelWasLoaded(int level)
    115.     {
    116.         while (null != m_asop  !m_asop.allowSceneActivation)
    117.         {
    118.             // Wait until the just-loaded scene is allowed to start
    119.             yield return null;
    120.         }
    121.  
    122.         m_asop = null;
    123.  
    124.         if (m_quitAfterCurrentLoad)
    125.         {
    126.             Application.Quit();
    127.         }
    128.     }
    129.  
    130.     void OnApplicationQuit()
    131.     {
    132.         if (null != m_asop)
    133.         {
    134.             m_quitAfterCurrentLoad = true;
    135.             Application.CancelQuit();
    136.             Debug.Log("OnApplicationQuit : CancelQuit! Attempting to quit while a scene is loading; quitting after scene load finishes. Called from: " + name);
    137.         }
    138.         else
    139.         {
    140.             Debug.Log("OnApplicationQuit : Shutting down. Called from: " + name);
    141.             s_instance = null;
    142.         }
    143.     }
    144.  
    145. }
    146.  
    The script uses the existence of the AsyncOperation object stored in m_asop to determine if a level load request is in progress. The "manualActivation" parameter to LoadLevel lets the caller start loading a scene early, but not unload the current level or switch to the new level until ActivateLoadedLevel is called. The purpose of the OnApplicationQuit handler is to prevent crashing if trying to quit during an asynchronous scene load; note however it doesn't help on iPhone, the WebPlayer, or the Editor because Application.CancelQuit is ignored.

    (The _LoadLevelAsyncProxy and _LoadLevelImmediateProxy functions are just a hack to get around the fact that Unity has no interface for converting a scene index to a scene name or vice versa. The hack enables LoadLevel to be called with either a scene index or a scene name... I'm sure there must be a better way of doing that though.)
     
    Last edited: Jun 25, 2013
  7. CgShady

    CgShady

    Joined:
    Mar 31, 2011
    Posts:
    10
    Can you not just implement both versions of the method ?
    Code (csharp):
    1.  
    2. public static void LoadMyLevel (int level)
    3. {
    4. }
    5. public static void LoadMyLevel (string level)
    6. {
    7. }
    8.  
     
  8. Jay1118

    Jay1118

    Joined:
    Oct 2, 2012
    Posts:
    9
    In case this helps anyone I came across this problem too and found out that the reason we get to 0.9 is that when you make allowSceneActivation = false it will stop downloading it at 90% and wait for it to be true to continue. So because of that it never gets to a 100% and it never changes the isDone to true. But if you leave allowSceneActivation = true then everything works just fine you even get the 100% and the isDone changes to true.
     
    PeteD likes this.
  9. Mouldi

    Mouldi

    Joined:
    Feb 23, 2013
    Posts:
    20
    Thanks
     
  10. PeteD

    PeteD

    Joined:
    Jul 30, 2013
    Posts:
    71
    Yep that's because otherwise the level would simply start as soon as it was finished downloading regardless of the state of the allow flag.
    Setting allow to false effectively stalls the completion of the loading process until you are ready. The only issue is that the unity team haven't documented that this is how it works!
     
  11. Fattie

    Fattie

    Joined:
    Jul 5, 2012
    Posts:
    476
    "what the F***" ??

    what then is the point of allowSceneActivation ?
     
  12. Apoll0

    Apoll0

    Joined:
    Jun 11, 2015
    Posts:
    16
    You can set allowSceneActivation to False, then:
    Code (CSharp):
    1.  
    2. AsyncOperation sceneLoading;
    3.  
    4. void Start ()
    5.     {
    6.         sceneLoading = SceneManager.LoadSceneAsync("MyNextScene");
    7.         sceneLoading.allowSceneActivation = false;
    8.         StartCoroutine(LoadSceneWait());
    9.     }
    10.  
    11. IEnumerator LoadSceneWait()
    12.     {
    13.         while(sceneLoading.progress < 0.9f)
    14.         {
    15.             yield return new WaitForSeconds(0.1);
    16.         }
    17.  
    18.         sceneLoading.allowSceneActivation = true;
    19.     }
    20.  
    And MyNextScene will change current only after full loading.
     
    AndreiKubyshkin likes this.
  13. BigGameCompany

    BigGameCompany

    Joined:
    Sep 29, 2016
    Posts:
    112
    Isn't that what would happen if you just left allowSceneActivation as true?

    I was loading a scene with a progress bar using this:

    Code (CSharp):
    1. async = SceneManager.LoadSceneAsync("secondScene");
    2.         async.allowSceneActivation = true;
    3.         while (!async.isDone)
    4.         {
    5.             loadingBar.value = Mathf.Max(0.04f, async.progress);
    6.             yield return null;
    7.         }
    and it was working fine, but I wanted to try having the next scene 'mostly' loaded in the background so the final part would load quickly when required, so I tried adding this to an Awake function in the first scene.

    Code (CSharp):
    1. async = SceneManager.LoadSceneAsync("secondScene");
    2. async.allowSceneActivation = false;
    However when this new code runs, secondScene becomes active straight away, even though allowSceneActivation is set to false. I must be misunderstanding something. Can anyone tell me how to have the 'next' scene ready to activate when the user clicks a button or something?

    Thanks
     
    FunFreighterGames likes this.
  14. BigGameCompany

    BigGameCompany

    Joined:
    Sep 29, 2016
    Posts:
    112
    metroidsnes_qg likes this.
  15. Fattie

    Fattie

    Joined:
    Jul 5, 2012
    Posts:
    476
    It's an absolute, total, screw-up by Unity.

    Anything can happen until they fix it.

    You always have the problem that not one employee at Unity, has ever, even opened the product. So you get these utterly bizarre problems, that you would notice instantly the first time you say "tried to make a game"

    Unity has never changed, and will never change. So you just have to deal with it.
     
  16. JoshuaMcKenzie

    JoshuaMcKenzie

    Joined:
    Jun 20, 2015
    Posts:
    916
    This is not a bug.

    I'm fairly certain that everything to do with the AsyncOperation (both the allowSceneActivation and the Awake behaviour) is both fully intended and documented. Awake should always fire the moment the Object is created, and allowSceneActivation shouldn't be the only exception to this rule. whether a scene starts or not doesn't change the fact that it loaded and those objects were instantiated. and objects that are instantiated call Awake


    As for the progress pausing at 90% when AllowSceneActivation is false. that's supposed to happen. and its clearly specified in the documentation.It has always been that way and there's no reason they should change that. its there to help you synchronize the scene loading with other expensive operations.

    This allows:
    • your other scripts (which activate in Awake) time to complete expensive calculations.
    • your loading screen time to perform animated transitions (for example, time for fading) towards the starting level
    • some other expensive AsyncOperations (eg ResourceRequest or AssetBundleRequest) time to complete before you want the level to start
    • all the audio (music, ambience, SFX) time to fully load before you even start the scene so that you don't hear desynced audio.
    when those other expensive assets are ready then set allowSceneActivation back to true. its not a private variable you can totally change it while the AsyncOperation is running and the progress will finally reach 100%. thats the entire idea behind the property.
     
  17. MrLucid72

    MrLucid72

    Joined:
    Jan 12, 2016
    Posts:
    996
    Mine didn't work either until I moved from Awake to Start. Would probably be nice to advise in the docs, if this is commonly mistaken~
     
  18. SkedgyEdgy

    SkedgyEdgy

    Joined:
    Apr 2, 2018
    Posts:
    2
    That's because the last 10% is the actual activation of the scene.
     
  19. FingerClicks

    FingerClicks

    Joined:
    Jul 30, 2017
    Posts:
    5
    Change your code to the the following
    Code (CSharp):
    1.  private void StartNow () {
    2.  
    3. DontDestroyOnLoad(gameObject);
    4.         AsyncOperation async = Application.LoadLevelAsync (1);
    5.      
    6.         // Set this false to wait changing the scene
    7.         async.allowSceneActivation = false;
    8.         StartCoroutine (LoadLevelProgress (async));
    9.     }
    10.  
    11.  
    12.     IEnumerator LoadLevelProgress (AsyncOperation async) {
    13.         while (!async.isDone) {
    14.             Debug.Log ("Loading " + async.progress);
    15.             yield return null;
    16.         }
    17.      
    18.         Debug.Log ("Loading complete");
    19.      
    20.         // This is where I'm actually changing the scene
    21.         async.allowSceneActivation = true;
    22. Destroy(gameObject);
    23.     }
     
  20. DSivtsov

    DSivtsov

    Joined:
    Feb 20, 2019
    Posts:
    151
    It will not work:
    1. because if you set
      async.allowSceneActivation = false
      the async.isDone will never be set to true.
    2. exist the Bug which demands to did absolutely the same as in Unity example see link