1
\$\begingroup\$

I have a game with two scenes, menu and maingame. I have uploaded the game to googleplay. However when I open the game and click on the play button in the menu scene, the next scene (maingame) takes about 4-5 seconds to load. Is there any way to load the maingame along with the menu so that there is no delay in loading?

EDIT

I have added code to the MainMenu.cs file so that when the game starts the LoadSceneAsync function is called and allowSceneActivation is set to false. Once the "play" button is pressed, the activation is set to true. The first time the game loads, there is still a bit of delay however if the player dies and I go the main menu and press play again, there is no delay.

AsyncOperation async;

void Start()
{        
    async = SceneManager.LoadSceneAsync(1);       
    async.allowSceneActivation = false;
}

public void PlayBtnPressed()
{
   async.allowSceneActivation = true;
   AudioManager.audiomanager.Play("PlayButton");       
}
\$\endgroup\$
4
  • 1
    \$\begingroup\$ What have you tried so far? \$\endgroup\$
    – Vaillancourt
    Commented Sep 2, 2020 at 19:25
  • \$\begingroup\$ Level loading in Unity is usually achieved via AsyncOperation or other non-blocking operations. However, one can find several different solutions when using these. \$\endgroup\$
    – liggiorgio
    Commented Sep 2, 2020 at 19:39
  • \$\begingroup\$ @Vaillancourt , so far I haven't added any async ops as I'm not sure how to do so...Once the game loads, it directly goes to scene 1 and if the player hits on play (in scene 1), then scene 2 loads... \$\endgroup\$ Commented Sep 3, 2020 at 5:06
  • \$\begingroup\$ @liggiorgio , is there any example or sample code that you can share? \$\endgroup\$ Commented Sep 3, 2020 at 5:06

2 Answers 2

2
\$\begingroup\$

Scene Loading

Basic

Tested this in a new project. The idea is to start scene preloading when Return button is pressed and save AsyncOperation that is doing it, disallow scene activation for it and allow it when you need the scene to be loaded as soon as it's ready.

I would expect it to be loaded into memory, be careful if you are RAM-bounded because both scenes will take up memory space until one is unloaded.

Reproduce:

  1. Create a new project.
  2. Create 2 scenes:
    • "menu"
    • "maingame"
  3. Add both scenes to Build Settings.
  4. Open "menu" scene.
  5. Create GameObject and add PreloadSceneInUnity component to it.
  6. Enter playmode.
  7. Press Return and see "maingame (is loading)" in Hierarchy window.
  8. Press Space - "maingame" scene will open.

Code

Git: Preload scene in unity

using System.Collections;

using UnityEngine;
using UnityEngine.SceneManagement;

public class PreloadSceneInUnity : MonoBehaviour
{
    [SerializeField] private string _sceneName = "maingame";
    public string _SceneName => this._sceneName;

    private AsyncOperation _asyncOperation;

    private IEnumerator LoadSceneAsyncProcess(string sceneName)
    {
        // Begin to load the Scene you have specified.
        this._asyncOperation = SceneManager.LoadSceneAsync(sceneName);

        // Don't let the Scene activate until you allow it to.
        this._asyncOperation.allowSceneActivation = false;

        while (!this._asyncOperation.isDone)
        {
            Debug.Log($"[scene]:{sceneName} [load progress]: {this._asyncOperation.progress}");

            yield return null;
        }
    }

    private void Update()
    {
        if (Input.GetKeyDown(KeyCode.Return) && this._asyncOperation == null)
        {
            Debug.Log("Started Scene Preloading");

            // Start scene preloading.
            this.StartCoroutine(this.LoadSceneAsyncProcess(sceneName: this._sceneName));
        }

        // Press the space key to activate the Scene.
        if (Input.GetKeyDown(KeyCode.Space) && this._asyncOperation != null)
        {
            Debug.Log("Allowed Scene Activation");

            this._asyncOperation.allowSceneActivation = true;
        }
    }
}

Advanced

If you are not satisfied with how scene loading works in UnityEngine.SceneManagement - I suggest you take a look at Addressables package that Unity provides, it has support for scene loading as well, but be aware that it's a lot more complicated to get into and there is a learning curve that requires to learn quite a few more things before scene loading.

\$\endgroup\$
18
  • \$\begingroup\$ Just curious: What's the purpose of the first yield return null; in LoadSceneAsyncProcess? \$\endgroup\$
    – Philipp
    Commented Sep 3, 2020 at 13:35
  • \$\begingroup\$ @Philipp I have tested without it and it doesn't make a difference, I guess it's redundant unless you need to wait a frame before loading scene async, but it's from the official documentation about AsyncOperation.allowSceneActivation. I guess it's from some old times when it mattered, I remember having this issue in one of my projects, but my memory of it now is very vague and something tells me it's related to Editor scripting. I am going to remove it since it doesn't seem relevant anymore, good catch, thanks. \$\endgroup\$ Commented Sep 3, 2020 at 14:41
  • 1
    \$\begingroup\$ @Philipp issuetracker.unity3d.com/issues/… > "In 2018, you could get around this by starting a coroutine and waiting one tick. This workaround no longer works for 2019." This is probably it. Waiting yield return null so scene loading would work in Awake. \$\endgroup\$ Commented Sep 6, 2020 at 10:39
  • 1
    \$\begingroup\$ @CandidMoon_Max_, thanks for this...... \$\endgroup\$ Commented Sep 8, 2020 at 10:48
  • 1
    \$\begingroup\$ @CandidMoon_Max_ thank you so much! \$\endgroup\$
    – hidd
    Commented Apr 6, 2022 at 18:31
1
\$\begingroup\$

You can load the next scene asynchronously using SceneManager.LoadSceneAsync() using coroutines. You can use this strategy on a splash / loading screen if you're wanting to see it load before showing the menu.

using System.Collections;
using UnityEngine;
using UnityEngine.SceneManagement;

public class Example : MonoBehaviour
{
    void Update()
    {
        // Press the space key to start coroutine
        if (Input.GetKeyDown(KeyCode.Space))
        {
            // Use a coroutine to load the Scene in the background
            StartCoroutine(LoadYourAsyncScene());
        }
    }

    IEnumerator LoadYourAsyncScene()
    {

        AsyncOperation asyncLoad = SceneManager.LoadSceneAsync("Scene2");

        // yield to other processes until the scene is loaded
        while (!asyncLoad.isDone)
        {
            yield return null;
        }

        // Do something here like enabling the play button or closing the splash/loading screen
    }
}

Code example from: https://docs.unity3d.com/ScriptReference/SceneManagement.SceneManager.LoadSceneAsync.html

EDIT: As has been indicated in the comments, if you want to control when the scene loads, you can use the allowSceneActivation flag on the above AsyncOperation object. asyncLoad.allowSceneActivation = false; When you want it to load the next scene: asyncLoad.allowSceneActivation = true;

\$\endgroup\$
4
  • \$\begingroup\$ One problem with this is that the scene will immediately appear and execute as soon as the async loading is finished. Would you know some convenient way to load it but not start rendering and updating it yet? \$\endgroup\$
    – Philipp
    Commented Sep 3, 2020 at 10:32
  • \$\begingroup\$ @Philipp docs.unity3d.com/ScriptReference/… \$\endgroup\$ Commented Sep 3, 2020 at 10:58
  • 1
    \$\begingroup\$ @CandidMoon_Max_ Maybe you would like to write an own answer which explains and demonstrates how to use this property to load a scene in the background without switching to it immediately? \$\endgroup\$
    – Philipp
    Commented Sep 3, 2020 at 11:02
  • \$\begingroup\$ @Philipp I will write it up, thanks. \$\endgroup\$ Commented Sep 3, 2020 at 11:40

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .