diff --git a/Assets/Scenes/MiniGames/StatueDecoration.unity b/Assets/Scenes/MiniGames/StatueDecoration.unity
index d0a924dd..5b430854 100644
--- a/Assets/Scenes/MiniGames/StatueDecoration.unity
+++ b/Assets/Scenes/MiniGames/StatueDecoration.unity
@@ -318,6 +318,42 @@ CanvasRenderer:
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 37633365}
m_CullTransparentMesh: 1
+--- !u!1 &61401515
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 61401516}
+ m_Layer: 5
+ m_Name: PhotoGallery
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!224 &61401516
+RectTransform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 61401515}
+ 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: 1612917661}
+ 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!1 &65358844
GameObject:
m_ObjectHideFlags: 0
@@ -1274,6 +1310,7 @@ RectTransform:
m_ConstrainProportionsScale: 0
m_Children:
- {fileID: 1443594949}
+ - {fileID: 61401516}
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0}
@@ -1508,6 +1545,141 @@ Transform:
m_Children: []
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!1 &1612917660
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 1612917661}
+ - component: {fileID: 1612917665}
+ - component: {fileID: 1612917664}
+ - component: {fileID: 1612917663}
+ - component: {fileID: 1612917662}
+ m_Layer: 5
+ m_Name: Button
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!224 &1612917661
+RectTransform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1612917660}
+ 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: 1, y: 1}
+ m_AnchorMax: {x: 1, y: 1}
+ m_AnchoredPosition: {x: -75, y: -75}
+ m_SizeDelta: {x: 172.83276, y: 0}
+ m_Pivot: {x: 1, y: 1}
+--- !u!114 &1612917662
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1612917660}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 86710e43de46f6f4bac7c8e50813a599, type: 3}
+ m_Name:
+ m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.AspectRatioFitter
+ m_AspectMode: 1
+ m_AspectRatio: 0.964
+--- !u!114 &1612917663
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1612917660}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3}
+ m_Name:
+ m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.Button
+ m_Navigation:
+ m_Mode: 3
+ m_WrapAround: 0
+ m_SelectOnUp: {fileID: 0}
+ m_SelectOnDown: {fileID: 0}
+ m_SelectOnLeft: {fileID: 0}
+ m_SelectOnRight: {fileID: 0}
+ m_Transition: 1
+ m_Colors:
+ m_NormalColor: {r: 1, g: 1, b: 1, a: 1}
+ m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1}
+ m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1}
+ m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1}
+ m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608}
+ m_ColorMultiplier: 1
+ m_FadeDuration: 0.1
+ m_SpriteState:
+ m_HighlightedSprite: {fileID: 0}
+ m_PressedSprite: {fileID: 0}
+ m_SelectedSprite: {fileID: 0}
+ m_DisabledSprite: {fileID: 0}
+ m_AnimationTriggers:
+ m_NormalTrigger: Normal
+ m_HighlightedTrigger: Highlighted
+ m_PressedTrigger: Pressed
+ m_SelectedTrigger: Selected
+ m_DisabledTrigger: Disabled
+ m_Interactable: 1
+ m_TargetGraphic: {fileID: 1612917664}
+ m_OnClick:
+ m_PersistentCalls:
+ m_Calls: []
+--- !u!114 &1612917664
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1612917660}
+ 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: 1917382497509789248, guid: 8d568408ea5f183458e17af541af2d8e, type: 3}
+ 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 &1612917665
+CanvasRenderer:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1612917660}
+ m_CullTransparentMesh: 1
--- !u!1 &1647993457
GameObject:
m_ObjectHideFlags: 0
@@ -1543,7 +1715,7 @@ MonoBehaviour:
takePhotoButton: {fileID: 37633367}
statue: {fileID: 1078270173}
uiElementsToHideForPhoto: []
- photoArea: {fileID: 0}
+ photoArea: {fileID: 65358845}
photoSaveKey: MrCementStatuePhoto
--- !u!4 &1647993459
Transform:
diff --git a/Assets/Scripts/Minigames/StatueDressup/Controllers/StatueDecorationController.cs b/Assets/Scripts/Minigames/StatueDressup/Controllers/StatueDecorationController.cs
index fd624a9a..7c4fba71 100644
--- a/Assets/Scripts/Minigames/StatueDressup/Controllers/StatueDecorationController.cs
+++ b/Assets/Scripts/Minigames/StatueDressup/Controllers/StatueDecorationController.cs
@@ -57,7 +57,8 @@ namespace Minigames.StatueDressup.Controllers
// Setup photo button
if (takePhotoButton != null)
{
- takePhotoButton.onClick.AddListener(OnTakePhoto);
+ // TODO: Remove comment when ready
+ // takePhotoButton.onClick.AddListener(OnTakePhoto);
}
// Subscribe to menu controller for tracking placed items
diff --git a/Assets/Scripts/Minigames/StatueDressup/DragDrop/DecorationDraggableInstance.cs b/Assets/Scripts/Minigames/StatueDressup/DragDrop/DecorationDraggableInstance.cs
index e4f2f0fb..77481188 100644
--- a/Assets/Scripts/Minigames/StatueDressup/DragDrop/DecorationDraggableInstance.cs
+++ b/Assets/Scripts/Minigames/StatueDressup/DragDrop/DecorationDraggableInstance.cs
@@ -5,6 +5,7 @@ using Minigames.StatueDressup.Utils;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
+using Utils;
namespace Minigames.StatueDressup.DragDrop
{
diff --git a/Assets/Scripts/Minigames/StatueDressup/Utils/StatuePhotoManager.cs b/Assets/Scripts/Minigames/StatueDressup/Utils/StatuePhotoManager.cs
index 74de576d..ca07c930 100644
--- a/Assets/Scripts/Minigames/StatueDressup/Utils/StatuePhotoManager.cs
+++ b/Assets/Scripts/Minigames/StatueDressup/Utils/StatuePhotoManager.cs
@@ -1,10 +1,10 @@
using System;
using System.Collections.Generic;
using System.IO;
-using System.Linq;
using Core;
using UnityEngine;
using SDev;
+using ScreenUtils = Utils.ScreenSpaceUtility;
namespace Minigames.StatueDressup.Utils
{
@@ -15,15 +15,15 @@ namespace Minigames.StatueDressup.Utils
///
public static class StatuePhotoManager
{
- private const string PHOTO_FOLDER = "StatuePhotos";
- private const string PHOTO_PREFIX = "MrCementStatue_";
- private const string METADATA_KEY_PREFIX = "StatuePhoto_Meta_";
- private const string PHOTO_INDEX_KEY = "StatuePhoto_Index";
+ private const string PhotoFolder = "StatuePhotos";
+ private const string PhotoPrefix = "MrCementStatue_";
+ private const string MetadataKeyPrefix = "StatuePhoto_Meta_";
+ private const string PhotoIndexKey = "StatuePhoto_Index";
///
/// Photo metadata stored in PlayerPrefs
///
- [System.Serializable]
+ [Serializable]
public class PhotoMetadata
{
public string photoId;
@@ -40,7 +40,12 @@ namespace Minigames.StatueDressup.Utils
/// RectTransform defining the capture region
/// Callback with captured Texture2D
/// Camera used for coordinate conversion (null = Camera.main)
- public static void CaptureAreaPhoto(RectTransform captureArea, Action onComplete, Camera mainCamera = null)
+ /// If true, clamps capture area to visible screen bounds
+ public static void CaptureAreaPhoto(
+ RectTransform captureArea,
+ Action onComplete,
+ Camera mainCamera = null,
+ bool clampToScreenBounds = true)
{
if (captureArea == null)
{
@@ -51,8 +56,14 @@ namespace Minigames.StatueDressup.Utils
if (mainCamera == null) mainCamera = Camera.main;
- // Get screen rect from RectTransform
- Rect screenRect = GetScreenRectFromRectTransform(captureArea, mainCamera);
+ // Use ScreenSpaceUtility to convert RectTransform to screen rect
+ // returnCenterPosition = true because ScreenshotHelper expects center position
+ Rect screenRect = ScreenUtils.RectTransformToScreenRect(
+ captureArea,
+ mainCamera,
+ clampToScreenBounds,
+ returnCenterPosition: true
+ );
Logging.Debug($"[StatuePhotoManager] Capturing area: pos={screenRect.position}, size={screenRect.size}");
@@ -60,7 +71,7 @@ namespace Minigames.StatueDressup.Utils
ScreenshotHelper.Instance.Capture(
screenRect.position,
screenRect.size,
- (Texture2D texture) =>
+ (texture) =>
{
if (texture != null)
{
@@ -75,24 +86,7 @@ namespace Minigames.StatueDressup.Utils
}
);
}
-
- ///
- /// Convert RectTransform world corners to screen space rect
- ///
- private static Rect GetScreenRectFromRectTransform(RectTransform rectTransform, Camera camera)
- {
- Vector3[] corners = new Vector3[4];
- rectTransform.GetWorldCorners(corners);
-
- Vector2 min = RectTransformUtility.WorldToScreenPoint(camera, corners[0]);
- Vector2 max = RectTransformUtility.WorldToScreenPoint(camera, corners[2]);
-
- // Ensure positive dimensions
- float width = Mathf.Abs(max.x - min.x);
- float height = Mathf.Abs(max.y - min.y);
-
- return new Rect(min.x, min.y, width, height);
- }
+
#endregion
@@ -115,13 +109,13 @@ namespace Minigames.StatueDressup.Utils
try
{
// Generate unique photo ID
- string photoId = $"{PHOTO_PREFIX}{DateTime.Now.Ticks}";
+ string photoId = $"{PhotoPrefix}{DateTime.Now.Ticks}";
// Save texture using FileSaveUtil
string savedPath = FileSaveUtil.Instance.SaveTextureAsPNG(
photo,
FileSaveUtil.AppPath.PersistentDataPath,
- PHOTO_FOLDER,
+ PhotoFolder,
photoId
);
@@ -232,7 +226,7 @@ namespace Minigames.StatueDressup.Utils
///
public static List GetAllPhotoIds()
{
- string indexJson = PlayerPrefs.GetString(PHOTO_INDEX_KEY, "[]");
+ string indexJson = PlayerPrefs.GetString(PhotoIndexKey, "[]");
List photoIds = JsonUtility.FromJson(WrapJsonArray(indexJson))?.ids ?? new List();
// Sort by timestamp descending (newest first)
@@ -328,7 +322,7 @@ namespace Minigames.StatueDressup.Utils
public static string GetPhotoDirectory()
{
- return Path.Combine(Application.persistentDataPath, PHOTO_FOLDER);
+ return Path.Combine(Application.persistentDataPath, PhotoFolder);
}
private static string GetPhotoFilePath(string photoId)
@@ -339,19 +333,19 @@ namespace Minigames.StatueDressup.Utils
private static void SaveMetadata(PhotoMetadata metadata)
{
string json = JsonUtility.ToJson(metadata);
- PlayerPrefs.SetString(METADATA_KEY_PREFIX + metadata.photoId, json);
+ PlayerPrefs.SetString(MetadataKeyPrefix + metadata.photoId, json);
PlayerPrefs.Save();
}
private static PhotoMetadata LoadMetadata(string photoId)
{
- string json = PlayerPrefs.GetString(METADATA_KEY_PREFIX + photoId, null);
+ string json = PlayerPrefs.GetString(MetadataKeyPrefix + photoId, null);
return string.IsNullOrEmpty(json) ? null : JsonUtility.FromJson(json);
}
private static void DeleteMetadata(string photoId)
{
- PlayerPrefs.DeleteKey(METADATA_KEY_PREFIX + photoId);
+ PlayerPrefs.DeleteKey(MetadataKeyPrefix + photoId);
PlayerPrefs.Save();
}
@@ -377,7 +371,7 @@ namespace Minigames.StatueDressup.Utils
private static void SavePhotoIndex(List photoIds)
{
string json = JsonUtility.ToJson(new PhotoIdList { ids = photoIds });
- PlayerPrefs.SetString(PHOTO_INDEX_KEY, json);
+ PlayerPrefs.SetString(PhotoIndexKey, json);
PlayerPrefs.Save();
}
@@ -387,7 +381,7 @@ namespace Minigames.StatueDressup.Utils
return json;
}
- [System.Serializable]
+ [Serializable]
private class PhotoIdList
{
public List ids = new List();
diff --git a/Assets/Scripts/Utils/ScreenSpaceUtility.cs b/Assets/Scripts/Utils/ScreenSpaceUtility.cs
new file mode 100644
index 00000000..b2061f1f
--- /dev/null
+++ b/Assets/Scripts/Utils/ScreenSpaceUtility.cs
@@ -0,0 +1,198 @@
+using Core;
+using UnityEngine;
+
+namespace Utils
+{
+ ///
+ /// Utility methods for screen space and UI coordinate conversions
+ ///
+ public static class ScreenSpaceUtility
+ {
+ ///
+ /// Convert RectTransform to screen space rect with center position.
+ /// Useful for screenshot capture or screen-based calculations.
+ ///
+ /// RectTransform to convert
+ /// Camera used for coordinate conversion (null = Camera.main)
+ /// If true, clamps the rect to visible screen area
+ /// If true, returns center position; if false, returns bottom-left position
+ /// Screen space rect (position is center or bottom-left based on returnCenterPosition)
+ public static Rect RectTransformToScreenRect(
+ RectTransform rectTransform,
+ Camera camera = null,
+ bool clampToScreenBounds = true,
+ bool returnCenterPosition = true)
+ {
+ if (rectTransform == null)
+ {
+ Logging.Error("[ScreenSpaceUtility] RectTransform is null!");
+ return new Rect(Screen.width / 2f, Screen.height / 2f, 0, 0);
+ }
+
+ if (camera == null) camera = Camera.main;
+
+ // Get world corners (0=bottom-left, 1=top-left, 2=top-right, 3=bottom-right)
+ Vector3[] corners = new Vector3[4];
+ rectTransform.GetWorldCorners(corners);
+
+ // Determine correct camera based on Canvas render mode
+ Camera canvasCamera = GetCanvasCamera(rectTransform, camera);
+
+ // Convert corners to screen space
+ Vector2 min = RectTransformUtility.WorldToScreenPoint(canvasCamera, corners[0]);
+ Vector2 max = RectTransformUtility.WorldToScreenPoint(canvasCamera, corners[2]);
+
+ Logging.Debug($"[ScreenSpaceUtility] Canvas mode: {rectTransform.GetComponentInParent