From aefff3d05080d566da337a0b40e5ebc9e1cc97a1 Mon Sep 17 00:00:00 2001 From: Michal Pikulski Date: Mon, 13 Oct 2025 14:25:11 +0200 Subject: [PATCH] Semi-working intro cinematic with loading screen --- .../Scripts/Cinematics/CinematicsManager.cs | 81 ++++++++++---- Assets/Scripts/Cinematics/SkipCinematic.cs | 5 +- Assets/Scripts/Core/SceneManagerService.cs | 39 ++++--- Assets/Scripts/Input/InputManager.cs | 2 +- Assets/Scripts/UI/LoadingScreenController.cs | 102 ++++++++++++++++-- Assets/Scripts/UI/PauseMenu.cs | 2 +- 6 files changed, 181 insertions(+), 50 deletions(-) diff --git a/Assets/Scripts/Cinematics/CinematicsManager.cs b/Assets/Scripts/Cinematics/CinematicsManager.cs index 63a6fdba..6ceb1e60 100644 --- a/Assets/Scripts/Cinematics/CinematicsManager.cs +++ b/Assets/Scripts/Cinematics/CinematicsManager.cs @@ -15,11 +15,11 @@ namespace Cinematics { public event System.Action OnCinematicStarted; public event System.Action OnCinematicStopped; - private static CinematicsManager _instance; private static bool _isQuitting; - private Image cinematicSprites; - public PlayableAsset cinematicToPlay; + private Image _cinematicSprites; + private bool _isCinematicPlaying = false; + public bool IsCinematicPlaying => _isCinematicPlaying; // Dictionary to track addressable handles by PlayableDirector private Dictionary> _addressableHandles @@ -69,20 +69,21 @@ namespace Cinematics /// public PlayableDirector PlayCinematic(PlayableAsset assetToPlay) { - cinematicSprites.enabled = true; + _cinematicSprites.enabled = true; playableDirector.stopped += OnPlayableDirectorStopped; playableDirector.Play(assetToPlay); Debug.Log("Playing cinematic " + assetToPlay.name); + _isCinematicPlaying = true; OnCinematicStarted?.Invoke(); return playableDirector; } void OnPlayableDirectorStopped(PlayableDirector director) { - cinematicSprites.enabled = false; + _cinematicSprites.enabled = false; Debug.Log("Cinematic stopped!"); + _isCinematicPlaying = false; OnCinematicStopped?.Invoke(); - // Release the addressable handle associated with this director ReleaseAddressableHandle(director); } @@ -115,14 +116,6 @@ namespace Cinematics playableDirector.Stop(); } } - - /// - /// Checks if a cinematic is currently playing - /// - public bool IsCinematicPlaying() - { - return playableDirector != null && playableDirector.state == PlayState.Playing; - } /// /// Releases the addressable handle associated with a specific PlayableDirector @@ -152,7 +145,54 @@ namespace Cinematics _addressableHandles.Clear(); } - private void Start() + private void Awake() + { + PlayStartCinematicOnGameLoad(); + } + + /// + /// 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) + { + Debug.Log($"[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 + Debug.Log($"[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; + + Debug.Log($"[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(); + + Debug.Log($"[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")) { @@ -161,14 +201,11 @@ namespace Cinematics _instance = this; - if (!SceneManager.GetActiveScene().name.ToLower().Contains("mainmenu")) - { - return; - } - playableDirector = GetComponent(); - cinematicSprites = GetComponentInChildren(true); - LoadAndPlayCinematic("IntroSequence"); + _cinematicSprites = GetComponentInChildren(true); + + // Use the new method with loading screen instead of direct load + await PlayCinematicWithLoadingScreen("IntroSequence"); } } } diff --git a/Assets/Scripts/Cinematics/SkipCinematic.cs b/Assets/Scripts/Cinematics/SkipCinematic.cs index 3eb97c9d..fb7f6738 100644 --- a/Assets/Scripts/Cinematics/SkipCinematic.cs +++ b/Assets/Scripts/Cinematics/SkipCinematic.cs @@ -25,6 +25,9 @@ namespace Cinematics void OnEnable() { + if (CinematicsManager.Instance.IsCinematicPlaying) + HandleCinematicStarted(); + CinematicsManager.Instance.OnCinematicStarted += HandleCinematicStarted; CinematicsManager.Instance.OnCinematicStopped += HandleCinematicStopped; } @@ -50,7 +53,7 @@ namespace Cinematics void Update() { // Only process while cinematic is playing and we're holding - if (_isHolding && CinematicsManager.Instance.IsCinematicPlaying()) + if (_isHolding && CinematicsManager.Instance.IsCinematicPlaying) { float holdTime = Time.time - _holdStartTime; float progress = Mathf.Clamp01(holdTime / holdDuration); diff --git a/Assets/Scripts/Core/SceneManagerService.cs b/Assets/Scripts/Core/SceneManagerService.cs index 0693f1ee..9d3a763b 100644 --- a/Assets/Scripts/Core/SceneManagerService.cs +++ b/Assets/Scripts/Core/SceneManagerService.cs @@ -12,8 +12,7 @@ namespace Core /// public class SceneManagerService : MonoBehaviour { - [SerializeField] private LoadingScreenController loadingScreen; - + private LoadingScreenController _loadingScreen; private static SceneManagerService _instance; private static bool _isQuitting = false; /// @@ -49,6 +48,15 @@ namespace Core private readonly Dictionary _activeUnloads = new(); private const string BootstrapSceneName = "BootstrapScene"; + void Start() + { + _loadingScreen = LoadingScreenController.Instance; + + // Set up loading screen event handlers + SetupLoadingScreenEvents(); + } + + void Awake() { _instance = this; @@ -64,9 +72,6 @@ namespace Core } } #endif - // Set up loading screen event handlers - SetupLoadingScreenEvents(); - // Ensure BootstrapScene is loaded at startup var bootstrap = SceneManager.GetSceneByName(BootstrapSceneName); if (!bootstrap.isLoaded) @@ -77,10 +82,10 @@ namespace Core private void SetupLoadingScreenEvents() { - if (loadingScreen == null) return; - - SceneLoadStarted += _ => loadingScreen.ShowLoadingScreen(); - SceneLoadCompleted += _ => loadingScreen.HideLoadingScreen(); + if (_loadingScreen == null) return; + + SceneLoadStarted += _ => _loadingScreen.ShowLoadingScreen(() => GetAggregateLoadProgress()); + SceneLoadCompleted += _ => _loadingScreen.HideLoadingScreen(); } void OnApplicationQuit() @@ -142,9 +147,9 @@ namespace Core public async Task LoadScenesAsync(IEnumerable sceneNames, IProgress progress = null) { // Show loading screen at the start of multiple scene loading - if (loadingScreen != null) + if (_loadingScreen != null) { - loadingScreen.ShowLoadingScreen(); + _loadingScreen.ShowLoadingScreen(); } int total = 0; @@ -181,9 +186,9 @@ namespace Core } // Hide loading screen after all scenes are loaded - if (loadingScreen != null) + if (_loadingScreen != null) { - loadingScreen.HideLoadingScreen(); + _loadingScreen.HideLoadingScreen(); } } @@ -195,9 +200,9 @@ namespace Core public async Task UnloadScenesAsync(IEnumerable sceneNames, IProgress progress = null) { // Show loading screen at the start of multiple scene unloading - if (loadingScreen != null) + if (_loadingScreen != null) { - loadingScreen.ShowLoadingScreen(); + _loadingScreen.ShowLoadingScreen(); } int total = 0; @@ -234,9 +239,9 @@ namespace Core } // Hide loading screen after all scenes are unloaded - if (loadingScreen != null) + if (_loadingScreen != null) { - loadingScreen.HideLoadingScreen(); + _loadingScreen.HideLoadingScreen(); } } diff --git a/Assets/Scripts/Input/InputManager.cs b/Assets/Scripts/Input/InputManager.cs index dbbd3c71..b3c65272 100644 --- a/Assets/Scripts/Input/InputManager.cs +++ b/Assets/Scripts/Input/InputManager.cs @@ -90,7 +90,7 @@ namespace Input if (sceneName.ToLower().Contains("mainmenu")) { Debug.Log("[InputManager] SwitchInputOnSceneLoaded - Setting InputMode to UI for MainMenu"); - SetInputMode(InputMode.UI); + SetInputMode(InputMode.GameAndUI); } else { diff --git a/Assets/Scripts/UI/LoadingScreenController.cs b/Assets/Scripts/UI/LoadingScreenController.cs index 9a629f7c..0d40e036 100644 --- a/Assets/Scripts/UI/LoadingScreenController.cs +++ b/Assets/Scripts/UI/LoadingScreenController.cs @@ -1,4 +1,5 @@ using System.Collections; +using System; using UnityEngine; using UnityEngine.UI; using Core; @@ -22,9 +23,52 @@ namespace UI private Coroutine _progressCoroutine; private bool _loadingComplete = false; private bool _animationComplete = false; + private Action _onLoadingScreenFullyHidden; + private static LoadingScreenController _instance; + private static bool _isQuitting; + + /// + /// 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; + + public static LoadingScreenController Instance + { + get + { + if (_instance == null && Application.isPlaying && !_isQuitting) + { + _instance = FindAnyObjectByType(); + if (_instance == null) + { + var go = new GameObject("LoadingScreenController"); + _instance = go.AddComponent(); + } + } + return _instance; + } + } + private void Awake() { + _instance = this; + if (loadingScreenContainer == null) loadingScreenContainer = gameObject; @@ -38,8 +82,16 @@ namespace UI /// /// Shows the loading screen and resets the progress bar to zero /// - public void ShowLoadingScreen() + /// 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) { @@ -67,7 +119,7 @@ namespace UI /// /// Animates the progress bar at a steady pace over the minimum display time, - /// while also checking actual loading progress from SceneManagerService + /// while also checking actual loading progress from the current progress provider /// private IEnumerator AnimateProgressBar() { @@ -80,12 +132,8 @@ namespace UI float elapsedTime = Time.time - startTime; float steadyProgress = Mathf.Clamp01(elapsedTime / minimumDisplayTime); - // Get the actual loading progress from SceneManagerService - float actualProgress = 0f; - if (SceneManagerService.Instance != null) - { - actualProgress = SceneManagerService.Instance.GetAggregateLoadProgress(); - } + // Get the actual loading progress from the current provider + float actualProgress = _currentProgressProvider(); // If loading is complete, actualProgress should be 1.0 if (_loadingComplete) @@ -133,6 +181,10 @@ namespace UI { loadingScreenContainer.SetActive(false); Debug.Log("[LoadingScreen] Animation AND loading complete, hiding screen"); + + // Invoke the callback when fully hidden + _onLoadingScreenFullyHidden?.Invoke(); + _onLoadingScreenFullyHidden = null; } } @@ -156,6 +208,10 @@ namespace UI { loadingScreenContainer.SetActive(false); Debug.Log("[LoadingScreen] Animation already complete, hiding screen immediately"); + + // Invoke the callback when fully hidden + _onLoadingScreenFullyHidden?.Invoke(); + _onLoadingScreenFullyHidden = null; } } else @@ -164,5 +220,35 @@ namespace UI // 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/UI/PauseMenu.cs b/Assets/Scripts/UI/PauseMenu.cs index a94f6b7f..c6c4532e 100644 --- a/Assets/Scripts/UI/PauseMenu.cs +++ b/Assets/Scripts/UI/PauseMenu.cs @@ -48,10 +48,10 @@ namespace UI // Subscribe to scene loaded events SceneManagerService.Instance.SceneLoadCompleted += SetPauseMenuByLevel; - #if UNITY_EDITOR // Set initial state based on current scene SetPauseMenuByLevel(SceneManager.GetActiveScene().name); + #if UNITY_EDITOR // Initialize pause menu state HidePauseMenu(false); #endif