Unity小功能——UGUI拖动到指定区域

Unity小功能——UGUI拖动到指定区域请添加图片描述

功能包含:
一,限定拖动区域,
1,无限制
2,屏幕内
3,父物体内
二,拖动位置
1,鼠标到指定位置
2,拖动的物品到指定区域
三,吸附物品释放等功能
四,具体功能需要自行扩展
在以下内容中添加功能
public UnityEvent onDragEntry; //进入
public UnityEvent onDragExit; //离开
public UnityEvent onDragNotExit; //未离开

脚本

DragHandle.cs

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<SlotHandle>();

            offset = eventData.position - (Vector2)transform.position;
            gameObject.GetComponent<CanvasGroup>().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<CanvasGroup>().blocksRaycasts = true;
            if (isOverlap)
            {

                bool overlap = false;
                int overlapIndex = 0;
                for (int i = 0; i < slotHandles.Length; i++)
                {
                    if (IsRectanglesOverlapping(GetComponent<RectTransform>(), slotHandles[i].GetComponent<RectTransform>()))
                    {
                        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();

        }

        /// <summary>
        /// 移除SlotHandle
        /// </summary>
        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 
    }
}

SlotHandle.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.EventSystems;

namespace MinorFunction
{

    public class SlotHandle : MonoBehaviour, IPointerEnterHandler, IPointerExitHandler, IDropHandler
    {
        [HideInInspector]
        public bool isEnter = false;

        [Tooltip("multipleNumber == 0 不做任何限制可以附加无限多个,multipleNumber > 0 任何限制只能附加 multipleNumber个,multipleNumber = -1 不允许附加")]
        public int multipleNumber = 0;


        private DragHandle temporaryDragHandle;
        public List<DragHandle> currentDragHandles = new List<DragHandle>();

        public UnityEvent<DragHandle> onDragEntry; //进入
        public UnityEvent<DragHandle> onDragExit;  //离开
        public UnityEvent<DragHandle> onDragNotExit;  //未离开

        public UnityEvent onPointerEnter;    //鼠标进入
        public UnityEvent onPointerExit;     //鼠标离开

        private void Awake()
        {
            SetMultipleNumber(multipleNumber);
        }

        public void OnPointerEnter(PointerEventData eventData)
        {
            isEnter = true;
            onPointerEnter?.Invoke();
        }

        public void OnPointerExit(PointerEventData eventData)
        {
            isEnter = false;
            onPointerExit?.Invoke();
        }

        public void OnDrop(PointerEventData eventData)
        {
           
            if (eventData.pointerDrag != null)
            {
                temporaryDragHandle = eventData.pointerDrag.GetComponent<DragHandle>();
                if (temporaryDragHandle.isOverlap)
                {
                    return;
                }
                ModifyDrag(temporaryDragHandle);
            }
        }

        public void ModifyDrag(DragHandle dragHandle)
        {
            temporaryDragHandle = dragHandle;
            if (!currentDragHandles.Contains(temporaryDragHandle))
            {
                if (multipleNumber == 0)  //不做任何限制
                {
                    RemoveDragHandle();
                    ShiftInDragHandle();
                }
                else if (multipleNumber > 0)
                {
                    if (currentDragHandles.Count < multipleNumber)
                    {
                        RemoveDragHandle();
                        ShiftInDragHandle();
                    }
                }
                else if (multipleNumber < 0)
                {
                    return;
                }
            }
            else
            {
                onDragNotExit?.Invoke(temporaryDragHandle);
            }
        } 

        public void RemoveDragHandle()
        {
            if (temporaryDragHandle.currentSlotHandle)
            {
                temporaryDragHandle.RemoveSlotHandle();
            }
        }

        public void ShiftInDragHandle()
        {
            temporaryDragHandle.currentSlotHandle = this;
            currentDragHandles.Add(temporaryDragHandle);

            onDragEntry?.Invoke(temporaryDragHandle);
        }

        /// <summary>
        /// 设置多选数量
        /// </summary>
        /// <param name="number">number == 0 不做任何限制可以附加无限多个,number > 0 任何限制只能附加 number 个,number = -1 不允许附加</param>
        public void SetMultipleNumber(int number)
        {
            if (number < 0)
            {
                multipleNumber = -1;
                RemoveAllDragHandles();
            }
            else
            {
                multipleNumber = number;
                if (number < currentDragHandles.Count)
                {
                    for (int i = multipleNumber - 1; i < currentDragHandles.Count; i++)
                    {
                        temporaryDragHandle = currentDragHandles[i];

                        RemoveDragHandle();
                    }
                }
            }
        }

        /// <summary>
        /// 移除全部DragHandle
        /// </summary>
        public void RemoveAllDragHandles()
        {
            for (int i = 0; i < currentDragHandles.Count; i++)
            {
                temporaryDragHandle = currentDragHandles[i];

                RemoveDragHandle();
            }
        }
        /// <summary>
        /// 移除指定DragHandle
        /// </summary>
        /// <param name="index"></param>
        public void RemoveIndexDragHandles(int index)
        {
            temporaryDragHandle = currentDragHandles[index];
            RemoveDragHandle();
        }
        /// <summary>
        /// 移除指定DragHandle
        /// </summary>
        /// <param name="dragHandle"></param>
        public void RemoveIndexDragHandles(DragHandle dragHandle)
        {
            temporaryDragHandle = dragHandle;
            RemoveDragHandle();
        }


    }
}

功能扩展案例

SlotHandleFunctionExpansion.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace MinorFunction
{
    public class SlotHandleFunctionExpansion : MonoBehaviour
    {
        public SlotHandle slotHandle;


        void Start()
        {
            slotHandle = GetComponent<SlotHandle>();
            AdsorptionFunction();
        }

        /// <summary>
        /// 吸附功能
        /// </summary>
        public void AdsorptionFunction()
        {
            slotHandle.onDragEntry.AddListener(call => {
                call.gameObject.transform.position = gameObject.transform.position;
                Debug.Log($"{call.gameObject.name}进入了{gameObject.name}");
            });
            slotHandle.onDragExit.AddListener(call => {
                Debug.Log($"{call.gameObject.name}离开了{gameObject.name}");
            });
            slotHandle.onDragNotExit.AddListener(call => {
                call.gameObject.transform.position = gameObject.transform.position;
                Debug.Log($"{call.gameObject.name}未离开{gameObject.name}");
            });

        }

    }

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值