This works, but not quite the same as the earlier more synchronous way. The problem is that it seems that for some reason there is not just 1 frame, but 2 frame difference between the two scenes being loaded whereas earlier there was 0 frames. This causes additional requirements for the initialization process (preparing the Zenject SceneContext), which I can handle otherwise, but the bigger problem is the 2 frame stutter is visible for the user.
So how can I make the loading happen asynchronously (thus keeping the previous scene running/animating as long as possible) without having any frames between the two scenes?
Unity’s async loading is pretty limited, there’s a single background queue that processes all async operations in sequence. If you unset allowSceneActivation, this will leave the load operation stuck in the queue, blocking all other async operations until you allow the load operation to continue.
While isDone is false, the AsyncOperation queue is stalled. For example, if a LoadSceneAsync.allowSceneActivation is set to false, and another AsyncOperation (e.g. SceneManager.UnloadSceneAsync) initializes, Unity does not call the second operation until the first AsyncOperation.allowSceneActivation is set to true.
My guess is your second load never actually starts, because the first load is stalling the queue. So it takes additional time for the second load to complete once you allow the first load to continue.
There’s no way around this limitation, you’ll have to design your systems accordingly. As a workaround, you can disable all game objects in your scene, so you can load and let it activate but delay the biggest activation cost to when you manually activate its game objects.
Also note that async loading is different in the editor and the player, where the editor isn’t able to do as much async as the player, so make sure you test your loading in a build.
In fact with classic while(condition) yield return null; you’d make the code more concise and clearer.
while (levelSceneOp.progress < 0.9f && gameSceneOp.progress < 0.9f)
yield return new WaitForEndOfFrame();
Note: I use wait for end of frame because I think the loading is done during or after Update so you needn’t necessarily wait for the ‘next’ frame - which may be an alternative explanation for the 1-frame delay.
Thanks for the comment. But sure the second load does start, and in fact it seems that the starting isn’t the problem, but the activation, and I just don’t know where the 2 frame delay.
What is that based on? I’d pretty much expect playmode to work like in builds, when it comes to something like this.
It’s not a problem there being a frame delay during the wait for the progress to reach 0.9f. That’s fine. The problem is that there is a frame delay between the activation of the two scenes, and I don’t get where that’s coming from. That delay causes the need to separately take that delay into account and worse than that the it being apparent visually.
Now for the time being it’s fine without the async loading, but eventually if I get bigger scenes to load, that might harm the UX a bit.
How come? I’d argue the opposite. All in all, there isn’t much of a difference, but this logic has no need to be in a monobehaviour, and in such a context simple async methods are cleaner.