Search code examples
c#unity-game-engineasynchronoussceneaddressables

Unity. Can't activate scene instance when it cached after download


I have Main menu provider:

    class MainMenuProvider
{
    private static SceneInstance _cachedScene;
    private static bool _isLoaded;

    public async Task<SceneInstance> Load()
    {
        if (!_isLoaded)
        {
            var mainMenuLoadHandle =
                Addressables.LoadSceneAsync("Assets/Bundles/UI/Scenes/MainMenu.unity", LoadSceneMode.Single, false);
            await mainMenuLoadHandle.Task;
            _cachedScene = mainMenuLoadHandle.Result;
            _isLoaded = true;
        }

        return _cachedScene;
    }
}

When I firstly invoke Load and then scene.ActivateSync it works perfect, but when I invoke Load and ActivateAsync the second time and my scene is cached, nothing happens.

_cachedScene.m_Operation.isDone == true

Solution

  • You set _isLoaded static so this whole thing is definitely only executed exactly once the entire application session lifecycle.

    However, if you then ever happen to use LoadSceneMode.Single for any other scene at any time, then this _cachedScene is unloaded and doesn't exist anymore.

    => _cachedScene will then contain an invalid scene which isn't loaded anymore and will never be loaded again.


    You would need to decouple the loading of the addressable and the actual loading of the scene. The Addressables.LoadSceneAsync already contains directly also the call to SceneManagement.SceneManager.LoadSceneAsync. You want to split that and only use e.g. only Addressables.LoadAssetAsync for your caching.

    And then only load and unload this scene via the mentioned SceneManagement.SceneManager.LoadSceneAsync somewhat like e.g.

    class MainMenuProvider
    {
        private static AsyncOperationHandle<Scene>? _sceneHandle;
    
        private static Scene? _loadedScene;       
    
        public async Task<Scene> Load()
        {
            if(TryGetLoadedScene(out var scene))
            {
                return scene;
            }
    
            if (_sceneHandle == null)
            {
                _sceneHandle = Addressables.LoadAssetAsync<Scene>("Assets/Bundles/UI/Scenes/MainMenu.unity");
            }
    
            await _sceneHandle.Task;
    
            if(TryGetLoadedScene(out var scene))
            {
                return scene;
            }
    
            var path = _sceneHandle.Result.path;
    
            await SceneManager.LoadSceneAsync(path, LoadSceneMode.Single, false);
    
            _loadedScene = SceneManager.GetSceneByPath(path);
    
            return _loadedScene;
        }
    
        private static bool TryGetLoadedScene(out Scene? loadedScene)
        {
            if(_loadedScene == null)
            {
                loadedScene = null;
                return false;
            }
            
            loadedScene = SceneManager.GetSceneByPath(_loadedScene.path);
            if(scene.isValid)
            {
                 _loadedScene = loadedScene;
                 return true;
            }
    
            return false;
        }
    }
    

    This would to certain extent reduce the advantage of the addressable of course since one of them is less memory usage by unloading the scene while it isn't used.