using System; using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.AddressableAssets; using UnityEngine.ResourceManagement.AsyncOperations; using UnityEngine.ResourceManagement.ResourceProviders; using UnityEngine.SceneManagement; /// /// This class manages the scene loading and unloading. /// public class SceneLoader : MonoBehaviour { [SerializeField] private GameSceneSO _gameplayScene = default; [Header("Load Events")] [SerializeField] private LoadEventChannelSO _loadLocation = default; [SerializeField] private LoadEventChannelSO _loadMenu = default; [SerializeField] private LoadEventChannelSO _coldStartupLocation = default; [Header("Broadcasting on")] [SerializeField] private BoolEventChannelSO _toggleLoadingScreen = default; [SerializeField] private VoidEventChannelSO _onSceneReady = default; private AsyncOperationHandle _loadingOperationHandle; private AsyncOperationHandle _gameplayManagerLoadingOpHandle; //Parameters coming from scene loading requests private GameSceneSO _sceneToLoad; private GameSceneSO _currentlyLoadedScene; private bool _showLoadingScreen; private SceneInstance _gameplayManagerSceneInstance = new SceneInstance(); private void OnEnable() { _loadLocation.OnLoadingRequested += LoadLocation; _loadMenu.OnLoadingRequested += LoadMenu; #if UNITY_EDITOR _coldStartupLocation.OnLoadingRequested += LocationColdStartup; #endif } private void OnDisable() { _loadLocation.OnLoadingRequested -= LoadLocation; _loadMenu.OnLoadingRequested -= LoadMenu; #if UNITY_EDITOR _coldStartupLocation.OnLoadingRequested -= LocationColdStartup; #endif } #if UNITY_EDITOR /// /// This special loading function is only used in the editor, when the developer presses Play in a Location scene, without passing by Initialisation. /// private void LocationColdStartup(GameSceneSO currentlyOpenedLocation, bool showLoadingScreen) { _currentlyLoadedScene = currentlyOpenedLocation; if(_currentlyLoadedScene.sceneType == GameSceneSO.GameSceneType.Location) { //Gameplay managers is loaded synchronously _gameplayManagerLoadingOpHandle = _gameplayScene.sceneReference.LoadSceneAsync(LoadSceneMode.Additive, true); _gameplayManagerLoadingOpHandle.WaitForCompletion(); _gameplayManagerSceneInstance = _gameplayManagerLoadingOpHandle.Result; StartGameplay(); } } #endif /// /// This function loads the location scenes passed as array parameter /// private void LoadLocation(GameSceneSO locationToLoad, bool showLoadingScreen) { _sceneToLoad = locationToLoad; _showLoadingScreen = showLoadingScreen; //In case we are coming from the main menu, we need to load the Gameplay manager scene first if (_gameplayManagerSceneInstance.Scene == null || !_gameplayManagerSceneInstance.Scene.isLoaded) { _gameplayManagerLoadingOpHandle = _gameplayScene.sceneReference.LoadSceneAsync(LoadSceneMode.Additive, true); _gameplayManagerLoadingOpHandle.Completed += OnGameplayMangersLoaded; } else { UnloadPreviousScene(); } } private void OnGameplayMangersLoaded(AsyncOperationHandle obj) { _gameplayManagerSceneInstance = _gameplayManagerLoadingOpHandle.Result; UnloadPreviousScene(); } /// /// Prepares to load the main menu scene, first removing the Gameplay scene in case the game is coming back from gameplay to menus. /// private void LoadMenu(GameSceneSO menuToLoad, bool showLoadingScreen) { _sceneToLoad = menuToLoad; _showLoadingScreen = showLoadingScreen; //In case we are coming from a Location back to the main menu, we need to get rid of the persistent Gameplay manager scene if (_gameplayManagerSceneInstance.Scene != null && _gameplayManagerSceneInstance.Scene.isLoaded) Addressables.UnloadSceneAsync(_gameplayManagerLoadingOpHandle, true); UnloadPreviousScene(); } /// /// In both Location and Menu loading, this function takes care of removing previously loaded scenes. /// private void UnloadPreviousScene() { if(_currentlyLoadedScene != null) //would be null if the game was started in Initialisation { if(_currentlyLoadedScene.sceneReference.OperationHandle.IsValid()) { //Unload the scene through its AssetReference, i.e. through the Addressable system _currentlyLoadedScene.sceneReference.UnLoadScene(); } #if UNITY_EDITOR else { //Only used when, after a "cold start", the player moves to a new scene //Since the AsyncOperationHandle has not been used (the scene was already open in the editor), //the scene needs to be unloaded using regular SceneManager instead of as an Addressable SceneManager.UnloadSceneAsync(_currentlyLoadedScene.sceneReference.editorAsset.name); } #endif } LoadNewScene(); } /// /// Kicks off the asynchronous loading of a scene, either menu or Location. /// private void LoadNewScene() { if (_showLoadingScreen) { _toggleLoadingScreen.RaiseEvent(true); } _loadingOperationHandle = _sceneToLoad.sceneReference.LoadSceneAsync(LoadSceneMode.Additive, true, 0); _loadingOperationHandle.Completed += OnNewSceneLoaded; } private void OnNewSceneLoaded(AsyncOperationHandle obj) { //Save loaded scenes (to be unloaded at next load request) _currentlyLoadedScene = _sceneToLoad; SetActiveScene(); if (_showLoadingScreen) { _toggleLoadingScreen.RaiseEvent(false); } } /// /// This function is called when all the scenes have been loaded /// private void SetActiveScene() { Scene s = ((SceneInstance)_loadingOperationHandle.Result).Scene; SceneManager.SetActiveScene(s); LightProbes.TetrahedralizeAsync(); StartGameplay(); } private void StartGameplay() { _onSceneReady.RaiseEvent(); //Spawn system will spawn the PigChef } private void ExitGame() { Application.Quit(); Debug.Log("Exit!"); } }