stash work
This commit is contained in:
committed by
Michal Pikulski
parent
8d410b42d3
commit
5bab6d9596
@@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
[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<string, DecorationData> _decorationDataDict;
|
||||
private AsyncOperationHandle<IList<DecorationData>> _decorationDataHandle;
|
||||
private AppleHills.Core.Settings.IStatueDressupSettings _settings;
|
||||
|
||||
internal override void OnManagedStart()
|
||||
{
|
||||
base.OnManagedStart();
|
||||
|
||||
_statueSpriteRenderer = GetComponent<SpriteRenderer>();
|
||||
|
||||
// Get settings
|
||||
_settings = GameManager.GetSettingsObject<AppleHills.Core.Settings.IStatueDressupSettings>();
|
||||
|
||||
// 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());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Coroutine wrapper for async loading and display
|
||||
/// </summary>
|
||||
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();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Load all DecorationData assets via Addressables and build lookup dictionary
|
||||
/// </summary>
|
||||
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<DecorationData, string>(
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Load decoration metadata and spawn decorations
|
||||
/// </summary>
|
||||
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<StatueDecorationData>(CaptureType.StatueMinigame)
|
||||
: PhotoManager.LoadDecorationMetadata<StatueDecorationData>(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");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculate coordinate conversion factor between source and target coordinate systems
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Spawn a single decoration from placement data
|
||||
/// Looks up DecorationData from pre-loaded dictionary and applies coordinate conversion
|
||||
/// </summary>
|
||||
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>();
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clear all existing decorations from decorationRoot
|
||||
/// </summary>
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cleanup - release Addressables handle
|
||||
/// </summary>
|
||||
private void OnDestroy()
|
||||
{
|
||||
// Release DecorationData handle
|
||||
AddressablesUtility.ReleaseHandle(_decorationDataHandle);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reload decorations (useful for testing)
|
||||
/// </summary>
|
||||
[ContextMenu("Reload Decorations")]
|
||||
public void ReloadDecorations()
|
||||
{
|
||||
LoadAndDisplayDecorations();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Load specific photo's decorations
|
||||
/// </summary>
|
||||
public void LoadSpecificPhoto(string photoId)
|
||||
{
|
||||
specificPhotoId = photoId;
|
||||
LoadAndDisplayDecorations();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 50d0f4591bbd40fc81dc615fa465e0c5
|
||||
timeCreated: 1764163758
|
||||
Reference in New Issue
Block a user