First pass over MPV for the cement-statue-sticker minigame (#63)
Co-authored-by: Michal Pikulski <michal.a.pikulski@gmail.com> Co-authored-by: Michal Pikulski <michal@foolhardyhorizons.com> Reviewed-on: #63
This commit is contained in:
@@ -0,0 +1,242 @@
|
||||
using Core;
|
||||
using Minigames.StatueDressup.Controllers;
|
||||
using Minigames.StatueDressup.Data;
|
||||
using Minigames.StatueDressup.Utils;
|
||||
using UnityEngine;
|
||||
using UnityEngine.EventSystems;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace Minigames.StatueDressup.DragDrop
|
||||
{
|
||||
/// <summary>
|
||||
/// Draggable instance of a decoration that can be placed on the statue.
|
||||
/// Created dynamically when dragging from menu or picking up from statue.
|
||||
/// Destroyed if dropped outside statue area.
|
||||
/// </summary>
|
||||
public class DecorationDraggableInstance : MonoBehaviour
|
||||
{
|
||||
[Header("References")]
|
||||
[SerializeField] private Image decorationImage;
|
||||
[SerializeField] private CanvasGroup canvasGroup;
|
||||
|
||||
private DecorationData _decorationData;
|
||||
private RectTransform _rectTransform;
|
||||
private Canvas _canvas;
|
||||
private RectTransform _statueOutline;
|
||||
private Transform _statueParent;
|
||||
private StatueDecorationController _controller;
|
||||
private AppleHills.Core.Settings.IStatueDressupSettings _settings;
|
||||
private System.Action _onFinishedCallback;
|
||||
|
||||
private bool _isDragging;
|
||||
private bool _isPlacedOnStatue;
|
||||
private Vector3 _dragOffset;
|
||||
|
||||
// Properties
|
||||
public DecorationData Data => _decorationData;
|
||||
public bool IsPlacedOnStatue => _isPlacedOnStatue;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
_rectTransform = GetComponent<RectTransform>();
|
||||
_canvas = GetComponentInParent<Canvas>();
|
||||
|
||||
if (canvasGroup == null)
|
||||
{
|
||||
canvasGroup = gameObject.AddComponent<CanvasGroup>();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the draggable instance
|
||||
/// </summary>
|
||||
public void Initialize(DecorationData data, RectTransform statueOutline, Transform statueParent,
|
||||
StatueDecorationController controller, AppleHills.Core.Settings.IStatueDressupSettings settings,
|
||||
System.Action onFinishedCallback)
|
||||
{
|
||||
_decorationData = data;
|
||||
_statueOutline = statueOutline;
|
||||
_statueParent = statueParent;
|
||||
_controller = controller;
|
||||
_settings = settings;
|
||||
_onFinishedCallback = onFinishedCallback;
|
||||
|
||||
// Set sprite
|
||||
if (decorationImage != null && data != null && data.DecorationSprite != null)
|
||||
{
|
||||
decorationImage.sprite = data.DecorationSprite;
|
||||
}
|
||||
|
||||
// Set authored size
|
||||
if (_rectTransform != null && data != null)
|
||||
{
|
||||
_rectTransform.sizeDelta = data.AuthoredSize;
|
||||
}
|
||||
|
||||
Logging.Debug($"[DecorationDraggableInstance] Initialized: {data?.DecorationName}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Start dragging from icon
|
||||
/// </summary>
|
||||
public void StartDragFromIcon(PointerEventData eventData)
|
||||
{
|
||||
_isDragging = true;
|
||||
|
||||
// Calculate offset from cursor to object center
|
||||
RectTransformUtility.ScreenPointToLocalPointInRectangle(
|
||||
_canvas.transform as RectTransform,
|
||||
eventData.position,
|
||||
eventData.pressEventCamera,
|
||||
out Vector2 localPoint);
|
||||
|
||||
_dragOffset = _rectTransform.localPosition - (Vector3)localPoint;
|
||||
|
||||
Logging.Debug($"[DecorationDraggableInstance] Started drag from icon: {_decorationData?.DecorationName}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Continue dragging
|
||||
/// </summary>
|
||||
public void ContinueDrag(PointerEventData eventData)
|
||||
{
|
||||
if (!_isDragging) return;
|
||||
|
||||
// Update position to follow cursor
|
||||
RectTransformUtility.ScreenPointToLocalPointInRectangle(
|
||||
_canvas.transform as RectTransform,
|
||||
eventData.position,
|
||||
eventData.pressEventCamera,
|
||||
out Vector2 localPoint);
|
||||
|
||||
_rectTransform.localPosition = localPoint + (Vector2)_dragOffset;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// End drag - check placement
|
||||
/// </summary>
|
||||
public void EndDrag(PointerEventData eventData)
|
||||
{
|
||||
_isDragging = false;
|
||||
|
||||
Logging.Debug($"[DecorationDraggableInstance] Drag ended: {_decorationData?.DecorationName}");
|
||||
|
||||
// Check if overlapping with statue
|
||||
if (IsOverlappingStatue())
|
||||
{
|
||||
PlaceOnStatue();
|
||||
}
|
||||
else
|
||||
{
|
||||
PlayPopOutAndDestroy();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if item overlaps with statue outline
|
||||
/// </summary>
|
||||
private bool IsOverlappingStatue()
|
||||
{
|
||||
if (_statueOutline == null || _rectTransform == null)
|
||||
{
|
||||
Logging.Warning($"[DecorationDraggableInstance] Cannot check overlap - statueOutline or RectTransform is null");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get bounds of this item in world space
|
||||
Rect itemRect = GetWorldRect(_rectTransform);
|
||||
Rect outlineRect = GetWorldRect(_statueOutline);
|
||||
|
||||
// Check for any overlap
|
||||
bool overlaps = itemRect.Overlaps(outlineRect);
|
||||
|
||||
Logging.Debug($"[DecorationDraggableInstance] Overlap check: {_decorationData?.DecorationName}, overlaps={overlaps}");
|
||||
|
||||
return overlaps;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get world space rect for a RectTransform
|
||||
/// </summary>
|
||||
private Rect GetWorldRect(RectTransform rectTransform)
|
||||
{
|
||||
Vector3[] corners = new Vector3[4];
|
||||
rectTransform.GetWorldCorners(corners);
|
||||
|
||||
Vector3 bottomLeft = corners[0];
|
||||
Vector3 topRight = corners[2];
|
||||
|
||||
return new Rect(bottomLeft.x, bottomLeft.y, topRight.x - bottomLeft.x, topRight.y - bottomLeft.y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Place item on statue at current position
|
||||
/// </summary>
|
||||
private void PlaceOnStatue()
|
||||
{
|
||||
Logging.Debug($"[DecorationDraggableInstance] Placing on statue: {_decorationData?.DecorationName}");
|
||||
|
||||
_isPlacedOnStatue = true;
|
||||
|
||||
// Move to statue parent if specified
|
||||
if (_statueParent != null && transform.parent != _statueParent)
|
||||
{
|
||||
transform.SetParent(_statueParent, true); // Keep world position
|
||||
}
|
||||
|
||||
// Register with controller
|
||||
if (_controller != null)
|
||||
{
|
||||
_controller.RegisterDecoration(this);
|
||||
}
|
||||
|
||||
// Notify menu controller to hide outline
|
||||
_onFinishedCallback?.Invoke();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Play pop-out animation and destroy
|
||||
/// </summary>
|
||||
private void PlayPopOutAndDestroy()
|
||||
{
|
||||
Logging.Debug($"[DecorationDraggableInstance] Pop-out and destroy: {_decorationData?.DecorationName}");
|
||||
|
||||
// Notify menu controller to hide outline immediately
|
||||
_onFinishedCallback?.Invoke();
|
||||
|
||||
float duration = _settings?.PlacementAnimationDuration ?? 0.3f;
|
||||
|
||||
// Play pop-out with fade animation
|
||||
TweenAnimationUtility.PopOutWithFade(transform, canvasGroup, duration, () =>
|
||||
{
|
||||
Destroy(gameObject);
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Allow picking up from statue for repositioning
|
||||
/// </summary>
|
||||
public void StartDragFromStatue(Vector3 pointerPosition)
|
||||
{
|
||||
if (_controller != null)
|
||||
{
|
||||
_controller.UnregisterDecoration(this);
|
||||
}
|
||||
|
||||
_isPlacedOnStatue = false;
|
||||
_isDragging = true;
|
||||
|
||||
// Calculate offset
|
||||
RectTransformUtility.ScreenPointToLocalPointInRectangle(
|
||||
_canvas.transform as RectTransform,
|
||||
pointerPosition,
|
||||
null,
|
||||
out Vector2 localPoint);
|
||||
|
||||
_dragOffset = _rectTransform.localPosition - (Vector3)localPoint;
|
||||
|
||||
Logging.Debug($"[DecorationDraggableInstance] Started drag from statue: {_decorationData?.DecorationName}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e4659fd035c74a79af0311de9e17f44a
|
||||
timeCreated: 1763991638
|
||||
@@ -0,0 +1,100 @@
|
||||
using Core;
|
||||
using Minigames.StatueDressup.Data;
|
||||
using UnityEngine;
|
||||
using UnityEngine.EventSystems;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace Minigames.StatueDressup.DragDrop
|
||||
{
|
||||
/// <summary>
|
||||
/// Static grid icon for decorations in the menu.
|
||||
/// Handles tap and drag initiation, but doesn't move itself.
|
||||
/// Spawns a draggable instance when drag starts.
|
||||
/// </summary>
|
||||
public class DecorationGridIcon : MonoBehaviour, IPointerClickHandler, IBeginDragHandler, IDragHandler, IEndDragHandler
|
||||
{
|
||||
[Header("References")]
|
||||
[SerializeField] private Image iconImage;
|
||||
[SerializeField] private DecorationData decorationData;
|
||||
|
||||
private Controllers.DecorationMenuController _menuController;
|
||||
private DecorationDraggableInstance _activeDraggableInstance;
|
||||
|
||||
// Properties
|
||||
public DecorationData Data => decorationData;
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the icon with decoration data
|
||||
/// </summary>
|
||||
public void Initialize(DecorationData data, Controllers.DecorationMenuController controller)
|
||||
{
|
||||
decorationData = data;
|
||||
_menuController = controller;
|
||||
|
||||
if (iconImage != null && data != null && data.DecorationSprite != null)
|
||||
{
|
||||
iconImage.sprite = data.DecorationSprite;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle tap/click on icon
|
||||
/// </summary>
|
||||
public void OnPointerClick(PointerEventData eventData)
|
||||
{
|
||||
// Only process clicks if we're not dragging
|
||||
if (_activeDraggableInstance == null)
|
||||
{
|
||||
Logging.Debug($"[DecorationGridIcon] Item tapped: {decorationData?.DecorationName}");
|
||||
// Future: Open detail view, preview, etc.
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle drag start - spawn draggable instance
|
||||
/// </summary>
|
||||
public void OnBeginDrag(PointerEventData eventData)
|
||||
{
|
||||
if (_menuController == null || decorationData == null)
|
||||
{
|
||||
Logging.Warning("[DecorationGridIcon] Cannot start drag - missing controller or data");
|
||||
return;
|
||||
}
|
||||
|
||||
Logging.Debug($"[DecorationGridIcon] Starting drag for: {decorationData.DecorationName}");
|
||||
|
||||
// Spawn draggable instance at cursor position
|
||||
_activeDraggableInstance = _menuController.SpawnDraggableInstance(decorationData, eventData.position);
|
||||
|
||||
// Start the drag on the spawned instance
|
||||
if (_activeDraggableInstance != null)
|
||||
{
|
||||
_activeDraggableInstance.StartDragFromIcon(eventData);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Forward drag events to the active draggable instance
|
||||
/// </summary>
|
||||
public void OnDrag(PointerEventData eventData)
|
||||
{
|
||||
if (_activeDraggableInstance != null)
|
||||
{
|
||||
_activeDraggableInstance.ContinueDrag(eventData);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Forward drag end to the active draggable instance
|
||||
/// </summary>
|
||||
public void OnEndDrag(PointerEventData eventData)
|
||||
{
|
||||
if (_activeDraggableInstance != null)
|
||||
{
|
||||
_activeDraggableInstance.EndDrag(eventData);
|
||||
_activeDraggableInstance = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9c806d80a321498c9f33f13d7a31065c
|
||||
timeCreated: 1763991611
|
||||
Reference in New Issue
Block a user