Update the statue game to include rotation etc.

This commit is contained in:
Michal Pikulski
2025-12-08 14:48:09 +01:00
parent fff50990f1
commit e338e28673
7 changed files with 2720 additions and 96 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 8ad71c1dc8542e54494a857fa06095fb
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

File diff suppressed because it is too large Load Diff

View File

@@ -26,6 +26,10 @@ namespace Minigames.StatueDressup.Controllers
[SerializeField] private GameObject statue; [SerializeField] private GameObject statue;
[SerializeField] private DecorationDraggableInstance draggablePrefab; // Prefab for spawning decorations [SerializeField] private DecorationDraggableInstance draggablePrefab; // Prefab for spawning decorations
[Header("Edit UI")]
[SerializeField] private UI.DecorationEditUI editUIPrefab; // Prefab for edit UI
private UI.DecorationEditUI _editUIInstance;
[Header("UI Pages")] [Header("UI Pages")]
[SerializeField] private UI.PlayAreaPage playAreaPage; [SerializeField] private UI.PlayAreaPage playAreaPage;
[SerializeField] private UI.PhotoGalleryPage photoGalleryPage; [SerializeField] private UI.PhotoGalleryPage photoGalleryPage;
@@ -37,8 +41,8 @@ namespace Minigames.StatueDressup.Controllers
[Header("Photo Settings")] [Header("Photo Settings")]
[SerializeField] private RectTransform photoArea; // Area to capture [SerializeField] private RectTransform photoArea; // Area to capture
private List<DecorationDraggableInstance> placedDecorations = new List<DecorationDraggableInstance>(); private List<DecorationDraggableInstance> _placedDecorations = new List<DecorationDraggableInstance>();
private bool minigameCompleted; private bool _minigameCompleted;
// Public properties // Public properties
public Transform StatueParent => statueParent; public Transform StatueParent => statueParent;
@@ -127,9 +131,9 @@ namespace Minigames.StatueDressup.Controllers
/// </summary> /// </summary>
public void RegisterDecoration(DecorationDraggableInstance decoration) 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}"); Logging.Debug($"[StatueDecorationController] Decoration placed: {decoration.Data?.DecorationName}");
// Auto-save state // Auto-save state
@@ -142,9 +146,9 @@ namespace Minigames.StatueDressup.Controllers
/// </summary> /// </summary>
public void UnregisterDecoration(DecorationDraggableInstance decoration) 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}"); Logging.Debug($"[StatueDecorationController] Decoration removed: {decoration.Data?.DecorationName}");
// Auto-save state // Auto-save state
@@ -157,7 +161,7 @@ namespace Minigames.StatueDressup.Controllers
/// </summary> /// </summary>
private void OnTakePhoto() private void OnTakePhoto()
{ {
if (minigameCompleted) if (_minigameCompleted)
{ {
Logging.Debug("[StatueDecorationController] Minigame already completed"); Logging.Debug("[StatueDecorationController] Minigame already completed");
return; return;
@@ -174,7 +178,7 @@ namespace Minigames.StatueDressup.Controllers
/// </summary> /// </summary>
private System.Collections.IEnumerator CapturePhotoCoroutine() private System.Collections.IEnumerator CapturePhotoCoroutine()
{ {
int decorationCount = placedDecorations.Count; int decorationCount = _placedDecorations.Count;
bool captureSuccess = false; bool captureSuccess = false;
string savedPhotoId = null; string savedPhotoId = null;
@@ -222,7 +226,7 @@ namespace Minigames.StatueDressup.Controllers
// Show completion feedback // Show completion feedback
ShowCompletionFeedback(); ShowCompletionFeedback();
minigameCompleted = true; _minigameCompleted = true;
} }
/// <summary> /// <summary>
@@ -290,7 +294,7 @@ namespace Minigames.StatueDressup.Controllers
}; };
// Collect all decoration placements // Collect all decoration placements
foreach (var decoration in placedDecorations) foreach (var decoration in _placedDecorations)
{ {
if (decoration == null || decoration.Data == null) continue; if (decoration == null || decoration.Data == null) continue;
@@ -455,6 +459,38 @@ namespace Minigames.StatueDressup.Controllers
return true; return true;
} }
/// <summary>
/// Show edit UI for a placed decoration
/// </summary>
public void ShowEditUI(DecorationDraggableInstance decoration)
{
if (decoration == null)
{
Logging.Warning("[StatueDecorationController] Cannot show edit UI - decoration is null");
return;
}
// Create edit UI instance if needed
if (_editUIInstance == null)
{
if (editUIPrefab == null)
{
Logging.Error("[StatueDecorationController] Edit UI prefab is not assigned!");
return;
}
// Instantiate as child of canvas (find appropriate parent)
Transform canvasTransform = statueArea != null ? statueArea.root : transform.root;
_editUIInstance = Instantiate(editUIPrefab, canvasTransform);
_editUIInstance.transform.SetAsLastSibling(); // Ensure it's on top
Logging.Debug("[StatueDecorationController] Created edit UI instance");
}
// Show the UI
_editUIInstance.Show(decoration);
}
/// <summary> /// <summary>
/// Cleanup when controller is destroyed /// Cleanup when controller is destroyed
/// </summary> /// </summary>

View File

@@ -21,36 +21,36 @@ namespace Minigames.StatueDressup.DragDrop
[SerializeField] private Image decorationImage; [SerializeField] private Image decorationImage;
[SerializeField] private CanvasGroup canvasGroup; [SerializeField] private CanvasGroup canvasGroup;
private DecorationData decorationData; private DecorationData _decorationData;
private RectTransform rectTransform; private RectTransform _rectTransform;
private Canvas canvas; private Canvas _canvas;
private Transform canvasParent; // Parent transform for dragging (usually canvas or draggable container) private Transform _canvasParent; // Parent transform for dragging (usually canvas or draggable container)
private RectTransform statueOutline; private RectTransform _statueOutline;
private Transform statueParent; private Transform _statueParent;
private StatueDecorationController controller; private StatueDecorationController _controller;
private AppleHills.Core.Settings.IStatueDressupSettings settings; private AppleHills.Core.Settings.IStatueDressupSettings _settings;
private System.Action onFinishedCallback; private System.Action _onFinishedCallback;
private System.Action onShowOutlineCallback; private System.Action _onShowOutlineCallback;
private System.Action onHideOutlineCallback; private System.Action _onHideOutlineCallback;
private bool isDragging; private bool _isDragging;
private bool isPlacedOnStatue; private bool _isPlacedOnStatue;
private Vector3 dragOffset; private Vector3 _dragOffset;
private bool dragStarted; // Track if drag actually started (vs just a click) private bool _dragStarted; // Track if drag actually started (vs just a click)
// Properties // Properties
public DecorationData Data => decorationData; public DecorationData Data => _decorationData;
public bool IsPlacedOnStatue => isPlacedOnStatue; public bool IsPlacedOnStatue => _isPlacedOnStatue;
private void Awake() private void Awake()
{ {
rectTransform = GetComponent<RectTransform>(); _rectTransform = GetComponent<RectTransform>();
canvas = GetComponentInParent<Canvas>(); _canvas = GetComponentInParent<Canvas>();
// Store initial parent for dragging context // Store initial parent for dragging context
if (transform.parent != null) if (transform.parent != null)
{ {
canvasParent = transform.parent; _canvasParent = transform.parent;
} }
if (canvasGroup == null) if (canvasGroup == null)
@@ -76,24 +76,24 @@ namespace Minigames.StatueDressup.DragDrop
return; return;
} }
decorationData = context.Data; _decorationData = context.Data;
statueOutline = context.StatueOutline; _statueOutline = context.StatueOutline;
statueParent = context.StatueParent; _statueParent = context.StatueParent;
controller = context.Controller; _controller = context.Controller;
settings = context.Settings; _settings = context.Settings;
onFinishedCallback = context.OnFinished; _onFinishedCallback = context.OnFinished;
onShowOutlineCallback = context.OnShowOutline; _onShowOutlineCallback = context.OnShowOutline;
onHideOutlineCallback = context.OnHideOutline; _onHideOutlineCallback = context.OnHideOutline;
// Handle placed vs new drag // Handle placed vs new drag
if (context.IsPlaced) if (context.IsPlaced)
{ {
isPlacedOnStatue = true; _isPlacedOnStatue = true;
isDragging = false; _isDragging = false;
statueParent = transform.parent; // Already parented to statue _statueParent = transform.parent; // Already parented to statue
if (context.CanvasParent != null) if (context.CanvasParent != null)
{ {
canvasParent = context.CanvasParent; _canvasParent = context.CanvasParent;
} }
} }
@@ -104,9 +104,9 @@ namespace Minigames.StatueDressup.DragDrop
} }
// Set authored size // Set authored size
if (rectTransform != null && context.Data != null) if (_rectTransform != null && context.Data != null)
{ {
rectTransform.sizeDelta = context.Data.AuthoredSize; _rectTransform.sizeDelta = context.Data.AuthoredSize;
} }
// Make interactive if placed (so it can be picked up) // Make interactive if placed (so it can be picked up)
@@ -154,22 +154,22 @@ namespace Minigames.StatueDressup.DragDrop
/// </summary> /// </summary>
public void StartDragFromIcon(PointerEventData eventData) public void StartDragFromIcon(PointerEventData eventData)
{ {
isDragging = true; _isDragging = true;
// Broadcast started dragging event (from grid) // Broadcast started dragging event (from grid)
var eventDataObj = new DecorationEventData(decorationData, gameObject, transform.position, fromStatue: false); var eventDataObj = new DecorationEventData(_decorationData, gameObject, transform.position, fromStatue: false);
DecorationEventsManager.BroadcastDecorationStartedDragging(eventDataObj); DecorationEventsManager.BroadcastDecorationStartedDragging(eventDataObj);
// Calculate offset from cursor to object center // Calculate offset from cursor to object center
RectTransformUtility.ScreenPointToLocalPointInRectangle( RectTransformUtility.ScreenPointToLocalPointInRectangle(
canvas.transform as RectTransform, _canvas.transform as RectTransform,
eventData.position, eventData.position,
eventData.pressEventCamera, eventData.pressEventCamera,
out Vector2 localPoint); out Vector2 localPoint);
dragOffset = rectTransform.localPosition - (Vector3)localPoint; _dragOffset = _rectTransform.localPosition - (Vector3)localPoint;
Logging.Debug($"[DecorationDraggableInstance] Started drag from icon: {decorationData?.DecorationName}"); Logging.Debug($"[DecorationDraggableInstance] Started drag from icon: {_decorationData?.DecorationName}");
} }
/// <summary> /// <summary>
@@ -177,16 +177,16 @@ namespace Minigames.StatueDressup.DragDrop
/// </summary> /// </summary>
public void ContinueDrag(PointerEventData eventData) public void ContinueDrag(PointerEventData eventData)
{ {
if (!isDragging) return; if (!_isDragging) return;
// Update position to follow cursor // Update position to follow cursor
RectTransformUtility.ScreenPointToLocalPointInRectangle( RectTransformUtility.ScreenPointToLocalPointInRectangle(
canvas.transform as RectTransform, _canvas.transform as RectTransform,
eventData.position, eventData.position,
eventData.pressEventCamera, eventData.pressEventCamera,
out Vector2 localPoint); out Vector2 localPoint);
rectTransform.localPosition = localPoint + (Vector2)dragOffset; _rectTransform.localPosition = localPoint + (Vector2)_dragOffset;
} }
/// <summary> /// <summary>
@@ -194,12 +194,12 @@ namespace Minigames.StatueDressup.DragDrop
/// </summary> /// </summary>
public void EndDrag(PointerEventData eventData) public void EndDrag(PointerEventData eventData)
{ {
isDragging = false; _isDragging = false;
Logging.Debug($"[DecorationDraggableInstance] Drag ended: {decorationData?.DecorationName}"); Logging.Debug($"[DecorationDraggableInstance] Drag ended: {_decorationData?.DecorationName}");
// Broadcast finished dragging event // Broadcast finished dragging event
var eventDataObj = new DecorationEventData(decorationData, gameObject, transform.position, fromStatue: isPlacedOnStatue); var eventDataObj = new DecorationEventData(_decorationData, gameObject, transform.position, fromStatue: _isPlacedOnStatue);
DecorationEventsManager.BroadcastDecorationFinishedDragging(eventDataObj); DecorationEventsManager.BroadcastDecorationFinishedDragging(eventDataObj);
// Check if overlapping with statue // Check if overlapping with statue
@@ -218,20 +218,20 @@ namespace Minigames.StatueDressup.DragDrop
/// </summary> /// </summary>
private bool IsOverlappingStatue() private bool IsOverlappingStatue()
{ {
if (statueOutline == null || rectTransform == null) if (_statueOutline == null || _rectTransform == null)
{ {
Logging.Warning($"[DecorationDraggableInstance] Cannot check overlap - statueOutline or RectTransform is null"); Logging.Warning($"[DecorationDraggableInstance] Cannot check overlap - statueOutline or RectTransform is null");
return false; return false;
} }
// Get bounds of this item in world space // Get bounds of this item in world space
Rect itemRect = GetWorldRect(rectTransform); Rect itemRect = GetWorldRect(_rectTransform);
Rect outlineRect = GetWorldRect(statueOutline); Rect outlineRect = GetWorldRect(_statueOutline);
// Check for any overlap // Check for any overlap
bool overlaps = itemRect.Overlaps(outlineRect); bool overlaps = itemRect.Overlaps(outlineRect);
Logging.Debug($"[DecorationDraggableInstance] Overlap check: {decorationData?.DecorationName}, overlaps={overlaps}"); Logging.Debug($"[DecorationDraggableInstance] Overlap check: {_decorationData?.DecorationName}, overlaps={overlaps}");
return overlaps; return overlaps;
} }
@@ -255,28 +255,28 @@ namespace Minigames.StatueDressup.DragDrop
/// </summary> /// </summary>
private void PlaceOnStatue() private void PlaceOnStatue()
{ {
Logging.Debug($"[DecorationDraggableInstance] Placing on statue: {decorationData?.DecorationName}"); Logging.Debug($"[DecorationDraggableInstance] Placing on statue: {_decorationData?.DecorationName}");
isPlacedOnStatue = true; _isPlacedOnStatue = true;
// Broadcast dropped on statue event // Broadcast dropped on statue event
var eventDataObj = new DecorationEventData(decorationData, gameObject, transform.position, fromStatue: false); var eventDataObj = new DecorationEventData(_decorationData, gameObject, transform.position, fromStatue: false);
DecorationEventsManager.BroadcastDecorationDroppedOnStatue(eventDataObj); DecorationEventsManager.BroadcastDecorationDroppedOnStatue(eventDataObj);
// Move to statue parent if specified // Move to statue parent if specified
if (statueParent != null && transform.parent != statueParent) if (_statueParent != null && transform.parent != _statueParent)
{ {
transform.SetParent(statueParent, true); // Keep world position transform.SetParent(_statueParent, true); // Keep world position
} }
// Register with controller // Register with controller
if (controller != null) if (_controller != null)
{ {
controller.RegisterDecoration(this); _controller.RegisterDecoration(this);
} }
// Notify menu controller to hide outline // Notify menu controller to hide outline
onFinishedCallback?.Invoke(); _onFinishedCallback?.Invoke();
} }
/// <summary> /// <summary>
@@ -284,22 +284,22 @@ namespace Minigames.StatueDressup.DragDrop
/// </summary> /// </summary>
private void PlayPopOutAndDestroy() private void PlayPopOutAndDestroy()
{ {
Logging.Debug($"[DecorationDraggableInstance] Pop-out and destroy: {decorationData?.DecorationName}"); Logging.Debug($"[DecorationDraggableInstance] Pop-out and destroy: {_decorationData?.DecorationName}");
// Broadcast dropped out event (animation starting) // Broadcast dropped out event (animation starting)
var eventDataObj = new DecorationEventData(decorationData, gameObject, transform.position, fromStatue: false); var eventDataObj = new DecorationEventData(_decorationData, gameObject, transform.position, fromStatue: false);
DecorationEventsManager.BroadcastDecorationDroppedOut(eventDataObj); DecorationEventsManager.BroadcastDecorationDroppedOut(eventDataObj);
// Notify menu controller to hide outline immediately // Notify menu controller to hide outline immediately
onFinishedCallback?.Invoke(); _onFinishedCallback?.Invoke();
float duration = settings?.PlacementAnimationDuration ?? StatueDressupConstants.DefaultAnimationDuration; float duration = _settings?.PlacementAnimationDuration ?? StatueDressupConstants.DefaultAnimationDuration;
// Play pop-out with fade animation // Play pop-out with fade animation
TweenAnimationUtility.PopOutWithFade(transform, canvasGroup, duration, () => TweenAnimationUtility.PopOutWithFade(transform, canvasGroup, duration, () =>
{ {
// Broadcast finished dropping out event (animation complete) // Broadcast finished dropping out event (animation complete)
var finalEventData = new DecorationEventData(decorationData, gameObject, transform.position, fromStatue: false); var finalEventData = new DecorationEventData(_decorationData, gameObject, transform.position, fromStatue: false);
DecorationEventsManager.BroadcastDecorationFinishedDroppingOut(finalEventData); DecorationEventsManager.BroadcastDecorationFinishedDroppingOut(finalEventData);
Destroy(gameObject); Destroy(gameObject);
@@ -311,26 +311,26 @@ namespace Minigames.StatueDressup.DragDrop
/// </summary> /// </summary>
public void StartDragFromStatue(PointerEventData eventData) public void StartDragFromStatue(PointerEventData eventData)
{ {
Logging.Debug($"[DecorationDraggableInstance] StartDragFromStatue called for: {decorationData?.DecorationName}"); Logging.Debug($"[DecorationDraggableInstance] StartDragFromStatue called for: {_decorationData?.DecorationName}");
Logging.Debug($"[DecorationDraggableInstance] Show outline callback is null: {onShowOutlineCallback == null}"); Logging.Debug($"[DecorationDraggableInstance] Show outline callback is null: {_onShowOutlineCallback == null}");
if (controller != null) if (_controller != null)
{ {
controller.UnregisterDecoration(this); _controller.UnregisterDecoration(this);
} }
isPlacedOnStatue = false; _isPlacedOnStatue = false;
isDragging = true; _isDragging = true;
// Broadcast started dragging event (from statue) // Broadcast started dragging event (from statue)
var eventDataObj = new DecorationEventData(decorationData, gameObject, transform.position, fromStatue: true); var eventDataObj = new DecorationEventData(_decorationData, gameObject, transform.position, fromStatue: true);
DecorationEventsManager.BroadcastDecorationStartedDragging(eventDataObj); DecorationEventsManager.BroadcastDecorationStartedDragging(eventDataObj);
// Show statue outline when picking up from statue // Show statue outline when picking up from statue
if (onShowOutlineCallback != null) if (_onShowOutlineCallback != null)
{ {
Logging.Debug("[DecorationDraggableInstance] Invoking show outline callback"); Logging.Debug("[DecorationDraggableInstance] Invoking show outline callback");
onShowOutlineCallback.Invoke(); _onShowOutlineCallback.Invoke();
} }
else else
{ {
@@ -338,24 +338,24 @@ namespace Minigames.StatueDressup.DragDrop
} }
// Reparent to canvas for dragging (so coordinates work correctly) // Reparent to canvas for dragging (so coordinates work correctly)
if (canvasParent != null && transform.parent != canvasParent) if (_canvasParent != null && transform.parent != _canvasParent)
{ {
// Store world position before reparenting // Store world position before reparenting
Vector3 worldPos = transform.position; Vector3 worldPos = transform.position;
transform.SetParent(canvasParent, false); transform.SetParent(_canvasParent, false);
transform.position = worldPos; // Restore world position transform.position = worldPos; // Restore world position
} }
// Calculate offset using proper camera // Calculate offset using proper camera
RectTransformUtility.ScreenPointToLocalPointInRectangle( RectTransformUtility.ScreenPointToLocalPointInRectangle(
canvas.transform as RectTransform, _canvas.transform as RectTransform,
eventData.position, eventData.position,
eventData.pressEventCamera, eventData.pressEventCamera,
out Vector2 localPoint); out Vector2 localPoint);
dragOffset = rectTransform.localPosition - (Vector3)localPoint; _dragOffset = _rectTransform.localPosition - (Vector3)localPoint;
Logging.Debug($"[DecorationDraggableInstance] Started drag from statue: {decorationData?.DecorationName}"); Logging.Debug($"[DecorationDraggableInstance] Started drag from statue: {_decorationData?.DecorationName}");
} }
#region Pointer Event Handlers #region Pointer Event Handlers
@@ -366,15 +366,23 @@ namespace Minigames.StatueDressup.DragDrop
public void OnPointerClick(PointerEventData eventData) public void OnPointerClick(PointerEventData eventData)
{ {
// Only handle clicks when placed on statue and not currently dragging // Only handle clicks when placed on statue and not currently dragging
if (!isPlacedOnStatue || dragStarted) return; if (!_isPlacedOnStatue || _dragStarted) return;
Logging.Debug($"[DecorationDraggableInstance] Decoration tapped: {decorationData?.DecorationName}"); Logging.Debug($"[DecorationDraggableInstance] Decoration tapped: {_decorationData?.DecorationName}");
// Broadcast tap event // Broadcast tap event
var eventDataObj = new DecorationEventData(decorationData, gameObject, transform.position, fromStatue: true); var eventDataObj = new DecorationEventData(_decorationData, gameObject, transform.position, fromStatue: true);
DecorationEventsManager.BroadcastDecorationTappedOnStatue(eventDataObj); DecorationEventsManager.BroadcastDecorationTappedOnStatue(eventDataObj);
// Future: Open detail view, play sound effect, show info popup, etc. // Show edit UI
if (_controller != null)
{
_controller.ShowEditUI(this);
}
else
{
Logging.Warning("[DecorationDraggableInstance] Cannot show edit UI - controller reference is null");
}
} }
/// <summary> /// <summary>
@@ -383,9 +391,9 @@ namespace Minigames.StatueDressup.DragDrop
public void OnBeginDrag(PointerEventData eventData) public void OnBeginDrag(PointerEventData eventData)
{ {
// Only handle drag from statue if already placed // Only handle drag from statue if already placed
if (!isPlacedOnStatue) return; if (!_isPlacedOnStatue) return;
dragStarted = true; _dragStarted = true;
StartDragFromStatue(eventData); StartDragFromStatue(eventData);
} }
@@ -394,7 +402,7 @@ namespace Minigames.StatueDressup.DragDrop
/// </summary> /// </summary>
public void OnDrag(PointerEventData eventData) public void OnDrag(PointerEventData eventData)
{ {
if (!isDragging) return; if (!_isDragging) return;
ContinueDrag(eventData); ContinueDrag(eventData);
} }
@@ -404,9 +412,9 @@ namespace Minigames.StatueDressup.DragDrop
/// </summary> /// </summary>
public void OnEndDrag(PointerEventData eventData) public void OnEndDrag(PointerEventData eventData)
{ {
if (!isDragging) return; if (!_isDragging) return;
dragStarted = false; _dragStarted = false;
EndDrag(eventData); EndDrag(eventData);
} }

View File

@@ -0,0 +1,288 @@
using Core;
using UnityEngine;
using UnityEngine.UI;
using Minigames.StatueDressup.DragDrop;
namespace Minigames.StatueDressup.UI
{
/// <summary>
/// UI panel for editing a placed decoration's scale and rotation.
/// Shows sliders for scale (0.1x - 2x) and rotation (-180° to 180°).
/// Changes are applied immediately to the decoration's transform.
/// </summary>
public class DecorationEditUI : MonoBehaviour
{
[Header("UI References")]
[SerializeField] private Slider scaleSlider;
[SerializeField] private Slider rotationSlider;
[SerializeField] private Button confirmButton;
[SerializeField] private Button resetButton;
[SerializeField] private CanvasGroup canvasGroup;
[Header("Slider Ranges")]
[SerializeField] private float minScale = 0.1f;
[SerializeField] private float maxScale = 2.0f;
[SerializeField] private float minRotation = -180f;
[SerializeField] private float maxRotation = 180f;
private DecorationDraggableInstance _targetDecoration;
private RectTransform _rectTransform;
private float _originalRotation;
private bool _isInitialized;
private void Awake()
{
// Get RectTransform
_rectTransform = GetComponent<RectTransform>();
// Ensure canvas group exists
if (canvasGroup == null)
{
canvasGroup = GetComponent<CanvasGroup>();
if (canvasGroup == null)
{
canvasGroup = gameObject.AddComponent<CanvasGroup>();
}
}
// Setup slider ranges
if (scaleSlider != null)
{
scaleSlider.minValue = minScale;
scaleSlider.maxValue = maxScale;
scaleSlider.onValueChanged.AddListener(OnScaleChanged);
}
if (rotationSlider != null)
{
rotationSlider.minValue = minRotation;
rotationSlider.maxValue = maxRotation;
rotationSlider.onValueChanged.AddListener(OnRotationChanged);
}
// Setup buttons
if (confirmButton != null)
{
confirmButton.onClick.AddListener(OnConfirm);
}
if (resetButton != null)
{
resetButton.onClick.AddListener(OnReset);
}
// Start hidden
gameObject.SetActive(false);
_isInitialized = true;
}
private void OnDestroy()
{
// Clean up listeners
if (scaleSlider != null)
{
scaleSlider.onValueChanged.RemoveListener(OnScaleChanged);
}
if (rotationSlider != null)
{
rotationSlider.onValueChanged.RemoveListener(OnRotationChanged);
}
if (confirmButton != null)
{
confirmButton.onClick.RemoveListener(OnConfirm);
}
if (resetButton != null)
{
resetButton.onClick.RemoveListener(OnReset);
}
}
/// <summary>
/// Show the edit UI for the given decoration
/// </summary>
public void Show(DecorationDraggableInstance decoration)
{
if (!_isInitialized)
{
Logging.Error("[DecorationEditUI] Attempted to show before initialization!");
return;
}
if (decoration == null)
{
Logging.Error("[DecorationEditUI] Cannot show edit UI - decoration is null!");
return;
}
_targetDecoration = decoration;
// Store original rotation for reference
_originalRotation = decoration.transform.localEulerAngles.z;
// Normalize rotation to -180 to 180 range
if (_originalRotation > 180f)
{
_originalRotation -= 360f;
}
// Initialize sliders from current transform values
if (scaleSlider != null)
{
// Use X component for uniform scale
float currentScale = decoration.transform.localScale.x;
scaleSlider.value = Mathf.Clamp(currentScale, minScale, maxScale);
}
if (rotationSlider != null)
{
rotationSlider.value = Mathf.Clamp(_originalRotation, minRotation, maxRotation);
}
// Disable decoration raycasts during editing
CanvasGroup decorationCanvasGroup = decoration.GetComponent<CanvasGroup>();
if (decorationCanvasGroup != null)
{
decorationCanvasGroup.blocksRaycasts = false;
}
// Position UI centered over the decoration (context menu style)
PositionOverDecoration(decoration);
// Show UI immediately
gameObject.SetActive(true);
if (canvasGroup != null)
{
canvasGroup.alpha = 1f;
}
Logging.Debug($"[DecorationEditUI] Showing edit UI for: {decoration.Data?.DecorationName}");
}
/// <summary>
/// Position the UI centered over the decoration (context menu style)
/// </summary>
private void PositionOverDecoration(DecorationDraggableInstance decoration)
{
if (_rectTransform == null || decoration == null) return;
// Get decoration's world position
Vector3 decorationWorldPos = decoration.transform.position;
// Convert to canvas space if using screen space overlay
Canvas canvas = GetComponentInParent<Canvas>();
if (canvas != null && canvas.renderMode == RenderMode.ScreenSpaceOverlay)
{
// For overlay canvas, world position is already correct
_rectTransform.position = decorationWorldPos;
}
else if (canvas != null)
{
// For other canvas modes, convert properly
RectTransformUtility.ScreenPointToLocalPointInRectangle(
canvas.transform as RectTransform,
RectTransformUtility.WorldToScreenPoint(canvas.worldCamera, decorationWorldPos),
canvas.worldCamera,
out Vector2 localPoint
);
_rectTransform.localPosition = localPoint;
}
Logging.Debug($"[DecorationEditUI] Positioned at decoration location: {decorationWorldPos}");
}
/// <summary>
/// Hide the edit UI
/// </summary>
public void Hide()
{
if (_targetDecoration != null)
{
// Re-enable decoration raycasts
CanvasGroup decorationCanvasGroup = _targetDecoration.GetComponent<CanvasGroup>();
if (decorationCanvasGroup != null)
{
decorationCanvasGroup.blocksRaycasts = true;
}
}
_targetDecoration = null;
gameObject.SetActive(false);
Logging.Debug("[DecorationEditUI] Edit UI hidden");
}
/// <summary>
/// Handle scale slider change
/// </summary>
private void OnScaleChanged(float value)
{
if (_targetDecoration == null) return;
// Apply uniform scale (X, Y, Z all the same)
_targetDecoration.transform.localScale = Vector3.one * value;
}
/// <summary>
/// Handle rotation slider change
/// </summary>
private void OnRotationChanged(float value)
{
if (_targetDecoration == null) return;
// Apply Z rotation only
_targetDecoration.transform.localEulerAngles = new Vector3(0f, 0f, value);
}
/// <summary>
/// Handle confirm button - save changes and close
/// </summary>
private void OnConfirm()
{
if (_targetDecoration != null)
{
// Trigger auto-save through the controller
var controller = Controllers.StatueDecorationController.Instance;
if (controller != null)
{
// The controller's RegisterDecoration already triggers SaveStatueState
// Since the decoration is already registered, we just need to trigger a save
// This happens automatically on the next RegisterDecoration/UnregisterDecoration call
Logging.Debug("[DecorationEditUI] Changes confirmed - will be auto-saved");
}
}
Hide();
}
/// <summary>
/// Handle reset button - restore original values
/// </summary>
private void OnReset()
{
if (_targetDecoration == null) return;
// Reset to authored size (scale 1.0) and 0 rotation
float defaultScale = 1.0f;
float defaultRotation = 0f;
if (scaleSlider != null)
{
scaleSlider.value = defaultScale;
}
if (rotationSlider != null)
{
rotationSlider.value = defaultRotation;
}
// Values are applied through the slider callbacks
Logging.Debug("[DecorationEditUI] Reset to defaults");
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 7f3e2a1b9c4d5e6f7a8b9c0d1e2f3a4b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: