DragHandle.cs 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304
  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. using UnityEngine.Events;
  5. using UnityEngine.EventSystems;
  6. namespace MinorFunction
  7. {
  8. public enum DragHandleMode
  9. {
  10. Free, // 自由拖拽
  11. WithinScreen, // 在屏幕内拖拽
  12. WithinParent, // 在父对象内拖拽
  13. }
  14. [RequireComponent(typeof(CanvasGroup))]
  15. public class DragHandle : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler
  16. {
  17. public DragHandleMode dragMode = DragHandleMode.Free;
  18. // 是否偏移
  19. public bool isOffset = true;
  20. // 是否位置重叠判定
  21. public bool isOverlap = true;
  22. // 拖动置顶层级
  23. public bool isDragTop = false;
  24. // 偏移向量
  25. private Vector2 offset = Vector2.zero;
  26. public UnityEvent onBeginDrag;// 开始拖动
  27. public UnityEvent onDrag;// 拖动
  28. public UnityEvent onEndDrag; // 拖动结束
  29. [HideInInspector]
  30. public SlotHandle currentSlotHandle;
  31. public SlotHandle[] slotHandles;
  32. public void OnBeginDrag(PointerEventData eventData)
  33. {
  34. slotHandles = Object.FindObjectsOfType<SlotHandle>();
  35. offset = eventData.position - (Vector2)transform.position;
  36. gameObject.GetComponent<CanvasGroup>().blocksRaycasts = false;
  37. if (isDragTop)
  38. {
  39. transform.SetAsLastSibling();
  40. }
  41. onBeginDrag?.Invoke();
  42. }
  43. public void OnDrag(PointerEventData eventData)
  44. {
  45. switch (dragMode)
  46. {
  47. case DragHandleMode.Free:
  48. SetFreeDrag(eventData);
  49. break;
  50. case DragHandleMode.WithinScreen:
  51. SetDragWithinScreen(eventData);
  52. break;
  53. case DragHandleMode.WithinParent:
  54. SetDragWithinParent(eventData);
  55. break;
  56. }
  57. onDrag?.Invoke();
  58. }
  59. public void OnEndDrag(PointerEventData eventData)
  60. {
  61. gameObject.GetComponent<CanvasGroup>().blocksRaycasts = true;
  62. if (isOverlap)
  63. {
  64. bool overlap = false;
  65. int overlapIndex = 0;
  66. for (int i = 0; i < slotHandles.Length; i++)
  67. {
  68. if (IsRectanglesOverlapping(GetComponent<RectTransform>(), slotHandles[i].GetComponent<RectTransform>()))
  69. {
  70. overlap = true;
  71. overlapIndex = i;
  72. break;
  73. }
  74. }
  75. if (overlap)
  76. {
  77. //Debug.Log($"{gameObject.name} 有重叠 {slotHandles[overlapIndex].gameObject.name}");
  78. slotHandles[overlapIndex].ModifyDrag(this);
  79. }
  80. else
  81. {
  82. if (currentSlotHandle)
  83. {
  84. RemoveSlotHandle();
  85. currentSlotHandle = null;
  86. }
  87. //Debug.Log("无任何重叠");
  88. }
  89. }
  90. else
  91. {
  92. if (currentSlotHandle)
  93. {
  94. if (!currentSlotHandle.isEnter)
  95. {
  96. RemoveSlotHandle();
  97. currentSlotHandle = null;
  98. }
  99. }
  100. }
  101. onEndDrag?.Invoke();
  102. }
  103. /// <summary>
  104. /// 移除SlotHandle
  105. /// </summary>
  106. public void RemoveSlotHandle()
  107. {
  108. currentSlotHandle.onDragExit?.Invoke(this);
  109. currentSlotHandle.currentDragHandles.Remove(this);
  110. currentSlotHandle = null;
  111. }
  112. #region 拖动模式
  113. // 自由拖动
  114. public void SetFreeDrag(PointerEventData eventData)
  115. {
  116. if (isOffset)
  117. {
  118. transform.position = eventData.position - offset;
  119. }
  120. else
  121. {
  122. transform.position = eventData.position;
  123. }
  124. }
  125. // 设置屏幕内拖动
  126. public void SetDragWithinScreen(PointerEventData eventData)
  127. {
  128. RectTransform rectTransform = transform as RectTransform;
  129. Vector2 pos = Vector2.zero;
  130. if (isOffset)
  131. {
  132. pos = eventData.position - offset;
  133. }
  134. else
  135. {
  136. pos = eventData.position;
  137. }
  138. // 屏幕范围
  139. float parentMinX = 0;
  140. float parentMinY = 0;
  141. float parentMaxX = Screen.width;
  142. float parentMaxY = Screen.height;
  143. var pivotDistance = GetPivotDistance(rectTransform);
  144. float pivotMinX = pivotDistance.pivotMinX;
  145. float pivotMinY = pivotDistance.pivotMinY;
  146. float pivotMaxX = pivotDistance.pivotMaxX;
  147. float pivotMaxY = pivotDistance.pivotMaxY;
  148. // 限制范围
  149. float minX = parentMinX + pivotMinX;
  150. float minY = parentMinY + pivotMinY;
  151. float maxX = parentMaxX - pivotMaxX;
  152. float maxY = parentMaxY - pivotMaxY;
  153. // 限制拖动范围
  154. pos.x = Mathf.Clamp(pos.x, minX, maxX);
  155. pos.y = Mathf.Clamp(pos.y, minY, maxY);
  156. transform.position = pos;
  157. }
  158. // 限制矩形内拖动
  159. public void SetDragWithinParent(PointerEventData eventData)
  160. {
  161. RectTransform rectTransform = transform as RectTransform;
  162. RectTransform parentRectTransform = transform.parent as RectTransform;
  163. Vector2 pos = Vector2.zero;
  164. if (isOffset)
  165. {
  166. pos = eventData.position - offset;
  167. }
  168. else
  169. {
  170. pos = eventData.position;
  171. }
  172. var parentMinMax = GetMinMax(parentRectTransform);
  173. float parentMinX = parentMinMax.minX;
  174. float parentMinY = parentMinMax.minY;
  175. float parentMaxX = parentMinMax.maxX;
  176. float parentMaxY = parentMinMax.maxY;
  177. var pivotDistance = GetPivotDistance(rectTransform);
  178. float pivotMinX = pivotDistance.pivotMinX;
  179. float pivotMinY = pivotDistance.pivotMinY;
  180. float pivotMaxX = pivotDistance.pivotMaxX;
  181. float pivotMaxY = pivotDistance.pivotMaxY;
  182. // 限制范围
  183. float minX = parentMinX + pivotMinX;
  184. float minY = parentMinY + pivotMinY;
  185. float maxX = parentMaxX - pivotMaxX;
  186. float maxY = parentMaxY - pivotMaxY;
  187. // 限制拖动范围
  188. pos.x = Mathf.Clamp(pos.x, minX, maxX);
  189. pos.y = Mathf.Clamp(pos.y, minY, maxY);
  190. transform.position = pos;
  191. }
  192. #endregion
  193. #region 功能
  194. // 计算物体中心点到四条边的距离
  195. public (float pivotMinX, float pivotMinY, float pivotMaxX, float pivotMaxY) GetPivotDistance(RectTransform rectTransform)
  196. {
  197. float width = rectTransform.rect.width;
  198. float height = rectTransform.rect.height;
  199. // 注:这里一定要使用全局坐标,不然计算会受到父物体缩放的影响
  200. float scaleX = rectTransform.lossyScale.x;
  201. float scaleY = rectTransform.lossyScale.y;
  202. Vector2 pivot = rectTransform.pivot;
  203. float pivotMinX = width * pivot.x * scaleX;
  204. float pivotMinY = height * pivot.y * scaleY;
  205. float pivotMaxX = width * (1 - pivot.x) * scaleX;
  206. float pivotMaxY = height * (1 - pivot.y) * scaleY;
  207. return (pivotMinX, pivotMinY, pivotMaxX, pivotMaxY);
  208. }
  209. // 计算物体X,Y最大值最小值
  210. public (float minX, float minY, float maxX, float maxY) GetMinMax(RectTransform rectTransform)
  211. {
  212. float parentX = rectTransform.position.x;
  213. float parentY = rectTransform.position.y;
  214. var pivotDistance = GetPivotDistance(rectTransform);
  215. float pivotMinX = pivotDistance.pivotMinX;
  216. float pivotMinY = pivotDistance.pivotMinY;
  217. float pivotMaxX = pivotDistance.pivotMaxX;
  218. float pivotMaxY = pivotDistance.pivotMaxY;
  219. float minX = parentX - pivotMinX;
  220. float minY = parentY - pivotMinY;
  221. float maxX = parentX + pivotMaxX;
  222. float maxY = parentY + pivotMaxY;
  223. return (minX, minY, maxX, maxY);
  224. }
  225. public bool IsRectanglesOverlapping(RectTransform rect1, RectTransform rect2)
  226. {
  227. Vector2 rect1Min = rect1.anchoredPosition - (rect1.sizeDelta * rect1.pivot) * rect1.lossyScale;
  228. Vector2 rect1Max = rect1.anchoredPosition + (rect1.sizeDelta * (Vector2.one - rect1.pivot)) * rect1.lossyScale;
  229. Vector2 rect2Min = rect2.anchoredPosition - (rect2.sizeDelta * rect2.pivot) * rect2.lossyScale;
  230. Vector2 rect2Max = rect2.anchoredPosition + (rect2.sizeDelta * (Vector2.one - rect2.pivot)) * rect2.lossyScale;
  231. // 判断矩形 1 的最右边小于矩形 2 的最左边,或者矩形 2 的最右边小于矩形 1 的最左边,说明没有水平方向重叠
  232. if (rect1Max.x < rect2Min.x || rect2Max.x < rect1Min.x || rect1Max.y < rect2Min.y || rect2Max.y < rect1Min.y)
  233. {
  234. return false;
  235. }
  236. return true;
  237. }
  238. #endregion
  239. }
  240. }