using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.Events; using UnityEngine.EventSystems; namespace MinorFunction { public enum DragHandleMode { Free, // 自由拖拽 WithinScreen, // 在屏幕内拖拽 WithinParent, // 在父对象内拖拽 } [RequireComponent(typeof(CanvasGroup))] public class DragHandle : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler { public DragHandleMode dragMode = DragHandleMode.Free; // 是否偏移 public bool isOffset = true; // 是否位置重叠判定 public bool isOverlap = true; // 拖动置顶层级 public bool isDragTop = false; // 偏移向量 private Vector2 offset = Vector2.zero; public UnityEvent onBeginDrag;// 开始拖动 public UnityEvent onDrag;// 拖动 public UnityEvent onEndDrag; // 拖动结束 [HideInInspector] public SlotHandle currentSlotHandle; public SlotHandle[] slotHandles; public void OnBeginDrag(PointerEventData eventData) { slotHandles = Object.FindObjectsOfType(); offset = eventData.position - (Vector2)transform.position; gameObject.GetComponent().blocksRaycasts = false; if (isDragTop) { transform.SetAsLastSibling(); } onBeginDrag?.Invoke(); } public void OnDrag(PointerEventData eventData) { switch (dragMode) { case DragHandleMode.Free: SetFreeDrag(eventData); break; case DragHandleMode.WithinScreen: SetDragWithinScreen(eventData); break; case DragHandleMode.WithinParent: SetDragWithinParent(eventData); break; } onDrag?.Invoke(); } public void OnEndDrag(PointerEventData eventData) { gameObject.GetComponent().blocksRaycasts = true; if (isOverlap) { bool overlap = false; int overlapIndex = 0; for (int i = 0; i < slotHandles.Length; i++) { if (IsRectanglesOverlapping(GetComponent(), slotHandles[i].GetComponent())) { overlap = true; overlapIndex = i; break; } } if (overlap) { //Debug.Log($"{gameObject.name} 有重叠 {slotHandles[overlapIndex].gameObject.name}"); slotHandles[overlapIndex].ModifyDrag(this); } else { if (currentSlotHandle) { RemoveSlotHandle(); currentSlotHandle = null; } //Debug.Log("无任何重叠"); } } else { if (currentSlotHandle) { if (!currentSlotHandle.isEnter) { RemoveSlotHandle(); currentSlotHandle = null; } } } onEndDrag?.Invoke(); } /// /// 移除SlotHandle /// public void RemoveSlotHandle() { currentSlotHandle.onDragExit?.Invoke(this); currentSlotHandle.currentDragHandles.Remove(this); currentSlotHandle = null; } #region 拖动模式 // 自由拖动 public void SetFreeDrag(PointerEventData eventData) { if (isOffset) { transform.position = eventData.position - offset; } else { transform.position = eventData.position; } } // 设置屏幕内拖动 public void SetDragWithinScreen(PointerEventData eventData) { RectTransform rectTransform = transform as RectTransform; Vector2 pos = Vector2.zero; if (isOffset) { pos = eventData.position - offset; } else { pos = eventData.position; } // 屏幕范围 float parentMinX = 0; float parentMinY = 0; float parentMaxX = Screen.width; float parentMaxY = Screen.height; var pivotDistance = GetPivotDistance(rectTransform); float pivotMinX = pivotDistance.pivotMinX; float pivotMinY = pivotDistance.pivotMinY; float pivotMaxX = pivotDistance.pivotMaxX; float pivotMaxY = pivotDistance.pivotMaxY; // 限制范围 float minX = parentMinX + pivotMinX; float minY = parentMinY + pivotMinY; float maxX = parentMaxX - pivotMaxX; float maxY = parentMaxY - pivotMaxY; // 限制拖动范围 pos.x = Mathf.Clamp(pos.x, minX, maxX); pos.y = Mathf.Clamp(pos.y, minY, maxY); transform.position = pos; } // 限制矩形内拖动 public void SetDragWithinParent(PointerEventData eventData) { RectTransform rectTransform = transform as RectTransform; RectTransform parentRectTransform = transform.parent as RectTransform; Vector2 pos = Vector2.zero; if (isOffset) { pos = eventData.position - offset; } else { pos = eventData.position; } var parentMinMax = GetMinMax(parentRectTransform); float parentMinX = parentMinMax.minX; float parentMinY = parentMinMax.minY; float parentMaxX = parentMinMax.maxX; float parentMaxY = parentMinMax.maxY; var pivotDistance = GetPivotDistance(rectTransform); float pivotMinX = pivotDistance.pivotMinX; float pivotMinY = pivotDistance.pivotMinY; float pivotMaxX = pivotDistance.pivotMaxX; float pivotMaxY = pivotDistance.pivotMaxY; // 限制范围 float minX = parentMinX + pivotMinX; float minY = parentMinY + pivotMinY; float maxX = parentMaxX - pivotMaxX; float maxY = parentMaxY - pivotMaxY; // 限制拖动范围 pos.x = Mathf.Clamp(pos.x, minX, maxX); pos.y = Mathf.Clamp(pos.y, minY, maxY); transform.position = pos; } #endregion #region 功能 // 计算物体中心点到四条边的距离 public (float pivotMinX, float pivotMinY, float pivotMaxX, float pivotMaxY) GetPivotDistance(RectTransform rectTransform) { float width = rectTransform.rect.width; float height = rectTransform.rect.height; // 注:这里一定要使用全局坐标,不然计算会受到父物体缩放的影响 float scaleX = rectTransform.lossyScale.x; float scaleY = rectTransform.lossyScale.y; Vector2 pivot = rectTransform.pivot; float pivotMinX = width * pivot.x * scaleX; float pivotMinY = height * pivot.y * scaleY; float pivotMaxX = width * (1 - pivot.x) * scaleX; float pivotMaxY = height * (1 - pivot.y) * scaleY; return (pivotMinX, pivotMinY, pivotMaxX, pivotMaxY); } // 计算物体X,Y最大值最小值 public (float minX, float minY, float maxX, float maxY) GetMinMax(RectTransform rectTransform) { float parentX = rectTransform.position.x; float parentY = rectTransform.position.y; var pivotDistance = GetPivotDistance(rectTransform); float pivotMinX = pivotDistance.pivotMinX; float pivotMinY = pivotDistance.pivotMinY; float pivotMaxX = pivotDistance.pivotMaxX; float pivotMaxY = pivotDistance.pivotMaxY; float minX = parentX - pivotMinX; float minY = parentY - pivotMinY; float maxX = parentX + pivotMaxX; float maxY = parentY + pivotMaxY; return (minX, minY, maxX, maxY); } public bool IsRectanglesOverlapping(RectTransform rect1, RectTransform rect2) { Vector2 rect1Min = rect1.anchoredPosition - (rect1.sizeDelta * rect1.pivot) * rect1.lossyScale; Vector2 rect1Max = rect1.anchoredPosition + (rect1.sizeDelta * (Vector2.one - rect1.pivot)) * rect1.lossyScale; Vector2 rect2Min = rect2.anchoredPosition - (rect2.sizeDelta * rect2.pivot) * rect2.lossyScale; Vector2 rect2Max = rect2.anchoredPosition + (rect2.sizeDelta * (Vector2.one - rect2.pivot)) * rect2.lossyScale; // 判断矩形 1 的最右边小于矩形 2 的最左边,或者矩形 2 的最右边小于矩形 1 的最左边,说明没有水平方向重叠 if (rect1Max.x < rect2Min.x || rect2Max.x < rect1Min.x || rect1Max.y < rect2Min.y || rect2Max.y < rect1Min.y) { return false; } return true; } #endregion } }