// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved. using System; using System.Collections.Generic; using System.Linq; using FlaxEngine; using FlaxEngine.Assertions; using FlaxEngine.Utilities; namespace FlaxEditor.States { /// /// In this state editor is changing loaded scenes collection. /// /// [HideInEditor] public sealed class ChangingScenesState : EditorState { private readonly List _scenesToLoad = new List(); private readonly List _scenesToUnload = new List(); private Guid _lastSceneFromRequest; internal ChangingScenesState(Editor editor) : base(editor) { } /// public override string Status => "Loading scene..."; /// /// Loads the scene. /// /// The scene asset ID. /// True if don't close opened scenes and just add new scene to the collection, otherwise will release current scenes and load single one. public void LoadScene(Guid sceneId, bool additive = false) { // Clear request _scenesToLoad.Clear(); _scenesToUnload.Clear(); // Setup request _scenesToLoad.Add(sceneId); if (!additive) { _scenesToUnload.AddRange(Level.Scenes); } TryEnter(); } /// /// Unloads the scene. /// /// The scene to unload. public void UnloadScene(Scene scene) { if (scene == null) throw new ArgumentNullException(); // Clear request _scenesToLoad.Clear(); _scenesToUnload.Clear(); // Setup request _scenesToUnload.Add(scene); TryEnter(); } /// /// Unloads the scenes collection. /// /// The scenes to unload. public void UnloadScene(IEnumerable scenes) { if (scenes == null) throw new ArgumentNullException(); if (!scenes.Any()) return; // Clear request _scenesToLoad.Clear(); _scenesToUnload.Clear(); // Setup request _scenesToUnload.AddRange(scenes); TryEnter(); } /// /// Changes the scenes. /// /// Scenes to load. /// Scenes to unload. public void ChangeScenes(IEnumerable toLoad, IEnumerable toUnload) { // Clear request _scenesToLoad.Clear(); _scenesToUnload.Clear(); // Setup request _scenesToLoad.AddRange(toLoad); _scenesToUnload.AddRange(toUnload); TryEnter(); } private void TryEnter() { // Reload existing scene if only 1 scene exists bool reloadExistingScene = false; if (Level.Scenes.Length == 1 && _scenesToLoad.Count == 1) { if (Level.FindScene(_scenesToLoad[0])) { reloadExistingScene = true; } } if (!reloadExistingScene) { // Remove redundant scene changes for (int i = 0; i < _scenesToUnload.Count; i++) { var id = _scenesToUnload[i].ID; for (int j = 0; j < _scenesToLoad.Count; j++) { if (_scenesToLoad[j] == id) { _scenesToLoad.RemoveAt(j--); _scenesToUnload.RemoveAt(i); break; } } } // Skip already loaded scenes for (int j = 0; j < _scenesToLoad.Count; j++) { if (Level.FindScene(_scenesToLoad[j])) { _scenesToLoad.RemoveAt(j--); if (_scenesToLoad.Count == 0) break; } } } // Check if won't change anything if (_scenesToLoad.Count + _scenesToUnload.Count == 0) return; // Request state change StateMachine.GoToState(this); } /// public override bool CanEditContent => false; /// public override void OnEnter() { Assert.AreEqual(Guid.Empty, _lastSceneFromRequest, "Invalid state."); // Bind events Level.SceneLoaded += OnSceneEvent; Level.SceneLoadError += OnSceneEvent; Level.SceneUnloaded += OnSceneEvent; // Push scenes changing requests for (int i = 0; i < _scenesToUnload.Count; i++) { _lastSceneFromRequest = _scenesToUnload[i].ID; Level.UnloadSceneAsync(_scenesToUnload[i]); } for (int i = 0; i < _scenesToLoad.Count; i++) { var id = _scenesToLoad[i]; bool failed = Level.LoadSceneAsync(id); if (!failed) _lastSceneFromRequest = id; } // Clear request _scenesToLoad.Clear(); _scenesToUnload.Clear(); // Spacial case when all scenes are unloaded and no scene loaded we won't be able too handle OnSceneEvent so just leave state now // It may be caused when scripts are not loaded due to compilation errors or cannot find scene asset or other internal engine. if (_lastSceneFromRequest == Guid.Empty) { Editor.LogWarning("Cannot perform scene change"); StateMachine.GoToState(); } } /// public override void OnExit(State nextState) { // Validate (but skip if next state is exit) if (!(nextState is ClosingState)) { Assert.AreEqual(Guid.Empty, _lastSceneFromRequest, "Invalid state."); } else { _lastSceneFromRequest = Guid.Empty; } // Unbind events Level.SceneLoaded -= OnSceneEvent; Level.SceneLoadError -= OnSceneEvent; Level.SceneUnloaded -= OnSceneEvent; } private void OnSceneEvent(Scene scene, Guid sceneId) { // Check if it's scene from the last request if (sceneId == _lastSceneFromRequest) { _lastSceneFromRequest = Guid.Empty; StateMachine.GoToState(); } } } }