using CustomComponent;
using DG.Tweening;
using Definition.Enum;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
namespace UI
{
///
/// 可拖拽拼装部件:负责拖拽交互、出生点回退与放置到槽位。
///
[RequireComponent(typeof(RectTransform))]
public class CombineDraggablePart : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler, IPointerEnterHandler, IPointerExitHandler
{
#region Inspector Config
[SerializeField] private CombinePartType _partType = CombinePartType.Dou;
[SerializeField] private bool _lockAfterPlaced = true;
[SerializeField] private RectTransform _rectTransform;
[SerializeField] private CanvasGroup _canvasGroup;
[SerializeField] private Image _partImage;
[SerializeField] private float _defaultScaleMultiplier = 0.8f;
[SerializeField] private float _hoverScaleMultiplier = 1f;
[SerializeField] private float _hoverTweenDuration = 0.15f;
[SerializeField] private Ease _hoverTweenEase = Ease.OutQuad;
[SerializeField] private float _returnToSpawnDuration = 0.4f;
[SerializeField] private Ease _returnToSpawnEase = Ease.OutCubic;
#endregion
#region Spawn State
private bool _spawnStateCached;
private Transform _spawnParent;
private Vector3 _spawnWorldPosition = Vector3.zero;
private Quaternion _spawnWorldRotation = Quaternion.identity;
private Vector3 _spawnWorldScale = Vector3.one;
private int _spawnSiblingIndex;
#endregion
#region Runtime State
private CombineComponent _controller;
private CombineSlot _currentSlot;
private bool _isPlaced;
private bool _isLocked;
private bool _isDragging;
private Vector3 _originalLocalScale = Vector3.one;
private Tween _scaleTween;
private Tween _returnTween;
private Vector2 _dragPointerOffset = Vector2.zero;
#endregion
#region Public Query
public CombinePartType PartType => _partType;
#endregion
#region Unity Lifecycle
private void Awake()
{
if (_rectTransform == null)
{
_rectTransform = transform as RectTransform;
}
if (_canvasGroup == null)
{
_canvasGroup = gameObject.GetOrAddComponent();
}
_originalLocalScale = _rectTransform.localScale;
ApplyScaleImmediate(_defaultScaleMultiplier);
}
private void OnDestroy()
{
KillScaleTween();
KillReturnTween();
}
#endregion
#region Setup
///
/// 绑定玩法控制器。
///
public void BindController(CombineComponent controller)
{
_controller = controller;
}
public void ConfigureRuntime(CombinePartType partType, Sprite sprite)
{
_partType = partType;
if (_partImage == null)
{
_partImage = GetComponentInChildren(true);
}
if (_partImage != null)
{
_partImage.sprite = sprite;
if (sprite != null)
{
_partImage.SetNativeSize();
}
}
}
///
/// 缓存出生点状态(仅首次缓存)。
///
public void CacheSpawnState()
{
if (_spawnStateCached)
{
return;
}
_spawnStateCached = true;
_spawnParent = _rectTransform.parent;
_spawnWorldPosition = _rectTransform.position;
_spawnWorldRotation = _rectTransform.rotation;
_spawnWorldScale = _rectTransform.localScale;
_spawnSiblingIndex = _rectTransform.GetSiblingIndex();
}
///
/// 重置部件到出生点,并清理槽位占用状态。
///
public void ResetToSpawn()
{
ClearSlotOccupancy();
_isDragging = false;
_isPlaced = false;
_isLocked = false;
ReturnToSpawn();
}
#endregion
#region Drag Flow
///
/// 开始拖拽:关闭射线阻挡并切到拖拽层级。
///
public void OnBeginDrag(PointerEventData eventData)
{
if (!CanStartDrag())
{
return;
}
KillReturnTween();
_isDragging = true;
_canvasGroup.blocksRaycasts = false;
MoveToDragRoot();
_rectTransform.SetAsLastSibling();
CacheDragOffset(eventData);
}
///
/// 拖拽中:按屏幕坐标映射到父节点局部坐标更新位置。
///
public void OnDrag(PointerEventData eventData)
{
if (!CanDrag(eventData))
{
return;
}
if (TryGetDragParent(out RectTransform dragParent) &&
TryGetPointerLocalPosition(eventData, dragParent, out Vector2 pointerLocalPosition))
{
_rectTransform.anchoredPosition = pointerLocalPosition + _dragPointerOffset;
return;
}
_rectTransform.anchoredPosition += eventData.delta;
}
///
/// 结束拖拽:恢复射线阻挡;未成功放置则回到出生点。
///
public void OnEndDrag(PointerEventData eventData)
{
_isDragging = false;
if (_isLocked)
{
return;
}
_canvasGroup.blocksRaycasts = true;
if (!_isPlaced)
{
if (ShouldKeepDroppedInPartArea(eventData))
{
return;
}
ReturnToSpawnAnimated();
}
}
public void OnPointerEnter(PointerEventData eventData)
{
if (_isLocked || _isPlaced || _isDragging)
{
return;
}
PlayScaleTween(_hoverScaleMultiplier);
}
public void OnPointerExit(PointerEventData eventData)
{
if (_isLocked || _isPlaced || _isDragging)
{
return;
}
PlayScaleTween(_defaultScaleMultiplier);
}
///
/// 校验是否允许开始拖拽。
///
private bool CanStartDrag()
{
return !_isLocked && !_isPlaced;
}
///
/// 校验拖拽中是否可更新位置。
///
private bool CanDrag(PointerEventData eventData)
{
return !_isLocked && !_isPlaced && eventData != null;
}
///
/// 将部件切到控制器提供的拖拽根节点。
///
private void MoveToDragRoot()
{
if (_controller == null)
{
return;
}
Transform dragRoot = _controller.GetDragRoot();
if (dragRoot != null)
{
_rectTransform.SetParent(dragRoot, true);
}
}
private void CacheDragOffset(PointerEventData eventData)
{
_dragPointerOffset = Vector2.zero;
if (!TryGetDragParent(out RectTransform dragParent))
{
return;
}
if (TryGetPointerLocalPosition(eventData, dragParent, out Vector2 pointerLocalPosition))
{
_dragPointerOffset = _rectTransform.anchoredPosition - pointerLocalPosition;
}
}
private bool TryGetDragParent(out RectTransform dragParent)
{
dragParent = _rectTransform.parent as RectTransform;
return dragParent != null;
}
private bool TryGetPointerLocalPosition(PointerEventData eventData, RectTransform targetRect,
out Vector2 localPosition)
{
localPosition = Vector2.zero;
if (eventData == null || targetRect == null)
{
return false;
}
Camera eventCamera = ResolveEventCamera(eventData);
return RectTransformUtility.ScreenPointToLocalPointInRectangle(targetRect, eventData.position, eventCamera,
out localPosition);
}
private Camera ResolveEventCamera(PointerEventData eventData)
{
if (eventData != null)
{
if (eventData.pressEventCamera != null)
{
return eventData.pressEventCamera;
}
if (eventData.enterEventCamera != null)
{
return eventData.enterEventCamera;
}
}
Canvas canvas = GetComponentInParent