Working scene transitions

This commit is contained in:
Michal Pikulski
2025-10-16 13:42:28 +02:00
parent 49c4d968aa
commit 270877b280
9 changed files with 656 additions and 183 deletions

View File

@@ -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}

View File

@@ -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}

View File

@@ -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
}
}
/// <summary>
/// Called when the initial loading screen is fully hidden
/// </summary>
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<float>(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();
}
}
/// <summary>
/// Unloads the StartingScene, completing the transition to the main menu
/// </summary>
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}");
}
}
/// <summary>
/// Helper class to handle progress reporting without running into explicit interface implementation issues
/// </summary>
private class ProgressHandler
{
private Action<float> _progressAction;
public ProgressHandler(Action<float> progressAction)
{
_progressAction = progressAction;
}
public void ReportProgress(float value)
{
_progressAction?.Invoke(value);
}
}
}

View 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;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 8968b564891a474baae157792b88e75f
timeCreated: 1760613320

View File

@@ -46,6 +46,14 @@ namespace Cinematics
}
public PlayableDirector playableDirector;
private void Awake()
{
_instance = this;
// Initialize required components
InitializeComponents();
}
private void OnEnable()
{
// Subscribe to application quit event to ensure cleanup
@@ -63,15 +71,55 @@ namespace Cinematics
private void OnApplicationQuit()
{
_isQuitting = true;
ReleaseAllHandles();
}
/// <summary>
/// Initializes required components for the CinematicsManager
/// </summary>
private void InitializeComponents()
{
// Initialize PlayableDirector if not set
if (playableDirector == null)
{
playableDirector = GetComponent<PlayableDirector>();
// If still null, try to add the component
if (playableDirector == null)
{
playableDirector = gameObject.AddComponent<PlayableDirector>();
Debug.Log("[CinematicsManager] Added missing PlayableDirector component");
}
}
// Initialize _cinematicSprites if not set
if (_cinematicSprites == null)
{
// First try to find in children
_cinematicSprites = GetComponentInChildren<Image>(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.");
}
}
}
/// <summary>
/// Plays a cinematic from an object reference and returns the PlayableDirector playing the timeline
/// </summary>
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()
{
}
/// <summary>
/// Loads a cinematic asynchronously while showing a loading screen, then plays it
/// </summary>
/// <param name="key">The addressable key of the cinematic to load</param>
/// <returns>The PlayableDirector playing the cinematic</returns>
public async System.Threading.Tasks.Task<PlayableDirector> 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<PlayableAsset> handle = Addressables.LoadAssetAsync<PlayableAsset>(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<PlayableDirector>();
_cinematicSprites = GetComponentInChildren<Image>(true);
// Use the new method with loading screen instead of direct load
await PlayCinematicWithLoadingScreen("IntroSequence");
}
}
}

View File

@@ -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<float> progress = null, bool autoHideLoadingScreen = true)
{

View File

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

View File

@@ -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