Implement MVP for the statue decoration minigame (#65)
MVP implemented with: - placing, removing etc. decorations - saving the state, displaying it on the map, restoring when game restarts - saving screenshots to folder on device Co-authored-by: Michal Pikulski <michal@foolhardyhorizons.com> Co-authored-by: Michal Pikulski <michal.a.pikulski@gmail.com> Reviewed-on: #65
This commit is contained in:
@@ -0,0 +1,214 @@
|
||||
using System.Collections.Generic;
|
||||
using Core;
|
||||
using Core.Lifecycle;
|
||||
using Minigames.StatueDressup.Data;
|
||||
using UnityEngine;
|
||||
using UnityEngine.ResourceManagement.AsyncOperations;
|
||||
using Utils;
|
||||
namespace Minigames.StatueDressup.Controllers
|
||||
{
|
||||
/// <summary>
|
||||
/// Singleton manager for statue dressup data loading and caching.
|
||||
/// Loads settings first, then decoration data via Addressables and provides shared access.
|
||||
/// Used by both minigame and town map to avoid duplicate loading.
|
||||
/// </summary>
|
||||
public class DecorationDataManager : ManagedBehaviour, IReadyNotifier
|
||||
{
|
||||
public static DecorationDataManager Instance { get; private set; }
|
||||
|
||||
// Static callback queue for when instance doesn't exist yet
|
||||
private static readonly List<System.Action> PendingCallbacks = new List<System.Action>();
|
||||
private AppleHills.Core.Settings.IStatueDressupSettings settings;
|
||||
private Dictionary<string, DecorationData> decorationDataDict;
|
||||
private AsyncOperationHandle<System.Collections.Generic.IList<DecorationData>> decorationDataHandle;
|
||||
private bool isLoaded;
|
||||
|
||||
/// <summary>
|
||||
/// Get the settings instance
|
||||
/// </summary>
|
||||
public AppleHills.Core.Settings.IStatueDressupSettings Settings => settings;
|
||||
|
||||
/// <summary>
|
||||
/// Check if data is loaded and ready (implements IReadyNotifier)
|
||||
/// </summary>
|
||||
public bool IsReady => isLoaded;
|
||||
|
||||
/// <summary>
|
||||
/// Event invoked when data is loaded and ready (implements IReadyNotifier)
|
||||
/// </summary>
|
||||
public event System.Action OnReady;
|
||||
|
||||
/// <summary>
|
||||
/// Static method to register callbacks that will execute when manager is ready.
|
||||
/// Can be called before instance exists - callbacks will be queued and executed when ready.
|
||||
/// Handles null instance gracefully by queuing callbacks until instance is created and ready.
|
||||
/// </summary>
|
||||
public static void WhenReady(System.Action callback)
|
||||
{
|
||||
if (callback == null) return;
|
||||
|
||||
// If instance exists and is ready, execute immediately
|
||||
if (Instance != null && Instance.IsReady)
|
||||
{
|
||||
callback.Invoke();
|
||||
return;
|
||||
}
|
||||
|
||||
// If instance exists but not ready, use instance method
|
||||
if (Instance != null)
|
||||
{
|
||||
(Instance as IReadyNotifier).WhenReady(callback);
|
||||
return;
|
||||
}
|
||||
|
||||
// Instance doesn't exist yet - queue callback
|
||||
PendingCallbacks.Add(callback);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Legacy property - use IsReady instead
|
||||
/// </summary>
|
||||
[System.Obsolete("Use IsReady instead")]
|
||||
public bool IsLoaded => isLoaded;
|
||||
|
||||
/// <summary>
|
||||
/// Get all decoration data as dictionary
|
||||
/// </summary>
|
||||
public Dictionary<string, DecorationData> AllData => decorationDataDict;
|
||||
|
||||
internal override void OnManagedAwake()
|
||||
{
|
||||
// Singleton pattern
|
||||
if (Instance != null && Instance != this)
|
||||
{
|
||||
Logging.Warning("[DecorationDataManager] Duplicate instance detected. Destroying duplicate.");
|
||||
Destroy(gameObject);
|
||||
return;
|
||||
}
|
||||
|
||||
Instance = this;
|
||||
}
|
||||
|
||||
internal override void OnManagedStart()
|
||||
{
|
||||
StartCoroutine(LoadSettingsAndData());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Load settings first, then decoration data
|
||||
/// </summary>
|
||||
private System.Collections.IEnumerator LoadSettingsAndData()
|
||||
{
|
||||
Logging.Debug("[DecorationDataManager] Waiting for GameManager to be accessible...");
|
||||
|
||||
// Wait until GameManager is accessible and settings can be retrieved
|
||||
settings = GameManager.GetSettingsObject<AppleHills.Core.Settings.IStatueDressupSettings>();
|
||||
|
||||
Logging.Debug("[DecorationDataManager] Settings loaded successfully");
|
||||
|
||||
// Now load decoration data
|
||||
var loadTask = LoadAllDecorationData();
|
||||
yield return new WaitUntil(() => loadTask.IsCompleted);
|
||||
|
||||
if (loadTask.IsFaulted)
|
||||
{
|
||||
Logging.Error($"[DecorationDataManager] Failed to load decoration data: {loadTask.Exception}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Load all DecorationData assets via Addressables
|
||||
/// </summary>
|
||||
private async System.Threading.Tasks.Task LoadAllDecorationData()
|
||||
{
|
||||
if (isLoaded)
|
||||
{
|
||||
Logging.Warning("[DecorationDataManager] Data already loaded");
|
||||
return;
|
||||
}
|
||||
|
||||
string label = settings?.DecorationDataLabel;
|
||||
|
||||
if (string.IsNullOrEmpty(label))
|
||||
{
|
||||
Logging.Error("[DecorationDataManager] Decoration data label not set in settings!");
|
||||
return;
|
||||
}
|
||||
|
||||
Logging.Debug($"[DecorationDataManager] Loading DecorationData with label '{label}'...");
|
||||
|
||||
// Use utility to load all DecorationData and create dictionary by ID
|
||||
var result = await AddressablesUtility.LoadAssetsByLabelAsync<DecorationData, string>(
|
||||
label,
|
||||
data => data.DecorationId
|
||||
);
|
||||
|
||||
decorationDataDict = result.dictionary;
|
||||
decorationDataHandle = result.handle;
|
||||
isLoaded = true;
|
||||
|
||||
Logging.Debug($"[DecorationDataManager] Loaded {decorationDataDict.Count} DecorationData assets");
|
||||
|
||||
// Subscribe all pending callbacks to OnReady event before invoking
|
||||
if (PendingCallbacks.Count > 0)
|
||||
{
|
||||
Logging.Debug($"[DecorationDataManager] Subscribing {PendingCallbacks.Count} pending callbacks to OnReady");
|
||||
foreach (var callback in PendingCallbacks)
|
||||
{
|
||||
OnReady += callback;
|
||||
}
|
||||
PendingCallbacks.Clear();
|
||||
}
|
||||
|
||||
// Mark as ready and notify listeners (including pending callbacks)
|
||||
OnReady?.Invoke();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get decoration data by ID
|
||||
/// </summary>
|
||||
public DecorationData GetData(string decorationId)
|
||||
{
|
||||
if (!isLoaded)
|
||||
{
|
||||
Logging.Warning("[DecorationDataManager] Data not loaded yet!");
|
||||
return null;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(decorationId))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
decorationDataDict.TryGetValue(decorationId, out DecorationData data);
|
||||
return data;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Try get decoration data by ID
|
||||
/// </summary>
|
||||
public bool TryGetData(string decorationId, out DecorationData data)
|
||||
{
|
||||
data = null;
|
||||
|
||||
if (!isLoaded || string.IsNullOrEmpty(decorationId))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return decorationDataDict.TryGetValue(decorationId, out data);
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
// Release Addressables handle
|
||||
AddressablesUtility.ReleaseHandle(decorationDataHandle);
|
||||
|
||||
if (Instance == this)
|
||||
{
|
||||
Instance = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: afa56ec5b1f84b32ba014a91d9d9a0a0
|
||||
timeCreated: 1764240174
|
||||
@@ -13,6 +13,8 @@ namespace Minigames.StatueDressup.Controllers
|
||||
/// </summary>
|
||||
public class DecorationMenuController : ManagedBehaviour
|
||||
{
|
||||
public static DecorationMenuController Instance { get; private set; }
|
||||
|
||||
[Header("References")]
|
||||
[SerializeField] private DecorationGridIcon iconPrefab;
|
||||
[SerializeField] private DecorationDraggableInstance draggablePrefab;
|
||||
@@ -20,34 +22,37 @@ namespace Minigames.StatueDressup.Controllers
|
||||
[SerializeField] private Transform draggableContainer; // Parent for spawned draggables
|
||||
[SerializeField] private Button nextPageButton;
|
||||
[SerializeField] private Button previousPageButton;
|
||||
[SerializeField] private StatueDecorationController statueController; // Controller for registration
|
||||
[SerializeField] private Image statueOutline; // Outline image shown during drag to indicate valid drop area
|
||||
|
||||
[Header("Layout")]
|
||||
[SerializeField] private GridLayoutGroup gridLayout;
|
||||
|
||||
private int _currentPage;
|
||||
private int _totalPages;
|
||||
private List<DecorationGridIcon> _spawnedIcons = new List<DecorationGridIcon>();
|
||||
private AppleHills.Core.Settings.IStatueDressupSettings _settings;
|
||||
private int currentPage;
|
||||
private int totalPages;
|
||||
private List<DecorationGridIcon> spawnedIcons = new List<DecorationGridIcon>();
|
||||
|
||||
// Properties
|
||||
public int CurrentPage => _currentPage;
|
||||
public int TotalPages => _totalPages;
|
||||
public int CurrentPage => currentPage;
|
||||
public int TotalPages => totalPages;
|
||||
|
||||
/// <summary>
|
||||
/// Early initialization - get settings reference
|
||||
/// </summary>
|
||||
internal override void OnManagedAwake()
|
||||
{
|
||||
base.OnManagedAwake();
|
||||
|
||||
// Get settings early
|
||||
_settings = GameManager.GetSettingsObject<AppleHills.Core.Settings.IStatueDressupSettings>();
|
||||
|
||||
if (_settings == null)
|
||||
// Singleton pattern
|
||||
if (Instance != null && Instance != this)
|
||||
{
|
||||
Logging.Error("[DecorationMenuController] Failed to load StatueDressupSettings!");
|
||||
Logging.Warning("[DecorationMenuController] Duplicate instance detected. Destroying duplicate.");
|
||||
Destroy(gameObject);
|
||||
return;
|
||||
}
|
||||
|
||||
Instance = this;
|
||||
|
||||
// Ensure outline starts hidden (do this early so it's ready for saved decorations)
|
||||
if (statueOutline != null)
|
||||
{
|
||||
statueOutline.gameObject.SetActive(false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,33 +63,41 @@ namespace Minigames.StatueDressup.Controllers
|
||||
{
|
||||
base.OnManagedStart();
|
||||
|
||||
if (_settings == null)
|
||||
// Wait for data manager to be ready before initializing
|
||||
DecorationDataManager.WhenReady(() =>
|
||||
{
|
||||
InitializeMenu();
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initialize menu once data manager is ready
|
||||
/// </summary>
|
||||
private void InitializeMenu()
|
||||
{
|
||||
var settings = DecorationDataManager.Instance?.Settings;
|
||||
|
||||
if (settings == null)
|
||||
{
|
||||
Logging.Error("[DecorationMenuController] Cannot initialize without settings!");
|
||||
return;
|
||||
}
|
||||
|
||||
// Ensure outline starts hidden
|
||||
if (statueOutline != null)
|
||||
{
|
||||
statueOutline.gameObject.SetActive(false);
|
||||
}
|
||||
|
||||
var allDecorations = _settings.AllDecorations;
|
||||
int itemsPerPage = _settings.ItemsPerPage;
|
||||
var allDecorations = settings.AllDecorations;
|
||||
int itemsPerPage = settings?.ItemsPerPage ?? StatueDressupConstants.DefaultMenuItemsPerPage;
|
||||
|
||||
Logging.Debug($"[DecorationMenuController] Initializing with {allDecorations?.Count ?? 0} decorations");
|
||||
|
||||
// Calculate total pages
|
||||
if (allDecorations != null && allDecorations.Count > 0)
|
||||
{
|
||||
_totalPages = Mathf.CeilToInt((float)allDecorations.Count / itemsPerPage);
|
||||
Logging.Debug($"[DecorationMenuController] Total pages: {_totalPages}");
|
||||
totalPages = Mathf.CeilToInt((float)allDecorations.Count / itemsPerPage);
|
||||
Logging.Debug($"[DecorationMenuController] Total pages: {totalPages}");
|
||||
}
|
||||
else
|
||||
{
|
||||
Logging.Warning("[DecorationMenuController] No decorations found in settings!");
|
||||
_totalPages = 0;
|
||||
totalPages = 0;
|
||||
}
|
||||
|
||||
// Setup buttons
|
||||
@@ -110,10 +123,11 @@ namespace Minigames.StatueDressup.Controllers
|
||||
/// </summary>
|
||||
private void PopulateCurrentPage()
|
||||
{
|
||||
if (_settings == null) return;
|
||||
var settings = DecorationDataManager.Instance?.Settings;
|
||||
if (settings == null) return;
|
||||
|
||||
var allDecorations = _settings.AllDecorations;
|
||||
int itemsPerPage = _settings.ItemsPerPage;
|
||||
var allDecorations = settings.AllDecorations;
|
||||
int itemsPerPage = settings.ItemsPerPage;
|
||||
|
||||
if (allDecorations == null || allDecorations.Count == 0)
|
||||
{
|
||||
@@ -121,13 +135,13 @@ namespace Minigames.StatueDressup.Controllers
|
||||
return;
|
||||
}
|
||||
|
||||
Logging.Debug($"[DecorationMenuController] Populating page {_currentPage + 1}/{_totalPages}");
|
||||
Logging.Debug($"[DecorationMenuController] Populating page {currentPage + 1}/{totalPages}");
|
||||
|
||||
// Clear existing icons
|
||||
ClearIcons();
|
||||
|
||||
// Calculate range for current page
|
||||
int startIndex = _currentPage * itemsPerPage;
|
||||
int startIndex = currentPage * itemsPerPage;
|
||||
int endIndex = Mathf.Min(startIndex + itemsPerPage, allDecorations.Count);
|
||||
|
||||
Logging.Debug($"[DecorationMenuController] Spawning icons {startIndex} to {endIndex - 1}");
|
||||
@@ -156,7 +170,7 @@ namespace Minigames.StatueDressup.Controllers
|
||||
DecorationGridIcon icon = Instantiate(iconPrefab, itemsContainer);
|
||||
icon.Initialize(data, this);
|
||||
|
||||
_spawnedIcons.Add(icon);
|
||||
spawnedIcons.Add(icon);
|
||||
|
||||
Logging.Debug($"[DecorationMenuController] Spawned icon: {data.DecorationName}");
|
||||
}
|
||||
@@ -167,7 +181,7 @@ namespace Minigames.StatueDressup.Controllers
|
||||
/// </summary>
|
||||
public DecorationDraggableInstance SpawnDraggableInstance(DecorationData data, Vector3 screenPosition)
|
||||
{
|
||||
if (draggablePrefab == null || statueController == null)
|
||||
if (draggablePrefab == null || StatueDecorationController.Instance == null)
|
||||
{
|
||||
Logging.Warning("[DecorationMenuController] Missing draggable prefab or statue controller");
|
||||
return null;
|
||||
@@ -185,16 +199,21 @@ namespace Minigames.StatueDressup.Controllers
|
||||
// Get outline RectTransform for overlap detection
|
||||
RectTransform outlineRect = statueOutline != null ? statueOutline.rectTransform : null;
|
||||
|
||||
// Initialize with references
|
||||
instance.Initialize(
|
||||
// Create context for new drag
|
||||
var context = DecorationDragContext.CreateForNewDrag(
|
||||
data,
|
||||
outlineRect,
|
||||
statueController.StatueParent,
|
||||
statueController,
|
||||
_settings,
|
||||
OnDraggableFinished
|
||||
StatueDecorationController.Instance.StatueParent,
|
||||
StatueDecorationController.Instance,
|
||||
DecorationDataManager.Instance.Settings,
|
||||
OnDraggableFinished,
|
||||
ShowStatueOutline,
|
||||
HideStatueOutline
|
||||
);
|
||||
|
||||
// Initialize with context
|
||||
instance.InitializeWithContext(context);
|
||||
|
||||
// Position at cursor (in local space)
|
||||
Canvas canvas = GetComponentInParent<Canvas>();
|
||||
if (canvas != null)
|
||||
@@ -220,19 +239,24 @@ namespace Minigames.StatueDressup.Controllers
|
||||
/// <summary>
|
||||
/// Show the statue outline to indicate valid drop area
|
||||
/// </summary>
|
||||
private void ShowStatueOutline()
|
||||
public void ShowStatueOutline()
|
||||
{
|
||||
Logging.Debug($"[DecorationMenuController] ShowStatueOutline called - statueOutline is null: {statueOutline == null}");
|
||||
if (statueOutline != null)
|
||||
{
|
||||
statueOutline.gameObject.SetActive(true);
|
||||
Logging.Debug("[DecorationMenuController] Statue outline shown");
|
||||
}
|
||||
else
|
||||
{
|
||||
Logging.Warning("[DecorationMenuController] Cannot show outline - statueOutline is null!");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Hide the statue outline after drag ends
|
||||
/// </summary>
|
||||
private void HideStatueOutline()
|
||||
public void HideStatueOutline()
|
||||
{
|
||||
if (statueOutline != null)
|
||||
{
|
||||
@@ -254,7 +278,7 @@ namespace Minigames.StatueDressup.Controllers
|
||||
/// </summary>
|
||||
private void ClearIcons()
|
||||
{
|
||||
foreach (var icon in _spawnedIcons)
|
||||
foreach (var icon in spawnedIcons)
|
||||
{
|
||||
if (icon != null)
|
||||
{
|
||||
@@ -262,7 +286,7 @@ namespace Minigames.StatueDressup.Controllers
|
||||
}
|
||||
}
|
||||
|
||||
_spawnedIcons.Clear();
|
||||
spawnedIcons.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -270,11 +294,11 @@ namespace Minigames.StatueDressup.Controllers
|
||||
/// </summary>
|
||||
private void OnNextPage()
|
||||
{
|
||||
if (_currentPage < _totalPages - 1)
|
||||
if (currentPage < totalPages - 1)
|
||||
{
|
||||
_currentPage++;
|
||||
currentPage++;
|
||||
PopulateCurrentPage();
|
||||
Logging.Debug($"[DecorationMenuController] Next page: {_currentPage + 1}/{_totalPages}");
|
||||
Logging.Debug($"[DecorationMenuController] Next page: {currentPage + 1}/{totalPages}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -283,11 +307,11 @@ namespace Minigames.StatueDressup.Controllers
|
||||
/// </summary>
|
||||
private void OnPreviousPage()
|
||||
{
|
||||
if (_currentPage > 0)
|
||||
if (currentPage > 0)
|
||||
{
|
||||
_currentPage--;
|
||||
currentPage--;
|
||||
PopulateCurrentPage();
|
||||
Logging.Debug($"[DecorationMenuController] Previous page: {_currentPage + 1}/{_totalPages}");
|
||||
Logging.Debug($"[DecorationMenuController] Previous page: {currentPage + 1}/{totalPages}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -296,14 +320,14 @@ namespace Minigames.StatueDressup.Controllers
|
||||
/// </summary>
|
||||
private void UpdateNavigationButtons()
|
||||
{
|
||||
if (previousPageButton != null)
|
||||
{
|
||||
previousPageButton.interactable = _currentPage > 0;
|
||||
}
|
||||
|
||||
if (nextPageButton != null)
|
||||
{
|
||||
nextPageButton.interactable = _currentPage < _totalPages - 1;
|
||||
nextPageButton.interactable = currentPage < totalPages - 1;
|
||||
}
|
||||
|
||||
if (previousPageButton != null)
|
||||
{
|
||||
previousPageButton.interactable = currentPage > 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -327,6 +351,12 @@ namespace Minigames.StatueDressup.Controllers
|
||||
|
||||
// Cleanup icons
|
||||
ClearIcons();
|
||||
|
||||
// Singleton cleanup
|
||||
if (Instance == this)
|
||||
{
|
||||
Instance = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,93 +0,0 @@
|
||||
using Core;
|
||||
using Minigames.StatueDressup.Utils;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace Minigames.StatueDressup.Controllers
|
||||
{
|
||||
/// <summary>
|
||||
/// Minimal test for photo capture - just capture and save to disk
|
||||
/// </summary>
|
||||
public class PhotoCaptureTestController : MonoBehaviour
|
||||
{
|
||||
[Header("Required")]
|
||||
[SerializeField] private RectTransform captureArea;
|
||||
[SerializeField] private Button captureButton;
|
||||
|
||||
[Header("Optional - UI to hide during capture")]
|
||||
[SerializeField] private GameObject[] hideTheseObjects;
|
||||
|
||||
private void Start()
|
||||
{
|
||||
if (captureButton != null)
|
||||
{
|
||||
captureButton.onClick.AddListener(OnCaptureClicked);
|
||||
}
|
||||
|
||||
Debug.Log($"[PhotoCaptureTest] Ready. Photo save path: {StatuePhotoManager.GetPhotoDirectory()}");
|
||||
}
|
||||
|
||||
private void OnCaptureClicked()
|
||||
{
|
||||
if (captureArea == null)
|
||||
{
|
||||
Debug.LogError("[PhotoCaptureTest] Capture Area not assigned!");
|
||||
return;
|
||||
}
|
||||
|
||||
Debug.Log("[PhotoCaptureTest] Starting capture...");
|
||||
StartCoroutine(CaptureCoroutine());
|
||||
}
|
||||
|
||||
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))
|
||||
{
|
||||
string path = $"{StatuePhotoManager.GetPhotoDirectory()}/{photoId}.png";
|
||||
Debug.Log($"[PhotoCaptureTest] ✅ SUCCESS! Photo saved: {path}");
|
||||
Debug.Log($"[PhotoCaptureTest] Photo size: {photo.width}x{photo.height}");
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError("[PhotoCaptureTest] ❌ Failed to save photo");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError("[PhotoCaptureTest] ❌ Failed to capture photo");
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
if (captureButton != null)
|
||||
captureButton.onClick.RemoveListener(OnCaptureClicked);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: deab1758ddef4bdea0e2c50554eaf568
|
||||
timeCreated: 1764077035
|
||||
@@ -1,79 +0,0 @@
|
||||
using Core;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using UnityEngine.EventSystems;
|
||||
|
||||
namespace Minigames.StatueDressup.Controllers
|
||||
{
|
||||
/// <summary>
|
||||
/// Individual photo thumbnail in the gallery grid.
|
||||
/// Handles click to show enlarged view.
|
||||
/// </summary>
|
||||
public class PhotoGridItem : MonoBehaviour, IPointerClickHandler
|
||||
{
|
||||
[Header("References")]
|
||||
[SerializeField] private Image thumbnailImage;
|
||||
[SerializeField] private GameObject loadingIndicator;
|
||||
|
||||
private string _photoId;
|
||||
private StatuePhotoGalleryController _galleryController;
|
||||
|
||||
/// <summary>
|
||||
/// Initialize grid item with photo ID
|
||||
/// </summary>
|
||||
public void Initialize(string photoId, StatuePhotoGalleryController controller)
|
||||
{
|
||||
_photoId = photoId;
|
||||
_galleryController = controller;
|
||||
|
||||
// Show loading state
|
||||
if (loadingIndicator != null)
|
||||
loadingIndicator.SetActive(true);
|
||||
|
||||
if (thumbnailImage != null)
|
||||
thumbnailImage.enabled = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the thumbnail texture
|
||||
/// </summary>
|
||||
public void SetThumbnail(Texture2D thumbnail)
|
||||
{
|
||||
if (thumbnail == null)
|
||||
{
|
||||
Logging.Warning($"[PhotoGridItem] Null thumbnail for photo: {_photoId}");
|
||||
return;
|
||||
}
|
||||
|
||||
// Create sprite from thumbnail
|
||||
Sprite thumbnailSprite = Sprite.Create(
|
||||
thumbnail,
|
||||
new Rect(0, 0, thumbnail.width, thumbnail.height),
|
||||
new Vector2(0.5f, 0.5f)
|
||||
);
|
||||
|
||||
if (thumbnailImage != null)
|
||||
{
|
||||
thumbnailImage.sprite = thumbnailSprite;
|
||||
thumbnailImage.enabled = true;
|
||||
}
|
||||
|
||||
// Hide loading indicator
|
||||
if (loadingIndicator != null)
|
||||
loadingIndicator.SetActive(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle click to show enlarged view
|
||||
/// </summary>
|
||||
public void OnPointerClick(PointerEventData eventData)
|
||||
{
|
||||
if (_galleryController != null && !string.IsNullOrEmpty(_photoId))
|
||||
{
|
||||
Logging.Debug($"[PhotoGridItem] Clicked: {_photoId}");
|
||||
_galleryController.ShowEnlargedView(_photoId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: acd8d97ee2f744d984a9507e75309be0
|
||||
timeCreated: 1764065014
|
||||
@@ -1,9 +1,13 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
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
|
||||
{
|
||||
@@ -13,36 +17,46 @@ namespace Minigames.StatueDressup.Controllers
|
||||
/// </summary>
|
||||
public class StatueDecorationController : ManagedBehaviour
|
||||
{
|
||||
public static StatueDecorationController Instance { get; private set; }
|
||||
|
||||
[Header("References")]
|
||||
[SerializeField] private RectTransform statueArea; // Statue area for overlap detection
|
||||
[SerializeField] private Transform statueParent; // Parent for placed decorations
|
||||
[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<DecorationDraggableInstance> _placedDecorations = new List<DecorationDraggableInstance>();
|
||||
private bool _minigameCompleted;
|
||||
private AppleHills.Core.Settings.IStatueDressupSettings _settings;
|
||||
private List<DecorationDraggableInstance> placedDecorations = new List<DecorationDraggableInstance>();
|
||||
private bool minigameCompleted;
|
||||
|
||||
// Public property for menu controller
|
||||
// Public properties
|
||||
public Transform StatueParent => statueParent;
|
||||
public RectTransform StatueArea => statueArea;
|
||||
|
||||
/// <summary>
|
||||
/// Early initialization - get settings reference
|
||||
/// </summary>
|
||||
internal override void OnManagedAwake()
|
||||
{
|
||||
base.OnManagedAwake();
|
||||
|
||||
// Get settings early
|
||||
_settings = GameManager.GetSettingsObject<AppleHills.Core.Settings.IStatueDressupSettings>();
|
||||
// Singleton pattern
|
||||
if (Instance != null && Instance != this)
|
||||
{
|
||||
Logging.Warning("[StatueDecorationController] Duplicate instance detected. Destroying duplicate.");
|
||||
Destroy(gameObject);
|
||||
return;
|
||||
}
|
||||
|
||||
Instance = this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -54,32 +68,68 @@ namespace Minigames.StatueDressup.Controllers
|
||||
|
||||
Logging.Debug("[StatueDecorationController] Initializing minigame");
|
||||
|
||||
// Wait for decoration data to be ready before initializing
|
||||
DecorationDataManager.WhenReady(() =>
|
||||
{
|
||||
Logging.Debug("[StatueDecorationController] DecorationData ready, initializing minigame");
|
||||
InitializeMinigame();
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the minigame after data is ready
|
||||
/// </summary>
|
||||
private void InitializeMinigame()
|
||||
{
|
||||
// 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)
|
||||
{
|
||||
takePhotoButton.onClick.AddListener(OnTakePhoto);
|
||||
}
|
||||
|
||||
// Subscribe to menu controller for tracking placed items
|
||||
// Items will manage their own placement via overlap detection
|
||||
if (menuController != null)
|
||||
{
|
||||
// Menu controller will handle spawning replacements
|
||||
Logging.Debug("[StatueDecorationController] Menu controller connected");
|
||||
}
|
||||
// TODO: If ever picture gallery
|
||||
// Setup gallery button (DISABLED - kept for future use)
|
||||
// if (openGalleryButton != null)
|
||||
// {
|
||||
// openGalleryButton.onClick.AddListener(OnOpenGallery);
|
||||
// }
|
||||
|
||||
// Load saved state if exists
|
||||
LoadStatueState();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Open photo gallery
|
||||
/// </summary>
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Register a decoration as placed on statue
|
||||
/// </summary>
|
||||
public void RegisterDecoration(DecorationDraggableInstance decoration)
|
||||
{
|
||||
if (decoration != null && !_placedDecorations.Contains(decoration))
|
||||
if (decoration != null && !placedDecorations.Contains(decoration))
|
||||
{
|
||||
_placedDecorations.Add(decoration);
|
||||
placedDecorations.Add(decoration);
|
||||
Logging.Debug($"[StatueDecorationController] Decoration placed: {decoration.Data?.DecorationName}");
|
||||
|
||||
// Auto-save state
|
||||
@@ -92,9 +142,9 @@ namespace Minigames.StatueDressup.Controllers
|
||||
/// </summary>
|
||||
public void UnregisterDecoration(DecorationDraggableInstance decoration)
|
||||
{
|
||||
if (decoration != null && _placedDecorations.Contains(decoration))
|
||||
if (decoration != null && placedDecorations.Contains(decoration))
|
||||
{
|
||||
_placedDecorations.Remove(decoration);
|
||||
placedDecorations.Remove(decoration);
|
||||
Logging.Debug($"[StatueDecorationController] Decoration removed: {decoration.Data?.DecorationName}");
|
||||
|
||||
// Auto-save state
|
||||
@@ -107,7 +157,7 @@ namespace Minigames.StatueDressup.Controllers
|
||||
/// </summary>
|
||||
private void OnTakePhoto()
|
||||
{
|
||||
if (_minigameCompleted)
|
||||
if (minigameCompleted)
|
||||
{
|
||||
Logging.Debug("[StatueDecorationController] Minigame already completed");
|
||||
return;
|
||||
@@ -115,76 +165,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());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Capture photo after UI is hidden
|
||||
/// Capture photo and save decoration metadata
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Award Blokkemon cards to player
|
||||
/// </summary>
|
||||
@@ -218,17 +258,62 @@ namespace Minigames.StatueDressup.Controllers
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Hide/show UI elements for photo
|
||||
/// Save decoration metadata for reconstruction
|
||||
/// </summary>
|
||||
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<RectTransform>() != null;
|
||||
|
||||
// Get statue size for coordinate conversion
|
||||
Vector2 statueSize = Vector2.one;
|
||||
if (isUIRectTransform && statue != null)
|
||||
{
|
||||
if (element != null)
|
||||
RectTransform statueRect = statue.GetComponent<RectTransform>();
|
||||
statueSize = statueRect.rect.size;
|
||||
}
|
||||
else if (statue != null)
|
||||
{
|
||||
SpriteRenderer statueSprite = statue.GetComponent<SpriteRenderer>();
|
||||
if (statueSprite != null && statueSprite.sprite != null)
|
||||
{
|
||||
element.SetActive(!hide);
|
||||
statueSize = statueSprite.sprite.bounds.size;
|
||||
}
|
||||
}
|
||||
|
||||
StatueDecorationData data = new StatueDecorationData
|
||||
{
|
||||
photoId = photoId,
|
||||
timestamp = 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;
|
||||
|
||||
RectTransform rectTransform = decoration.GetComponent<RectTransform>();
|
||||
SpriteRenderer spriteRenderer = decoration.GetComponent<SpriteRenderer>();
|
||||
|
||||
DecorationPlacement placement = new DecorationPlacement
|
||||
{
|
||||
decorationId = decoration.Data.DecorationId,
|
||||
localPosition = decoration.transform.localPosition,
|
||||
localScale = decoration.transform.localScale,
|
||||
sizeDelta = rectTransform != null ? rectTransform.sizeDelta : Vector2.zero,
|
||||
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");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -236,8 +321,10 @@ namespace Minigames.StatueDressup.Controllers
|
||||
/// </summary>
|
||||
private void SaveStatueState()
|
||||
{
|
||||
var settings = DecorationDataManager.Instance?.Settings;
|
||||
|
||||
// Check if persistence is enabled
|
||||
if (_settings == null || !_settings.EnableStatePersistence)
|
||||
if (settings == null || !settings.EnableStatePersistence)
|
||||
{
|
||||
Logging.Debug("[StatueDecorationController] State persistence disabled");
|
||||
return;
|
||||
@@ -247,25 +334,125 @@ namespace Minigames.StatueDressup.Controllers
|
||||
// Save decoration ID + position + rotation for each placed item
|
||||
// Respect MaxSavedDecorations limit
|
||||
|
||||
Logging.Debug($"[StatueDecorationController] State saved to {_settings.StateSaveKey} (TODO: implement persistence)");
|
||||
Logging.Debug($"[StatueDecorationController] State saved to {settings.StateSaveKey} (TODO: implement persistence)");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Load saved statue decoration state
|
||||
/// Load saved statue decoration state from metadata
|
||||
/// </summary>
|
||||
private void LoadStatueState()
|
||||
{
|
||||
var settings = DecorationDataManager.Instance?.Settings;
|
||||
|
||||
// Check if persistence is enabled
|
||||
if (_settings == null || !_settings.EnableStatePersistence)
|
||||
if (settings == null || !settings.EnableStatePersistence)
|
||||
{
|
||||
Logging.Debug("[StatueDecorationController] State persistence disabled");
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: Implement load system
|
||||
// Restore decorations from saved state
|
||||
// Check if DecorationData is loaded
|
||||
if (DecorationDataManager.Instance == null || !DecorationDataManager.Instance.IsLoaded)
|
||||
{
|
||||
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<StatueDecorationData>(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");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Spawn a saved decoration from placement data
|
||||
/// </summary>
|
||||
private bool SpawnSavedDecoration(DecorationPlacement placement)
|
||||
{
|
||||
// Look up DecorationData from manager
|
||||
if (!DecorationDataManager.Instance.TryGetData(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);
|
||||
|
||||
// Get canvas parent for drag context (used when picking up)
|
||||
Transform canvasParent = statueParent.parent; // Typically the canvas or draggable container
|
||||
|
||||
// Create callbacks for outline show/hide
|
||||
System.Action showOutlineCallback = DecorationMenuController.Instance != null ? (System.Action)DecorationMenuController.Instance.ShowStatueOutline : null;
|
||||
System.Action hideOutlineCallback = DecorationMenuController.Instance != null ? (System.Action)DecorationMenuController.Instance.HideStatueOutline : null;
|
||||
|
||||
// Create context for placed decoration
|
||||
var context = DecorationDragContext.CreateForPlaced(
|
||||
decorationData,
|
||||
this,
|
||||
DecorationDataManager.Instance.Settings,
|
||||
statueArea,
|
||||
canvasParent,
|
||||
showOutlineCallback,
|
||||
hideOutlineCallback,
|
||||
hideOutlineCallback
|
||||
);
|
||||
|
||||
// Initialize in "placed" state (skip drag logic)
|
||||
instance.InitializeWithContext(context);
|
||||
|
||||
// Apply saved transform
|
||||
instance.transform.localPosition = placement.localPosition;
|
||||
instance.transform.localScale = placement.localScale;
|
||||
instance.transform.localEulerAngles = new Vector3(0, 0, placement.rotation);
|
||||
|
||||
// Apply saved sizeDelta if available (overrides AuthoredSize from InitializeAsPlaced)
|
||||
if (placement.sizeDelta != Vector2.zero)
|
||||
{
|
||||
RectTransform rectTransform = instance.GetComponent<RectTransform>();
|
||||
if (rectTransform != null)
|
||||
{
|
||||
rectTransform.sizeDelta = placement.sizeDelta;
|
||||
}
|
||||
}
|
||||
|
||||
// Set sorting order if has SpriteRenderer
|
||||
SpriteRenderer spriteRenderer = instance.GetComponent<SpriteRenderer>();
|
||||
if (spriteRenderer != null)
|
||||
{
|
||||
spriteRenderer.sortingOrder = placement.sortingOrder;
|
||||
}
|
||||
|
||||
// Register with controller
|
||||
RegisterDecoration(instance);
|
||||
|
||||
Logging.Debug($"[StatueDecorationController] Restored decoration: {placement.decorationId} at {placement.localPosition}");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -275,12 +462,33 @@ namespace Minigames.StatueDressup.Controllers
|
||||
{
|
||||
base.OnManagedDestroy();
|
||||
|
||||
// Cleanup button listener
|
||||
// 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);
|
||||
// }
|
||||
|
||||
// Singleton cleanup
|
||||
if (Instance == this)
|
||||
{
|
||||
Instance = null;
|
||||
}
|
||||
}
|
||||
|
||||
public async void ExitToAppleHills()
|
||||
{
|
||||
// Replace with the actual scene name as set in Build Settings
|
||||
var progress = new Progress<float>(p => Logging.Debug($"Loading progress: {p * 100:F0}%"));
|
||||
await SceneManagerService.Instance.SwitchSceneAsync("AppleHillsOverworld", progress);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
using System.Collections;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Core;
|
||||
using Core.Lifecycle;
|
||||
using Minigames.StatueDressup.Utils;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using Utils;
|
||||
|
||||
namespace Minigames.StatueDressup.Controllers
|
||||
{
|
||||
@@ -15,52 +15,81 @@ namespace Minigames.StatueDressup.Controllers
|
||||
/// </summary>
|
||||
public class StatuePhotoGalleryController : ManagedBehaviour
|
||||
{
|
||||
public static StatuePhotoGalleryController Instance { get; private set; }
|
||||
|
||||
[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;
|
||||
private List<string> allPhotoIds = new List<string>();
|
||||
private Dictionary<string, PhotoGridItem> activeGridItems = new Dictionary<string, PhotoGridItem>();
|
||||
private Dictionary<string, Texture2D> thumbnailCache = new Dictionary<string, Texture2D>();
|
||||
private Dictionary<string, Texture2D> fullPhotoCache = new Dictionary<string, Texture2D>(); // Cache full photos for enlargement
|
||||
private Queue<string> thumbnailCacheOrder = new Queue<string>();
|
||||
private bool isLoadingPage;
|
||||
private PhotoEnlargeController enlargeController;
|
||||
|
||||
private int _currentPage = 0;
|
||||
private List<string> _allPhotoIds = new List<string>();
|
||||
private Dictionary<string, PhotoGridItem> _activeGridItems = new Dictionary<string, PhotoGridItem>();
|
||||
private Dictionary<string, Texture2D> _thumbnailCache = new Dictionary<string, Texture2D>();
|
||||
private Queue<string> _thumbnailCacheOrder = new Queue<string>();
|
||||
private string _currentEnlargedPhotoId = null;
|
||||
private Texture2D _currentEnlargedTexture = null;
|
||||
internal override void OnManagedAwake()
|
||||
{
|
||||
base.OnManagedAwake();
|
||||
|
||||
// Singleton pattern
|
||||
if (Instance != null && Instance != this)
|
||||
{
|
||||
Logging.Warning("[StatuePhotoGalleryController] Duplicate instance detected. Destroying duplicate.");
|
||||
Destroy(gameObject);
|
||||
return;
|
||||
}
|
||||
|
||||
Instance = this;
|
||||
}
|
||||
|
||||
internal override void OnManagedStart()
|
||||
{
|
||||
base.OnManagedStart();
|
||||
|
||||
// Setup buttons
|
||||
if (closeEnlargedButton != null)
|
||||
closeEnlargedButton.onClick.AddListener(CloseEnlargedView);
|
||||
// Wait for data manager to be ready before initializing
|
||||
DecorationDataManager.WhenReady(() =>
|
||||
{
|
||||
InitializeGallery();
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initialize gallery once data manager is ready
|
||||
/// </summary>
|
||||
private void InitializeGallery()
|
||||
{
|
||||
var settings = DecorationDataManager.Instance?.Settings;
|
||||
|
||||
if (deletePhotoButton != null)
|
||||
deletePhotoButton.onClick.AddListener(DeleteCurrentPhoto);
|
||||
// Initialize enlarge controller
|
||||
enlargeController = new PhotoEnlargeController(backdrop, enlargedContainer,
|
||||
settings?.GalleryAnimationDuration ?? StatueDressupConstants.DefaultAnimationDuration);
|
||||
|
||||
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 +101,35 @@ namespace Minigames.StatueDressup.Controllers
|
||||
public void RefreshGallery()
|
||||
{
|
||||
// Clear existing items
|
||||
ClearGallery();
|
||||
ClearGrid();
|
||||
|
||||
// Get all photo IDs
|
||||
_allPhotoIds = StatuePhotoManager.GetAllPhotoIds();
|
||||
_currentPage = 0;
|
||||
allPhotoIds = PhotoManager.GetAllPhotoIds(CaptureType.StatueMinigame);
|
||||
currentPage = 0;
|
||||
|
||||
Logging.Debug($"[StatuePhotoGalleryController] Gallery refreshed: {_allPhotoIds.Count} photos");
|
||||
Logging.Debug($"[StatuePhotoGalleryController] Gallery refreshed: {allPhotoIds.Count} photos");
|
||||
|
||||
// Load first page
|
||||
LoadNextPage();
|
||||
// Display first page
|
||||
DisplayCurrentPage();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Load next page of photos
|
||||
/// Display the current page of photos (clears grid and shows only current page)
|
||||
/// </summary>
|
||||
private void LoadNextPage()
|
||||
private void DisplayCurrentPage()
|
||||
{
|
||||
List<string> 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 = DecorationDataManager.Instance?.Settings?.GalleryItemsPerPage ?? StatueDressupConstants.DefaultGalleryItemsPerPage;
|
||||
List<string> 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 +137,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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update page navigation button states
|
||||
/// </summary>
|
||||
private void UpdatePageButtons()
|
||||
{
|
||||
int itemsPerPage = DecorationDataManager.Instance?.Settings?.GalleryItemsPerPage ?? StatueDressupConstants.DefaultGalleryItemsPerPage;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Navigate to previous page
|
||||
/// </summary>
|
||||
private void OnPreviousPageClicked()
|
||||
{
|
||||
if (currentPage > 0)
|
||||
{
|
||||
currentPage--;
|
||||
DisplayCurrentPage();
|
||||
Logging.Debug($"[StatuePhotoGalleryController] Navigated to previous page: {currentPage}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Navigate to next page
|
||||
/// </summary>
|
||||
private void OnNextPageClicked()
|
||||
{
|
||||
int itemsPerPage = DecorationDataManager.Instance?.Settings?.GalleryItemsPerPage ?? StatueDressupConstants.DefaultGalleryItemsPerPage;
|
||||
int totalPages = Mathf.CeilToInt((float)allPhotoIds.Count / itemsPerPage);
|
||||
|
||||
if (currentPage < totalPages - 1)
|
||||
{
|
||||
currentPage++;
|
||||
DisplayCurrentPage();
|
||||
Logging.Debug($"[StatuePhotoGalleryController] Navigated to next page: {currentPage}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -123,7 +202,7 @@ namespace Minigames.StatueDressup.Controllers
|
||||
/// </summary>
|
||||
private void SpawnGridItem(string photoId)
|
||||
{
|
||||
if (_activeGridItems.ContainsKey(photoId))
|
||||
if (activeGridItems.ContainsKey(photoId))
|
||||
{
|
||||
Logging.Warning($"[StatuePhotoGalleryController] Grid item already exists: {photoId}");
|
||||
return;
|
||||
@@ -132,7 +211,7 @@ namespace Minigames.StatueDressup.Controllers
|
||||
PhotoGridItem gridItem = Instantiate(gridItemPrefab, gridContainer);
|
||||
gridItem.Initialize(photoId, this);
|
||||
|
||||
_activeGridItems[photoId] = gridItem;
|
||||
activeGridItems[photoId] = gridItem;
|
||||
|
||||
// Load thumbnail asynchronously
|
||||
StartCoroutine(LoadThumbnailAsync(photoId, gridItem));
|
||||
@@ -144,7 +223,7 @@ namespace Minigames.StatueDressup.Controllers
|
||||
private IEnumerator LoadThumbnailAsync(string photoId, PhotoGridItem gridItem)
|
||||
{
|
||||
// Check cache first
|
||||
if (_thumbnailCache.TryGetValue(photoId, out Texture2D cachedThumbnail))
|
||||
if (thumbnailCache.TryGetValue(photoId, out Texture2D cachedThumbnail))
|
||||
{
|
||||
gridItem.SetThumbnail(cachedThumbnail);
|
||||
yield break;
|
||||
@@ -154,7 +233,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 +242,8 @@ namespace Minigames.StatueDressup.Controllers
|
||||
}
|
||||
|
||||
// Create thumbnail
|
||||
Texture2D thumbnail = StatuePhotoManager.CreateThumbnail(fullPhoto, thumbnailSize);
|
||||
int thumbSize = DecorationDataManager.Instance?.Settings?.GalleryThumbnailSize ?? StatueDressupConstants.DefaultThumbnailSize;
|
||||
Texture2D thumbnail = PhotoManager.CreateThumbnail(fullPhoto, thumbSize);
|
||||
|
||||
// Destroy full photo immediately (we only need thumbnail)
|
||||
Destroy(fullPhoto);
|
||||
@@ -184,211 +264,162 @@ namespace Minigames.StatueDressup.Controllers
|
||||
private void CacheThumbnail(string photoId, Texture2D thumbnail)
|
||||
{
|
||||
// Add to cache
|
||||
_thumbnailCache[photoId] = thumbnail;
|
||||
_thumbnailCacheOrder.Enqueue(photoId);
|
||||
thumbnailCache[photoId] = thumbnail;
|
||||
thumbnailCacheOrder.Enqueue(photoId);
|
||||
|
||||
// Evict oldest if over limit
|
||||
while (_thumbnailCache.Count > maxCachedThumbnails && _thumbnailCacheOrder.Count > 0)
|
||||
int maxCached = DecorationDataManager.Instance?.Settings?.GalleryMaxCachedThumbnails ?? StatueDressupConstants.DefaultMaxCachedThumbnails;
|
||||
while (thumbnailCache.Count > maxCached && thumbnailCacheOrder.Count > 0)
|
||||
{
|
||||
string oldestId = _thumbnailCacheOrder.Dequeue();
|
||||
string oldestId = thumbnailCacheOrder.Dequeue();
|
||||
|
||||
if (_thumbnailCache.TryGetValue(oldestId, out Texture2D oldThumbnail))
|
||||
if (thumbnailCache.TryGetValue(oldestId, out Texture2D oldThumbnail))
|
||||
{
|
||||
Destroy(oldThumbnail);
|
||||
_thumbnailCache.Remove(oldestId);
|
||||
thumbnailCache.Remove(oldestId);
|
||||
Logging.Debug($"[StatuePhotoGalleryController] Evicted thumbnail from cache: {oldestId}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Show enlarged view of a photo (called by PhotoGridItem)
|
||||
/// Enlarge a photo (called by PhotoGridItem)
|
||||
/// </summary>
|
||||
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 = DecorationDataManager.Instance?.Settings?.GalleryEnlargedScale ?? StatueDressupConstants.DefaultEnlargedScale;
|
||||
|
||||
// Update photo info
|
||||
UpdatePhotoInfo(photoId);
|
||||
|
||||
// Show panel
|
||||
enlargedViewPanel.SetActive(true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Close enlarged view
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Delete currently viewed photo
|
||||
/// </summary>
|
||||
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}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update photo info text in enlarged view
|
||||
/// </summary>
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cleanup when gallery is closed
|
||||
/// </summary>
|
||||
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();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update status text
|
||||
/// </summary>
|
||||
private void UpdateStatusText(string message)
|
||||
{
|
||||
if (statusText != null)
|
||||
statusText.text = message;
|
||||
if (pageStatusText != null)
|
||||
pageStatusText.text = message;
|
||||
|
||||
Logging.Debug($"[StatuePhotoGalleryController] Status: {message}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clear all grid items and cached data
|
||||
/// Clear only the grid items (used when switching pages)
|
||||
/// </summary>
|
||||
private void ClearGallery()
|
||||
private void ClearGrid()
|
||||
{
|
||||
// Destroy grid items
|
||||
foreach (var gridItem in _activeGridItems.Values)
|
||||
foreach (var gridItem in activeGridItems.Values)
|
||||
{
|
||||
if (gridItem != null)
|
||||
Destroy(gridItem.gameObject);
|
||||
}
|
||||
_activeGridItems.Clear();
|
||||
activeGridItems.Clear();
|
||||
|
||||
Logging.Debug("[StatuePhotoGalleryController] Grid cleared");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clear all grid items and cached data (full cleanup)
|
||||
/// </summary>
|
||||
private void ClearGallery()
|
||||
{
|
||||
ClearGrid();
|
||||
|
||||
// Clear thumbnail cache
|
||||
foreach (var thumbnail in _thumbnailCache.Values)
|
||||
foreach (var thumbnail in thumbnailCache.Values)
|
||||
{
|
||||
if (thumbnail != null)
|
||||
Destroy(thumbnail);
|
||||
}
|
||||
_thumbnailCache.Clear();
|
||||
_thumbnailCacheOrder.Clear();
|
||||
thumbnailCache.Clear();
|
||||
thumbnailCacheOrder.Clear();
|
||||
|
||||
Logging.Debug("[StatuePhotoGalleryController] Gallery cleared");
|
||||
Logging.Debug("[StatuePhotoGalleryController] Gallery fully cleared");
|
||||
}
|
||||
|
||||
internal override void OnManagedDestroy()
|
||||
{
|
||||
base.OnManagedDestroy();
|
||||
|
||||
// Cleanup
|
||||
// Singleton cleanup
|
||||
if (Instance == this)
|
||||
{
|
||||
Instance = null;
|
||||
}
|
||||
|
||||
// Clean up cached textures
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user