Revamp the prompt system, the bootstrapper system, the starting cinematic
This commit is contained in:
240
Assets/Scripts/Bootstrap/InitialLoadingScreen.cs
Normal file
240
Assets/Scripts/Bootstrap/InitialLoadingScreen.cs
Normal file
@@ -0,0 +1,240 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using Core;
|
||||
|
||||
namespace Bootstrap
|
||||
{
|
||||
/// <summary>
|
||||
/// Specialized loading screen controller specifically for the initial boot sequence.
|
||||
/// This handles the combined progress of bootstrap initialization and main menu loading.
|
||||
/// </summary>
|
||||
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;
|
||||
|
||||
/// <summary>
|
||||
/// Event that fires when the loading screen is fully hidden (both loading and animation completed)
|
||||
/// </summary>
|
||||
public event Action OnLoadingScreenFullyHidden;
|
||||
|
||||
/// <summary>
|
||||
/// Delegate for providing progress values from different sources
|
||||
/// </summary>
|
||||
public delegate float ProgressProvider();
|
||||
|
||||
/// <summary>
|
||||
/// Current progress provider being used for the loading screen
|
||||
/// </summary>
|
||||
private ProgressProvider _currentProgressProvider;
|
||||
|
||||
/// <summary>
|
||||
/// Default progress provider that returns 0 (or 1 if loading is complete)
|
||||
/// </summary>
|
||||
private float DefaultProgressProvider() => _loadingComplete ? 1f : 0f;
|
||||
|
||||
/// <summary>
|
||||
/// Check if the loading screen is currently active
|
||||
/// </summary>
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shows the loading screen and resets the progress bar to zero
|
||||
/// </summary>
|
||||
/// <param name="progressProvider">Optional delegate to provide progress values (0-1). If null, uses default provider.</param>
|
||||
/// <param name="onComplete">Optional callback when loading screen is fully hidden</param>
|
||||
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());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Animates the progress bar at a steady pace over the minimum display time,
|
||||
/// while also checking actual loading progress from the current progress provider
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when the actual loading process is complete
|
||||
/// </summary>
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Waits until the loading screen is fully hidden before continuing
|
||||
/// </summary>
|
||||
/// <returns>Task that completes when the loading screen is hidden</returns>
|
||||
public System.Threading.Tasks.Task WaitForLoadingScreenToHideAsync()
|
||||
{
|
||||
var tcs = new System.Threading.Tasks.TaskCompletionSource<bool>();
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user