diff --git a/Assets/Prefabs/Managers/SceneManager.prefab b/Assets/Prefabs/Managers/SceneManager.prefab index ea036003..7c1883c3 100644 --- a/Assets/Prefabs/Managers/SceneManager.prefab +++ b/Assets/Prefabs/Managers/SceneManager.prefab @@ -29,7 +29,8 @@ Transform: m_LocalPosition: {x: -3.4031, y: -1.84829, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 - m_Children: [] + m_Children: + - {fileID: 4689617562113187593} m_Father: {fileID: 0} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!114 &5327225408302228741 @@ -44,3 +45,105 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 360f320f4d7a48e38f5fd7cdfa28144a, type: 3} m_Name: m_EditorClassIdentifier: +--- !u!1001 &4881964705042195055 +PrefabInstance: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Modification: + serializedVersion: 3 + m_TransformParent: {fileID: 3506046067200272545} + m_Modifications: + - target: {fileID: 204042265062571366, guid: 19fad826fce26d34ba304620216a7f47, type: 3} + propertyPath: m_Pivot.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 204042265062571366, guid: 19fad826fce26d34ba304620216a7f47, type: 3} + propertyPath: m_Pivot.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 204042265062571366, guid: 19fad826fce26d34ba304620216a7f47, type: 3} + propertyPath: m_AnchorMax.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 204042265062571366, guid: 19fad826fce26d34ba304620216a7f47, type: 3} + propertyPath: m_AnchorMax.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 204042265062571366, guid: 19fad826fce26d34ba304620216a7f47, type: 3} + propertyPath: m_AnchorMin.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 204042265062571366, guid: 19fad826fce26d34ba304620216a7f47, type: 3} + propertyPath: m_AnchorMin.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 204042265062571366, guid: 19fad826fce26d34ba304620216a7f47, type: 3} + propertyPath: m_SizeDelta.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 204042265062571366, guid: 19fad826fce26d34ba304620216a7f47, type: 3} + propertyPath: m_SizeDelta.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 204042265062571366, guid: 19fad826fce26d34ba304620216a7f47, type: 3} + propertyPath: m_LocalPosition.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 204042265062571366, guid: 19fad826fce26d34ba304620216a7f47, type: 3} + propertyPath: m_LocalPosition.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 204042265062571366, guid: 19fad826fce26d34ba304620216a7f47, type: 3} + propertyPath: m_LocalPosition.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 204042265062571366, guid: 19fad826fce26d34ba304620216a7f47, type: 3} + propertyPath: m_LocalRotation.w + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 204042265062571366, guid: 19fad826fce26d34ba304620216a7f47, type: 3} + propertyPath: m_LocalRotation.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 204042265062571366, guid: 19fad826fce26d34ba304620216a7f47, type: 3} + propertyPath: m_LocalRotation.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 204042265062571366, guid: 19fad826fce26d34ba304620216a7f47, type: 3} + propertyPath: m_LocalRotation.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 204042265062571366, guid: 19fad826fce26d34ba304620216a7f47, type: 3} + propertyPath: m_AnchoredPosition.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 204042265062571366, guid: 19fad826fce26d34ba304620216a7f47, type: 3} + propertyPath: m_AnchoredPosition.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 204042265062571366, guid: 19fad826fce26d34ba304620216a7f47, type: 3} + propertyPath: m_LocalEulerAnglesHint.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 204042265062571366, guid: 19fad826fce26d34ba304620216a7f47, type: 3} + propertyPath: m_LocalEulerAnglesHint.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 204042265062571366, guid: 19fad826fce26d34ba304620216a7f47, type: 3} + propertyPath: m_LocalEulerAnglesHint.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4869161796575291839, guid: 19fad826fce26d34ba304620216a7f47, type: 3} + propertyPath: m_Name + value: LoadingScreen + objectReference: {fileID: 0} + m_RemovedComponents: [] + m_RemovedGameObjects: [] + m_AddedGameObjects: [] + m_AddedComponents: [] + m_SourcePrefab: {fileID: 100100000, guid: 19fad826fce26d34ba304620216a7f47, type: 3} +--- !u!224 &4689617562113187593 stripped +RectTransform: + m_CorrespondingSourceObject: {fileID: 204042265062571366, guid: 19fad826fce26d34ba304620216a7f47, type: 3} + m_PrefabInstance: {fileID: 4881964705042195055} + m_PrefabAsset: {fileID: 0} diff --git a/Assets/Scenes/StartingScene.unity b/Assets/Scenes/StartingScene.unity index bae07f91..da88ad34 100644 --- a/Assets/Scenes/StartingScene.unity +++ b/Assets/Scenes/StartingScene.unity @@ -119,6 +119,144 @@ NavMeshSettings: debug: m_Flags: 0 m_NavMeshData: {fileID: 0} +--- !u!1001 &180679694 +PrefabInstance: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Modification: + serializedVersion: 3 + m_TransformParent: {fileID: 0} + m_Modifications: + - target: {fileID: 204042265062571366, guid: 19fad826fce26d34ba304620216a7f47, type: 3} + propertyPath: m_Pivot.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 204042265062571366, guid: 19fad826fce26d34ba304620216a7f47, type: 3} + propertyPath: m_Pivot.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 204042265062571366, guid: 19fad826fce26d34ba304620216a7f47, type: 3} + propertyPath: m_AnchorMax.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 204042265062571366, guid: 19fad826fce26d34ba304620216a7f47, type: 3} + propertyPath: m_AnchorMax.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 204042265062571366, guid: 19fad826fce26d34ba304620216a7f47, type: 3} + propertyPath: m_AnchorMin.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 204042265062571366, guid: 19fad826fce26d34ba304620216a7f47, type: 3} + propertyPath: m_AnchorMin.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 204042265062571366, guid: 19fad826fce26d34ba304620216a7f47, type: 3} + propertyPath: m_SizeDelta.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 204042265062571366, guid: 19fad826fce26d34ba304620216a7f47, type: 3} + propertyPath: m_SizeDelta.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 204042265062571366, guid: 19fad826fce26d34ba304620216a7f47, type: 3} + propertyPath: m_LocalPosition.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 204042265062571366, guid: 19fad826fce26d34ba304620216a7f47, type: 3} + propertyPath: m_LocalPosition.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 204042265062571366, guid: 19fad826fce26d34ba304620216a7f47, type: 3} + propertyPath: m_LocalPosition.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 204042265062571366, guid: 19fad826fce26d34ba304620216a7f47, type: 3} + propertyPath: m_LocalRotation.w + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 204042265062571366, guid: 19fad826fce26d34ba304620216a7f47, type: 3} + propertyPath: m_LocalRotation.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 204042265062571366, guid: 19fad826fce26d34ba304620216a7f47, type: 3} + propertyPath: m_LocalRotation.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 204042265062571366, guid: 19fad826fce26d34ba304620216a7f47, type: 3} + propertyPath: m_LocalRotation.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 204042265062571366, guid: 19fad826fce26d34ba304620216a7f47, type: 3} + propertyPath: m_AnchoredPosition.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 204042265062571366, guid: 19fad826fce26d34ba304620216a7f47, type: 3} + propertyPath: m_AnchoredPosition.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 204042265062571366, guid: 19fad826fce26d34ba304620216a7f47, type: 3} + propertyPath: m_LocalEulerAnglesHint.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 204042265062571366, guid: 19fad826fce26d34ba304620216a7f47, type: 3} + propertyPath: m_LocalEulerAnglesHint.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 204042265062571366, guid: 19fad826fce26d34ba304620216a7f47, type: 3} + propertyPath: m_LocalEulerAnglesHint.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4869161796575291839, guid: 19fad826fce26d34ba304620216a7f47, type: 3} + propertyPath: m_Name + value: LoadingScreen + objectReference: {fileID: 0} + m_RemovedComponents: + - {fileID: 5737877680156686392, guid: 19fad826fce26d34ba304620216a7f47, type: 3} + m_RemovedGameObjects: [] + m_AddedGameObjects: [] + m_AddedComponents: + - targetCorrespondingSourceObject: {fileID: 4869161796575291839, guid: 19fad826fce26d34ba304620216a7f47, type: 3} + insertIndex: -1 + addedObject: {fileID: 180679698} + m_SourcePrefab: {fileID: 100100000, guid: 19fad826fce26d34ba304620216a7f47, type: 3} +--- !u!1 &180679695 stripped +GameObject: + m_CorrespondingSourceObject: {fileID: 4869161796575291839, guid: 19fad826fce26d34ba304620216a7f47, type: 3} + m_PrefabInstance: {fileID: 180679694} + m_PrefabAsset: {fileID: 0} +--- !u!114 &180679696 stripped +MonoBehaviour: + m_CorrespondingSourceObject: {fileID: 1674678211233966532, guid: 19fad826fce26d34ba304620216a7f47, type: 3} + m_PrefabInstance: {fileID: 180679694} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.Image +--- !u!1 &180679697 stripped +GameObject: + m_CorrespondingSourceObject: {fileID: 7270617579256400696, guid: 19fad826fce26d34ba304620216a7f47, type: 3} + m_PrefabInstance: {fileID: 180679694} + m_PrefabAsset: {fileID: 0} +--- !u!114 &180679698 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 180679695} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 8968b564891a474baae157792b88e75f, type: 3} + m_Name: + m_EditorClassIdentifier: AppleHillsScripts::Bootstrap.InitialLoadingScreen + loadingScreenContainer: {fileID: 180679697} + progressBarImage: {fileID: 180679696} + minimumDisplayTime: 2 + progressUpdateInterval: 0.1 --- !u!1 &400217123 GameObject: m_ObjectHideFlags: 0 @@ -256,103 +394,6 @@ MonoBehaviour: m_VarianceClampScale: 0.9 m_ContrastAdaptiveSharpening: 0 m_Version: 2 ---- !u!1001 &1355357999 -PrefabInstance: - m_ObjectHideFlags: 0 - serializedVersion: 2 - m_Modification: - serializedVersion: 3 - m_TransformParent: {fileID: 0} - m_Modifications: - - target: {fileID: 204042265062571366, guid: 19fad826fce26d34ba304620216a7f47, type: 3} - propertyPath: m_Pivot.x - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 204042265062571366, guid: 19fad826fce26d34ba304620216a7f47, type: 3} - propertyPath: m_Pivot.y - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 204042265062571366, guid: 19fad826fce26d34ba304620216a7f47, type: 3} - propertyPath: m_AnchorMax.x - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 204042265062571366, guid: 19fad826fce26d34ba304620216a7f47, type: 3} - propertyPath: m_AnchorMax.y - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 204042265062571366, guid: 19fad826fce26d34ba304620216a7f47, type: 3} - propertyPath: m_AnchorMin.x - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 204042265062571366, guid: 19fad826fce26d34ba304620216a7f47, type: 3} - propertyPath: m_AnchorMin.y - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 204042265062571366, guid: 19fad826fce26d34ba304620216a7f47, type: 3} - propertyPath: m_SizeDelta.x - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 204042265062571366, guid: 19fad826fce26d34ba304620216a7f47, type: 3} - propertyPath: m_SizeDelta.y - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 204042265062571366, guid: 19fad826fce26d34ba304620216a7f47, type: 3} - propertyPath: m_LocalPosition.x - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 204042265062571366, guid: 19fad826fce26d34ba304620216a7f47, type: 3} - propertyPath: m_LocalPosition.y - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 204042265062571366, guid: 19fad826fce26d34ba304620216a7f47, type: 3} - propertyPath: m_LocalPosition.z - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 204042265062571366, guid: 19fad826fce26d34ba304620216a7f47, type: 3} - propertyPath: m_LocalRotation.w - value: 1 - objectReference: {fileID: 0} - - target: {fileID: 204042265062571366, guid: 19fad826fce26d34ba304620216a7f47, type: 3} - propertyPath: m_LocalRotation.x - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 204042265062571366, guid: 19fad826fce26d34ba304620216a7f47, type: 3} - propertyPath: m_LocalRotation.y - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 204042265062571366, guid: 19fad826fce26d34ba304620216a7f47, type: 3} - propertyPath: m_LocalRotation.z - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 204042265062571366, guid: 19fad826fce26d34ba304620216a7f47, type: 3} - propertyPath: m_AnchoredPosition.x - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 204042265062571366, guid: 19fad826fce26d34ba304620216a7f47, type: 3} - propertyPath: m_AnchoredPosition.y - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 204042265062571366, guid: 19fad826fce26d34ba304620216a7f47, type: 3} - propertyPath: m_LocalEulerAnglesHint.x - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 204042265062571366, guid: 19fad826fce26d34ba304620216a7f47, type: 3} - propertyPath: m_LocalEulerAnglesHint.y - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 204042265062571366, guid: 19fad826fce26d34ba304620216a7f47, type: 3} - propertyPath: m_LocalEulerAnglesHint.z - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 4869161796575291839, guid: 19fad826fce26d34ba304620216a7f47, type: 3} - propertyPath: m_Name - value: LoadingScreen - objectReference: {fileID: 0} - m_RemovedComponents: [] - m_RemovedGameObjects: [] - m_AddedGameObjects: [] - m_AddedComponents: [] - m_SourcePrefab: {fileID: 100100000, guid: 19fad826fce26d34ba304620216a7f47, type: 3} --- !u!1 &1710655392 GameObject: m_ObjectHideFlags: 0 @@ -385,6 +426,8 @@ MonoBehaviour: mainMenuSceneName: MainMenu minDelayAfterBoot: 0.5 debugMode: 0 + initialLoadingScreen: {fileID: 180679698} + bootProgressWeight: 0.5 --- !u!4 &1710655394 Transform: m_ObjectHideFlags: 0 @@ -405,5 +448,5 @@ SceneRoots: m_ObjectHideFlags: 0 m_Roots: - {fileID: 400217126} - - {fileID: 1355357999} - {fileID: 1710655394} + - {fileID: 180679694} diff --git a/Assets/Scripts/Bootstrap/BootSceneController.cs b/Assets/Scripts/Bootstrap/BootSceneController.cs index 239d8b0c..d2877526 100644 --- a/Assets/Scripts/Bootstrap/BootSceneController.cs +++ b/Assets/Scripts/Bootstrap/BootSceneController.cs @@ -3,6 +3,7 @@ using UnityEngine; using UI; using Core; using UnityEngine.SceneManagement; +using Cinematics; namespace Bootstrap { @@ -14,6 +15,7 @@ namespace Bootstrap [SerializeField] private string mainMenuSceneName = "MainMenu"; [SerializeField] private float minDelayAfterBoot = 0.5f; // Small delay after boot to ensure smooth transition [SerializeField] private bool debugMode = false; + [SerializeField] private InitialLoadingScreen initialLoadingScreen; // Reference to our specialized loading screen // Progress distribution between bootstrap and scene loading [SerializeField, Range(0.1f, 0.9f)] private float bootProgressWeight = 0.5f; // Default 50/50 split @@ -29,20 +31,18 @@ namespace Bootstrap { Debug.Log("[BootSceneController] Boot scene started"); - // Ensure the loading screen controller exists - if (LoadingScreenController.Instance == null) + // Ensure the initial loading screen exists + if (initialLoadingScreen == null) { - Debug.LogError("[BootSceneController] No LoadingScreenController found in the scene!"); + Debug.LogError("[BootSceneController] No InitialLoadingScreen assigned! Please assign it in the inspector."); return; } + // Subscribe to the loading screen completion event + initialLoadingScreen.OnLoadingScreenFullyHidden += OnInitialLoadingComplete; + // Show the loading screen immediately with our combined progress provider - LoadingScreenController.Instance.ShowLoadingScreen( - progressProvider: GetCombinedProgress, - onComplete: () => { - Debug.Log("[BootSceneController] Loading screen fully hidden, boot sequence completed"); - } - ); + initialLoadingScreen.ShowLoadingScreen(GetCombinedProgress); // Start the boot process if not already initialized if (!CustomBoot.Initialised) @@ -68,12 +68,43 @@ namespace Bootstrap } } + /// + /// Called when the initial loading screen is fully hidden + /// + private void OnInitialLoadingComplete() + { + Debug.Log("[BootSceneController] Initial loading screen fully hidden, boot sequence completed"); + + // Play the intro cinematic if available + if (CinematicsManager.Instance != null) + { + Debug.Log("[BootSceneController] Attempting to play intro cinematic"); + + // Use LoadAndPlayCinematic to play the intro sequence + CinematicsManager.Instance.LoadAndPlayCinematic("IntroSequence"); + + // Immediately unload the StartingScene - no need to wait for cinematic to finish + // since CinematicsManager is bootstrapped and won't be unloaded + UnloadStartingScene(); + } + else + { + // If no cinematics manager, unload the StartingScene directly + UnloadStartingScene(); + } + } + private void OnDestroy() { // Clean up event subscriptions CustomBoot.OnBootCompleted -= OnBootCompleted; CustomBoot.OnBootProgressChanged -= OnBootProgressChanged; + if (initialLoadingScreen != null) + { + initialLoadingScreen.OnLoadingScreenFullyHidden -= OnInitialLoadingComplete; + } + if (debugMode) { CancelInvoke(nameof(LogDebugInfo)); @@ -146,31 +177,97 @@ namespace Bootstrap // Initialize scene loading progress to 0 to ensure proper remapping _sceneLoadingProgress = 0f; - // Create a progress object that remaps scene loading progress (0-1) to our second phase range - var progress = new Progress(p => { + // Create a custom progress reporter using a custom class + var progressHandler = new ProgressHandler(value => { // Store the raw scene loading progress (0-1) - _sceneLoadingProgress = p; + _sceneLoadingProgress = value; if (debugMode) { - Debug.Log($"[BootSceneController] Scene loading raw: {p:P0}, Combined: {GetCombinedProgress():P0}"); + Debug.Log($"[BootSceneController] Scene loading raw: {value:P0}, Combined: {GetCombinedProgress():P0}"); } }); - // Load the scene but don't auto-hide loading screen (we're managing that ourselves) - await SceneManagerService.Instance.SwitchSceneAsync(mainMenuSceneName, progress, autoHideLoadingScreen: false); + // Step 1: Additively load the main menu scene - don't unload StartingScene yet + var op = SceneManager.LoadSceneAsync(mainMenuSceneName, LoadSceneMode.Additive); + + // Disable scene activation until we're ready to show it + op.allowSceneActivation = true; + + // Track progress while loading + while (!op.isDone) + { + progressHandler.ReportProgress(op.progress); + await System.Threading.Tasks.Task.Yield(); + } + + // Update the current gameplay scene in SceneManagerService + SceneManagerService.Instance.CurrentGameplayScene = mainMenuSceneName; // Ensure progress is complete _sceneLoadingProgress = 1f; - // Scene is fully loaded, we can now hide the loading screen - LoadingScreenController.Instance.HideLoadingScreen(); + // Step 2: Scene is fully loaded, now hide the loading screen + // This will trigger OnInitialLoadingComplete via the event when animation completes + initialLoadingScreen.HideLoadingScreen(); + + // Step 3: The OnInitialLoadingComplete method will handle playing the intro cinematic + // Step 4: StartingScene will be unloaded after the cinematic completes in OnIntroCinematicFinished } catch (Exception e) { Debug.LogError($"[BootSceneController] Error loading main menu: {e.Message}"); // Still try to hide the loading screen even if there was an error - LoadingScreenController.Instance.HideLoadingScreen(); + initialLoadingScreen.HideLoadingScreen(); + } + } + + /// + /// Unloads the StartingScene, completing the transition to the main menu + /// + private async void UnloadStartingScene() + { + try + { + // Get the current scene (StartingScene) + Scene currentScene = SceneManager.GetActiveScene(); + string startingSceneName = currentScene.name; + + Debug.Log($"[BootSceneController] Unloading StartingScene: {startingSceneName}"); + + // Unload the StartingScene + await SceneManager.UnloadSceneAsync(startingSceneName); + + // Set the main menu scene as the active scene + Scene mainMenuScene = SceneManager.GetSceneByName(mainMenuSceneName); + SceneManager.SetActiveScene(mainMenuScene); + + Debug.Log($"[BootSceneController] Transition complete: {startingSceneName} unloaded, {mainMenuSceneName} is now active"); + + // Destroy the boot scene controller since its job is done + Destroy(gameObject); + } + catch (Exception e) + { + Logging.Warning($"[BootSceneController] Error unloading StartingScene: {e.Message}"); + } + } + + /// + /// Helper class to handle progress reporting without running into explicit interface implementation issues + /// + private class ProgressHandler + { + private Action _progressAction; + + public ProgressHandler(Action progressAction) + { + _progressAction = progressAction; + } + + public void ReportProgress(float value) + { + _progressAction?.Invoke(value); } } } diff --git a/Assets/Scripts/Bootstrap/InitialLoadingScreen.cs b/Assets/Scripts/Bootstrap/InitialLoadingScreen.cs new file mode 100644 index 00000000..bebe4d9b --- /dev/null +++ b/Assets/Scripts/Bootstrap/InitialLoadingScreen.cs @@ -0,0 +1,240 @@ +using System; +using System.Collections; +using UnityEngine; +using UnityEngine.UI; +using Core; + +namespace Bootstrap +{ + /// + /// Specialized loading screen controller specifically for the initial boot sequence. + /// This handles the combined progress of bootstrap initialization and main menu loading. + /// + public class InitialLoadingScreen : MonoBehaviour + { + [Header("UI References")] + [SerializeField] private GameObject loadingScreenContainer; + [SerializeField] private Image progressBarImage; + + [Header("Settings")] + [SerializeField] private float minimumDisplayTime = 1.0f; + [SerializeField] private float progressUpdateInterval = 0.1f; + + private float _displayStartTime; + private Coroutine _progressCoroutine; + private bool _loadingComplete = false; + private bool _animationComplete = false; + private Action _onLoadingScreenFullyHidden; + + /// + /// Event that fires when the loading screen is fully hidden (both loading and animation completed) + /// + public event Action OnLoadingScreenFullyHidden; + + /// + /// Delegate for providing progress values from different sources + /// + public delegate float ProgressProvider(); + + /// + /// Current progress provider being used for the loading screen + /// + private ProgressProvider _currentProgressProvider; + + /// + /// Default progress provider that returns 0 (or 1 if loading is complete) + /// + private float DefaultProgressProvider() => _loadingComplete ? 1f : 0f; + + /// + /// Check if the loading screen is currently active + /// + public bool IsActive => loadingScreenContainer != null && loadingScreenContainer.activeSelf; + + private void Awake() + { + if (loadingScreenContainer == null) + loadingScreenContainer = gameObject; + + // Ensure the loading screen is initially hidden + if (loadingScreenContainer != null) + { + loadingScreenContainer.SetActive(false); + } + } + + /// + /// Shows the loading screen and resets the progress bar to zero + /// + /// Optional delegate to provide progress values (0-1). If null, uses default provider. + /// Optional callback when loading screen is fully hidden + public void ShowLoadingScreen(ProgressProvider progressProvider = null, Action onComplete = null) + { + // Store the completion callback + _onLoadingScreenFullyHidden = onComplete; + + // Set the progress provider, use default if none provided + _currentProgressProvider = progressProvider ?? DefaultProgressProvider; + + // Stop any existing progress coroutine + if (_progressCoroutine != null) + { + StopCoroutine(_progressCoroutine); + _progressCoroutine = null; + } + + _displayStartTime = Time.time; + _loadingComplete = false; + _animationComplete = false; + + if (progressBarImage != null) + { + progressBarImage.fillAmount = 0f; + } + + if (loadingScreenContainer != null) + { + loadingScreenContainer.SetActive(true); + } + + // Start the progress filling coroutine + _progressCoroutine = StartCoroutine(AnimateProgressBar()); + } + + /// + /// Animates the progress bar at a steady pace over the minimum display time, + /// while also checking actual loading progress from the current progress provider + /// + private IEnumerator AnimateProgressBar() + { + float startTime = Time.time; + + // Continue until both animation and loading are complete + while (!_animationComplete) + { + // Calculate the steady progress based on elapsed time + float elapsedTime = Time.time - startTime; + float steadyProgress = Mathf.Clamp01(elapsedTime / minimumDisplayTime); + + // Get the actual loading progress from the current provider + float actualProgress = _currentProgressProvider(); + + // If loading is complete, actualProgress should be 1.0 + if (_loadingComplete) + { + actualProgress = 1.0f; + } + + // Use the minimum of steady progress and actual progress + // This ensures we don't show more progress than actual loading + float displayProgress = Mathf.Min(steadyProgress, actualProgress); + + // Log the progress values for debugging + Debug.Log($"[InitialLoadingScreen] Progress - Default: {steadyProgress:F2}, Actual: {actualProgress:F2}, Display: {displayProgress:F2}"); + + // Directly set the progress bar fill amount without smoothing + if (progressBarImage != null) + { + progressBarImage.fillAmount = displayProgress; + } + + // Check if the animation has completed + // Animation is complete when we've reached the minimum display time AND we're at 100% progress + if (steadyProgress >= 1.0f && displayProgress >= 1.0f) + { + _animationComplete = true; + Debug.Log("[InitialLoadingScreen] Animation complete"); + break; + } + + // Wait for the configured interval before updating again + yield return new WaitForSeconds(progressUpdateInterval); + } + + // Ensure we end at 100% progress + if (progressBarImage != null) + { + progressBarImage.fillAmount = 1.0f; + Debug.Log("[InitialLoadingScreen] Final progress set to 1.0"); + } + + // Hide the screen if loading is also complete + if (_loadingComplete) + { + if (loadingScreenContainer != null) + { + loadingScreenContainer.SetActive(false); + Debug.Log("[InitialLoadingScreen] Animation AND loading complete, hiding screen"); + + // Invoke the callback when fully hidden + _onLoadingScreenFullyHidden?.Invoke(); + OnLoadingScreenFullyHidden?.Invoke(); + _onLoadingScreenFullyHidden = null; + } + } + + _progressCoroutine = null; + } + + /// + /// Called when the actual loading process is complete + /// + public void HideLoadingScreen() + { + Debug.Log("[InitialLoadingScreen] Loading complete, marking loading as finished"); + + // Mark that loading is complete + _loadingComplete = true; + + // If animation is already complete, we can hide the screen now + if (_animationComplete) + { + if (loadingScreenContainer != null) + { + loadingScreenContainer.SetActive(false); + Debug.Log("[InitialLoadingScreen] Animation already complete, hiding screen immediately"); + + // Invoke the callback when fully hidden + _onLoadingScreenFullyHidden?.Invoke(); + OnLoadingScreenFullyHidden?.Invoke(); + _onLoadingScreenFullyHidden = null; + } + } + else + { + Debug.Log("[InitialLoadingScreen] Animation still in progress, waiting for it to complete"); + // The coroutine will handle hiding when animation completes + } + } + + /// + /// Waits until the loading screen is fully hidden before continuing + /// + /// Task that completes when the loading screen is hidden + public System.Threading.Tasks.Task WaitForLoadingScreenToHideAsync() + { + var tcs = new System.Threading.Tasks.TaskCompletionSource(); + + // If the loading screen is not active, complete immediately + if (!IsActive) + { + tcs.SetResult(true); + return tcs.Task; + } + + // Store existing callback to chain it + Action existingCallback = _onLoadingScreenFullyHidden; + + // Set new callback + _onLoadingScreenFullyHidden = () => { + // Call existing callback if any + existingCallback?.Invoke(); + + // Complete the task + tcs.SetResult(true); + }; + + return tcs.Task; + } + } +} diff --git a/Assets/Scripts/Bootstrap/InitialLoadingScreen.cs.meta b/Assets/Scripts/Bootstrap/InitialLoadingScreen.cs.meta new file mode 100644 index 00000000..cf2bd6e7 --- /dev/null +++ b/Assets/Scripts/Bootstrap/InitialLoadingScreen.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 8968b564891a474baae157792b88e75f +timeCreated: 1760613320 \ No newline at end of file diff --git a/Assets/Scripts/Cinematics/CinematicsManager.cs b/Assets/Scripts/Cinematics/CinematicsManager.cs index 884090d1..0fcbefd1 100644 --- a/Assets/Scripts/Cinematics/CinematicsManager.cs +++ b/Assets/Scripts/Cinematics/CinematicsManager.cs @@ -45,6 +45,14 @@ namespace Cinematics } } public PlayableDirector playableDirector; + + private void Awake() + { + _instance = this; + + // Initialize required components + InitializeComponents(); + } private void OnEnable() { @@ -63,15 +71,55 @@ namespace Cinematics private void OnApplicationQuit() { + _isQuitting = true; ReleaseAllHandles(); } + /// + /// Initializes required components for the CinematicsManager + /// + private void InitializeComponents() + { + // Initialize PlayableDirector if not set + if (playableDirector == null) + { + playableDirector = GetComponent(); + + // If still null, try to add the component + if (playableDirector == null) + { + playableDirector = gameObject.AddComponent(); + Debug.Log("[CinematicsManager] Added missing PlayableDirector component"); + } + } + + // Initialize _cinematicSprites if not set + if (_cinematicSprites == null) + { + // First try to find in children + _cinematicSprites = GetComponentInChildren(true); + + // If still null, create a new UI Image for cinematics + if (_cinematicSprites == null) + { + Debug.LogWarning("[CinematicsManager] No Image found for cinematics display. Cinematics may not display correctly."); + } + } + } + /// /// Plays a cinematic from an object reference and returns the PlayableDirector playing the timeline /// public PlayableDirector PlayCinematic(PlayableAsset assetToPlay) { - _cinematicSprites.enabled = true; + // Ensure components are initialized before playing + InitializeComponents(); + + if (_cinematicSprites != null) + { + _cinematicSprites.enabled = true; + } + playableDirector.stopped += OnPlayableDirectorStopped; playableDirector.Play(assetToPlay); Logging.Debug("Playing cinematic " + assetToPlay.name); @@ -146,68 +194,5 @@ namespace Cinematics } _addressableHandles.Clear(); } - - private void Start() - { - - } - - /// - /// Loads a cinematic asynchronously while showing a loading screen, then plays it - /// - /// The addressable key of the cinematic to load - /// The PlayableDirector playing the cinematic - public async System.Threading.Tasks.Task PlayCinematicWithLoadingScreen(string key) - { - Logging.Debug($"[CinematicsManager] Preparing to load cinematic with loading screen: {key}"); - - // First, show the loading screen BEFORE creating any async operations - UI.LoadingScreenController.Instance.ShowLoadingScreen(); - - // Give the loading screen a frame to render - await System.Threading.Tasks.Task.Yield(); - - // Now create the load handle and track its progress - Logging.Debug($"[CinematicsManager] Starting cinematic asset load: {key}"); - AsyncOperationHandle handle = Addressables.LoadAssetAsync(key); - - // Update the loading screen with the progress provider after the handle is created - UI.LoadingScreenController.Instance.ShowLoadingScreen(() => handle.PercentComplete); - - // Wait for the loading to complete - var result = await handle.Task; - - // Store the handle for later release - _addressableHandles[playableDirector] = handle; - - Logging.Debug($"[CinematicsManager] Cinematic loaded: {key}"); - - // Hide the loading screen - UI.LoadingScreenController.Instance.HideLoadingScreen(); - - // Important: Wait for the loading screen to be fully hidden before playing the cinematic - await UI.LoadingScreenController.Instance.WaitForLoadingScreenToHideAsync(); - - Logging.Debug($"[CinematicsManager] Loading screen hidden, now playing cinematic: {key}"); - - // Play the cinematic - return PlayCinematic(result); - } - - private async void PlayStartCinematicOnGameLoad() - { - if (!SceneManager.GetActiveScene().name.ToLower().Contains("mainmenu")) - { - return; - } - - _instance = this; - - playableDirector = GetComponent(); - _cinematicSprites = GetComponentInChildren(true); - - // Use the new method with loading screen instead of direct load - await PlayCinematicWithLoadingScreen("IntroSequence"); - } } } diff --git a/Assets/Scripts/Core/SceneManagerService.cs b/Assets/Scripts/Core/SceneManagerService.cs index c36e7405..2df3ca5f 100644 --- a/Assets/Scripts/Core/SceneManagerService.cs +++ b/Assets/Scripts/Core/SceneManagerService.cs @@ -262,7 +262,7 @@ namespace Core } // Tracks the currently loaded gameplay scene (not persistent/bootstrapper) - public string CurrentGameplayScene { get; private set; } = "MainMenu"; + public string CurrentGameplayScene { get; set; } = "MainMenu"; public async Task ReloadCurrentScene(IProgress progress = null, bool autoHideLoadingScreen = true) { diff --git a/Assets/Scripts/UI/LoadingScreenController.cs b/Assets/Scripts/UI/LoadingScreenController.cs index 085fdead..e902b7f7 100644 --- a/Assets/Scripts/UI/LoadingScreenController.cs +++ b/Assets/Scripts/UI/LoadingScreenController.cs @@ -68,7 +68,6 @@ namespace UI private void Awake() { _instance = this; - DontDestroyOnLoad(gameObject); if (loadingScreenContainer == null) loadingScreenContainer = gameObject; diff --git a/ProjectSettings/EditorBuildSettings.asset b/ProjectSettings/EditorBuildSettings.asset index cf1d829f..9ac34527 100644 --- a/ProjectSettings/EditorBuildSettings.asset +++ b/ProjectSettings/EditorBuildSettings.asset @@ -5,6 +5,9 @@ EditorBuildSettings: m_ObjectHideFlags: 0 serializedVersion: 2 m_Scenes: + - enabled: 1 + path: Assets/Scenes/StartingScene.unity + guid: cf01e2d0135b06c4486d00ef393d0274 - enabled: 1 path: Assets/Scenes/MainMenu.unity guid: b93f2f3b39a62684c8474ba79c8f698d