Author a bootstrap scene first approach

This commit is contained in:
Michal Pikulski
2025-10-16 11:06:05 +02:00
parent 1ae065b45d
commit 1ee5519716
9 changed files with 630 additions and 130 deletions

View File

@@ -0,0 +1,127 @@
using System;
using UnityEngine;
using UI;
using Core;
using UnityEngine.SceneManagement;
namespace Bootstrap
{
/// <summary>
/// Controller for the boot scene that coordinates bootstrap initialization with loading screen
/// </summary>
public class BootSceneController : MonoBehaviour
{
[SerializeField] private string mainMenuSceneName = "MainMenu";
[SerializeField] private float minDelayAfterBoot = 0.5f; // Small delay after boot to ensure smooth transition
[SerializeField] private bool debugMode = false;
private bool _bootComplete = false;
private bool _hasStartedLoading = false;
private void Start()
{
Debug.Log("[BootSceneController] Boot scene started");
// Ensure the loading screen controller exists
if (LoadingScreenController.Instance == null)
{
Debug.LogError("[BootSceneController] No LoadingScreenController found in the scene!");
return;
}
// Show the loading screen immediately
LoadingScreenController.Instance.ShowLoadingScreen(
// Use the CustomBoot progress as the progress provider
progressProvider: () => CustomBoot.CurrentProgress,
// When loading screen is fully hidden, load main menu
onComplete: () => LoadMainMenu()
);
// Start the boot process if not already initialized
if (!CustomBoot.Initialised)
{
// Subscribe to the boot completion event
CustomBoot.OnBootCompleted += OnBootCompleted;
CustomBoot.OnBootProgressChanged += OnBootProgressChanged;
// Start initialization
CustomBoot.PerformInitialisation();
}
else
{
// If already initialized (can happen in editor testing), proceed immediately
Debug.Log("[BootSceneController] Bootstrap already initialized, proceeding to main menu");
OnBootCompleted();
}
// In debug mode, log additional information
if (debugMode)
{
InvokeRepeating(nameof(LogDebugInfo), 0.1f, 0.5f);
}
}
private void OnDestroy()
{
// Clean up event subscriptions
CustomBoot.OnBootCompleted -= OnBootCompleted;
CustomBoot.OnBootProgressChanged -= OnBootProgressChanged;
if (debugMode)
{
CancelInvoke(nameof(LogDebugInfo));
}
}
private void OnBootProgressChanged(float progress)
{
if (debugMode)
{
Debug.Log($"[BootSceneController] Boot progress: {progress:P0}");
}
}
private void LogDebugInfo()
{
Debug.Log($"[BootSceneController] Debug - Boot Progress: {CustomBoot.CurrentProgress:P0}, Boot Complete: {_bootComplete}, Loading Started: {_hasStartedLoading}");
}
private void OnBootCompleted()
{
// Unsubscribe to prevent duplicate calls
CustomBoot.OnBootCompleted -= OnBootCompleted;
Debug.Log("[BootSceneController] Boot process completed");
_bootComplete = true;
// After a small delay, tell the loading screen we're done
// This prevents jerky transitions if boot happens very quickly
Invoke(nameof(CompleteLoadingScreen), minDelayAfterBoot);
}
private void CompleteLoadingScreen()
{
Debug.Log("[BootSceneController] Hiding loading screen");
// Tell loading screen that loading is complete
if (LoadingScreenController.Instance != null)
{
LoadingScreenController.Instance.HideLoadingScreen();
}
}
private async void LoadMainMenu()
{
// Prevent multiple scene loads
if (_hasStartedLoading)
return;
_hasStartedLoading = true;
Debug.Log($"[BootSceneController] Loading main menu scene: {mainMenuSceneName}");
// Load the main menu scene
var progress = new Progress<float>(p => Debug.Log($"Loading main menu: {p * 100:F0}%"));
await SceneManagerService.Instance.SwitchSceneAsync(mainMenuSceneName, progress);
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: fdb797d6fcdc469bb9bfb9ad3c5f51b5
timeCreated: 1760604860

View File

@@ -1,4 +1,5 @@
using System.Threading.Tasks;
using System;
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;
@@ -14,9 +15,24 @@ namespace Bootstrap
/// Current initialisation status
/// </summary>
public static bool Initialised { get; private set; }
/// <summary>
/// Event triggered when boot progress changes
/// </summary>
public static event Action<float> OnBootProgressChanged;
/// <summary>
/// Event triggered when boot process completes
/// </summary>
public static event Action OnBootCompleted;
/// <summary>
/// Current progress of the boot process (0-1)
/// </summary>
public static float CurrentProgress { get; private set; }
/// <summary>
// Called as soon as the game begins
/// Called as soon as the game begins
/// </summary>
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSplashScreen)]
private static void Initialise()
@@ -32,6 +48,9 @@ namespace Bootstrap
/// </summary>
public static void PerformInitialisation()
{
//Reset progress
CurrentProgress = 0f;
//In editor, perform initialisation synchronously
if (Application.isEditor)
{
@@ -72,6 +91,9 @@ namespace Bootstrap
{
await LoadCustomBootSettings();
Initialised = true;
CurrentProgress = 1f;
OnBootProgressChanged?.Invoke(1f);
OnBootCompleted?.Invoke();
}
/// <summary>
@@ -81,6 +103,9 @@ namespace Bootstrap
{
LoadCustomBootSettingsSync();
Initialised = true;
CurrentProgress = 1f;
OnBootProgressChanged?.Invoke(1f);
OnBootCompleted?.Invoke();
}
@@ -177,5 +202,16 @@ namespace Bootstrap
result.InitialiseSync();
return handle;
}
/// <summary>
/// Updates the current progress value and triggers the progress event
/// </summary>
/// <param name="progress">Progress value between 0-1</param>
internal static void UpdateProgress(float progress)
{
CurrentProgress = Mathf.Clamp01(progress);
OnBootProgressChanged?.Invoke(CurrentProgress);
Debug.Log($"[CustomBoot] Progress: {CurrentProgress:P0}");
}
}
}

View File

@@ -31,15 +31,37 @@ namespace Bootstrap
RuntimeContainer = new GameObject($"{name}_Container");
DontDestroyOnLoad(RuntimeContainer);
Instances = new GameObject[BootPrefabs.Length];
// Calculate total prefabs for progress tracking
int totalPrefabs = BootPrefabs.Length;
float progressPerPrefab = totalPrefabs > 0 ? 1f / totalPrefabs : 1f;
float currentProgress = 0f;
for (var i = 0; i < BootPrefabs.Length; i++)
{
if (!BootPrefabs[i]) continue;
if (!BootPrefabs[i])
{
// Report incremental progress even for null prefabs
currentProgress = (i + 1) * progressPerPrefab;
CustomBoot.UpdateProgress(currentProgress);
continue;
}
var instance = GameObject.InstantiateAsync(BootPrefabs[i], RuntimeContainer.transform);
while (!instance.isDone)
{
// Report partial progress while waiting
float progressInStep = instance.progress * progressPerPrefab;
float overallProgress = i * progressPerPrefab + progressInStep;
CustomBoot.UpdateProgress(overallProgress);
await Task.Yield();
}
Instances[i] = instance.Result[0];
// Report completion of this step
currentProgress = (i + 1) * progressPerPrefab;
CustomBoot.UpdateProgress(currentProgress);
}
}
@@ -55,12 +77,28 @@ namespace Bootstrap
}
Instances = new GameObject[BootPrefabs.Length];
// Calculate total prefabs for progress tracking
int totalPrefabs = BootPrefabs.Length;
float progressPerPrefab = totalPrefabs > 0 ? 1f / totalPrefabs : 1f;
float currentProgress = 0f;
for (var i = 0; i < BootPrefabs.Length; i++)
{
if (!BootPrefabs[i]) continue;
if (!BootPrefabs[i])
{
// Report incremental progress even for null prefabs
currentProgress = (i + 1) * progressPerPrefab;
CustomBoot.UpdateProgress(currentProgress);
continue;
}
var instance = GameObject.Instantiate(BootPrefabs[i], RuntimeContainer.transform);
Instances[i] = instance;
// Report completion of this step
currentProgress = (i + 1) * progressPerPrefab;
CustomBoot.UpdateProgress(currentProgress);
}
}

View File

@@ -68,6 +68,7 @@ namespace UI
private void Awake()
{
_instance = this;
DontDestroyOnLoad(gameObject);
if (loadingScreenContainer == null)
loadingScreenContainer = gameObject;

View File

@@ -81,9 +81,11 @@ namespace UI
return;
bool isMainMenu = levelName.ToLower().Contains("mainmenu");
gameObject.SetActive(!isMainMenu);
bool isStartingLevel = levelName.ToLower().Contains("startingscene");
if(!isMainMenu)
gameObject.SetActive(!(isMainMenu || isStartingLevel));
if(!isMainMenu && !isStartingLevel)
HidePauseMenu(false); // Ensure menu is hidden when switching to a game level
Logging.Debug($"[PauseMenu] Setting pause menu active: {!isMainMenu} for scene: {levelName}");