From 5bab6d959614711c2248bd78c075ebd300387cf1 Mon Sep 17 00:00:00 2001 From: Michal Pikulski Date: Wed, 26 Nov 2025 17:11:02 +0100 Subject: [PATCH] stash work --- .../AddressableAssetGroupSortSettings.asset | 1 + ...eDecorations_BundledAssetGroupSchema.asset | 48 + ...rations_BundledAssetGroupSchema.asset.meta | 8 + ...Decorations_ContentUpdateGroupSchema.asset | 16 + ...ations_ContentUpdateGroupSchema.asset.meta | 8 + .../AssetGroups/StatueDecorations.asset | 83 ++ .../AssetGroups/StatueDecorations.asset.meta | 8 + .../DecorationMinigame.png.meta | 38 +- .../DecorationDraggableInstance.prefab | 2 +- .../Scenes/Levels/AppleHillsOverworld.unity | 64 +- .../Scenes/MiniGames/StatueDecoration.unity | 1278 ++++++++++++++++- .../Core/Settings/SettingsInterfaces.cs | 11 + .../Core/Settings/StatueDressupSettings.cs | 25 + .../Controllers/PhotoCaptureTestController.cs | 65 +- .../Controllers/PhotoEnlargeController.cs | 163 +++ .../PhotoEnlargeController.cs.meta | 3 + .../Controllers/PhotoGridItem.cs | 4 +- .../Controllers/StatueDecorationController.cs | 318 +++- .../StatuePhotoGalleryController.cs | 354 +++-- .../StatueDressup/Data/DecorationMetadata.cs | 42 + .../Data/DecorationMetadata.cs.meta | 3 + .../Minigames/StatueDressup/Display.meta | 3 + .../Display/StatueDecorationLoader.cs | 305 ++++ .../Display/StatueDecorationLoader.cs.meta | 3 + .../DragDrop/DecorationDraggableInstance.cs | 35 +- .../Scripts/Minigames/StatueDressup/UI.meta | 3 + .../StatueDressup/UI/PhotoGalleryPage.cs | 28 + .../StatueDressup/UI/PhotoGalleryPage.cs.meta | 3 + .../StatueDressup/UI/PlayAreaPage.cs | 32 + .../StatueDressup/UI/PlayAreaPage.cs.meta | 3 + Assets/Scripts/Utils/AddressablesUtility.cs | 104 ++ .../Scripts/Utils/AddressablesUtility.cs.meta | 3 + Assets/Scripts/Utils/PhotoCaptureConfig.cs | 71 + .../Scripts/Utils/PhotoCaptureConfig.cs.meta | 3 + Assets/Scripts/Utils/PhotoManager.cs | 635 ++++++++ Assets/Scripts/Utils/PhotoManager.cs.meta | 3 + Assets/Settings/Developer/DebugSettings.asset | 2 +- docs/statue_photo_gallery_setup.md | 164 +++ 38 files changed, 3634 insertions(+), 308 deletions(-) create mode 100644 Assets/AddressableAssetsData/AssetGroups/Schemas/StatueDecorations_BundledAssetGroupSchema.asset create mode 100644 Assets/AddressableAssetsData/AssetGroups/Schemas/StatueDecorations_BundledAssetGroupSchema.asset.meta create mode 100644 Assets/AddressableAssetsData/AssetGroups/Schemas/StatueDecorations_ContentUpdateGroupSchema.asset create mode 100644 Assets/AddressableAssetsData/AssetGroups/Schemas/StatueDecorations_ContentUpdateGroupSchema.asset.meta create mode 100644 Assets/AddressableAssetsData/AssetGroups/StatueDecorations.asset create mode 100644 Assets/AddressableAssetsData/AssetGroups/StatueDecorations.asset.meta create mode 100644 Assets/Scripts/Minigames/StatueDressup/Controllers/PhotoEnlargeController.cs create mode 100644 Assets/Scripts/Minigames/StatueDressup/Controllers/PhotoEnlargeController.cs.meta create mode 100644 Assets/Scripts/Minigames/StatueDressup/Data/DecorationMetadata.cs create mode 100644 Assets/Scripts/Minigames/StatueDressup/Data/DecorationMetadata.cs.meta create mode 100644 Assets/Scripts/Minigames/StatueDressup/Display.meta create mode 100644 Assets/Scripts/Minigames/StatueDressup/Display/StatueDecorationLoader.cs create mode 100644 Assets/Scripts/Minigames/StatueDressup/Display/StatueDecorationLoader.cs.meta create mode 100644 Assets/Scripts/Minigames/StatueDressup/UI.meta create mode 100644 Assets/Scripts/Minigames/StatueDressup/UI/PhotoGalleryPage.cs create mode 100644 Assets/Scripts/Minigames/StatueDressup/UI/PhotoGalleryPage.cs.meta create mode 100644 Assets/Scripts/Minigames/StatueDressup/UI/PlayAreaPage.cs create mode 100644 Assets/Scripts/Minigames/StatueDressup/UI/PlayAreaPage.cs.meta create mode 100644 Assets/Scripts/Utils/AddressablesUtility.cs create mode 100644 Assets/Scripts/Utils/AddressablesUtility.cs.meta create mode 100644 Assets/Scripts/Utils/PhotoCaptureConfig.cs create mode 100644 Assets/Scripts/Utils/PhotoCaptureConfig.cs.meta create mode 100644 Assets/Scripts/Utils/PhotoManager.cs create mode 100644 Assets/Scripts/Utils/PhotoManager.cs.meta create mode 100644 docs/statue_photo_gallery_setup.md diff --git a/Assets/AddressableAssetsData/AddressableAssetGroupSortSettings.asset b/Assets/AddressableAssetsData/AddressableAssetGroupSortSettings.asset index d37c1b27..21081059 100644 --- a/Assets/AddressableAssetsData/AddressableAssetGroupSortSettings.asset +++ b/Assets/AddressableAssetsData/AddressableAssetGroupSortSettings.asset @@ -13,6 +13,7 @@ MonoBehaviour: m_Name: AddressableAssetGroupSortSettings m_EditorClassIdentifier: sortOrder: + - b58f7c15a2ffcbc4483cb2bd0a0dee90 - 21420e71d44619f468badaed8efc42a7 - 0d5d36d6da388314b92b9c6967d23f39 - 75e1f68b7bf77f34f8ad4aeab74d4244 diff --git a/Assets/AddressableAssetsData/AssetGroups/Schemas/StatueDecorations_BundledAssetGroupSchema.asset b/Assets/AddressableAssetsData/AssetGroups/Schemas/StatueDecorations_BundledAssetGroupSchema.asset new file mode 100644 index 00000000..cd88548e --- /dev/null +++ b/Assets/AddressableAssetsData/AssetGroups/Schemas/StatueDecorations_BundledAssetGroupSchema.asset @@ -0,0 +1,48 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &11400000 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: e5d17a21594effb4e9591490b009e7aa, type: 3} + m_Name: StatueDecorations_BundledAssetGroupSchema + m_EditorClassIdentifier: + m_Group: {fileID: 11400000, guid: fd8b6934620e3a54a818495e62a103e0, type: 2} + m_InternalBundleIdMode: 1 + m_Compression: 1 + m_IncludeAddressInCatalog: 1 + m_IncludeGUIDInCatalog: 1 + m_IncludeLabelsInCatalog: 1 + m_InternalIdNamingMode: 0 + m_CacheClearBehavior: 0 + m_IncludeInBuild: 1 + m_BundledAssetProviderType: + m_AssemblyName: Unity.ResourceManager, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + m_ClassName: UnityEngine.ResourceManagement.ResourceProviders.BundledAssetProvider + m_StripDownloadOptions: 0 + m_ForceUniqueProvider: 0 + m_UseAssetBundleCache: 1 + m_UseAssetBundleCrc: 1 + m_UseAssetBundleCrcForCachedBundles: 1 + m_UseUWRForLocalBundles: 0 + m_Timeout: 0 + m_ChunkedTransfer: 0 + m_RedirectLimit: -1 + m_RetryCount: 0 + m_BuildPath: + m_Id: c258e2bcd3e8ac742ba98f152c0e6322 + m_LoadPath: + m_Id: 88323bf0b2ef98446961cedd6232bd47 + m_BundleMode: 0 + m_AssetBundleProviderType: + m_AssemblyName: Unity.ResourceManager, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + m_ClassName: UnityEngine.ResourceManagement.ResourceProviders.AssetBundleProvider + m_UseDefaultSchemaSettings: 0 + m_SelectedPathPairIndex: 0 + m_BundleNaming: 0 + m_AssetLoadMode: 0 diff --git a/Assets/AddressableAssetsData/AssetGroups/Schemas/StatueDecorations_BundledAssetGroupSchema.asset.meta b/Assets/AddressableAssetsData/AssetGroups/Schemas/StatueDecorations_BundledAssetGroupSchema.asset.meta new file mode 100644 index 00000000..ce69843c --- /dev/null +++ b/Assets/AddressableAssetsData/AssetGroups/Schemas/StatueDecorations_BundledAssetGroupSchema.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 5acac321571c9e84f8b97709b80b77ea +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/AddressableAssetsData/AssetGroups/Schemas/StatueDecorations_ContentUpdateGroupSchema.asset b/Assets/AddressableAssetsData/AssetGroups/Schemas/StatueDecorations_ContentUpdateGroupSchema.asset new file mode 100644 index 00000000..ffae72c6 --- /dev/null +++ b/Assets/AddressableAssetsData/AssetGroups/Schemas/StatueDecorations_ContentUpdateGroupSchema.asset @@ -0,0 +1,16 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &11400000 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5834b5087d578d24c926ce20cd31e6d6, type: 3} + m_Name: StatueDecorations_ContentUpdateGroupSchema + m_EditorClassIdentifier: + m_Group: {fileID: 11400000, guid: fd8b6934620e3a54a818495e62a103e0, type: 2} + m_StaticContent: 0 diff --git a/Assets/AddressableAssetsData/AssetGroups/Schemas/StatueDecorations_ContentUpdateGroupSchema.asset.meta b/Assets/AddressableAssetsData/AssetGroups/Schemas/StatueDecorations_ContentUpdateGroupSchema.asset.meta new file mode 100644 index 00000000..949b3820 --- /dev/null +++ b/Assets/AddressableAssetsData/AssetGroups/Schemas/StatueDecorations_ContentUpdateGroupSchema.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 17c82ac3ce25c6b4e90e5e7e0e3cc941 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/AddressableAssetsData/AssetGroups/StatueDecorations.asset b/Assets/AddressableAssetsData/AssetGroups/StatueDecorations.asset new file mode 100644 index 00000000..16850e6c --- /dev/null +++ b/Assets/AddressableAssetsData/AssetGroups/StatueDecorations.asset @@ -0,0 +1,83 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &11400000 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: bbb281ee3bf0b054c82ac2347e9e782c, type: 3} + m_Name: StatueDecorations + m_EditorClassIdentifier: Unity.Addressables.Editor::UnityEditor.AddressableAssets.Settings.AddressableAssetGroup + m_GroupName: StatueDecorations + m_GUID: b58f7c15a2ffcbc4483cb2bd0a0dee90 + m_SerializeEntries: + - m_GUID: 2ea75de9ff6dbfb4b8c246a654868479 + m_Address: Assets/Data/Minigames/StatueDressup/TestDecorationData.asset + m_ReadOnly: 0 + m_SerializedLabels: + - StatueDecorations + FlaggedDuringContentUpdateRestriction: 0 + - m_GUID: 4101d48e428899d409df02f24c83571f + m_Address: Assets/Data/Minigames/StatueDressup/TestDecorationData 7.asset + m_ReadOnly: 0 + m_SerializedLabels: + - StatueDecorations + FlaggedDuringContentUpdateRestriction: 0 + - m_GUID: 5efa934e009bc234e920904b05db3c2f + m_Address: Assets/Data/Minigames/StatueDressup/TestDecorationData 2.asset + m_ReadOnly: 0 + m_SerializedLabels: + - StatueDecorations + FlaggedDuringContentUpdateRestriction: 0 + - m_GUID: 8819ec8b1f4910a4494755cf043636d1 + m_Address: Assets/Data/Minigames/StatueDressup/TestDecorationData 3.asset + m_ReadOnly: 0 + m_SerializedLabels: + - StatueDecorations + FlaggedDuringContentUpdateRestriction: 0 + - m_GUID: 8838477f768600848813a215ab6a46fe + m_Address: Assets/Data/Minigames/StatueDressup/TestDecorationData 5.asset + m_ReadOnly: 0 + m_SerializedLabels: + - StatueDecorations + FlaggedDuringContentUpdateRestriction: 0 + - m_GUID: a5d493c2c7c9cf74cab038023b401273 + m_Address: Assets/Data/Minigames/StatueDressup/TestDecorationData 9.asset + m_ReadOnly: 0 + m_SerializedLabels: + - StatueDecorations + FlaggedDuringContentUpdateRestriction: 0 + - m_GUID: b01ee8334ee052b4784225337e9a5ece + m_Address: Assets/Data/Minigames/StatueDressup/TestDecorationData 8.asset + m_ReadOnly: 0 + m_SerializedLabels: + - StatueDecorations + FlaggedDuringContentUpdateRestriction: 0 + - m_GUID: b09b79db8ef15144bb2138ec59f26a9c + m_Address: Assets/Data/Minigames/StatueDressup/TestDecorationData 4.asset + m_ReadOnly: 0 + m_SerializedLabels: + - StatueDecorations + FlaggedDuringContentUpdateRestriction: 0 + - m_GUID: ca949a6208ce5b5488e90ea3e2eed6df + m_Address: Assets/Data/Minigames/StatueDressup/TestDecorationData 1.asset + m_ReadOnly: 0 + m_SerializedLabels: + - StatueDecorations + FlaggedDuringContentUpdateRestriction: 0 + - m_GUID: f0df83df3cff9d84ba9fd4895e5d1b58 + m_Address: Assets/Data/Minigames/StatueDressup/TestDecorationData 6.asset + m_ReadOnly: 0 + m_SerializedLabels: + - StatueDecorations + FlaggedDuringContentUpdateRestriction: 0 + m_ReadOnly: 0 + m_Settings: {fileID: 11400000, guid: 11da9bb90d9dd5848b4f7629415a6937, type: 2} + m_SchemaSet: + m_Schemas: + - {fileID: 11400000, guid: 5acac321571c9e84f8b97709b80b77ea, type: 2} + - {fileID: 11400000, guid: 17c82ac3ce25c6b4e90e5e7e0e3cc941, type: 2} diff --git a/Assets/AddressableAssetsData/AssetGroups/StatueDecorations.asset.meta b/Assets/AddressableAssetsData/AssetGroups/StatueDecorations.asset.meta new file mode 100644 index 00000000..6cc1974c --- /dev/null +++ b/Assets/AddressableAssetsData/AssetGroups/StatueDecorations.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: fd8b6934620e3a54a818495e62a103e0 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Art/Sprites/Spritesheets/TownMap/PuzzleLevelIcons/DecorationMinigame.png.meta b/Assets/Art/Sprites/Spritesheets/TownMap/PuzzleLevelIcons/DecorationMinigame.png.meta index 8b7d2cef..891bba28 100644 --- a/Assets/Art/Sprites/Spritesheets/TownMap/PuzzleLevelIcons/DecorationMinigame.png.meta +++ b/Assets/Art/Sprites/Spritesheets/TownMap/PuzzleLevelIcons/DecorationMinigame.png.meta @@ -122,6 +122,32 @@ TextureImporter: ignorePlatformSupport: 0 androidETC2FallbackOverride: 0 forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 4 + buildTarget: WebGL + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 4 + buildTarget: WindowsStoreApps + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 spriteSheet: serializedVersion: 2 sprites: @@ -129,12 +155,12 @@ TextureImporter: name: DecorationMinigame_0 rect: serializedVersion: 2 - x: 143 - y: 59 - width: 701 - height: 983 + x: 0 + y: 0 + width: 962 + height: 1109 alignment: 0 - pivot: {x: 0, y: 0} + pivot: {x: 0.5, y: 0.5} border: {x: 0, y: 0, z: 0, w: 0} customData: outline: [] @@ -151,7 +177,7 @@ TextureImporter: customData: physicsShape: [] bones: [] - spriteID: + spriteID: 99610a41e95434e42bc921e55c481562 internalID: 0 vertices: [] indices: diff --git a/Assets/Prefabs/Minigames/StatueDressup/DecorationDraggableInstance.prefab b/Assets/Prefabs/Minigames/StatueDressup/DecorationDraggableInstance.prefab index c9e89879..cb75d2ff 100644 --- a/Assets/Prefabs/Minigames/StatueDressup/DecorationDraggableInstance.prefab +++ b/Assets/Prefabs/Minigames/StatueDressup/DecorationDraggableInstance.prefab @@ -102,4 +102,4 @@ MonoBehaviour: m_Name: m_EditorClassIdentifier: AppleHillsScripts::Minigames.StatueDressup.DragDrop.DecorationDraggableInstance decorationImage: {fileID: 597267714783345863} - canvasGroup: {fileID: 0} + canvasGroup: {fileID: 3617977973382190563} diff --git a/Assets/Scenes/Levels/AppleHillsOverworld.unity b/Assets/Scenes/Levels/AppleHillsOverworld.unity index eb3e8a51..4960e08e 100644 --- a/Assets/Scenes/Levels/AppleHillsOverworld.unity +++ b/Assets/Scenes/Levels/AppleHillsOverworld.unity @@ -468,6 +468,11 @@ PrefabInstance: addedObject: {fileID: 706319199} m_AddedComponents: [] m_SourcePrefab: {fileID: 100100000, guid: 539b408cd1191614abdcd99506f1157d, type: 3} +--- !u!1 &175866977 stripped +GameObject: + m_CorrespondingSourceObject: {fileID: 7404622075362872657, guid: f44866deaba5f5c4a90f0330dd9957f0, type: 3} + m_PrefabInstance: {fileID: 1460027141} + m_PrefabAsset: {fileID: 0} --- !u!1 &201656041 GameObject: m_ObjectHideFlags: 0 @@ -2885,6 +2890,37 @@ Transform: m_Children: [] m_Father: {fileID: 0} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &1292467736 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1292467737} + m_Layer: 10 + m_Name: DecorationsRoot + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1292467737 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1292467736} + serializedVersion: 2 + 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: 1460027142} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!4 &1298174946 stripped Transform: m_CorrespondingSourceObject: {fileID: 5179840719513743426, guid: 4c8aa6e55c410284aaa57c461eba5bdf, type: 3} @@ -3060,11 +3096,11 @@ PrefabInstance: objectReference: {fileID: 0} - target: {fileID: 8766058064105819536, guid: f44866deaba5f5c4a90f0330dd9957f0, type: 3} propertyPath: m_SpriteTilingProperty.oldSize.x - value: 7.01 + value: 9.62 objectReference: {fileID: 0} - target: {fileID: 8766058064105819536, guid: f44866deaba5f5c4a90f0330dd9957f0, type: 3} propertyPath: m_SpriteTilingProperty.oldSize.y - value: 9.83 + value: 11.09 objectReference: {fileID: 0} m_RemovedComponents: [] m_RemovedGameObjects: [] @@ -3075,13 +3111,35 @@ PrefabInstance: - targetCorrespondingSourceObject: {fileID: 5727794563305583620, guid: f44866deaba5f5c4a90f0330dd9957f0, type: 3} insertIndex: -1 addedObject: {fileID: 1253055066} - m_AddedComponents: [] + - targetCorrespondingSourceObject: {fileID: 5727794563305583620, guid: f44866deaba5f5c4a90f0330dd9957f0, type: 3} + insertIndex: -1 + addedObject: {fileID: 1292467737} + m_AddedComponents: + - targetCorrespondingSourceObject: {fileID: 7404622075362872657, guid: f44866deaba5f5c4a90f0330dd9957f0, type: 3} + insertIndex: -1 + addedObject: {fileID: 1460027146} m_SourcePrefab: {fileID: 100100000, guid: f44866deaba5f5c4a90f0330dd9957f0, type: 3} --- !u!4 &1460027142 stripped Transform: m_CorrespondingSourceObject: {fileID: 5727794563305583620, guid: f44866deaba5f5c4a90f0330dd9957f0, type: 3} m_PrefabInstance: {fileID: 1460027141} m_PrefabAsset: {fileID: 0} +--- !u!114 &1460027146 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 175866977} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 50d0f4591bbd40fc81dc615fa465e0c5, type: 3} + m_Name: + m_EditorClassIdentifier: AppleHillsScripts::Minigames.StatueDressup.Display.StatueDecorationLoader + decorationDataLabel: StatueDecorations + decorationRoot: {fileID: 1292467737} + specificPhotoId: + showDebugInfo: 1 --- !u!4 &1572710089 stripped Transform: m_CorrespondingSourceObject: {fileID: 5507990123417429516, guid: afbb486e5456a20479aee4cf8bc949b6, type: 3} diff --git a/Assets/Scenes/MiniGames/StatueDecoration.unity b/Assets/Scenes/MiniGames/StatueDecoration.unity index 5b430854..d2bd0482 100644 --- a/Assets/Scenes/MiniGames/StatueDecoration.unity +++ b/Assets/Scenes/MiniGames/StatueDecoration.unity @@ -198,6 +198,81 @@ Transform: m_Children: [] m_Father: {fileID: 0} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &1678977 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1678978} + - component: {fileID: 1678980} + - component: {fileID: 1678979} + m_Layer: 5 + m_Name: PhotoFlash + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 0 +--- !u!224 &1678978 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1678977} + 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: 1217454518} + 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!114 &1678979 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1678977} + 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: 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!222 &1678980 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1678977} + m_CullTransparentMesh: 1 --- !u!1 &37633365 GameObject: m_ObjectHideFlags: 0 @@ -318,6 +393,81 @@ CanvasRenderer: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 37633365} m_CullTransparentMesh: 1 +--- !u!1 &52826699 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 52826700} + - component: {fileID: 52826702} + - component: {fileID: 52826701} + m_Layer: 5 + m_Name: Image (6) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &52826700 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 52826699} + 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: 1733895146} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 1} + m_AnchorMax: {x: 0, y: 1} + m_AnchoredPosition: {x: 467.99994, y: -875.99994} + m_SizeDelta: {x: 250, y: 250} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &52826701 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 52826699} + 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: 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!222 &52826702 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 52826699} + m_CullTransparentMesh: 1 --- !u!1 &61401515 GameObject: m_ObjectHideFlags: 0 @@ -327,13 +477,14 @@ GameObject: serializedVersion: 6 m_Component: - component: {fileID: 61401516} + - component: {fileID: 61401517} m_Layer: 5 m_Name: PhotoGallery m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 - m_IsActive: 1 + m_IsActive: 0 --- !u!224 &61401516 RectTransform: m_ObjectHideFlags: 0 @@ -346,7 +497,9 @@ RectTransform: m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: - - {fileID: 1612917661} + - {fileID: 598551045} + - {fileID: 1774956263} + - {fileID: 487762464} m_Father: {fileID: 1217454518} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} @@ -354,6 +507,20 @@ RectTransform: m_AnchoredPosition: {x: 0, y: 0} m_SizeDelta: {x: 0, y: 0} m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &61401517 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 61401515} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 3136a7edf8de421e9860c6bd4e1dca10, type: 3} + m_Name: + m_EditorClassIdentifier: AppleHillsScripts::Minigames.StatueDressup.UI.PhotoGalleryPage + PageName: PhotoGalleryPage + transitionDuration: 0.3 --- !u!1 &65358844 GameObject: m_ObjectHideFlags: 0 @@ -386,6 +553,7 @@ RectTransform: - {fileID: 1078270174} - {fileID: 1325274730} - {fileID: 166400579} + - {fileID: 2147347253} m_Father: {fileID: 1443594949} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} @@ -401,6 +569,156 @@ CanvasRenderer: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 65358844} m_CullTransparentMesh: 1 +--- !u!1 &103521088 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 103521089} + - component: {fileID: 103521091} + - component: {fileID: 103521090} + m_Layer: 5 + m_Name: Image (3) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &103521089 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 103521088} + 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: 1733895146} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 1} + m_AnchorMax: {x: 0, y: 1} + m_AnchoredPosition: {x: 467.99994, y: -575.99994} + m_SizeDelta: {x: 250, y: 250} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &103521090 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 103521088} + 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: 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!222 &103521091 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 103521088} + m_CullTransparentMesh: 1 +--- !u!1 &115868432 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 115868433} + - component: {fileID: 115868435} + - component: {fileID: 115868434} + m_Layer: 5 + m_Name: Image (4) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &115868433 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 115868432} + 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: 1733895146} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 1} + m_AnchorMax: {x: 0, y: 1} + m_AnchoredPosition: {x: 767.99994, y: -575.99994} + m_SizeDelta: {x: 250, y: 250} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &115868434 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 115868432} + 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: 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!222 &115868435 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 115868432} + m_CullTransparentMesh: 1 --- !u!1 &131357294 GameObject: m_ObjectHideFlags: 0 @@ -474,6 +792,81 @@ RectTransform: m_AnchoredPosition: {x: 0, y: 0} m_SizeDelta: {x: 100, y: 100} m_Pivot: {x: 0.5, y: 0.5} +--- !u!1 &167351357 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 167351358} + - component: {fileID: 167351360} + - component: {fileID: 167351359} + m_Layer: 5 + m_Name: Image (8) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &167351358 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 167351357} + 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: 1733895146} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 1} + m_AnchorMax: {x: 0, y: 1} + m_AnchoredPosition: {x: 1068, y: -875.99994} + m_SizeDelta: {x: 250, y: 250} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &167351359 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 167351357} + 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: 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!222 &167351360 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 167351357} + m_CullTransparentMesh: 1 --- !u!1 &194466354 GameObject: m_ObjectHideFlags: 0 @@ -631,6 +1024,81 @@ RectTransform: m_AnchoredPosition: {x: 0.000030517578, y: 0} m_SizeDelta: {x: 0, y: 0} m_Pivot: {x: 0.5, y: 0.5} +--- !u!1 &254256966 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 254256967} + - component: {fileID: 254256969} + - component: {fileID: 254256968} + m_Layer: 5 + m_Name: Background + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &254256967 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 254256966} + 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: 1774956263} + 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!114 &254256968 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 254256966} + 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, g: 0, b: 0, a: 0.2784314} + 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!222 &254256969 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 254256966} + m_CullTransparentMesh: 1 --- !u!1 &403667977 GameObject: m_ObjectHideFlags: 0 @@ -699,6 +1167,81 @@ MonoBehaviour: statueController: {fileID: 1647993458} statueOutline: {fileID: 1325274732} gridLayout: {fileID: 699865156} +--- !u!1 &435994659 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 435994660} + - component: {fileID: 435994662} + - component: {fileID: 435994661} + m_Layer: 5 + m_Name: Image + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &435994660 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 435994659} + 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: 1733895146} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 1} + m_AnchorMax: {x: 0, y: 1} + m_AnchoredPosition: {x: 467.99994, y: -275.99994} + m_SizeDelta: {x: 250, y: 250} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &435994661 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 435994659} + 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: 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!222 &435994662 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 435994659} + m_CullTransparentMesh: 1 --- !u!1 &483064110 GameObject: m_ObjectHideFlags: 0 @@ -789,6 +1332,179 @@ Transform: m_Children: [] m_Father: {fileID: 0} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &487762463 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 487762464} + m_Layer: 5 + m_Name: PhotoPreview + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &487762464 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 487762463} + 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: 533750042} + - {fileID: 2034809390} + m_Father: {fileID: 61401516} + 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!1 &533750041 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 533750042} + - component: {fileID: 533750044} + - component: {fileID: 533750043} + m_Layer: 5 + m_Name: EnlargeBackdrop + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &533750042 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 533750041} + 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: 487762464} + 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!114 &533750043 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 533750041} + 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, g: 0, b: 0, a: 0.36862746} + 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!222 &533750044 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 533750041} + m_CullTransparentMesh: 1 +--- !u!1 &598551044 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 598551045} + - component: {fileID: 598551046} + m_Layer: 5 + m_Name: GalleryController + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &598551045 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 598551044} + 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: 61401516} + 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: 100, y: 100} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &598551046 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 598551044} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: a7339274a0c54f8c9134942f84d47140, type: 3} + m_Name: + m_EditorClassIdentifier: AppleHillsScripts::Minigames.StatueDressup.Controllers.StatuePhotoGalleryController + gridContainer: {fileID: 1733895146} + gridItemPrefab: {fileID: 0} + enlargedContainer: {fileID: 0} + backdrop: {fileID: 0} + enlargedPreviewPrefab: {fileID: 0} + previousPageButton: {fileID: 0} + nextPageButton: {fileID: 0} + pageStatusText: {fileID: 0} + itemsPerPage: 20 + thumbnailSize: 256 + maxCachedThumbnails: 50 + enlargedScale: 2.5 + animationDuration: 0.3 --- !u!1 &699865154 GameObject: m_ObjectHideFlags: 0 @@ -1019,6 +1735,81 @@ Transform: m_Children: [] m_Father: {fileID: 0} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &892106116 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 892106117} + - component: {fileID: 892106119} + - component: {fileID: 892106118} + m_Layer: 5 + m_Name: Image (2) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &892106117 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 892106116} + 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: 1733895146} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 1} + m_AnchorMax: {x: 0, y: 1} + m_AnchoredPosition: {x: 1068, y: -275.99994} + m_SizeDelta: {x: 250, y: 250} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &892106118 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 892106116} + 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: 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!222 &892106119 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 892106116} + m_CullTransparentMesh: 1 --- !u!1 &1034770168 GameObject: m_ObjectHideFlags: 0 @@ -1215,6 +2006,81 @@ Transform: m_Children: [] m_Father: {fileID: 0} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &1140141871 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1140141872} + - component: {fileID: 1140141874} + - component: {fileID: 1140141873} + m_Layer: 5 + m_Name: Image (5) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1140141872 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1140141871} + 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: 1733895146} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 1} + m_AnchorMax: {x: 0, y: 1} + m_AnchoredPosition: {x: 1068, y: -575.99994} + m_SizeDelta: {x: 250, y: 250} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1140141873 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1140141871} + 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: 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!222 &1140141874 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1140141871} + m_CullTransparentMesh: 1 --- !u!1 &1217454514 GameObject: m_ObjectHideFlags: 0 @@ -1311,6 +2177,7 @@ RectTransform: m_Children: - {fileID: 1443594949} - {fileID: 61401516} + - {fileID: 1678978} m_Father: {fileID: 0} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} @@ -1318,6 +2185,81 @@ RectTransform: m_AnchoredPosition: {x: 0, y: 0} m_SizeDelta: {x: 0, y: 0} m_Pivot: {x: 0, y: 0} +--- !u!1 &1259957078 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1259957079} + - component: {fileID: 1259957081} + - component: {fileID: 1259957080} + m_Layer: 5 + m_Name: Image (7) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1259957079 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1259957078} + 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: 1733895146} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 1} + m_AnchorMax: {x: 0, y: 1} + m_AnchoredPosition: {x: 767.99994, y: -875.99994} + m_SizeDelta: {x: 250, y: 250} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1259957080 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1259957078} + 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: 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!222 &1259957081 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1259957078} + m_CullTransparentMesh: 1 --- !u!1 &1325274729 GameObject: m_ObjectHideFlags: 0 @@ -1417,6 +2359,7 @@ GameObject: serializedVersion: 6 m_Component: - component: {fileID: 1443594949} + - component: {fileID: 1443594950} m_Layer: 5 m_Name: PlayArea m_TagString: Untagged @@ -1438,6 +2381,7 @@ RectTransform: m_Children: - {fileID: 65358845} - {fileID: 403667978} + - {fileID: 1612917661} m_Father: {fileID: 1217454518} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} @@ -1445,6 +2389,20 @@ RectTransform: m_AnchoredPosition: {x: 0, y: 0} m_SizeDelta: {x: 0, y: 0} m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1443594950 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1443594948} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: edd2867b8a4f42d8af202f215c3ecc47, type: 3} + m_Name: + m_EditorClassIdentifier: AppleHillsScripts::Minigames.StatueDressup.UI.PlayAreaPage + PageName: PlayArea + transitionDuration: 0.3 --- !u!1 &1542794366 GameObject: m_ObjectHideFlags: 0 @@ -1545,6 +2503,81 @@ Transform: m_Children: [] m_Father: {fileID: 0} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &1598728281 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1598728282} + - component: {fileID: 1598728284} + - component: {fileID: 1598728283} + m_Layer: 5 + m_Name: Image (1) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1598728282 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1598728281} + 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: 1733895146} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 1} + m_AnchorMax: {x: 0, y: 1} + m_AnchoredPosition: {x: 767.99994, y: -275.99994} + m_SizeDelta: {x: 250, y: 250} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1598728283 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1598728281} + 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: 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!222 &1598728284 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1598728281} + m_CullTransparentMesh: 1 --- !u!1 &1612917660 GameObject: m_ObjectHideFlags: 0 @@ -1564,7 +2597,7 @@ GameObject: m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 - m_IsActive: 1 + m_IsActive: 0 --- !u!224 &1612917661 RectTransform: m_ObjectHideFlags: 0 @@ -1572,17 +2605,17 @@ RectTransform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1612917660} - m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + 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: 61401516} + m_Father: {fileID: 1443594949} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 1, y: 1} m_AnchorMax: {x: 1, y: 1} m_AnchoredPosition: {x: -75, y: -75} - m_SizeDelta: {x: 172.83276, y: 0} + m_SizeDelta: {x: 172.83276, y: 179.2871} m_Pivot: {x: 1, y: 1} --- !u!114 &1612917662 MonoBehaviour: @@ -1714,7 +2747,13 @@ MonoBehaviour: menuController: {fileID: 403667980} takePhotoButton: {fileID: 37633367} statue: {fileID: 1078270173} + playAreaPage: {fileID: 0} + photoGalleryPage: {fileID: 0} + openGalleryButton: {fileID: 0} uiElementsToHideForPhoto: [] + additionalElementsToHideForCutout: + - {fileID: 483064110} + - {fileID: 403667977} photoArea: {fileID: 65358845} photoSaveKey: MrCementStatuePhoto --- !u!4 &1647993459 @@ -1748,7 +2787,7 @@ GameObject: m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 - m_IsActive: 1 + m_IsActive: 0 --- !u!114 &1685271990 MonoBehaviour: m_ObjectHideFlags: 0 @@ -1779,6 +2818,156 @@ Transform: m_Children: [] m_Father: {fileID: 0} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &1733895145 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1733895146} + - component: {fileID: 1733895148} + - component: {fileID: 1733895147} + m_Layer: 5 + m_Name: GridContainer + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1733895146 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1733895145} + 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: 435994660} + - {fileID: 1598728282} + - {fileID: 892106117} + - {fileID: 103521089} + - {fileID: 115868433} + - {fileID: 1140141872} + - {fileID: 52826700} + - {fileID: 1259957079} + - {fileID: 167351358} + m_Father: {fileID: 1774956263} + 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 &1733895147 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1733895145} + m_CullTransparentMesh: 1 +--- !u!114 &1733895148 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1733895145} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 8a8695521f0d02e499659fee002a26c2, type: 3} + m_Name: + m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.GridLayoutGroup + m_Padding: + m_Left: 75 + m_Right: 75 + m_Top: 75 + m_Bottom: 75 + m_ChildAlignment: 4 + m_StartCorner: 0 + m_StartAxis: 0 + m_CellSize: {x: 250, y: 250} + m_Spacing: {x: 50, y: 50} + m_Constraint: 1 + m_ConstraintCount: 3 +--- !u!1 &1774956262 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1774956263} + m_Layer: 5 + m_Name: GalleryGrid + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1774956263 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1774956262} + 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: 254256967} + - {fileID: 1733895146} + m_Father: {fileID: 61401516} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.1, y: 0.1} + m_AnchorMax: {x: 0.9, y: 0.9} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!1 &2034809389 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2034809390} + m_Layer: 5 + m_Name: EnlargedPhotoContainer + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &2034809390 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2034809389} + 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: 487762464} + 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: 100, y: 100} + m_Pivot: {x: 0.5, y: 0.5} --- !u!1 &2071711337 GameObject: m_ObjectHideFlags: 0 @@ -1899,6 +3088,81 @@ CanvasRenderer: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 2071711337} m_CullTransparentMesh: 1 +--- !u!1 &2147347252 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2147347253} + - component: {fileID: 2147347255} + - component: {fileID: 2147347254} + m_Layer: 5 + m_Name: PhotoOverlay + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 0 +--- !u!224 &2147347253 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2147347252} + 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: 65358845} + 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!114 &2147347254 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2147347252} + 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: 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!222 &2147347255 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2147347252} + m_CullTransparentMesh: 1 --- !u!1660057539 &9223372036854775807 SceneRoots: m_ObjectHideFlags: 0 diff --git a/Assets/Scripts/Core/Settings/SettingsInterfaces.cs b/Assets/Scripts/Core/Settings/SettingsInterfaces.cs index 4a68006c..93c79dcb 100644 --- a/Assets/Scripts/Core/Settings/SettingsInterfaces.cs +++ b/Assets/Scripts/Core/Settings/SettingsInterfaces.cs @@ -188,10 +188,21 @@ namespace AppleHills.Core.Settings float HoverAnimationDuration { get; } float PlacementAnimationDuration { get; } + // Addressables + string DecorationDataLabel { get; } + // Photo Settings string PhotoSaveKey { get; } int PhotoQuality { get; } bool CaptureFullScreen { get; } + string CutoutPhotoFilename { get; } // Hardcoded filename for transparent cutout version + + // Photo Gallery (DEPRECATED - Feature temporarily disabled) + int GalleryItemsPerPage { get; } + int GalleryThumbnailSize { get; } + int GalleryMaxCachedThumbnails { get; } + float GalleryEnlargedScale { get; } + float GalleryAnimationDuration { get; } // Rewards int CardsRewardCount { get; } diff --git a/Assets/Scripts/Core/Settings/StatueDressupSettings.cs b/Assets/Scripts/Core/Settings/StatueDressupSettings.cs index 1310d21b..f3d98f44 100644 --- a/Assets/Scripts/Core/Settings/StatueDressupSettings.cs +++ b/Assets/Scripts/Core/Settings/StatueDressupSettings.cs @@ -49,6 +49,10 @@ namespace Core.Settings [Tooltip("Duration for placement animation")] [SerializeField] private float placementAnimationDuration = 0.15f; + [Header("Addressables")] + [Tooltip("Addressables label for loading DecorationData assets")] + [SerializeField] private string decorationDataLabel = "StatueDecorations"; + [Header("Photo Settings")] [Tooltip("PlayerPrefs key for saving the statue photo")] [SerializeField] private string photoSaveKey = "MrCementStatuePhoto"; @@ -59,6 +63,16 @@ namespace Core.Settings [Tooltip("Whether to capture full screen or just statue area")] [SerializeField] private bool captureFullScreen; + [Tooltip("Filename for transparent cutout version (overwrites each time)")] + [SerializeField] private string cutoutPhotoFilename = "StatueCutout_Latest"; + + [Header("Photo Gallery - DEPRECATED (Feature Disabled)")] + [HideInInspector] [SerializeField] private int galleryItemsPerPage = 20; + [HideInInspector] [SerializeField] private int galleryThumbnailSize = 256; + [HideInInspector] [SerializeField] private int galleryMaxCachedThumbnails = 50; + [HideInInspector] [SerializeField] private float galleryEnlargedScale = 2.5f; + [HideInInspector] [SerializeField] private float galleryAnimationDuration = 0.3f; + [Header("Rewards")] [Tooltip("Number of Blokkemon cards awarded on completion")] [SerializeField] private int cardsRewardCount = 3; @@ -97,10 +111,21 @@ namespace Core.Settings public float HoverAnimationDuration => hoverAnimationDuration; public float PlacementAnimationDuration => placementAnimationDuration; + // IStatueDressupSettings implementation - Addressables + public string DecorationDataLabel => decorationDataLabel; + // IStatueDressupSettings implementation - Photo Settings public string PhotoSaveKey => photoSaveKey; public int PhotoQuality => photoQuality; public bool CaptureFullScreen => captureFullScreen; + public string CutoutPhotoFilename => cutoutPhotoFilename; + + // IStatueDressupSettings implementation - Photo Gallery (DEPRECATED) + public int GalleryItemsPerPage => galleryItemsPerPage; + public int GalleryThumbnailSize => galleryThumbnailSize; + public int GalleryMaxCachedThumbnails => galleryMaxCachedThumbnails; + public float GalleryEnlargedScale => galleryEnlargedScale; + public float GalleryAnimationDuration => galleryAnimationDuration; // IStatueDressupSettings implementation - Rewards public int CardsRewardCount => cardsRewardCount; diff --git a/Assets/Scripts/Minigames/StatueDressup/Controllers/PhotoCaptureTestController.cs b/Assets/Scripts/Minigames/StatueDressup/Controllers/PhotoCaptureTestController.cs index 9f177755..be2f30ee 100644 --- a/Assets/Scripts/Minigames/StatueDressup/Controllers/PhotoCaptureTestController.cs +++ b/Assets/Scripts/Minigames/StatueDressup/Controllers/PhotoCaptureTestController.cs @@ -1,7 +1,6 @@ -using Core; -using Minigames.StatueDressup.Utils; -using UnityEngine; +using UnityEngine; using UnityEngine.UI; +using Utils; namespace Minigames.StatueDressup.Controllers { @@ -24,7 +23,7 @@ namespace Minigames.StatueDressup.Controllers captureButton.onClick.AddListener(OnCaptureClicked); } - Debug.Log($"[PhotoCaptureTest] Ready. Photo save path: {StatuePhotoManager.GetPhotoDirectory()}"); + Debug.Log($"[PhotoCaptureTest] Ready. Photo save path: {PhotoManager.GetCaptureDirectory(CaptureType.StatueMinigame)}"); } private void OnCaptureClicked() @@ -41,47 +40,29 @@ namespace Minigames.StatueDressup.Controllers private System.Collections.IEnumerator CaptureCoroutine() { - // Hide UI - foreach (var obj in hideTheseObjects) - if (obj != null) obj.SetActive(false); - - yield return new WaitForEndOfFrame(); - - // Capture - bool done = false; - Texture2D photo = null; - - StatuePhotoManager.CaptureAreaPhoto(captureArea, (texture) => { - photo = texture; - done = true; - }); - - yield return new WaitUntil(() => done); - - // Restore UI - foreach (var obj in hideTheseObjects) - if (obj != null) obj.SetActive(true); - - // Save - if (photo != null) - { - string photoId = StatuePhotoManager.SavePhoto(photo, 0); - - if (!string.IsNullOrEmpty(photoId)) + // Use new PhotoManager plug-and-play coroutine + yield return PhotoManager.CaptureAndSaveCoroutine( + CaptureType.StatueMinigame, + captureArea, + hideTheseObjects, + onSuccess: (photoId) => { - string path = $"{StatuePhotoManager.GetPhotoDirectory()}/{photoId}.png"; + string path = $"{PhotoManager.GetCaptureDirectory(CaptureType.StatueMinigame)}/{photoId}.png"; Debug.Log($"[PhotoCaptureTest] ✅ SUCCESS! Photo saved: {path}"); - Debug.Log($"[PhotoCaptureTest] Photo size: {photo.width}x{photo.height}"); - } - else + + // Load back to verify + Texture2D loadedPhoto = PhotoManager.LoadPhoto(CaptureType.StatueMinigame, photoId); + if (loadedPhoto != null) + { + Debug.Log($"[PhotoCaptureTest] Photo size: {loadedPhoto.width}x{loadedPhoto.height}"); + } + }, + onFailure: (error) => { - Debug.LogError("[PhotoCaptureTest] ❌ Failed to save photo"); - } - } - else - { - Debug.LogError("[PhotoCaptureTest] ❌ Failed to capture photo"); - } + Debug.LogError($"[PhotoCaptureTest] ❌ Failed: {error}"); + }, + metadata: 0 + ); } private void OnDestroy() diff --git a/Assets/Scripts/Minigames/StatueDressup/Controllers/PhotoEnlargeController.cs b/Assets/Scripts/Minigames/StatueDressup/Controllers/PhotoEnlargeController.cs new file mode 100644 index 00000000..d1929328 --- /dev/null +++ b/Assets/Scripts/Minigames/StatueDressup/Controllers/PhotoEnlargeController.cs @@ -0,0 +1,163 @@ +using Core; +using UnityEngine; +using UnityEngine.EventSystems; +using Utils; + +namespace Minigames.StatueDressup.Controllers +{ + /// + /// Manages enlarged photo preview display. + /// Handles backdrop, preview spawning, and click-to-dismiss. + /// Similar to CardEnlargeController but simpler (no state machine). + /// + public class PhotoEnlargeController + { + private readonly GameObject _backdrop; + private readonly Transform _enlargedContainer; + private readonly float _animationDuration; + + private GameObject _currentEnlargedPreview; + private PhotoGridItem _currentSourceItem; + + /// + /// Constructor + /// + public PhotoEnlargeController(GameObject backdrop, Transform enlargedContainer, float animationDuration = 0.3f) + { + _backdrop = backdrop; + _enlargedContainer = enlargedContainer; + _animationDuration = animationDuration; + } + + /// + /// Check if a photo is currently enlarged + /// + public bool IsPhotoEnlarged => _currentEnlargedPreview != null; + + /// + /// Enlarge a photo from a grid item + /// + public void EnlargePhoto(PhotoGridItem sourceItem, GameObject previewPrefab, Texture2D photoTexture, float enlargedScale) + { + if (sourceItem == null || previewPrefab == null || photoTexture == null) + { + Logging.Error("[PhotoEnlargeController] Invalid parameters for EnlargePhoto"); + return; + } + + // Don't allow multiple enlargements + if (_currentEnlargedPreview != null) + { + Logging.Warning("[PhotoEnlargeController] Photo already enlarged"); + return; + } + + _currentSourceItem = sourceItem; + + // Show backdrop + if (_backdrop != null) + { + _backdrop.SetActive(true); + } + + // Spawn preview clone + _currentEnlargedPreview = Object.Instantiate(previewPrefab, _enlargedContainer); + _currentEnlargedPreview.transform.SetAsLastSibling(); + + // Position at source item's world position + _currentEnlargedPreview.transform.position = sourceItem.transform.position; + _currentEnlargedPreview.transform.localScale = sourceItem.transform.localScale; + + // Set photo texture on preview + var previewImage = _currentEnlargedPreview.GetComponent(); + if (previewImage != null) + { + // Create sprite from texture + Sprite photoSprite = Sprite.Create( + photoTexture, + new Rect(0, 0, photoTexture.width, photoTexture.height), + new Vector2(0.5f, 0.5f) + ); + previewImage.sprite = photoSprite; + } + + // Add click handler to preview + var clickHandler = _currentEnlargedPreview.GetComponent(); + if (clickHandler == null) + { + clickHandler = _currentEnlargedPreview.AddComponent(); + } + + var pointerClickEntry = new EventTrigger.Entry { eventID = EventTriggerType.PointerClick }; + pointerClickEntry.callback.AddListener((_) => { ShrinkPhoto(); }); + clickHandler.triggers.Add(pointerClickEntry); + + // Animate to center and scale up + TweenAnimationUtility.AnimateLocalPosition(_currentEnlargedPreview.transform, Vector3.zero, _animationDuration); + TweenAnimationUtility.AnimateScale(_currentEnlargedPreview.transform, Vector3.one * enlargedScale, _animationDuration); + + // Play audio feedback + AudioManager.Instance.LoadAndPlayUIAudio("card_albumdrop_deep", false); + + Logging.Debug("[PhotoEnlargeController] Photo enlarged"); + } + + /// + /// Shrink the currently enlarged photo back to grid + /// + public void ShrinkPhoto() + { + if (_currentEnlargedPreview == null || _currentSourceItem == null) + { + Logging.Warning("[PhotoEnlargeController] No photo to shrink"); + return; + } + + // Hide backdrop + if (_backdrop != null) + { + _backdrop.SetActive(false); + } + + // Get target position from source item + Vector3 targetWorldPos = _currentSourceItem.transform.position; + Vector3 targetScale = _currentSourceItem.transform.localScale; + + GameObject previewToDestroy = _currentEnlargedPreview; + + // Animate back to source position + Pixelplacement.Tween.Position(previewToDestroy.transform, targetWorldPos, _animationDuration, 0f, Pixelplacement.Tween.EaseInOut); + TweenAnimationUtility.AnimateScale(previewToDestroy.transform, targetScale, _animationDuration, () => + { + // Destroy preview after animation + Object.Destroy(previewToDestroy); + }); + + // Clear references + _currentEnlargedPreview = null; + _currentSourceItem = null; + + Logging.Debug("[PhotoEnlargeController] Photo shrunk"); + } + + /// + /// Cleanup - call when gallery is closed + /// + public void Cleanup() + { + if (_currentEnlargedPreview != null) + { + Object.Destroy(_currentEnlargedPreview); + _currentEnlargedPreview = null; + } + + if (_backdrop != null) + { + _backdrop.SetActive(false); + } + + _currentSourceItem = null; + } + } +} + diff --git a/Assets/Scripts/Minigames/StatueDressup/Controllers/PhotoEnlargeController.cs.meta b/Assets/Scripts/Minigames/StatueDressup/Controllers/PhotoEnlargeController.cs.meta new file mode 100644 index 00000000..920465e1 --- /dev/null +++ b/Assets/Scripts/Minigames/StatueDressup/Controllers/PhotoEnlargeController.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 8ba20462f9a647ee90389c7480147d2e +timeCreated: 1764151481 \ No newline at end of file diff --git a/Assets/Scripts/Minigames/StatueDressup/Controllers/PhotoGridItem.cs b/Assets/Scripts/Minigames/StatueDressup/Controllers/PhotoGridItem.cs index 240ede7a..8f652162 100644 --- a/Assets/Scripts/Minigames/StatueDressup/Controllers/PhotoGridItem.cs +++ b/Assets/Scripts/Minigames/StatueDressup/Controllers/PhotoGridItem.cs @@ -64,14 +64,14 @@ namespace Minigames.StatueDressup.Controllers } /// - /// Handle click to show enlarged view + /// Handle click to enlarge/shrink photo /// public void OnPointerClick(PointerEventData eventData) { if (_galleryController != null && !string.IsNullOrEmpty(_photoId)) { Logging.Debug($"[PhotoGridItem] Clicked: {_photoId}"); - _galleryController.ShowEnlargedView(_photoId); + _galleryController.OnGridItemClicked(this, _photoId); } } } diff --git a/Assets/Scripts/Minigames/StatueDressup/Controllers/StatueDecorationController.cs b/Assets/Scripts/Minigames/StatueDressup/Controllers/StatueDecorationController.cs index 7c4fba71..42c8c697 100644 --- a/Assets/Scripts/Minigames/StatueDressup/Controllers/StatueDecorationController.cs +++ b/Assets/Scripts/Minigames/StatueDressup/Controllers/StatueDecorationController.cs @@ -1,9 +1,12 @@ using System.Collections.Generic; using Core; using Core.Lifecycle; +using Minigames.StatueDressup.Data; using Minigames.StatueDressup.DragDrop; +using UI.Core; using UnityEngine; using UnityEngine.UI; +using Utils; namespace Minigames.StatueDressup.Controllers { @@ -19,17 +22,24 @@ namespace Minigames.StatueDressup.Controllers [SerializeField] private DecorationMenuController menuController; [SerializeField] private Button takePhotoButton; [SerializeField] private GameObject statue; + [SerializeField] private DecorationDraggableInstance draggablePrefab; // Prefab for spawning decorations + + [Header("UI Pages")] + [SerializeField] private UI.PlayAreaPage playAreaPage; + [SerializeField] private UI.PhotoGalleryPage photoGalleryPage; + [SerializeField] private Button openGalleryButton; [Header("UI Elements to Hide for Photo")] [SerializeField] private GameObject[] uiElementsToHideForPhoto; [Header("Photo Settings")] [SerializeField] private RectTransform photoArea; // Area to capture - [SerializeField] private string photoSaveKey = "MrCementStatuePhoto"; private List _placedDecorations = new List(); private bool _minigameCompleted; private AppleHills.Core.Settings.IStatueDressupSettings _settings; + private Dictionary _decorationDataDict; + private UnityEngine.ResourceManagement.AsyncOperations.AsyncOperationHandle> _decorationDataHandle; // Public property for menu controller public Transform StatueParent => statueParent; @@ -48,19 +58,36 @@ namespace Minigames.StatueDressup.Controllers /// /// Main initialization after all managers are ready /// - internal override void OnManagedStart() + internal override async void OnManagedStart() { base.OnManagedStart(); Logging.Debug("[StatueDecorationController] Initializing minigame"); + // Load all DecorationData via Addressables first + await LoadDecorationDataAsync(); + + // TODO: If ever picture gallery + // Setup UI pages (DISABLED - kept for future gallery integration) + // if (playAreaPage != null && UIPageController.Instance != null) + // { + // UIPageController.Instance.PushPage(playAreaPage); + // Logging.Debug("[StatueDecorationController] Play area page registered"); + // } + // Setup photo button if (takePhotoButton != null) { - // TODO: Remove comment when ready - // takePhotoButton.onClick.AddListener(OnTakePhoto); + takePhotoButton.onClick.AddListener(OnTakePhoto); } + // TODO: If ever picture gallery + // Setup gallery button (DISABLED - kept for future use) + // if (openGalleryButton != null) + // { + // openGalleryButton.onClick.AddListener(OnOpenGallery); + // } + // Subscribe to menu controller for tracking placed items // Items will manage their own placement via overlap detection if (menuController != null) @@ -73,6 +100,49 @@ namespace Minigames.StatueDressup.Controllers LoadStatueState(); } + /// + /// Load all DecorationData assets via Addressables and build lookup dictionary + /// + private async System.Threading.Tasks.Task LoadDecorationDataAsync() + { + string label = _settings?.DecorationDataLabel; + + if (string.IsNullOrEmpty(label)) + { + Logging.Error("[StatueDecorationController] Decoration data label not set in settings!"); + return; + } + + Logging.Debug($"[StatueDecorationController] Loading DecorationData with label '{label}'..."); + + // Use utility to load all DecorationData and create dictionary by ID + var result = await AddressablesUtility.LoadAssetsByLabelAsync( + label, + data => data.DecorationId + ); + + _decorationDataDict = result.dictionary; + _decorationDataHandle = result.handle; + + Logging.Debug($"[StatueDecorationController] Loaded {_decorationDataDict.Count} DecorationData assets"); + } + + /// + /// Open photo gallery + /// + private void OnOpenGallery() + { + if (photoGalleryPage != null && UIPageController.Instance != null) + { + UIPageController.Instance.PushPage(photoGalleryPage); + Logging.Debug("[StatueDecorationController] Opening photo gallery"); + } + else + { + Logging.Warning("[StatueDecorationController] Photo gallery page not assigned"); + } + } + /// /// Register a decoration as placed on statue /// @@ -116,76 +186,66 @@ namespace Minigames.StatueDressup.Controllers Logging.Debug("[StatueDecorationController] Taking photo of statue"); - // Hide UI elements - HideUIForPhoto(true); - - // Wait a frame for UI to hide, then capture + // CapturePhotoCoroutine handles all UI hiding/showing StartCoroutine(CapturePhotoCoroutine()); } /// - /// Capture photo after UI is hidden + /// Capture photo and save decoration metadata /// private System.Collections.IEnumerator CapturePhotoCoroutine() { - yield return new WaitForEndOfFrame(); + int decorationCount = _placedDecorations.Count; + bool captureSuccess = false; + string savedPhotoId = null; - // Capture using Screenshot Helper via StatuePhotoManager - bool captureComplete = false; - Texture2D capturedPhoto = null; - - Utils.StatuePhotoManager.CaptureAreaPhoto( + // Capture photo + yield return PhotoManager.CaptureAndSaveCoroutine( + CaptureType.StatueMinigame, photoArea, - (Texture2D texture) => - { - capturedPhoto = texture; - captureComplete = true; - }, - Camera.main - ); - - // Wait for capture to complete - yield return new WaitUntil(() => captureComplete); - - if (capturedPhoto != null) - { - // Save photo with StatuePhotoManager - int decorationCount = _placedDecorations.Count; - string photoId = Utils.StatuePhotoManager.SavePhoto(capturedPhoto, decorationCount); - - if (!string.IsNullOrEmpty(photoId)) + uiElementsToHideForPhoto, + onSuccess: (photoId) => { Logging.Debug($"[StatueDecorationController] Photo saved: {photoId}"); - - // Award cards - AwardCards(); - - // Update town icon - UpdateTownIcon(capturedPhoto); - - // Show completion feedback - ShowCompletionFeedback(); - - _minigameCompleted = true; - } - else + savedPhotoId = photoId; + captureSuccess = true; + }, + onFailure: (error) => { - Logging.Error("[StatueDecorationController] Failed to save photo!"); + Logging.Error($"[StatueDecorationController] Photo capture failed: {error}"); DebugUIMessage.Show("Failed to save photo!", Color.red); - } - } - else + captureSuccess = false; + }, + metadata: decorationCount + ); + + // If photo failed, abort + if (!captureSuccess) { - Logging.Error("[StatueDecorationController] Failed to capture photo!"); - DebugUIMessage.Show("Failed to capture photo!", Color.red); + yield break; } - // Restore UI - HideUIForPhoto(false); + // Save decoration metadata + SaveDecorationMetadata(savedPhotoId); + + // Load the saved photo for town icon update + Texture2D savedPhoto = PhotoManager.LoadPhoto(CaptureType.StatueMinigame, savedPhotoId); + + // Award cards + AwardCards(); + + // Update town icon + if (savedPhoto != null) + { + UpdateTownIcon(savedPhoto); + } + + // Show completion feedback + ShowCompletionFeedback(); + + _minigameCompleted = true; } - - /// /// Award Blokkemon cards to player /// @@ -219,17 +279,60 @@ namespace Minigames.StatueDressup.Controllers } /// - /// Hide/show UI elements for photo + /// Save decoration metadata for reconstruction /// - private void HideUIForPhoto(bool hide) + private void SaveDecorationMetadata(string photoId) { - foreach (var element in uiElementsToHideForPhoto) + // Determine coordinate system type + bool isUIRectTransform = statue != null && statue.GetComponent() != null; + + // Get statue size for coordinate conversion + Vector2 statueSize = Vector2.one; + if (isUIRectTransform && statue != null) { - if (element != null) + RectTransform statueRect = statue.GetComponent(); + statueSize = statueRect.rect.size; + } + else if (statue != null) + { + SpriteRenderer statueSprite = statue.GetComponent(); + if (statueSprite != null && statueSprite.sprite != null) { - element.SetActive(!hide); + statueSize = statueSprite.sprite.bounds.size; } } + + StatueDecorationData data = new StatueDecorationData + { + photoId = photoId, + timestamp = System.DateTime.Now.ToString("o"), + coordinateSystem = isUIRectTransform ? CoordinateSystemType.UIRectTransform : CoordinateSystemType.WorldSpace, + sourceStatueSize = statueSize + }; + + // Collect all decoration placements + foreach (var decoration in _placedDecorations) + { + if (decoration == null || decoration.Data == null) continue; + + SpriteRenderer spriteRenderer = decoration.GetComponent(); + + DecorationPlacement placement = new DecorationPlacement + { + decorationId = decoration.Data.DecorationId, + localPosition = decoration.transform.localPosition, + localScale = decoration.transform.localScale, + rotation = decoration.transform.eulerAngles.z, + sortingOrder = spriteRenderer != null ? spriteRenderer.sortingOrder : 0 + }; + + data.placements.Add(placement); + } + + // Save metadata using PhotoManager + PhotoManager.SaveDecorationMetadata(CaptureType.StatueMinigame, photoId, data); + + Logging.Debug($"[StatueDecorationController] Saved decoration metadata: {data.placements.Count} decorations"); } /// @@ -252,7 +355,7 @@ namespace Minigames.StatueDressup.Controllers } /// - /// Load saved statue decoration state + /// Load saved statue decoration state from metadata /// private void LoadStatueState() { @@ -263,10 +366,83 @@ namespace Minigames.StatueDressup.Controllers return; } - // TODO: Implement load system - // Restore decorations from saved state + // Check if DecorationData is loaded + if (_decorationDataDict == null || _decorationDataDict.Count == 0) + { + Logging.Warning("[StatueDecorationController] DecorationData not loaded yet. Cannot restore state."); + return; + } - Logging.Debug($"[StatueDecorationController] State loaded from {_settings.StateSaveKey} (TODO: implement persistence)"); + // Load latest decoration metadata + StatueDecorationData data = PhotoManager.LoadLatestDecorationMetadata(CaptureType.StatueMinigame); + + if (data == null || data.placements == null || data.placements.Count == 0) + { + Logging.Debug("[StatueDecorationController] No saved state found"); + return; + } + + Logging.Debug($"[StatueDecorationController] Restoring {data.placements.Count} decorations from saved state"); + + // Spawn each decoration + int successCount = 0; + foreach (var placement in data.placements) + { + if (SpawnSavedDecoration(placement)) + { + successCount++; + } + } + + Logging.Debug($"[StatueDecorationController] Successfully restored {successCount}/{data.placements.Count} decorations"); + } + + /// + /// Spawn a saved decoration from placement data + /// + private bool SpawnSavedDecoration(DecorationPlacement placement) + { + // Look up DecorationData from dictionary + if (!_decorationDataDict.TryGetValue(placement.decorationId, out DecorationData decorationData)) + { + Logging.Warning($"[StatueDecorationController] DecorationData not found for ID: {placement.decorationId}"); + return false; + } + + if (draggablePrefab == null || statueParent == null) + { + Logging.Error("[StatueDecorationController] Missing draggable prefab or statue parent"); + return false; + } + + // Spawn decoration instance + DecorationDraggableInstance instance = Instantiate(draggablePrefab, statueParent); + + // Initialize in "placed" state (skip drag logic) + instance.InitializeAsPlaced( + decorationData, + this, + _settings + ); + + // Apply saved transform + instance.transform.localPosition = placement.localPosition; + instance.transform.localScale = placement.localScale; + instance.transform.localEulerAngles = new Vector3(0, 0, placement.rotation); + + // Set sorting order if has SpriteRenderer + SpriteRenderer spriteRenderer = instance.GetComponent(); + if (spriteRenderer != null) + { + spriteRenderer.sortingOrder = placement.sortingOrder; + } + + // Register with controller + RegisterDecoration(instance); + + Logging.Debug($"[StatueDecorationController] Restored decoration: {placement.decorationId} at {placement.localPosition}"); + + return true; } /// @@ -276,11 +452,21 @@ namespace Minigames.StatueDressup.Controllers { base.OnManagedDestroy(); - // Cleanup button listener + // Release Addressables handle + AddressablesUtility.ReleaseHandle(_decorationDataHandle); + + // Cleanup button listeners if (takePhotoButton != null) { takePhotoButton.onClick.RemoveListener(OnTakePhoto); } + + // TODO: If ever picture gallery + // Gallery button cleanup (DISABLED - kept for future use) + // if (openGalleryButton != null) + // { + // openGalleryButton.onClick.RemoveListener(OnOpenGallery); + // } } } } diff --git a/Assets/Scripts/Minigames/StatueDressup/Controllers/StatuePhotoGalleryController.cs b/Assets/Scripts/Minigames/StatueDressup/Controllers/StatuePhotoGalleryController.cs index 271093e3..e52c1e42 100644 --- a/Assets/Scripts/Minigames/StatueDressup/Controllers/StatuePhotoGalleryController.cs +++ b/Assets/Scripts/Minigames/StatueDressup/Controllers/StatuePhotoGalleryController.cs @@ -2,9 +2,9 @@ using System.Collections.Generic; using Core; using Core.Lifecycle; -using Minigames.StatueDressup.Utils; using UnityEngine; using UnityEngine.UI; +using Utils; namespace Minigames.StatueDressup.Controllers { @@ -18,49 +18,50 @@ namespace Minigames.StatueDressup.Controllers [Header("Gallery UI")] [SerializeField] private Transform gridContainer; [SerializeField] private PhotoGridItem gridItemPrefab; - [SerializeField] private ScrollRect scrollRect; [Header("Enlarged View")] - [SerializeField] private GameObject enlargedViewPanel; - [SerializeField] private Image enlargedPhotoImage; - [SerializeField] private Button closeEnlargedButton; - [SerializeField] private Button deletePhotoButton; - [SerializeField] private Text photoInfoText; + [SerializeField] private Transform enlargedContainer; // Container for enlarged preview (top layer) + [SerializeField] private GameObject backdrop; // Dark backdrop for enlarged view + [SerializeField] private GameObject enlargedPreviewPrefab; // Prefab for enlarged preview (same as grid item) [Header("Pagination")] - [SerializeField] private Button loadMoreButton; - [SerializeField] private Text statusText; + [SerializeField] private Button previousPageButton; + [SerializeField] private Button nextPageButton; + [SerializeField] private Text pageStatusText; - [Header("Settings")] - [SerializeField] private int itemsPerPage = 20; - [SerializeField] private int thumbnailSize = 256; - [SerializeField] private int maxCachedThumbnails = 50; // Keep recent thumbnails in memory - - private int _currentPage = 0; + private AppleHills.Core.Settings.IStatueDressupSettings _settings; + private int _currentPage; private List _allPhotoIds = new List(); private Dictionary _activeGridItems = new Dictionary(); private Dictionary _thumbnailCache = new Dictionary(); + private Dictionary _fullPhotoCache = new Dictionary(); // Cache full photos for enlargement private Queue _thumbnailCacheOrder = new Queue(); - private string _currentEnlargedPhotoId = null; - private Texture2D _currentEnlargedTexture = null; + private bool _isLoadingPage; + private PhotoEnlargeController _enlargeController; internal override void OnManagedStart() { base.OnManagedStart(); - // Setup buttons - if (closeEnlargedButton != null) - closeEnlargedButton.onClick.AddListener(CloseEnlargedView); + // Get settings + _settings = GameManager.GetSettingsObject(); - if (deletePhotoButton != null) - deletePhotoButton.onClick.AddListener(DeleteCurrentPhoto); + // Initialize enlarge controller + _enlargeController = new PhotoEnlargeController(backdrop, enlargedContainer, _settings?.GalleryAnimationDuration ?? 0.3f); - if (loadMoreButton != null) - loadMoreButton.onClick.AddListener(LoadNextPage); + // Setup page navigation buttons + if (previousPageButton != null) + previousPageButton.onClick.AddListener(OnPreviousPageClicked); - // Hide enlarged view initially - if (enlargedViewPanel != null) - enlargedViewPanel.SetActive(false); + if (nextPageButton != null) + nextPageButton.onClick.AddListener(OnNextPageClicked); + + // Hide backdrop initially + if (backdrop != null) + backdrop.SetActive(false); + + // Clear grid initially (in case there are leftover items from scene setup) + ClearGrid(); // Load first page RefreshGallery(); @@ -72,35 +73,35 @@ namespace Minigames.StatueDressup.Controllers public void RefreshGallery() { // Clear existing items - ClearGallery(); + ClearGrid(); // Get all photo IDs - _allPhotoIds = StatuePhotoManager.GetAllPhotoIds(); + _allPhotoIds = PhotoManager.GetAllPhotoIds(CaptureType.StatueMinigame); _currentPage = 0; Logging.Debug($"[StatuePhotoGalleryController] Gallery refreshed: {_allPhotoIds.Count} photos"); - // Load first page - LoadNextPage(); + // Display first page + DisplayCurrentPage(); } /// - /// Load next page of photos + /// Display the current page of photos (clears grid and shows only current page) /// - private void LoadNextPage() + private void DisplayCurrentPage() { - List pagePhotoIds = StatuePhotoManager.GetPhotoIdsPage(_currentPage, itemsPerPage); + if (_isLoadingPage) return; - if (pagePhotoIds.Count == 0) - { - if (loadMoreButton != null) - loadMoreButton.gameObject.SetActive(false); - - UpdateStatusText($"All photos loaded ({_allPhotoIds.Count} total)"); - return; - } + _isLoadingPage = true; - Logging.Debug($"[StatuePhotoGalleryController] Loading page {_currentPage}: {pagePhotoIds.Count} items"); + // Clear current grid + ClearGrid(); + + // Get photos for current page + int itemsPerPage = _settings?.GalleryItemsPerPage ?? 20; + List pagePhotoIds = PhotoManager.GetPhotoIdsPage(CaptureType.StatueMinigame, _currentPage, itemsPerPage); + + Logging.Debug($"[StatuePhotoGalleryController] Displaying page {_currentPage + 1}: {pagePhotoIds.Count} items"); // Spawn grid items for this page foreach (string photoId in pagePhotoIds) @@ -108,14 +109,64 @@ namespace Minigames.StatueDressup.Controllers SpawnGridItem(photoId); } - _currentPage++; + // Update button states + UpdatePageButtons(); - // Update UI state - bool hasMore = _currentPage * itemsPerPage < _allPhotoIds.Count; - if (loadMoreButton != null) - loadMoreButton.gameObject.SetActive(hasMore); + // Update status text + int totalPages = Mathf.CeilToInt((float)_allPhotoIds.Count / itemsPerPage); + UpdateStatusText($"Page {_currentPage + 1}/{totalPages} ({_allPhotoIds.Count} photos)"); - UpdateStatusText($"Showing {_activeGridItems.Count} of {_allPhotoIds.Count} photos"); + _isLoadingPage = false; + } + + /// + /// Update page navigation button states + /// + private void UpdatePageButtons() + { + int itemsPerPage = _settings?.GalleryItemsPerPage ?? 20; + int totalPages = Mathf.CeilToInt((float)_allPhotoIds.Count / itemsPerPage); + + // Enable/disable previous button + if (previousPageButton != null) + { + previousPageButton.interactable = _currentPage > 0; + } + + // Enable/disable next button + if (nextPageButton != null) + { + nextPageButton.interactable = _currentPage < totalPages - 1; + } + } + + /// + /// Navigate to previous page + /// + private void OnPreviousPageClicked() + { + if (_currentPage > 0) + { + _currentPage--; + DisplayCurrentPage(); + Logging.Debug($"[StatuePhotoGalleryController] Navigated to previous page: {_currentPage}"); + } + } + + /// + /// Navigate to next page + /// + private void OnNextPageClicked() + { + int itemsPerPage = _settings?.GalleryItemsPerPage ?? 20; + int totalPages = Mathf.CeilToInt((float)_allPhotoIds.Count / itemsPerPage); + + if (_currentPage < totalPages - 1) + { + _currentPage++; + DisplayCurrentPage(); + Logging.Debug($"[StatuePhotoGalleryController] Navigated to next page: {_currentPage}"); + } } /// @@ -154,7 +205,7 @@ namespace Minigames.StatueDressup.Controllers yield return null; // Load full photo - Texture2D fullPhoto = StatuePhotoManager.LoadPhoto(photoId); + Texture2D fullPhoto = PhotoManager.LoadPhoto(CaptureType.StatueMinigame, photoId); if (fullPhoto == null) { @@ -163,7 +214,8 @@ namespace Minigames.StatueDressup.Controllers } // Create thumbnail - Texture2D thumbnail = StatuePhotoManager.CreateThumbnail(fullPhoto, thumbnailSize); + int thumbSize = _settings?.GalleryThumbnailSize ?? 256; + Texture2D thumbnail = PhotoManager.CreateThumbnail(fullPhoto, thumbSize); // Destroy full photo immediately (we only need thumbnail) Destroy(fullPhoto); @@ -188,7 +240,8 @@ namespace Minigames.StatueDressup.Controllers _thumbnailCacheOrder.Enqueue(photoId); // Evict oldest if over limit - while (_thumbnailCache.Count > maxCachedThumbnails && _thumbnailCacheOrder.Count > 0) + int maxCached = _settings?.GalleryMaxCachedThumbnails ?? 50; + while (_thumbnailCache.Count > maxCached && _thumbnailCacheOrder.Count > 0) { string oldestId = _thumbnailCacheOrder.Dequeue(); @@ -202,154 +255,90 @@ namespace Minigames.StatueDressup.Controllers } /// - /// Show enlarged view of a photo (called by PhotoGridItem) + /// Enlarge a photo (called by PhotoGridItem) /// - public void ShowEnlargedView(string photoId) + public void OnGridItemClicked(PhotoGridItem gridItem, string photoId) { - if (enlargedViewPanel == null || enlargedPhotoImage == null) + if (_enlargeController == null) { - Logging.Warning("[StatuePhotoGalleryController] Enlarged view UI not configured"); + Logging.Error("[StatuePhotoGalleryController] Enlarge controller not initialized"); return; } - Logging.Debug($"[StatuePhotoGalleryController] Showing enlarged view: {photoId}"); - - // Clear previous enlarged texture - if (_currentEnlargedTexture != null) + // If already enlarged, shrink it + if (_enlargeController.IsPhotoEnlarged) { - Destroy(_currentEnlargedTexture); - _currentEnlargedTexture = null; - } - - // Load full-size photo - _currentEnlargedTexture = StatuePhotoManager.LoadPhoto(photoId); - - if (_currentEnlargedTexture == null) - { - Logging.Error($"[StatuePhotoGalleryController] Failed to load enlarged photo: {photoId}"); + _enlargeController.ShrinkPhoto(); return; } - // Create sprite from texture - Sprite enlargedSprite = Sprite.Create( - _currentEnlargedTexture, - new Rect(0, 0, _currentEnlargedTexture.width, _currentEnlargedTexture.height), - new Vector2(0.5f, 0.5f) - ); + Logging.Debug($"[StatuePhotoGalleryController] Enlarging photo: {photoId}"); - enlargedPhotoImage.sprite = enlargedSprite; - _currentEnlargedPhotoId = photoId; + float enlargedScale = _settings?.GalleryEnlargedScale ?? 2.5f; - // Update photo info - UpdatePhotoInfo(photoId); - - // Show panel - enlargedViewPanel.SetActive(true); - } - - /// - /// Close enlarged view - /// - private void CloseEnlargedView() - { - if (enlargedViewPanel != null) - enlargedViewPanel.SetActive(false); - - // Clean up texture - if (_currentEnlargedTexture != null) + // Check cache first + if (_fullPhotoCache.TryGetValue(photoId, out Texture2D fullPhoto)) { - Destroy(_currentEnlargedTexture); - _currentEnlargedTexture = null; - } - - _currentEnlargedPhotoId = null; - } - - /// - /// Delete currently viewed photo - /// - private void DeleteCurrentPhoto() - { - if (string.IsNullOrEmpty(_currentEnlargedPhotoId)) - { - Logging.Warning("[StatuePhotoGalleryController] No photo selected for deletion"); - return; - } - - string photoIdToDelete = _currentEnlargedPhotoId; - - // Close enlarged view first - CloseEnlargedView(); - - // Delete photo - bool deleted = StatuePhotoManager.DeletePhoto(photoIdToDelete); - - if (deleted) - { - // Remove from grid - if (_activeGridItems.TryGetValue(photoIdToDelete, out PhotoGridItem gridItem)) - { - Destroy(gridItem.gameObject); - _activeGridItems.Remove(photoIdToDelete); - } - - // Remove from cache - if (_thumbnailCache.TryGetValue(photoIdToDelete, out Texture2D thumbnail)) - { - Destroy(thumbnail); - _thumbnailCache.Remove(photoIdToDelete); - } - - // Refresh photo list - _allPhotoIds.Remove(photoIdToDelete); - - UpdateStatusText($"Photo deleted. {_allPhotoIds.Count} photos remaining"); - - Logging.Debug($"[StatuePhotoGalleryController] Photo deleted: {photoIdToDelete}"); - } - } - - /// - /// Update photo info text in enlarged view - /// - private void UpdatePhotoInfo(string photoId) - { - if (photoInfoText == null) return; - - StatuePhotoManager.PhotoMetadata metadata = StatuePhotoManager.GetPhotoMetadata(photoId); - - if (metadata != null) - { - System.DateTime timestamp = System.DateTime.Parse(metadata.timestamp); - string dateStr = timestamp.ToString("MMM dd, yyyy hh:mm tt"); - - float fileSizeMB = metadata.fileSizeBytes / (1024f * 1024f); - - photoInfoText.text = $"Date: {dateStr}\n" + - $"Decorations: {metadata.decorationCount}\n" + - $"Size: {fileSizeMB:F2} MB"; + // Use cached photo + _enlargeController.EnlargePhoto(gridItem, enlargedPreviewPrefab != null ? enlargedPreviewPrefab : gridItem.gameObject, fullPhoto, enlargedScale); } else { - photoInfoText.text = "Photo information unavailable"; + // Load full-size photo + fullPhoto = PhotoManager.LoadPhoto(CaptureType.StatueMinigame, photoId); + + if (fullPhoto == null) + { + Logging.Error($"[StatuePhotoGalleryController] Failed to load photo: {photoId}"); + return; + } + + // Cache it (limited cache) + if (_fullPhotoCache.Count < 10) // Keep only recent 10 full photos + { + _fullPhotoCache[photoId] = fullPhoto; + } + + _enlargeController.EnlargePhoto(gridItem, enlargedPreviewPrefab != null ? enlargedPreviewPrefab : gridItem.gameObject, fullPhoto, enlargedScale); } } + /// + /// Cleanup when gallery is closed + /// + public void CleanupGallery() + { + if (_enlargeController != null) + { + _enlargeController.Cleanup(); + } + + // Clean up cached full photos + foreach (var photo in _fullPhotoCache.Values) + { + if (photo != null) + { + Destroy(photo); + } + } + _fullPhotoCache.Clear(); + } + /// /// Update status text /// private void UpdateStatusText(string message) { - if (statusText != null) - statusText.text = message; + if (pageStatusText != null) + pageStatusText.text = message; Logging.Debug($"[StatuePhotoGalleryController] Status: {message}"); } /// - /// Clear all grid items and cached data + /// Clear only the grid items (used when switching pages) /// - private void ClearGallery() + private void ClearGrid() { // Destroy grid items foreach (var gridItem in _activeGridItems.Values) @@ -359,6 +348,16 @@ namespace Minigames.StatueDressup.Controllers } _activeGridItems.Clear(); + Logging.Debug("[StatuePhotoGalleryController] Grid cleared"); + } + + /// + /// Clear all grid items and cached data (full cleanup) + /// + private void ClearGallery() + { + ClearGrid(); + // Clear thumbnail cache foreach (var thumbnail in _thumbnailCache.Values) { @@ -368,7 +367,7 @@ namespace Minigames.StatueDressup.Controllers _thumbnailCache.Clear(); _thumbnailCacheOrder.Clear(); - Logging.Debug("[StatuePhotoGalleryController] Gallery cleared"); + Logging.Debug("[StatuePhotoGalleryController] Gallery fully cleared"); } internal override void OnManagedDestroy() @@ -377,17 +376,14 @@ namespace Minigames.StatueDressup.Controllers // Cleanup ClearGallery(); - CloseEnlargedView(); + CleanupGallery(); // Unsubscribe buttons - if (closeEnlargedButton != null) - closeEnlargedButton.onClick.RemoveListener(CloseEnlargedView); + if (previousPageButton != null) + previousPageButton.onClick.RemoveListener(OnPreviousPageClicked); - if (deletePhotoButton != null) - deletePhotoButton.onClick.RemoveListener(DeleteCurrentPhoto); - - if (loadMoreButton != null) - loadMoreButton.onClick.RemoveListener(LoadNextPage); + if (nextPageButton != null) + nextPageButton.onClick.RemoveListener(OnNextPageClicked); } } } diff --git a/Assets/Scripts/Minigames/StatueDressup/Data/DecorationMetadata.cs b/Assets/Scripts/Minigames/StatueDressup/Data/DecorationMetadata.cs new file mode 100644 index 00000000..65b9bae2 --- /dev/null +++ b/Assets/Scripts/Minigames/StatueDressup/Data/DecorationMetadata.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections.Generic; +using UnityEngine; + +namespace Minigames.StatueDressup.Data +{ + /// + /// Metadata for a single decoration placement + /// + [Serializable] + public class DecorationPlacement + { + public string decorationId; // Unique ID to load decoration + public Vector2 localPosition; // Position relative to statue + public Vector2 localScale; // Scale relative to statue + public float rotation; // Z rotation in degrees + public int sortingOrder; // Sprite sorting order + } + + /// + /// Coordinate system type used when saving decoration positions + /// + public enum CoordinateSystemType + { + WorldSpace, // Regular Transform (world units) + UIRectTransform // UI RectTransform (pixel coordinates) + } + + /// + /// Collection of decoration placements for a statue + /// + [Serializable] + public class StatueDecorationData + { + public string photoId; // Associated photo ID + public string timestamp; // When captured + public CoordinateSystemType coordinateSystem; // Source coordinate system + public Vector2 sourceStatueSize; // Size of statue in source units (for conversion) + public List placements = new List(); + } +} + diff --git a/Assets/Scripts/Minigames/StatueDressup/Data/DecorationMetadata.cs.meta b/Assets/Scripts/Minigames/StatueDressup/Data/DecorationMetadata.cs.meta new file mode 100644 index 00000000..067a3fd0 --- /dev/null +++ b/Assets/Scripts/Minigames/StatueDressup/Data/DecorationMetadata.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: bde3a2b8ef7247d29967999bb5e9dbd8 +timeCreated: 1764163703 \ No newline at end of file diff --git a/Assets/Scripts/Minigames/StatueDressup/Display.meta b/Assets/Scripts/Minigames/StatueDressup/Display.meta new file mode 100644 index 00000000..9cf2b339 --- /dev/null +++ b/Assets/Scripts/Minigames/StatueDressup/Display.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 21f5742e8db64d52a293d822c93df4f3 +timeCreated: 1764163758 \ No newline at end of file diff --git a/Assets/Scripts/Minigames/StatueDressup/Display/StatueDecorationLoader.cs b/Assets/Scripts/Minigames/StatueDressup/Display/StatueDecorationLoader.cs new file mode 100644 index 00000000..84a9738e --- /dev/null +++ b/Assets/Scripts/Minigames/StatueDressup/Display/StatueDecorationLoader.cs @@ -0,0 +1,305 @@ +using System.Collections.Generic; +using Core; +using Core.Lifecycle; +using Minigames.StatueDressup.Data; +using UnityEngine; +using UnityEngine.ResourceManagement.AsyncOperations; +using Utils; + +namespace Minigames.StatueDressup.Display +{ + /// + /// Loads decoration metadata and reconstructs decorations on a statue sprite. + /// Place this component on a GameObject with a SpriteRenderer showing the statue. + /// On Start, loads all DecorationData via Addressables label, then spawns decorations from metadata. + /// + [RequireComponent(typeof(SpriteRenderer))] + public class StatueDecorationLoader : ManagedBehaviour + { + [Header("Settings")] + [Tooltip("Root GameObject for spawning decorations (clears only this, not statue children)")] + [SerializeField] private Transform decorationRoot; + + [Tooltip("Load specific photo ID, or leave empty to load latest")] + [SerializeField] private string specificPhotoId = ""; + + [Header("Debug")] + [SerializeField] private bool showDebugInfo = true; + + private SpriteRenderer _statueSpriteRenderer; + private Dictionary _decorationDataDict; + private AsyncOperationHandle> _decorationDataHandle; + private AppleHills.Core.Settings.IStatueDressupSettings _settings; + + internal override void OnManagedStart() + { + base.OnManagedStart(); + + _statueSpriteRenderer = GetComponent(); + + // Get settings + _settings = GameManager.GetSettingsObject(); + + // Ensure decoration root exists + if (decorationRoot == null) + { + GameObject rootObj = new GameObject("DecorationRoot"); + rootObj.transform.SetParent(transform, false); + decorationRoot = rootObj.transform; + + if (showDebugInfo) + { + Logging.Debug("[StatueDecorationLoader] Created decoration root automatically"); + } + } + + // Start async loading via coroutine wrapper + StartCoroutine(LoadAndDisplayDecorationsCoroutine()); + } + + /// + /// Coroutine wrapper for async loading and display + /// + private System.Collections.IEnumerator LoadAndDisplayDecorationsCoroutine() + { + // Convert async Task to coroutine-compatible operation + var loadTask = LoadDecorationDataAsync(); + + // Wait for async operation to complete + while (!loadTask.IsCompleted) + { + yield return null; + } + + // Check for exceptions + if (loadTask.IsFaulted) + { + Logging.Error($"[StatueDecorationLoader] Failed to load decoration data: {loadTask.Exception?.GetBaseException().Message}"); + yield break; + } + + // Load and display decorations + LoadAndDisplayDecorations(); + } + + /// + /// Load all DecorationData assets via Addressables and build lookup dictionary + /// + private async System.Threading.Tasks.Task LoadDecorationDataAsync() + { + string label = _settings?.DecorationDataLabel; + + if (string.IsNullOrEmpty(label)) + { + Logging.Error("[StatueDecorationLoader] Decoration data label not set in settings!"); + return; + } + + if (showDebugInfo) + { + Logging.Debug($"[StatueDecorationLoader] Loading DecorationData with label '{label}'..."); + } + + // Use utility to load all DecorationData and create dictionary by ID + var result = await AddressablesUtility.LoadAssetsByLabelAsync( + label, + data => data.DecorationId, // Key selector: use DecorationId as key + progress => { /* Optional: could show loading bar */ } + ); + + _decorationDataDict = result.dictionary; + _decorationDataHandle = result.handle; + + if (showDebugInfo) + { + Logging.Debug($"[StatueDecorationLoader] Loaded {_decorationDataDict.Count} DecorationData assets"); + } + } + + /// + /// Load decoration metadata and spawn decorations + /// + public void LoadAndDisplayDecorations() + { + // Check if DecorationData is loaded + if (_decorationDataDict == null || _decorationDataDict.Count == 0) + { + Logging.Warning("[StatueDecorationLoader] DecorationData not loaded yet. Cannot display decorations."); + return; + } + + // Load metadata + StatueDecorationData data = string.IsNullOrEmpty(specificPhotoId) + ? PhotoManager.LoadLatestDecorationMetadata(CaptureType.StatueMinigame) + : PhotoManager.LoadDecorationMetadata(CaptureType.StatueMinigame, specificPhotoId); + + if (data == null) + { + Logging.Warning("[StatueDecorationLoader] No decoration metadata found"); + return; + } + + if (showDebugInfo) + { + Logging.Debug($"[StatueDecorationLoader] Loading {data.placements.Count} decorations from {data.photoId}"); + Logging.Debug($"[StatueDecorationLoader] Source coordinate system: {data.coordinateSystem}, statue size: {data.sourceStatueSize}"); + } + + // Clear existing decorations (in case reloading) + ClearDecorations(); + + // Calculate coordinate conversion factor if needed + float conversionFactor = CalculateCoordinateConversion(data); + + // Spawn each decoration synchronously (data already loaded) + int successCount = 0; + foreach (var placement in data.placements) + { + if (SpawnDecoration(placement, conversionFactor)) + { + successCount++; + } + } + + if (showDebugInfo) + { + Logging.Debug($"[StatueDecorationLoader] Successfully loaded {successCount}/{data.placements.Count} decorations"); + } + } + + /// + /// Calculate coordinate conversion factor between source and target coordinate systems + /// + private float CalculateCoordinateConversion(StatueDecorationData data) + { + // If source was world space and we're also world space, no conversion needed + if (data.coordinateSystem == CoordinateSystemType.WorldSpace) + { + if (showDebugInfo) + { + Logging.Debug("[StatueDecorationLoader] No coordinate conversion needed (WorldSpace → WorldSpace)"); + } + return 1f; + } + + // Source was UI RectTransform (pixels), target is WorldSpace (units) + // Need to convert from source statue pixel size to target statue world size + + // Get target statue size (world units) + Vector2 targetStatueSize = Vector2.one; + if (_statueSpriteRenderer != null && _statueSpriteRenderer.sprite != null) + { + targetStatueSize = _statueSpriteRenderer.sprite.bounds.size; + } + + // Calculate conversion factor (target size / source size) + float conversionX = targetStatueSize.x / data.sourceStatueSize.x; + float conversionY = targetStatueSize.y / data.sourceStatueSize.y; + + // Use average of X and Y for uniform scaling (or could use separate X/Y) + float conversionFactor = (conversionX + conversionY) / 2f; + + if (showDebugInfo) + { + Logging.Debug($"[StatueDecorationLoader] Coordinate conversion: UI({data.sourceStatueSize}) → World({targetStatueSize}) = factor {conversionFactor:F3}"); + } + + return conversionFactor; + } + + /// + /// Spawn a single decoration from placement data + /// Looks up DecorationData from pre-loaded dictionary and applies coordinate conversion + /// + private bool SpawnDecoration(DecorationPlacement placement, float conversionFactor) + { + // Look up DecorationData from dictionary + if (!_decorationDataDict.TryGetValue(placement.decorationId, out DecorationData decorationData)) + { + Logging.Warning($"[StatueDecorationLoader] DecorationData not found for ID: {placement.decorationId}"); + return false; + } + + // Get sprite from DecorationData + Sprite decorationSprite = decorationData.DecorationSprite; + + if (decorationSprite == null) + { + Logging.Warning($"[StatueDecorationLoader] DecorationData has null sprite: {placement.decorationId}"); + return false; + } + + // Create GameObject for decoration + GameObject decorationObj = new GameObject($"Decoration_{placement.decorationId}"); + decorationObj.transform.SetParent(decorationRoot, false); // false = keep local position + + // Add SpriteRenderer + SpriteRenderer spriteRenderer = decorationObj.AddComponent(); + spriteRenderer.sprite = decorationSprite; + spriteRenderer.sortingLayerName = "Foreground"; + spriteRenderer.sortingOrder = _statueSpriteRenderer.sortingOrder + placement.sortingOrder; + + // Apply transform with coordinate conversion + Vector3 convertedPosition = placement.localPosition * conversionFactor; + decorationObj.transform.localPosition = convertedPosition; + decorationObj.transform.localScale = placement.localScale; + decorationObj.transform.localEulerAngles = new Vector3(0, 0, placement.rotation); + + if (showDebugInfo) + { + Logging.Debug($"[StatueDecorationLoader] Spawned: {placement.decorationId} at {convertedPosition} (original: {placement.localPosition}, factor: {conversionFactor:F3})"); + } + + return true; + } + + /// + /// Clear all existing decorations from decorationRoot + /// + public void ClearDecorations() + { + if (decorationRoot == null) return; + + // Remove all children from decoration root only + for (int i = decorationRoot.childCount - 1; i >= 0; i--) + { + if (Application.isPlaying) + { + Destroy(decorationRoot.GetChild(i).gameObject); + } + else + { + DestroyImmediate(decorationRoot.GetChild(i).gameObject); + } + } + } + + /// + /// Cleanup - release Addressables handle + /// + private void OnDestroy() + { + // Release DecorationData handle + AddressablesUtility.ReleaseHandle(_decorationDataHandle); + } + + /// + /// Reload decorations (useful for testing) + /// + [ContextMenu("Reload Decorations")] + public void ReloadDecorations() + { + LoadAndDisplayDecorations(); + } + + /// + /// Load specific photo's decorations + /// + public void LoadSpecificPhoto(string photoId) + { + specificPhotoId = photoId; + LoadAndDisplayDecorations(); + } + } +} + diff --git a/Assets/Scripts/Minigames/StatueDressup/Display/StatueDecorationLoader.cs.meta b/Assets/Scripts/Minigames/StatueDressup/Display/StatueDecorationLoader.cs.meta new file mode 100644 index 00000000..ae856cca --- /dev/null +++ b/Assets/Scripts/Minigames/StatueDressup/Display/StatueDecorationLoader.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 50d0f4591bbd40fc81dc615fa465e0c5 +timeCreated: 1764163758 \ No newline at end of file diff --git a/Assets/Scripts/Minigames/StatueDressup/DragDrop/DecorationDraggableInstance.cs b/Assets/Scripts/Minigames/StatueDressup/DragDrop/DecorationDraggableInstance.cs index 77481188..d6446961 100644 --- a/Assets/Scripts/Minigames/StatueDressup/DragDrop/DecorationDraggableInstance.cs +++ b/Assets/Scripts/Minigames/StatueDressup/DragDrop/DecorationDraggableInstance.cs @@ -1,7 +1,6 @@ using Core; using Minigames.StatueDressup.Controllers; using Minigames.StatueDressup.Data; -using Minigames.StatueDressup.Utils; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.UI; @@ -77,6 +76,40 @@ namespace Minigames.StatueDressup.DragDrop Logging.Debug($"[DecorationDraggableInstance] Initialized: {data?.DecorationName}"); } + /// + /// Initialize as already placed decoration (from saved state) + /// Skips drag logic and sets up as if already placed on statue + /// + public void InitializeAsPlaced(DecorationData data, StatueDecorationController controller, + AppleHills.Core.Settings.IStatueDressupSettings settings) + { + _decorationData = data; + _controller = controller; + _settings = settings; + _isPlacedOnStatue = true; + _isDragging = false; + + // Set sprite + if (decorationImage != null && data != null && data.DecorationSprite != null) + { + decorationImage.sprite = data.DecorationSprite; + } + + // Set authored size + if (_rectTransform != null && data != null) + { + _rectTransform.sizeDelta = data.AuthoredSize; + } + + // Make non-interactive for placed state (can be made interactive later if needed) + if (canvasGroup != null) + { + canvasGroup.blocksRaycasts = true; + } + + Logging.Debug($"[DecorationDraggableInstance] Initialized as placed: {data?.DecorationName}"); + } + /// /// Start dragging from icon /// diff --git a/Assets/Scripts/Minigames/StatueDressup/UI.meta b/Assets/Scripts/Minigames/StatueDressup/UI.meta new file mode 100644 index 00000000..31ed8298 --- /dev/null +++ b/Assets/Scripts/Minigames/StatueDressup/UI.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 49800fa9079d485cbc65922d238d214a +timeCreated: 1764151454 \ No newline at end of file diff --git a/Assets/Scripts/Minigames/StatueDressup/UI/PhotoGalleryPage.cs b/Assets/Scripts/Minigames/StatueDressup/UI/PhotoGalleryPage.cs new file mode 100644 index 00000000..cdcdb5dc --- /dev/null +++ b/Assets/Scripts/Minigames/StatueDressup/UI/PhotoGalleryPage.cs @@ -0,0 +1,28 @@ +using UI.Core; + +namespace Minigames.StatueDressup.UI +{ + /// + /// UIPage wrapper for the photo gallery. + /// Simple stock page with no transition animations. + /// + public class PhotoGalleryPage : UIPage + { + protected override void DoTransitionIn(System.Action onComplete) + { + // Instant transition - just show + gameObject.SetActive(true); + onComplete?.Invoke(); + } + + protected override void DoTransitionOut(System.Action onComplete) + { + // Instant transition - just hide + gameObject.SetActive(false); + onComplete?.Invoke(); + } + + // OnBackPressed uses default behavior (pops the page) + } +} + diff --git a/Assets/Scripts/Minigames/StatueDressup/UI/PhotoGalleryPage.cs.meta b/Assets/Scripts/Minigames/StatueDressup/UI/PhotoGalleryPage.cs.meta new file mode 100644 index 00000000..f94b1f2e --- /dev/null +++ b/Assets/Scripts/Minigames/StatueDressup/UI/PhotoGalleryPage.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 3136a7edf8de421e9860c6bd4e1dca10 +timeCreated: 1764151460 \ No newline at end of file diff --git a/Assets/Scripts/Minigames/StatueDressup/UI/PlayAreaPage.cs b/Assets/Scripts/Minigames/StatueDressup/UI/PlayAreaPage.cs new file mode 100644 index 00000000..30b03cfd --- /dev/null +++ b/Assets/Scripts/Minigames/StatueDressup/UI/PlayAreaPage.cs @@ -0,0 +1,32 @@ +using UI.Core; + +namespace Minigames.StatueDressup.UI +{ + /// + /// UIPage wrapper for the statue decoration play area. + /// Simple stock page with no transition animations. + /// + public class PlayAreaPage : UIPage + { + protected override void DoTransitionIn(System.Action onComplete) + { + // Instant transition - just show + gameObject.SetActive(true); + onComplete?.Invoke(); + } + + protected override void DoTransitionOut(System.Action onComplete) + { + // Instant transition - just hide + gameObject.SetActive(false); + onComplete?.Invoke(); + } + + public override void OnBackPressed() + { + // Play area is the root page - don't allow back navigation + // Override if you want custom behavior (e.g., quit minigame) + } + } +} + diff --git a/Assets/Scripts/Minigames/StatueDressup/UI/PlayAreaPage.cs.meta b/Assets/Scripts/Minigames/StatueDressup/UI/PlayAreaPage.cs.meta new file mode 100644 index 00000000..4ba84080 --- /dev/null +++ b/Assets/Scripts/Minigames/StatueDressup/UI/PlayAreaPage.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: edd2867b8a4f42d8af202f215c3ecc47 +timeCreated: 1764151454 \ No newline at end of file diff --git a/Assets/Scripts/Utils/AddressablesUtility.cs b/Assets/Scripts/Utils/AddressablesUtility.cs new file mode 100644 index 00000000..639dc261 --- /dev/null +++ b/Assets/Scripts/Utils/AddressablesUtility.cs @@ -0,0 +1,104 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Core; +using UnityEngine.AddressableAssets; +using UnityEngine.ResourceManagement.AsyncOperations; + +namespace Utils +{ + /// + /// Utility class for common Addressables operations. + /// Provides generic methods for loading assets by label and creating lookup dictionaries. + /// + public static class AddressablesUtility + { + /// + /// Load all assets with a specific label and create a dictionary indexed by a key selector. + /// + /// Type of asset to load + /// Type of key for dictionary + /// Addressables label to filter by + /// Function to extract key from asset (e.g., asset => asset.Id) + /// Optional callback for progress updates (0-1) + /// Dictionary of assets indexed by key, and operation handle for cleanup + public static async Task<(Dictionary dictionary, AsyncOperationHandle> handle)> + LoadAssetsByLabelAsync( + string label, + Func keySelector, + Action onProgress = null) + { + Dictionary dictionary = new Dictionary(); + + // Load all assets with the specified label + AsyncOperationHandle> handle = Addressables.LoadAssetsAsync( + label, + asset => + { + // This callback is invoked for each asset as it loads + if (asset != null) + { + TKey key = keySelector(asset); + if (key != null && !dictionary.ContainsKey(key)) + { + dictionary[key] = asset; + } + } + }); + + // Report progress if callback provided + while (!handle.IsDone) + { + onProgress?.Invoke(handle.PercentComplete); + await Task.Yield(); + } + + // Final progress update + onProgress?.Invoke(1f); + + // Check if load was successful + if (handle.Status != AsyncOperationStatus.Succeeded) + { + Logging.Error($"[AddressablesUtility] Failed to load assets with label '{label}': {handle.OperationException?.Message}"); + return (dictionary, handle); + } + + Logging.Debug($"[AddressablesUtility] Loaded {dictionary.Count} assets with label '{label}'"); + + return (dictionary, handle); + } + + /// + /// Load a single asset by address + /// + /// Type of asset to load + /// Addressable address/key + /// Loaded asset and operation handle for cleanup + public static async Task<(TAsset asset, AsyncOperationHandle handle)> LoadAssetAsync(string address) + { + AsyncOperationHandle handle = Addressables.LoadAssetAsync(address); + + await handle.Task; + + if (handle.Status != AsyncOperationStatus.Succeeded) + { + Logging.Warning($"[AddressablesUtility] Failed to load asset '{address}': {handle.OperationException?.Message}"); + return (default(TAsset), handle); + } + + return (handle.Result, handle); + } + + /// + /// Safely release an Addressables handle + /// + public static void ReleaseHandle(AsyncOperationHandle handle) + { + if (handle.IsValid()) + { + Addressables.Release(handle); + } + } + } +} + diff --git a/Assets/Scripts/Utils/AddressablesUtility.cs.meta b/Assets/Scripts/Utils/AddressablesUtility.cs.meta new file mode 100644 index 00000000..1dd6edfb --- /dev/null +++ b/Assets/Scripts/Utils/AddressablesUtility.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 81470e29c2d54df3967e373b71d18a0d +timeCreated: 1764164457 \ No newline at end of file diff --git a/Assets/Scripts/Utils/PhotoCaptureConfig.cs b/Assets/Scripts/Utils/PhotoCaptureConfig.cs new file mode 100644 index 00000000..ba97f10a --- /dev/null +++ b/Assets/Scripts/Utils/PhotoCaptureConfig.cs @@ -0,0 +1,71 @@ +using System; +using System.Collections.Generic; + +namespace Utils +{ + /// + /// Capture types for different photo contexts + /// + public enum CaptureType + { + StatueMinigame, + DivingMinigame + } + + /// + /// Configuration for a specific capture type + /// + [Serializable] + public class CaptureConfig + { + public string subFolder; + public string photoPrefix; + public string metadataPrefix; + public string indexKey; + + public CaptureConfig(string subFolder, string photoPrefix, string metadataPrefix, string indexKey) + { + this.subFolder = subFolder; + this.photoPrefix = photoPrefix; + this.metadataPrefix = metadataPrefix; + this.indexKey = indexKey; + } + } + + /// + /// Static configuration registry for all capture types + /// + public static class PhotoCaptureConfigs + { + private static readonly Dictionary Configs = new Dictionary + { + [CaptureType.StatueMinigame] = new CaptureConfig( + subFolder: "StatueMinigame", + photoPrefix: "Statue_", + metadataPrefix: "StatuePhoto_Meta_", + indexKey: "StatuePhoto_Index" + ), + + [CaptureType.DivingMinigame] = new CaptureConfig( + subFolder: "DivingMinigame", + photoPrefix: "Diving_", + metadataPrefix: "DivingPhoto_Meta_", + indexKey: "DivingPhoto_Index" + ) + }; + + /// + /// Get configuration for a specific capture type + /// + public static CaptureConfig GetConfig(CaptureType type) + { + if (Configs.TryGetValue(type, out CaptureConfig config)) + { + return config; + } + + throw new ArgumentException($"No configuration found for CaptureType: {type}"); + } + } +} + diff --git a/Assets/Scripts/Utils/PhotoCaptureConfig.cs.meta b/Assets/Scripts/Utils/PhotoCaptureConfig.cs.meta new file mode 100644 index 00000000..f518c2e5 --- /dev/null +++ b/Assets/Scripts/Utils/PhotoCaptureConfig.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 96dbe33216574c7f95d9a829f7768e69 +timeCreated: 1764159658 \ No newline at end of file diff --git a/Assets/Scripts/Utils/PhotoManager.cs b/Assets/Scripts/Utils/PhotoManager.cs new file mode 100644 index 00000000..a5fea87b --- /dev/null +++ b/Assets/Scripts/Utils/PhotoManager.cs @@ -0,0 +1,635 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using Core; +using UnityEngine; + +namespace Utils +{ + /// + /// Generalized photo capture, storage, and retrieval manager. + /// Supports multiple capture types (minigames, screenshots, etc.) with type-based configuration. + /// + public static class PhotoManager + { + private const string RootCapturesFolder = "Captures"; + + /// + /// Photo metadata stored in PlayerPrefs + /// + [Serializable] + public class PhotoMetadata + { + public string photoId; + public CaptureType captureType; + public string timestamp; + public int genericMetadata; // Can be decoration count, score, collectibles, etc. + public long fileSizeBytes; + } + + #region Plug-and-Play Coroutine + + /// + /// Capture and save photo in one coroutine call. Handles UI hiding, capture, save, and restoration. + /// + /// Type of capture (determines folder/prefix) + /// RectTransform defining the capture region + /// Optional UI elements to hide during capture + /// Callback with photoId on success + /// Callback with error message on failure + /// Generic metadata (decoration count, score, etc.) + /// Camera for coordinate conversion (null = Camera.main) + /// If true, clamps capture area to visible screen + public static IEnumerator CaptureAndSaveCoroutine( + CaptureType captureType, + RectTransform captureArea, + GameObject[] uiToHide = null, + Action onSuccess = null, + Action onFailure = null, + int metadata = 0, + Camera mainCamera = null, + bool clampToScreenBounds = true) + { + if (captureArea == null) + { + string error = "[PhotoManager] CaptureArea RectTransform is null!"; + Logging.Error(error); + onFailure?.Invoke(error); + yield break; + } + + // Hide UI elements + if (uiToHide != null) + { + foreach (var obj in uiToHide) + { + if (obj != null) obj.SetActive(false); + } + } + + // Wait for UI to hide + yield return new WaitForEndOfFrame(); + + // Capture photo + bool captureComplete = false; + Texture2D capturedPhoto = null; + + CaptureAreaPhoto(captureArea, (texture) => + { + capturedPhoto = texture; + captureComplete = true; + }, mainCamera, clampToScreenBounds); + + // Wait for capture to complete + yield return new WaitUntil(() => captureComplete); + + // Restore UI elements + if (uiToHide != null) + { + foreach (var obj in uiToHide) + { + if (obj != null) obj.SetActive(true); + } + } + + // Save photo + if (capturedPhoto != null) + { + string photoId = SavePhoto(captureType, capturedPhoto, metadata); + + if (!string.IsNullOrEmpty(photoId)) + { + Logging.Debug($"[PhotoManager] Photo saved successfully: {photoId}"); + onSuccess?.Invoke(photoId); + } + else + { + string error = "[PhotoManager] Failed to save photo!"; + Logging.Error(error); + onFailure?.Invoke(error); + } + } + else + { + string error = "[PhotoManager] Photo capture returned null!"; + Logging.Error(error); + onFailure?.Invoke(error); + } + } + + #endregion + + #region Capture + + /// + /// Capture a specific area of the screen using Screenshot Helper + /// + public static void CaptureAreaPhoto( + RectTransform captureArea, + Action onComplete, + Camera mainCamera = null, + bool clampToScreenBounds = true) + { + if (captureArea == null) + { + Logging.Error("[PhotoManager] CaptureArea RectTransform is null!"); + onComplete?.Invoke(null); + return; + } + + if (mainCamera == null) mainCamera = Camera.main; + + // Use ScreenSpaceUtility to convert RectTransform to screen rect + Rect screenRect = ScreenSpaceUtility.RectTransformToScreenRect( + captureArea, + mainCamera, + clampToScreenBounds, + returnCenterPosition: true + ); + + Logging.Debug($"[PhotoManager] Capturing area: pos={screenRect.position}, size={screenRect.size}"); + + // Use Screenshot Helper's Capture method + ScreenshotHelper.Instance.Capture( + screenRect.position, + screenRect.size, + (texture) => + { + if (texture != null) + { + Logging.Debug($"[PhotoManager] Photo captured: {texture.width}x{texture.height}"); + onComplete?.Invoke(texture); + } + else + { + Logging.Error("[PhotoManager] Screenshot Helper returned null texture!"); + onComplete?.Invoke(null); + } + } + ); + } + + #endregion + + #region Save/Load + + /// + /// Save photo to persistent storage with metadata + /// + public static string SavePhoto(CaptureType captureType, Texture2D photo, int metadata = 0) + { + if (photo == null) + { + Logging.Error("[PhotoManager] Cannot save null photo"); + return null; + } + + try + { + CaptureConfig config = PhotoCaptureConfigs.GetConfig(captureType); + + // Generate unique photo ID + string photoId = $"{config.photoPrefix}{DateTime.Now.Ticks}"; + + // Get capture directory for this type + string captureDirectory = GetCaptureDirectory(captureType); + + // Ensure directory exists + if (!Directory.Exists(captureDirectory)) + { + Directory.CreateDirectory(captureDirectory); + } + + // Save texture + string filePath = Path.Combine(captureDirectory, $"{photoId}.png"); + byte[] pngData = photo.EncodeToPNG(); + File.WriteAllBytes(filePath, pngData); + + // Calculate file size + FileInfo fileInfo = new FileInfo(filePath); + long fileSize = fileInfo.Exists ? fileInfo.Length : 0; + + // Save metadata + PhotoMetadata photoMetadata = new PhotoMetadata + { + photoId = photoId, + captureType = captureType, + timestamp = DateTime.Now.ToString("o"), + genericMetadata = metadata, + fileSizeBytes = fileSize + }; + + SaveMetadata(captureType, photoMetadata); + AddToPhotoIndex(captureType, photoId); + + Logging.Debug($"[PhotoManager] Photo saved: {filePath} ({fileSize} bytes)"); + return photoId; + } + catch (Exception e) + { + Logging.Error($"[PhotoManager] Failed to save photo: {e.Message}"); + return null; + } + } + + /// + /// Load photo texture from storage + /// + public static Texture2D LoadPhoto(CaptureType captureType, string photoId) + { + if (string.IsNullOrEmpty(photoId)) + { + Logging.Warning("[PhotoManager] PhotoId is null or empty"); + return null; + } + + try + { + string filePath = GetPhotoFilePath(captureType, photoId); + + if (!File.Exists(filePath)) + { + Logging.Warning($"[PhotoManager] Photo not found: {filePath}"); + return null; + } + + byte[] fileData = File.ReadAllBytes(filePath); + Texture2D texture = new Texture2D(2, 2, TextureFormat.RGBA32, false); + + if (texture.LoadImage(fileData)) + { + Logging.Debug($"[PhotoManager] Photo loaded: {photoId} ({texture.width}x{texture.height})"); + return texture; + } + else + { + Logging.Error($"[PhotoManager] Failed to decode image: {photoId}"); + UnityEngine.Object.Destroy(texture); + return null; + } + } + catch (Exception e) + { + Logging.Error($"[PhotoManager] Failed to load photo {photoId}: {e.Message}"); + return null; + } + } + + /// + /// Load multiple photos (most recent first) + /// + public static List LoadPhotos(CaptureType captureType, int count) + { + List photoIds = GetPhotoIds(captureType, count); + List photos = new List(); + + foreach (string photoId in photoIds) + { + Texture2D photo = LoadPhoto(captureType, photoId); + if (photo != null) + { + photos.Add(photo); + } + } + + return photos; + } + + /// + /// Load all photos for a capture type + /// + public static List LoadAllPhotos(CaptureType captureType) + { + return LoadPhotos(captureType, -1); + } + + /// + /// Delete photo and its metadata + /// + public static bool DeletePhoto(CaptureType captureType, string photoId) + { + if (string.IsNullOrEmpty(photoId)) return false; + + try + { + string filePath = GetPhotoFilePath(captureType, photoId); + + if (File.Exists(filePath)) + { + File.Delete(filePath); + } + + DeleteMetadata(captureType, photoId); + RemoveFromPhotoIndex(captureType, photoId); + + Logging.Debug($"[PhotoManager] Photo deleted: {photoId}"); + return true; + } + catch (Exception e) + { + Logging.Error($"[PhotoManager] Failed to delete photo {photoId}: {e.Message}"); + return false; + } + } + + #endregion + + #region Retrieval & Queries + + /// + /// Get photo IDs for a capture type (most recent first) + /// + /// Type of capture + /// Number of IDs to return (-1 = all) + public static List GetPhotoIds(CaptureType captureType, int count = -1) + { + List allIds = GetAllPhotoIds(captureType); + + if (count < 0 || count >= allIds.Count) + { + return allIds; + } + + return allIds.GetRange(0, count); + } + + /// + /// Get all photo IDs sorted by timestamp (newest first) + /// + public static List GetAllPhotoIds(CaptureType captureType) + { + CaptureConfig config = PhotoCaptureConfigs.GetConfig(captureType); + string indexJson = PlayerPrefs.GetString(config.indexKey, "[]"); + List photoIds = JsonUtility.FromJson(WrapJsonArray(indexJson))?.ids ?? new List(); + + // Sort by timestamp descending (newest first) + photoIds.Sort((a, b) => + { + PhotoMetadata metaA = LoadMetadata(captureType, a); + PhotoMetadata metaB = LoadMetadata(captureType, b); + + DateTime dateA = DateTime.Parse(metaA?.timestamp ?? DateTime.MinValue.ToString("o")); + DateTime dateB = DateTime.Parse(metaB?.timestamp ?? DateTime.MinValue.ToString("o")); + + return dateB.CompareTo(dateA); + }); + + return photoIds; + } + + /// + /// Get paginated photo IDs for optimized gallery loading + /// + public static List GetPhotoIdsPage(CaptureType captureType, int page, int pageSize) + { + List allIds = GetAllPhotoIds(captureType); + int startIndex = page * pageSize; + + if (startIndex >= allIds.Count) return new List(); + + int count = Mathf.Min(pageSize, allIds.Count - startIndex); + return allIds.GetRange(startIndex, count); + } + + /// + /// Get total number of saved photos + /// + public static int GetPhotoCount(CaptureType captureType) + { + return GetAllPhotoIds(captureType).Count; + } + + /// + /// Get latest photo ID (most recent) + /// + public static string GetLatestPhotoId(CaptureType captureType) + { + List allIds = GetAllPhotoIds(captureType); + return allIds.Count > 0 ? allIds[0] : null; + } + + /// + /// Load photo metadata + /// + public static PhotoMetadata GetPhotoMetadata(CaptureType captureType, string photoId) + { + return LoadMetadata(captureType, photoId); + } + + #endregion + + #region Utility Methods + + /// + /// Create thumbnail from full-size photo (for gallery preview) + /// + public static Texture2D CreateThumbnail(Texture2D fullSizePhoto, int maxSize = 256) + { + if (fullSizePhoto == null) return null; + + int width = fullSizePhoto.width; + int height = fullSizePhoto.height; + + // Calculate thumbnail size maintaining aspect ratio + float scale = Mathf.Min((float)maxSize / width, (float)maxSize / height); + int thumbWidth = Mathf.RoundToInt(width * scale); + int thumbHeight = Mathf.RoundToInt(height * scale); + + // Create thumbnail using bilinear filtering + RenderTexture rt = RenderTexture.GetTemporary(thumbWidth, thumbHeight, 0, RenderTextureFormat.ARGB32); + RenderTexture.active = rt; + + Graphics.Blit(fullSizePhoto, rt); + + Texture2D thumbnail = new Texture2D(thumbWidth, thumbHeight, TextureFormat.RGB24, false); + thumbnail.ReadPixels(new Rect(0, 0, thumbWidth, thumbHeight), 0, 0); + thumbnail.Apply(); + + RenderTexture.active = null; + RenderTexture.ReleaseTemporary(rt); + + return thumbnail; + } + + /// + /// Get capture directory for a specific type + /// + public static string GetCaptureDirectory(CaptureType captureType) + { + CaptureConfig config = PhotoCaptureConfigs.GetConfig(captureType); + return Path.Combine(Application.persistentDataPath, RootCapturesFolder, config.subFolder); + } + + #endregion + + #region Decoration Metadata + + /// + /// Save decoration metadata for a photo + /// Saves both with photo ID and as "latest" for easy reference + /// + public static void SaveDecorationMetadata(CaptureType captureType, string photoId, T metadata) where T : class + { + try + { + string directory = GetCaptureDirectory(captureType); + + // Ensure directory exists + if (!Directory.Exists(directory)) + { + Directory.CreateDirectory(directory); + } + + string json = JsonUtility.ToJson(metadata, true); + + // Save with photo ID (for loading specific photo's decorations) + string specificPath = Path.Combine(directory, $"{photoId}_decorations.json"); + File.WriteAllText(specificPath, json); + + // Also save as "latest" for easy reference + CaptureConfig config = PhotoCaptureConfigs.GetConfig(captureType); + string latestPath = Path.Combine(directory, $"{config.subFolder}_latest.json"); + File.WriteAllText(latestPath, json); + + Logging.Debug($"[PhotoManager] Decoration metadata saved: {specificPath}"); + } + catch (Exception e) + { + Logging.Error($"[PhotoManager] Failed to save decoration metadata: {e.Message}"); + } + } + + /// + /// Load decoration metadata for a specific photo + /// + public static T LoadDecorationMetadata(CaptureType captureType, string photoId) where T : class + { + try + { + string directory = GetCaptureDirectory(captureType); + string filePath = Path.Combine(directory, $"{photoId}_decorations.json"); + + if (!File.Exists(filePath)) + { + Logging.Warning($"[PhotoManager] Decoration metadata not found: {filePath}"); + return null; + } + + string json = File.ReadAllText(filePath); + T metadata = JsonUtility.FromJson(json); + + Logging.Debug($"[PhotoManager] Decoration metadata loaded: {filePath}"); + return metadata; + } + catch (Exception e) + { + Logging.Error($"[PhotoManager] Failed to load decoration metadata: {e.Message}"); + return null; + } + } + + /// + /// Load the latest decoration metadata (most recent capture) + /// + public static T LoadLatestDecorationMetadata(CaptureType captureType) where T : class + { + try + { + CaptureConfig config = PhotoCaptureConfigs.GetConfig(captureType); + string directory = GetCaptureDirectory(captureType); + string filePath = Path.Combine(directory, $"{config.subFolder}_latest.json"); + + if (!File.Exists(filePath)) + { + Logging.Warning($"[PhotoManager] Latest decoration metadata not found: {filePath}"); + return null; + } + + string json = File.ReadAllText(filePath); + T metadata = JsonUtility.FromJson(json); + + Logging.Debug($"[PhotoManager] Latest decoration metadata loaded: {filePath}"); + return metadata; + } + catch (Exception e) + { + Logging.Error($"[PhotoManager] Failed to load latest decoration metadata: {e.Message}"); + return null; + } + } + + #endregion + + #region Internal Helpers + + private static string GetPhotoFilePath(CaptureType captureType, string photoId) + { + return Path.Combine(GetCaptureDirectory(captureType), $"{photoId}.png"); + } + + private static void SaveMetadata(CaptureType captureType, PhotoMetadata metadata) + { + CaptureConfig config = PhotoCaptureConfigs.GetConfig(captureType); + string json = JsonUtility.ToJson(metadata); + PlayerPrefs.SetString(config.metadataPrefix + metadata.photoId, json); + PlayerPrefs.Save(); + } + + private static PhotoMetadata LoadMetadata(CaptureType captureType, string photoId) + { + CaptureConfig config = PhotoCaptureConfigs.GetConfig(captureType); + string json = PlayerPrefs.GetString(config.metadataPrefix + photoId, null); + return string.IsNullOrEmpty(json) ? null : JsonUtility.FromJson(json); + } + + private static void DeleteMetadata(CaptureType captureType, string photoId) + { + CaptureConfig config = PhotoCaptureConfigs.GetConfig(captureType); + PlayerPrefs.DeleteKey(config.metadataPrefix + photoId); + PlayerPrefs.Save(); + } + + private static void AddToPhotoIndex(CaptureType captureType, string photoId) + { + List photoIds = GetAllPhotoIds(captureType); + if (!photoIds.Contains(photoId)) + { + photoIds.Add(photoId); + SavePhotoIndex(captureType, photoIds); + } + } + + private static void RemoveFromPhotoIndex(CaptureType captureType, string photoId) + { + List photoIds = GetAllPhotoIds(captureType); + if (photoIds.Remove(photoId)) + { + SavePhotoIndex(captureType, photoIds); + } + } + + private static void SavePhotoIndex(CaptureType captureType, List photoIds) + { + CaptureConfig config = PhotoCaptureConfigs.GetConfig(captureType); + string json = JsonUtility.ToJson(new PhotoIdList { ids = photoIds }); + PlayerPrefs.SetString(config.indexKey, json); + PlayerPrefs.Save(); + } + + private static string WrapJsonArray(string json) + { + if (json.StartsWith("[")) return "{\"ids\":" + json + "}"; + return json; + } + + [Serializable] + private class PhotoIdList + { + public List ids = new List(); + } + + #endregion + } +} + diff --git a/Assets/Scripts/Utils/PhotoManager.cs.meta b/Assets/Scripts/Utils/PhotoManager.cs.meta new file mode 100644 index 00000000..59ac866d --- /dev/null +++ b/Assets/Scripts/Utils/PhotoManager.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: c558069d1a8e46febc1c3716e9b76490 +timeCreated: 1764159714 \ No newline at end of file diff --git a/Assets/Settings/Developer/DebugSettings.asset b/Assets/Settings/Developer/DebugSettings.asset index 0779a2cf..0fa6f0dd 100644 --- a/Assets/Settings/Developer/DebugSettings.asset +++ b/Assets/Settings/Developer/DebugSettings.asset @@ -16,7 +16,7 @@ MonoBehaviour: pauseTimeOnPauseGame: 0 useSaveLoadSystem: 1 autoClearSaves: 0 - dontSaveOnQuit: 1 + dontSaveOnQuit: 0 bootstrapLogVerbosity: 0 settingsLogVerbosity: 0 gameManagerLogVerbosity: 0 diff --git a/docs/statue_photo_gallery_setup.md b/docs/statue_photo_gallery_setup.md new file mode 100644 index 00000000..95791837 --- /dev/null +++ b/docs/statue_photo_gallery_setup.md @@ -0,0 +1,164 @@ +# Photo Gallery Scene Setup Guide + +## Current Scene Status +✅ MainCanvas exists +✅ StatueDecorationController exists +✅ PhotoCaptureTestController exists (testing only) + +--- + +## Required Scene Setup + +### 1. **Create UIPage Wrappers** + +#### PlayAreaPage +1. Create empty GameObject under `MainCanvas` → name: `PlayAreaPage` +2. Add component: `PlayAreaPage.cs` +3. Move **existing decoration UI** as children under PlayAreaPage: + - Decoration menu + - Take photo button + - Any other play area UI + +#### PhotoGalleryPage +1. Create empty GameObject under `MainCanvas` → name: `PhotoGalleryPage` +2. Add component: `PhotoGalleryPage.cs` +3. Set initially inactive (will be shown when opened) + +--- + +### 2. **Setup Photo Gallery UI** (under PhotoGalleryPage) + +``` +PhotoGalleryPage +├── GalleryController (GameObject) +│ └── StatuePhotoGalleryController.cs +├── GridContainer (GameObject with GridLayoutGroup) +├── EnlargedContainer (GameObject - top layer) +├── Backdrop (UI Image - dark/black, alpha 0.8) +├── PageStatusText (UI Text - shows "Page X/Y") +└── Navigation + ├── PreviousPageButton (UI Button - "< Prev") + └── NextPageButton (UI Button - "Next >") +``` + +**Components to assign on StatuePhotoGalleryController:** +- `gridContainer` → GridContainer transform +- `gridItemPrefab` → Create prefab with PhotoGridItem.cs + UI Image +- `enlargedContainer` → EnlargedContainer transform +- `backdrop` → Backdrop GameObject +- `enlargedPreviewPrefab` → Same as gridItemPrefab (or leave null to clone item) +- `previousPageButton` → Previous page button +- `nextPageButton` → Next page button +- `pageStatusText` → Optional status text showing page info + +**GridLayoutGroup Settings (on GridContainer):** +- Cell Size: e.g., 200x200 +- Spacing: e.g., 10x10 +- Constraint: Fixed Column Count (3-4 columns recommended) +- Child Alignment: Upper Left (or as preferred) + +--- + +### 3. **Create PhotoGridItem Prefab** + +1. Create UI → Image in scene +2. Add `PhotoGridItem.cs` component +3. Assign `thumbnailImage` → the Image component itself +4. Optional: Add loading indicator child (simple spinner/text) +5. Drag to Prefabs folder → name: `PhotoGridItem` +6. Delete from scene + +--- + +### 4. **Update StatueDecorationController** + +**Assign in Inspector:** +- `playAreaPage` → PlayAreaPage GameObject +- `photoGalleryPage` → PhotoGalleryPage GameObject +- `openGalleryButton` → Create button in PlayAreaPage UI, assign here +- `photoArea` → RectTransform defining capture area (statue + decorations) +- `takePhotoButton` → Existing button (uncomment line 97 in code when ready) + +--- + +### 5. **Create Gallery Open Button** + +1. Add Button in PlayAreaPage UI → name: "OpenGalleryButton" +2. Button Text: "📷 Gallery" or "View Photos" +3. Assign to StatueDecorationController's `openGalleryButton` field + +--- + +### 6. **Page Navigation** (PhotoGalleryPage) + +1. Create two buttons for Previous/Next page +2. Buttons will be auto-wired by StatuePhotoGalleryController +3. Controller auto-disables buttons when at first/last page +4. Grid clears and reloads on each page change + +--- + +## Key Features + +### ✅ Button-Based Pagination +- Previous/Next buttons navigate pages +- Grid **clears completely** and shows only current page +- Buttons auto-disable at boundaries (first/last page) +- Status text shows "Page X/Y (total photos)" + +### ✅ Grid Clearing +- Grid is cleared on initialization +- Grid clears when switching pages +- Prevents leftover items from scene setup + +### ✅ Enlarge System +- Click photo → spawns preview clone, animates to center +- Click again → shrinks back, destroys preview +- Original grid items never move +- Backdrop blocks grid interaction + +--- + +## Testing Checklist + +### Photo Capture (Already Working) +- ✅ PhotoCaptureTestController captures and saves photos +- ✅ Photos saved to `Application.persistentDataPath/StatuePhotos/` + +### Gallery Integration (New Setup) +1. Play scene +2. Click "Open Gallery" button → Gallery page shows +3. First page of photos loads in grid +4. Click "Next" → Grid clears, next page loads +5. Click "Previous" → Grid clears, previous page loads +6. Click photo → Enlarges to center with backdrop +7. Click enlarged photo → Shrinks back to grid +8. Back button → Returns to play area + +--- + +## Quick Setup Time +- **PlayAreaPage wrapper:** 2 minutes +- **PhotoGalleryPage structure:** 5 minutes +- **PhotoGridItem prefab:** 3 minutes +- **Wire references:** 5 minutes + +**Total: ~15 minutes** + +--- + +## Configuration Variables +Set these on `StatuePhotoGalleryController`: +- `itemsPerPage`: 20 (photos per page) +- `thumbnailSize`: 256 (pixels) +- `maxCachedThumbnails`: 50 (LRU cache) +- `enlargedScale`: 2.5 (zoom amount) +- `animationDuration`: 0.3s + +--- + +## Cleanup (Optional) +Once gallery works, you can: +- Remove `PhotoCaptureTestController` GameObject +- Keep for quick testing if preferred +