From 447d33fe55d641d1d0a0373148a38cadde788ac8 Mon Sep 17 00:00:00 2001 From: Michal Pikulski Date: Mon, 13 Oct 2025 10:41:58 +0200 Subject: [PATCH] Add a loading screen between scenes --- Assets/Prefabs/Managers/SceneManager.prefab | 125 ++++- Assets/Prefabs/UI/LoadingScreen.prefab | 517 ++++++++++++------ Assets/Prefabs/UI/LoadingScreen.prefab.meta | 2 +- Assets/Scripts/Core/SceneManagerService.cs | 480 ++++++++-------- Assets/Scripts/LevelS/LevelSwitch.cs | 3 +- Assets/Scripts/UI/LoadingScreenController.cs | 168 ++++++ .../UI/LoadingScreenController.cs.meta | 3 + Assets/Scripts/UI/MainMenu.cs | 1 + Assets/Scripts/UI/PauseMenu.cs | 1 + 9 files changed, 919 insertions(+), 381 deletions(-) create mode 100644 Assets/Scripts/UI/LoadingScreenController.cs create mode 100644 Assets/Scripts/UI/LoadingScreenController.cs.meta diff --git a/Assets/Prefabs/Managers/SceneManager.prefab b/Assets/Prefabs/Managers/SceneManager.prefab index ea036003..d28c028c 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: 7090108953567368886} m_Father: {fileID: 0} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!114 &5327225408302228741 @@ -44,3 +45,125 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 360f320f4d7a48e38f5fd7cdfa28144a, type: 3} m_Name: m_EditorClassIdentifier: + loadingScreen: {fileID: 3391437592962192360} +--- !u!1001 &6967569849783118800 +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} + - target: {fileID: 4869161796575291839, guid: 19fad826fce26d34ba304620216a7f47, type: 3} + propertyPath: m_IsActive + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 5737877680156686392, guid: 19fad826fce26d34ba304620216a7f47, type: 3} + propertyPath: minimumDisplayTime + value: 1 + objectReference: {fileID: 0} + m_RemovedComponents: [] + m_RemovedGameObjects: [] + m_AddedGameObjects: [] + m_AddedComponents: [] + m_SourcePrefab: {fileID: 100100000, guid: 19fad826fce26d34ba304620216a7f47, type: 3} +--- !u!114 &3391437592962192360 stripped +MonoBehaviour: + m_CorrespondingSourceObject: {fileID: 5737877680156686392, guid: 19fad826fce26d34ba304620216a7f47, type: 3} + m_PrefabInstance: {fileID: 6967569849783118800} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 1494b10574e74acd880f9101b4248239, type: 3} + m_Name: + m_EditorClassIdentifier: AppleHillsScripts::UI.LoadingScreenController +--- !u!224 &7090108953567368886 stripped +RectTransform: + m_CorrespondingSourceObject: {fileID: 204042265062571366, guid: 19fad826fce26d34ba304620216a7f47, type: 3} + m_PrefabInstance: {fileID: 6967569849783118800} + m_PrefabAsset: {fileID: 0} diff --git a/Assets/Prefabs/UI/LoadingScreen.prefab b/Assets/Prefabs/UI/LoadingScreen.prefab index ec83272c..ddbfae6f 100644 --- a/Assets/Prefabs/UI/LoadingScreen.prefab +++ b/Assets/Prefabs/UI/LoadingScreen.prefab @@ -1,6 +1,6 @@ %YAML 1.1 %TAG !u! tag:unity3d.com,2011: ---- !u!1 &6227327056795603591 +--- !u!1 &1125713904569917005 GameObject: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} @@ -8,198 +8,283 @@ GameObject: m_PrefabAsset: {fileID: 0} serializedVersion: 6 m_Component: - - component: {fileID: 5037978548184922544} - - component: {fileID: 2250902395970853255} - - component: {fileID: 3558142398769816589} - m_Layer: 5 - m_Name: LoadElement - m_TagString: Untagged - m_Icon: {fileID: 0} - m_NavMeshLayer: 0 - m_StaticEditorFlags: 0 - m_IsActive: 1 ---- !u!224 &5037978548184922544 -RectTransform: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 6227327056795603591} - m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} - m_LocalPosition: {x: 0, y: 0, z: 0} - m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 - m_Children: [] - m_Father: {fileID: 3551826825771426741} - m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} - m_AnchorMin: {x: 0, y: 0} - m_AnchorMax: {x: 1, y: 1} - m_AnchoredPosition: {x: 0, y: 0} - m_SizeDelta: {x: 0, y: 0} - m_Pivot: {x: 0.5, y: 0.5} ---- !u!222 &2250902395970853255 -CanvasRenderer: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 6227327056795603591} - m_CullTransparentMesh: 1 ---- !u!114 &3558142398769816589 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 6227327056795603591} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} - m_Name: - m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.Image - m_Material: {fileID: 0} - m_Color: {r: 1, g: 1, b: 1, a: 1} - m_RaycastTarget: 1 - m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} - m_Maskable: 1 - m_OnCullStateChanged: - m_PersistentCalls: - m_Calls: [] - m_Sprite: {fileID: -1067459232888207889, guid: 6767e1e5c0a16f14e926a72a81bf95cb, type: 3} - m_Type: 3 - m_PreserveAspect: 0 - m_FillCenter: 1 - m_FillMethod: 1 - m_FillAmount: 0 - m_FillClockwise: 1 - m_FillOrigin: 0 - m_UseSpriteMesh: 0 - m_PixelsPerUnitMultiplier: 1 ---- !u!1 &7358822077113160787 -GameObject: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - serializedVersion: 6 - m_Component: - - component: {fileID: 3551826825771426741} - - component: {fileID: 7315297104366629978} - - component: {fileID: 3595348625820180868} - m_Layer: 5 - m_Name: LoadingScreen - m_TagString: Untagged - m_Icon: {fileID: 0} - m_NavMeshLayer: 0 - m_StaticEditorFlags: 0 - m_IsActive: 1 ---- !u!224 &3551826825771426741 -RectTransform: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 7358822077113160787} - m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} - m_LocalPosition: {x: 0, y: 0, z: 0} - m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 - m_Children: - - {fileID: 1060159907441385154} - - {fileID: 5037978548184922544} - m_Father: {fileID: 0} - m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} - m_AnchorMin: {x: 0.5, y: 0.5} - m_AnchorMax: {x: 0.5, y: 0.5} - m_AnchoredPosition: {x: 0, y: 0} - m_SizeDelta: {x: 0, y: 0} - m_Pivot: {x: 0.5, y: 0.5} ---- !u!114 &7315297104366629978 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 7358822077113160787} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: 306cc8c2b49d7114eaa3623786fc2126, type: 3} - m_Name: - m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.LayoutElement - m_IgnoreLayout: 0 - m_MinWidth: -1 - m_MinHeight: -1 - m_PreferredWidth: 400 - m_PreferredHeight: 400 - m_FlexibleWidth: -1 - m_FlexibleHeight: -1 - m_LayoutPriority: 1 ---- !u!114 &3595348625820180868 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 7358822077113160787} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: 3245ec927659c4140ac4f8d17403cc18, type: 3} - m_Name: - m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.ContentSizeFitter - m_HorizontalFit: 2 - m_VerticalFit: 2 ---- !u!1 &8774492328535489678 -GameObject: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - serializedVersion: 6 - m_Component: - - component: {fileID: 1060159907441385154} - - component: {fileID: 8332608793126180474} - - component: {fileID: 2562764243080155070} - m_Layer: 5 + - component: {fileID: 1829050514129388015} + - component: {fileID: 6854969622723068570} + - component: {fileID: 2721768192801054246} + m_Layer: 0 m_Name: Background m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 ---- !u!224 &1060159907441385154 +--- !u!224 &1829050514129388015 RectTransform: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 8774492328535489678} + m_GameObject: {fileID: 1125713904569917005} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 2752257465779931077} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 2240, y: 980} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!222 &6854969622723068570 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1125713904569917005} + m_CullTransparentMesh: 1 +--- !u!114 &2721768192801054246 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1125713904569917005} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.Image + m_Material: {fileID: 0} + m_Color: {r: 0.4641509, g: 0.4641509, b: 0.4641509, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 0} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!1 &4630651415052704154 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1663577520044158588} + m_Layer: 5 + m_Name: LoadingScreenElements + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1663577520044158588 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4630651415052704154} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 2948407004548698891} + - {fileID: 7185167273988469881} + m_Father: {fileID: 2752257465779931077} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 400, y: 400} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!1 &4869161796575291839 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 204042265062571366} + - component: {fileID: 7965354554598863860} + - component: {fileID: 1530258026314376533} + - component: {fileID: 6014203435857962984} + - component: {fileID: 5737877680156686392} + m_Layer: 0 + m_Name: LoadingScreen + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &204042265062571366 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4869161796575291839} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 0, y: 0, z: 0} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 2752257465779931077} + m_Father: {fileID: 0} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 0} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0, y: 0} +--- !u!223 &7965354554598863860 +Canvas: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4869161796575291839} + m_Enabled: 1 + serializedVersion: 3 + m_RenderMode: 0 + m_Camera: {fileID: 0} + m_PlaneDistance: 100 + m_PixelPerfect: 0 + m_ReceivesEvents: 1 + m_OverrideSorting: 0 + m_OverridePixelPerfect: 0 + m_SortingBucketNormalizedSize: 0 + m_VertexColorAlwaysGammaSpace: 0 + m_AdditionalShaderChannelsFlag: 0 + m_UpdateRectTransformForStandalone: 0 + m_SortingLayerID: 0 + m_SortingOrder: 10 + m_TargetDisplay: 0 +--- !u!114 &1530258026314376533 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4869161796575291839} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 0cd44c1031e13a943bb63640046fad76, type: 3} + m_Name: + m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.CanvasScaler + m_UiScaleMode: 0 + m_ReferencePixelsPerUnit: 100 + m_ScaleFactor: 1 + m_ReferenceResolution: {x: 800, y: 600} + m_ScreenMatchMode: 0 + m_MatchWidthOrHeight: 0 + m_PhysicalUnit: 3 + m_FallbackScreenDPI: 96 + m_DefaultSpriteDPI: 96 + m_DynamicPixelsPerUnit: 1 + m_PresetInfoIsWorld: 0 +--- !u!114 &6014203435857962984 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4869161796575291839} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: dc42784cf147c0c48a680349fa168899, type: 3} + m_Name: + m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.GraphicRaycaster + m_IgnoreReversedGraphics: 1 + m_BlockingObjects: 0 + m_BlockingMask: + serializedVersion: 2 + m_Bits: 4294967295 +--- !u!114 &5737877680156686392 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4869161796575291839} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 1494b10574e74acd880f9101b4248239, type: 3} + m_Name: + m_EditorClassIdentifier: AppleHillsScripts::UI.LoadingScreenController + loadingScreenContainer: {fileID: 7270617579256400696} + progressBarImage: {fileID: 1674678211233966532} + minimumDisplayTime: 2 + animateProgressBar: 1 + progressBarSmoothTime: 0.1 + progressUpdateInterval: 0.1 +--- !u!1 &6888795583318782279 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2948407004548698891} + - component: {fileID: 6196942992508754867} + - component: {fileID: 417829661404037751} + m_Layer: 5 + m_Name: LoadBackground + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &2948407004548698891 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6888795583318782279} m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] - m_Father: {fileID: 3551826825771426741} + m_Father: {fileID: 1663577520044158588} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} m_AnchorMax: {x: 1, y: 1} m_AnchoredPosition: {x: 0, y: 0} m_SizeDelta: {x: 0, y: 0} m_Pivot: {x: 0.5, y: 0.5} ---- !u!222 &8332608793126180474 +--- !u!222 &6196942992508754867 CanvasRenderer: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 8774492328535489678} + m_GameObject: {fileID: 6888795583318782279} m_CullTransparentMesh: 1 ---- !u!114 &2562764243080155070 +--- !u!114 &417829661404037751 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 8774492328535489678} + m_GameObject: {fileID: 6888795583318782279} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} @@ -223,3 +308,115 @@ MonoBehaviour: m_FillOrigin: 0 m_UseSpriteMesh: 0 m_PixelsPerUnitMultiplier: 1 +--- !u!1 &7270617579256400696 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2752257465779931077} + m_Layer: 0 + m_Name: Container + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &2752257465779931077 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7270617579256400696} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 1829050514129388015} + - {fileID: 1663577520044158588} + m_Father: {fileID: 204042265062571366} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: -2240, y: -980} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!1 &8086003862407389006 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 7185167273988469881} + - component: {fileID: 4134417229148755022} + - component: {fileID: 1674678211233966532} + m_Layer: 5 + m_Name: LoadElement + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &7185167273988469881 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 8086003862407389006} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 1663577520044158588} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!222 &4134417229148755022 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 8086003862407389006} + m_CullTransparentMesh: 1 +--- !u!114 &1674678211233966532 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 8086003862407389006} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.Image + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: -1067459232888207889, guid: 6767e1e5c0a16f14e926a72a81bf95cb, type: 3} + m_Type: 3 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 1 + m_FillAmount: 0 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 diff --git a/Assets/Prefabs/UI/LoadingScreen.prefab.meta b/Assets/Prefabs/UI/LoadingScreen.prefab.meta index f7098a9d..0f9825e0 100644 --- a/Assets/Prefabs/UI/LoadingScreen.prefab.meta +++ b/Assets/Prefabs/UI/LoadingScreen.prefab.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 0e04621f47997754192ca4c53ed8c118 +guid: 19fad826fce26d34ba304620216a7f47 PrefabImporter: externalObjects: {} userData: diff --git a/Assets/Scripts/Core/SceneManagerService.cs b/Assets/Scripts/Core/SceneManagerService.cs index db3af600..0693f1ee 100644 --- a/Assets/Scripts/Core/SceneManagerService.cs +++ b/Assets/Scripts/Core/SceneManagerService.cs @@ -1,255 +1,299 @@ using System; using System.Collections.Generic; using System.Threading.Tasks; +using UI; using UnityEngine; using UnityEngine.SceneManagement; -/// -/// Singleton service for loading and unloading Unity scenes asynchronously, with events for progress and completion. -/// -public class SceneManagerService : MonoBehaviour +namespace Core { - private static SceneManagerService _instance; - private static bool _isQuitting = false; /// - /// Singleton instance of the SceneManagerService. + /// Singleton service for loading and unloading Unity scenes asynchronously, with events for progress and completion. /// - public static SceneManagerService Instance + public class SceneManagerService : MonoBehaviour { - get + [SerializeField] private LoadingScreenController loadingScreen; + + private static SceneManagerService _instance; + private static bool _isQuitting = false; + /// + /// Singleton instance of the SceneManagerService. + /// + public static SceneManagerService Instance { - if (_instance == null && Application.isPlaying && !_isQuitting) + get { - _instance = FindAnyObjectByType(); - if (_instance == null) + if (_instance == null && Application.isPlaying && !_isQuitting) { - var go = new GameObject("SceneManagerService"); - _instance = go.AddComponent(); - // DontDestroyOnLoad(go); + _instance = FindAnyObjectByType(); + if (_instance == null) + { + var go = new GameObject("SceneManagerService"); + _instance = go.AddComponent(); + // DontDestroyOnLoad(go); + } + } + return _instance; + } + } + + // Events for scene lifecycle + public event Action SceneLoadStarted; + public event Action SceneLoadProgress; + public event Action SceneLoadCompleted; + public event Action SceneUnloadStarted; + public event Action SceneUnloadProgress; + public event Action SceneUnloadCompleted; + + private readonly Dictionary _activeLoads = new(); + private readonly Dictionary _activeUnloads = new(); + private const string BootstrapSceneName = "BootstrapScene"; + + void Awake() + { + _instance = this; + // DontDestroyOnLoad(gameObject); +#if UNITY_EDITOR + // In Editor, set CurrentGameplayScene to the currently open scene at play start + if (Application.isPlaying) + { + var activeScene = SceneManager.GetActiveScene(); + if (activeScene.IsValid()) + { + CurrentGameplayScene = activeScene.name; } } - return _instance; - } - } - - // Events for scene lifecycle - public event Action SceneLoadStarted; - public event Action SceneLoadProgress; - public event Action SceneLoadCompleted; - public event Action SceneUnloadStarted; - public event Action SceneUnloadProgress; - public event Action SceneUnloadCompleted; - - private readonly Dictionary _activeLoads = new(); - private readonly Dictionary _activeUnloads = new(); - private const string BootstrapSceneName = "BootstrapScene"; - - void Awake() - { - _instance = this; - // DontDestroyOnLoad(gameObject); -#if UNITY_EDITOR - // In Editor, set CurrentGameplayScene to the currently open scene at play start - if (Application.isPlaying) - { - var activeScene = SceneManager.GetActiveScene(); - if (activeScene.IsValid()) - { - CurrentGameplayScene = activeScene.name; - } - } #endif - // Ensure BootstrapScene is loaded at startup - var bootstrap = SceneManager.GetSceneByName(BootstrapSceneName); - if (!bootstrap.isLoaded) - { - SceneManager.LoadScene(BootstrapSceneName, LoadSceneMode.Additive); - } - } - - void OnApplicationQuit() - { - _isQuitting = true; - } - - /// - /// Load a single scene asynchronously (additive). - /// - /// Name of the scene to load. - /// Optional progress reporter. - public async Task LoadSceneAsync(string sceneName, IProgress progress = null) - { - SceneLoadStarted?.Invoke(sceneName); - var op = SceneManager.LoadSceneAsync(sceneName, LoadSceneMode.Additive); - _activeLoads[sceneName] = op; - while (!op.isDone) - { - progress?.Report(op.progress); - SceneLoadProgress?.Invoke(sceneName, op.progress); - await Task.Yield(); - } - _activeLoads.Remove(sceneName); - SceneLoadCompleted?.Invoke(sceneName); - } - - /// - /// Unload a single scene asynchronously. - /// - /// Name of the scene to unload. - /// Optional progress reporter. - public async Task UnloadSceneAsync(string sceneName, IProgress progress = null) - { - var scene = SceneManager.GetSceneByName(sceneName); - if (!scene.isLoaded) - { - Debug.LogWarning($"SceneManagerService: Attempted to unload scene '{sceneName}', but it is not loaded."); - return; - } - SceneUnloadStarted?.Invoke(sceneName); - var op = SceneManager.UnloadSceneAsync(sceneName); - _activeUnloads[sceneName] = op; - while (!op.isDone) - { - progress?.Report(op.progress); - SceneUnloadProgress?.Invoke(sceneName, op.progress); - await Task.Yield(); - } - _activeUnloads.Remove(sceneName); - SceneUnloadCompleted?.Invoke(sceneName); - } - - /// - /// Load multiple scenes asynchronously. - /// - /// Enumerable of scene names to load. - /// Optional progress reporter. - public async Task LoadScenesAsync(IEnumerable sceneNames, IProgress progress = null) - { - int total = 0; - int done = 0; - var ops = new List(); - foreach (var name in sceneNames) - { - total++; - var op = SceneManager.LoadSceneAsync(name, LoadSceneMode.Additive); - _activeLoads[name] = op; - ops.Add(op); - SceneLoadStarted?.Invoke(name); - } - while (done < total) - { - done = 0; - float aggregate = 0f; - foreach (var op in ops) + // Set up loading screen event handlers + SetupLoadingScreenEvents(); + + // Ensure BootstrapScene is loaded at startup + var bootstrap = SceneManager.GetSceneByName(BootstrapSceneName); + if (!bootstrap.isLoaded) { - if (op.isDone) done++; - aggregate += op.progress; + SceneManager.LoadScene(BootstrapSceneName, LoadSceneMode.Additive); } - float avgProgress = aggregate / total; - progress?.Report(avgProgress); - // Optionally, could invoke SceneLoadProgress for each scene - await Task.Yield(); } - foreach (var name in sceneNames) + + private void SetupLoadingScreenEvents() { - _activeLoads.Remove(name); - SceneLoadCompleted?.Invoke(name); + if (loadingScreen == null) return; + + SceneLoadStarted += _ => loadingScreen.ShowLoadingScreen(); + SceneLoadCompleted += _ => loadingScreen.HideLoadingScreen(); } - } - /// - /// Unload multiple scenes asynchronously. - /// - /// Enumerable of scene names to unload. - /// Optional progress reporter. - public async Task UnloadScenesAsync(IEnumerable sceneNames, IProgress progress = null) - { - int total = 0; - int done = 0; - var ops = new List(); - foreach (var name in sceneNames) + void OnApplicationQuit() { - total++; - var op = SceneManager.UnloadSceneAsync(name); - _activeUnloads[name] = op; - ops.Add(op); - SceneUnloadStarted?.Invoke(name); + _isQuitting = true; } - while (done < total) + + /// + /// Load a single scene asynchronously (additive). + /// + /// Name of the scene to load. + /// Optional progress reporter. + public async Task LoadSceneAsync(string sceneName, IProgress progress = null) { - done = 0; - float aggregate = 0f; - foreach (var op in ops) + SceneLoadStarted?.Invoke(sceneName); + var op = SceneManager.LoadSceneAsync(sceneName, LoadSceneMode.Additive); + _activeLoads[sceneName] = op; + while (!op.isDone) { - aggregate += op.progress; - if (op.isDone) done++; + progress?.Report(op.progress); + SceneLoadProgress?.Invoke(sceneName, op.progress); + await Task.Yield(); } - float avg = aggregate / total; - progress?.Report(avg); + _activeLoads.Remove(sceneName); + SceneLoadCompleted?.Invoke(sceneName); + } + + /// + /// Unload a single scene asynchronously. + /// + /// Name of the scene to unload. + /// Optional progress reporter. + public async Task UnloadSceneAsync(string sceneName, IProgress progress = null) + { + var scene = SceneManager.GetSceneByName(sceneName); + if (!scene.isLoaded) + { + Debug.LogWarning($"SceneManagerService: Attempted to unload scene '{sceneName}', but it is not loaded."); + return; + } + SceneUnloadStarted?.Invoke(sceneName); + var op = SceneManager.UnloadSceneAsync(sceneName); + _activeUnloads[sceneName] = op; + while (!op.isDone) + { + progress?.Report(op.progress); + SceneUnloadProgress?.Invoke(sceneName, op.progress); + await Task.Yield(); + } + _activeUnloads.Remove(sceneName); + SceneUnloadCompleted?.Invoke(sceneName); + } + + /// + /// Load multiple scenes asynchronously. + /// + /// Enumerable of scene names to load. + /// Optional progress reporter. + public async Task LoadScenesAsync(IEnumerable sceneNames, IProgress progress = null) + { + // Show loading screen at the start of multiple scene loading + if (loadingScreen != null) + { + loadingScreen.ShowLoadingScreen(); + } + + int total = 0; + int done = 0; + var ops = new List(); foreach (var name in sceneNames) - SceneUnloadProgress?.Invoke(name, avg); - await Task.Yield(); - } - foreach (var name in sceneNames) - { - _activeUnloads.Remove(name); - SceneUnloadCompleted?.Invoke(name); - } - } - - // Optionally: expose current progress for all active operations - public float GetAggregateLoadProgress() - { - if (_activeLoads.Count == 0) return 1f; - float sum = 0f; - foreach (var op in _activeLoads.Values) sum += op.progress; - return sum / _activeLoads.Count; - } - public float GetAggregateUnloadProgress() - { - if (_activeUnloads.Count == 0) return 1f; - float sum = 0f; - foreach (var op in _activeUnloads.Values) sum += op.progress; - return sum / _activeUnloads.Count; - } - - // Tracks the currently loaded gameplay scene (not persistent/bootstrapper) - public string CurrentGameplayScene { get; private set; } = "MainMenu"; - - // Switches from current gameplay scene to a new one - public async Task SwitchSceneAsync(string newSceneName, IProgress progress = null) - { - // Remove all AstarPath (A* Pathfinder) singletons before loading the new scene - var astarPaths = FindObjectsByType(FindObjectsSortMode.None); - foreach (var astar in astarPaths) - { - if (Application.isPlaying) - Destroy(astar.gameObject); - else - DestroyImmediate(astar.gameObject); - } - // Unload previous gameplay scene (if not BootstrapScene and not same as new) - if (!string.IsNullOrEmpty(CurrentGameplayScene) && CurrentGameplayScene != newSceneName && CurrentGameplayScene != BootstrapSceneName) - { - var prevScene = SceneManager.GetSceneByName(CurrentGameplayScene); - if (prevScene.isLoaded) { - await UnloadSceneAsync(CurrentGameplayScene); + total++; + var op = SceneManager.LoadSceneAsync(name, LoadSceneMode.Additive); + _activeLoads[name] = op; + ops.Add(op); + SceneLoadStarted?.Invoke(name); } - else + + while (done < total) { - Debug.LogWarning($"SceneManagerService: Previous scene '{CurrentGameplayScene}' is not loaded, skipping unload."); + done = 0; + float aggregate = 0f; + foreach (var op in ops) + { + if (op.isDone) done++; + aggregate += op.progress; + } + float avgProgress = aggregate / total; + progress?.Report(avgProgress); + + await Task.Yield(); + } + + foreach (var name in sceneNames) + { + _activeLoads.Remove(name); + SceneLoadCompleted?.Invoke(name); + } + + // Hide loading screen after all scenes are loaded + if (loadingScreen != null) + { + loadingScreen.HideLoadingScreen(); } } - // Ensure BootstrapScene is loaded before loading new scene - var bootstrap = SceneManager.GetSceneByName(BootstrapSceneName); - if (!bootstrap.isLoaded) + + /// + /// Unload multiple scenes asynchronously. + /// + /// Enumerable of scene names to unload. + /// Optional progress reporter. + public async Task UnloadScenesAsync(IEnumerable sceneNames, IProgress progress = null) { - SceneManager.LoadScene(BootstrapSceneName, LoadSceneMode.Additive); + // Show loading screen at the start of multiple scene unloading + if (loadingScreen != null) + { + loadingScreen.ShowLoadingScreen(); + } + + int total = 0; + int done = 0; + var ops = new List(); + foreach (var name in sceneNames) + { + total++; + var op = SceneManager.UnloadSceneAsync(name); + _activeUnloads[name] = op; + ops.Add(op); + SceneUnloadStarted?.Invoke(name); + } + + while (done < total) + { + done = 0; + float aggregate = 0f; + foreach (var op in ops) + { + aggregate += op.progress; + if (op.isDone) done++; + } + float avg = aggregate / total; + progress?.Report(avg); + + await Task.Yield(); + } + + foreach (var name in sceneNames) + { + _activeUnloads.Remove(name); + SceneUnloadCompleted?.Invoke(name); + } + + // Hide loading screen after all scenes are unloaded + if (loadingScreen != null) + { + loadingScreen.HideLoadingScreen(); + } + } + + // Optionally: expose current progress for all active operations + public float GetAggregateLoadProgress() + { + if (_activeLoads.Count == 0) return 1f; + float sum = 0f; + foreach (var op in _activeLoads.Values) sum += op.progress; + return sum / _activeLoads.Count; + } + public float GetAggregateUnloadProgress() + { + if (_activeUnloads.Count == 0) return 1f; + float sum = 0f; + foreach (var op in _activeUnloads.Values) sum += op.progress; + return sum / _activeUnloads.Count; + } + + // Tracks the currently loaded gameplay scene (not persistent/bootstrapper) + public string CurrentGameplayScene { get; private set; } = "MainMenu"; + + // Switches from current gameplay scene to a new one + public async Task SwitchSceneAsync(string newSceneName, IProgress progress = null) + { + // Remove all AstarPath (A* Pathfinder) singletons before loading the new scene + var astarPaths = FindObjectsByType(FindObjectsSortMode.None); + foreach (var astar in astarPaths) + { + if (Application.isPlaying) + Destroy(astar.gameObject); + else + DestroyImmediate(astar.gameObject); + } + // Unload previous gameplay scene (if not BootstrapScene and not same as new) + if (!string.IsNullOrEmpty(CurrentGameplayScene) && CurrentGameplayScene != newSceneName && CurrentGameplayScene != BootstrapSceneName) + { + var prevScene = SceneManager.GetSceneByName(CurrentGameplayScene); + if (prevScene.isLoaded) + { + await UnloadSceneAsync(CurrentGameplayScene); + } + else + { + Debug.LogWarning($"SceneManagerService: Previous scene '{CurrentGameplayScene}' is not loaded, skipping unload."); + } + } + // Ensure BootstrapScene is loaded before loading new scene + var bootstrap = SceneManager.GetSceneByName(BootstrapSceneName); + if (!bootstrap.isLoaded) + { + SceneManager.LoadScene(BootstrapSceneName, LoadSceneMode.Additive); + } + // Load new gameplay scene + await LoadSceneAsync(newSceneName, progress); + // Update tracker + CurrentGameplayScene = newSceneName; } - // Load new gameplay scene - await LoadSceneAsync(newSceneName, progress); - // Update tracker - CurrentGameplayScene = newSceneName; } } diff --git a/Assets/Scripts/LevelS/LevelSwitch.cs b/Assets/Scripts/LevelS/LevelSwitch.cs index b37f9725..e94edf50 100644 --- a/Assets/Scripts/LevelS/LevelSwitch.cs +++ b/Assets/Scripts/LevelS/LevelSwitch.cs @@ -2,7 +2,8 @@ using Input; using Interactions; using UnityEngine; -using AppleHills.Core.Settings; // Added for IInteractionSettings +using AppleHills.Core.Settings; +using Core; // Added for IInteractionSettings /// /// Handles level switching when interacted with. Applies switch data and triggers scene transitions. diff --git a/Assets/Scripts/UI/LoadingScreenController.cs b/Assets/Scripts/UI/LoadingScreenController.cs new file mode 100644 index 00000000..9a629f7c --- /dev/null +++ b/Assets/Scripts/UI/LoadingScreenController.cs @@ -0,0 +1,168 @@ +using System.Collections; +using UnityEngine; +using UnityEngine.UI; +using Core; + +namespace UI +{ + /// + /// Controls the loading screen UI display, progress updates, and timing + /// + public class LoadingScreenController : 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 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 + /// + public void ShowLoadingScreen() + { + // 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 SceneManagerService + /// + 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 SceneManagerService + float actualProgress = 0f; + if (SceneManagerService.Instance != null) + { + actualProgress = SceneManagerService.Instance.GetAggregateLoadProgress(); + } + + // 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($"[LoadingScreen] 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("[LoadingScreen] 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("[LoadingScreen] Final progress set to 1.0"); + } + + // Hide the screen if loading is also complete + if (_loadingComplete) + { + if (loadingScreenContainer != null) + { + loadingScreenContainer.SetActive(false); + Debug.Log("[LoadingScreen] Animation AND loading complete, hiding screen"); + } + } + + _progressCoroutine = null; + } + + /// + /// Called when the actual loading process is complete + /// + public void HideLoadingScreen() + { + Debug.Log("[LoadingScreen] 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("[LoadingScreen] Animation already complete, hiding screen immediately"); + } + } + else + { + Debug.Log("[LoadingScreen] Animation still in progress, waiting for it to complete"); + // The coroutine will handle hiding when animation completes + } + } + } +} diff --git a/Assets/Scripts/UI/LoadingScreenController.cs.meta b/Assets/Scripts/UI/LoadingScreenController.cs.meta new file mode 100644 index 00000000..403584a1 --- /dev/null +++ b/Assets/Scripts/UI/LoadingScreenController.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 1494b10574e74acd880f9101b4248239 +timeCreated: 1760341032 \ No newline at end of file diff --git a/Assets/Scripts/UI/MainMenu.cs b/Assets/Scripts/UI/MainMenu.cs index 59dc1e02..6aca0c16 100644 --- a/Assets/Scripts/UI/MainMenu.cs +++ b/Assets/Scripts/UI/MainMenu.cs @@ -1,4 +1,5 @@ using System; +using Core; using UnityEngine; using UnityEngine.SceneManagement; diff --git a/Assets/Scripts/UI/PauseMenu.cs b/Assets/Scripts/UI/PauseMenu.cs index ed500867..a94f6b7f 100644 --- a/Assets/Scripts/UI/PauseMenu.cs +++ b/Assets/Scripts/UI/PauseMenu.cs @@ -1,4 +1,5 @@ using System; +using Core; using UnityEngine; using UnityEngine.SceneManagement; using Input;