Unity UGUI 拖拽组件

效果展示

在这里插入图片描述

使用方式

拖到图片上即可用

父节点会约束它的活动范围哦~
父节点会约束它的活动范围哦~
父节点会约束它的活动范围哦~

在这里插入图片描述

源码: 点击图片开始拖动 受到父节点区域影响

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

/// <summary>
/// UI DragComponent 
/// 
/// Click on the image area to start dragging
/// The parent node scope was affected. Procedure
/// 
/// Easy~~~
/// 
/// @anchor ChenJC
/// @time: 2023/02/28
/// </summary>
public class DragComponent : MonoBehaviour, IBeginDragHandler, IDragHandler, IDropHandler
{
    
    
    RectTransform rectTransform, parentRectTrans;
    float minX, minY, maxX, maxY;
    Vector2 offset;

    public delegate void DragHandlerEvent( Vector2 currentPos );
   
    public DragHandlerEvent dragBeginEvent;
    public DragHandlerEvent dragEvent;
    public DragHandlerEvent dropEvent;

    #region Monobehavior Methods


    private void Awake()
    {
    
    
        parentRectTrans = transform.parent as RectTransform;
        rectTransform = transform as RectTransform;
    }

    private void Start()
    {
    
    
        var parentAnchorX = parentRectTrans.pivot.x * parentRectTrans.rect.width;
        var parentAnchorY = parentRectTrans.pivot.y * parentRectTrans.rect.height;
        minX = rectTransform.rect.width * 0.5f - parentAnchorX;
        minY = rectTransform.rect.height * 0.5f - parentAnchorY;
        maxX = parentRectTrans.rect.width - rectTransform.rect.width * 0.5f - parentAnchorX;
        maxY = parentRectTrans.rect.height - rectTransform.rect.height * 0.5f - parentAnchorY;
    }

    #endregion


    #region Internal Methods

    private Vector2 ConstraintWithinParentNode( Vector2 pos )
    {
    
    
        pos.x = Mathf.Clamp( pos.x, minX, maxX );
        pos.y = Mathf.Clamp( pos.y, minY, maxY );
        return pos;
    }
    private bool Convert2local( Vector2 screenPos, out Vector2 localPos, Camera camera )
    {
    
    
        return RectTransformUtility.ScreenPointToLocalPointInRectangle( parentRectTrans, screenPos, camera, out localPos );
    }

    #endregion


    #region Drag Handler Methods

    public void OnBeginDrag( PointerEventData eventData )
    {
    
    
        Vector2 localPos;
        if ( Convert2local( eventData.position, out localPos, eventData.pressEventCamera ) )
        {
    
    
            Vector2 src = rectTransform.localPosition;
            offset = src - localPos;
            dragBeginEvent?.Invoke( src );
        }
    }

    public void OnDrag( PointerEventData eventData )
    {
    
    
        Vector2 localPos;
        if ( Convert2local( eventData.position, out localPos, eventData.pressEventCamera ) )
        {
    
    
            Vector2 dest = ConstraintWithinParentNode( localPos + offset );
            rectTransform.localPosition = dest;
            dragEvent?.Invoke( dest );
        }
    }


    public void OnDrop( PointerEventData eventData )
    {
    
    
        Vector2 localPos;
        if ( Convert2local( eventData.position, out localPos, eventData.pressEventCamera ) )
        {
    
    
            Vector2 dest = ConstraintWithinParentNode( localPos + offset );
            dropEvent?.Invoke( dest );
        }
    }

    #endregion
}


拖拽事件监听

依次 开始拖拽时触发; 拖拽过程中持续触发; 拖拽结束时触发
在这里插入图片描述

原理介绍

在这里插入图片描述

开始拖拽的时候

offset = sub.localtionPos - p.localtionPos

通过计算鼠标点 计算出 相对于图片原点的 偏移 并缓存

  public void OnBeginDrag( PointerEventData eventData )
    {
    
    
        Vector2 localPos;
        if ( Convert2local( eventData.position, out localPos, eventData.pressEventCamera ) )
        {
    
    
            Vector2 src = rectTransform.localPosition;
            offset = src - localPos;
            dragBeginEvent?.Invoke( src );
        }
    }

拖拽过程中 我们加上这个偏移向量 就能得到相对偏移的拖拽方式

sub.locationsPos = p.locationPos + offset

public void OnDrag( PointerEventData eventData )
    {
    
    
        Vector2 localPos;
        if ( Convert2local( eventData.position, out localPos, eventData.pressEventCamera ) )
        {
    
    
            Vector2 dest = ConstraintWithinParentNode( localPos + offset );
            rectTransform.localPosition = dest;
            dragEvent?.Invoke( dest );
        }
    }

限制活动范围 保持在父节点以内活动

示意图

在这里插入图片描述

如图可以知道 最小X 等于自身宽 的一半 同时要减去父节点 宽的一半
可以知道 最小Y 等于自身高 的一半 同时要减去父节点 高的一半

最大值 是父节点一半 - 自身大小的一半 在Unity里 你可以通过 pivot 来获取 图片锚点相对于图片自身size的百分比值 ( 0~1)

计算出最小X,最小Y,最大X,最大Y

  private void Start()
    {
    
    
        var parentAnchorX = parentRectTrans.pivot.x * parentRectTrans.rect.width;
        var parentAnchorY = parentRectTrans.pivot.y * parentRectTrans.rect.height;
        minX = rectTransform.rect.width * 0.5f - parentAnchorX;
        minY = rectTransform.rect.height * 0.5f - parentAnchorY;
        maxX = parentRectTrans.rect.width - rectTransform.rect.width * 0.5f - parentAnchorX;
        maxY = parentRectTrans.rect.height - rectTransform.rect.height * 0.5f - parentAnchorY;
    }

新的位置约束在范围内

在这里插入图片描述

   private Vector2 ConstraintWithinParentNode( Vector2 pos )
    {
    
    
        pos.x = Mathf.Clamp( pos.x, minX, maxX );
        pos.y = Mathf.Clamp( pos.y, minY, maxY );
        return pos;
    }

源码: 屏幕任意位置可拖动 受到父节点区域影响

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

/// <summary>
/// Unrestricted UI DragComponent 
/// 
/// 
/// Drag the component anywhere.
/// The parent node scope was affected. Procedure
/// 
/// @anchor ChenJC
/// @time: 2023/03/01
/// </summary>
[RequireComponent( typeof( RectTransform ) )]
public class UnrestrictedDragComponent : MonoBehaviour
{
    
    
    public bool realtimeUpdatePosition = false, hasCaptureFingerDropPoint = false;
    protected RectTransform rectTransform, parentRectTrans;
    protected float minX, minY, maxX, maxY;
    private Vector2 ImageRelativeDragPosition;
    public delegate void DragHandlerEvent( Vector2 currentPos );
    public DragHandlerEvent beginDragEvent;
    public DragHandlerEvent dragEvent;
    public DragHandlerEvent endDragEvent;
    #region Monobehavior Methods

    private void Awake()
    {
    
    
        parentRectTrans = transform.parent as RectTransform;
        rectTransform = transform as RectTransform;
    }

    private void OnTransformParentChanged()
    {
    
    
        parentRectTrans = transform.parent as RectTransform;
    }

    private void OnEnable() => hasCaptureFingerDropPoint = false;

    private void Update()
    {
    
    

#if UNITY_EDITOR

        if ( Input.anyKey )
        {
    
    

            if ( Input.GetMouseButtonDown( 0 ) )
            {
    
    
                hasCaptureFingerDropPoint = true;
                TouchBegin( Input.mousePosition );
            }

            if ( Input.GetMouseButton( 0 ) )
            {
    
    
                if ( !hasCaptureFingerDropPoint )
                {
    
    
                    hasCaptureFingerDropPoint = true;
                    TouchBegin( Input.mousePosition );
                    return;
                }
                TouchMoveing( Input.mousePosition );
            }

        }

        if ( Input.GetMouseButtonUp( 0 ) )
        {
    
    
            TouchEnd( Input.mousePosition );
        }

#elif UNITY_ANDROID || UNITY_IOS
        if ( Input.touchSupported && Input.touchCount > 0 )
        {
    
    
            var touch = Input.GetTouch( 0 );

            if ( touch.phase == TouchPhase.Began )
            {
    
    
                hasCaptureFingerDropPoint = true;
                TouchBegin( touch.position );
            }

            if ( touch.phase == TouchPhase.Moved || touch.phase == TouchPhase.Stationary )
            {
    
    
                if ( !hasCaptureFingerDropPoint )
                {
    
    
                    hasCaptureFingerDropPoint = true;
                    TouchBegin( Input.mousePosition );
                    return;
                }
                TouchMoveing( touch.position );
            }

            if ( touch.phase == TouchPhase.Ended || touch.phase == TouchPhase.Canceled )
            {
    
    
                TouchEnd( touch.position );
            }
        }
#endif
        if ( realtimeUpdatePosition )
        {
    
    
            rectTransform.localPosition = ConstraintWithinParentNode( rectTransform.localPosition );
        }
    }

    #endregion


    #region Internal Methods


    //实时计算包围盒
    protected virtual void RealtimeCalcBounds()
    {
    
    
        var rad = rectTransform.localEulerAngles.z * Mathf.Deg2Rad;

        var a = rectTransform.rect.height * ( 1.0f - rectTransform.pivot.y ) * Mathf.Cos( rad );
        var b = rectTransform.rect.height * rectTransform.pivot.y * Mathf.Sin( rad );
        var selfMinX = Mathf.Abs( Mathf.Min( a, b ) );
        var selfMaxX = Mathf.Abs( Mathf.Max( a, b ) );


        var c = rectTransform.rect.height * rectTransform.pivot.y * Mathf.Cos( rad );
        var d = rectTransform.rect.height * ( 1.0f - rectTransform.pivot.y ) * Mathf.Sin( rad );
        var selfMinY = Mathf.Abs( Mathf.Max( c, d ) );
        var selfMaxY = Mathf.Abs( Mathf.Min( c, d ) );

        var layoutOffsetX = ( rectTransform.pivot.x - parentRectTrans.anchorMin.x ) * rectTransform.rect.width;
        var layoutOffsetY = ( rectTransform.pivot.y - parentRectTrans.anchorMin.y ) * rectTransform.rect.height;

        minX = selfMinX + layoutOffsetX - parentRectTrans.pivot.x * parentRectTrans.rect.width;
        minY = selfMinY + layoutOffsetY - parentRectTrans.pivot.y * parentRectTrans.rect.height;
        maxX = parentRectTrans.rect.width * ( 1.0f - parentRectTrans.pivot.x ) - selfMaxX;
        maxY = parentRectTrans.rect.height * ( 1.0f - parentRectTrans.pivot.y ) - selfMaxY;
    }

    //约束在父节点以内
    private Vector2 ConstraintWithinParentNode( Vector2 pos )
    {
    
    
        RealtimeCalcBounds();
        pos.x = Mathf.Clamp( pos.x, minX, maxX );
        pos.y = Mathf.Clamp( pos.y, minY, maxY );
        return pos;
    }

    //如果你的相机 不是用 MainCamera 来渲染UI
    //请将这里的Camera调整成对应Canvas Camera
    private bool Convert2local( Vector2 screenPos, out Vector2 localPos )
    {
    
    
        return RectTransformUtility.ScreenPointToLocalPointInRectangle( parentRectTrans, screenPos, Camera.main, out localPos );
    }

    /// <summary>
    /// 更新位置
    /// </summary>
    private void UpdatePosition( ref Vector2 pos )
    {
    
    
        if ( Convert2local( pos, out Vector2 localpos ) )
        {
    
    
            rectTransform.localPosition = ConstraintWithinParentNode( localpos + ImageRelativeDragPosition );
        }
    }

    #endregion


    #region Touch Handler Methods

    //开始点击的时候触发
    private void TouchBegin( Vector2 pos )
    {
    
    
        if ( Convert2local( pos, out Vector2 localpos ) )
        {
    
    
            Vector2 src = rectTransform.localPosition;
            ImageRelativeDragPosition = src - localpos;
            beginDragEvent?.Invoke( src );
        }
    }

    //拖拽的时候触发
    private void TouchMoveing( Vector2 pos )
    {
    
    
        UpdatePosition( ref pos );
        dragEvent?.Invoke( rectTransform.localPosition );
    }

    //抬起手指的时候触发
    private void TouchEnd( Vector2 pos )
    {
    
    
        UpdatePosition( ref pos );
        endDragEvent?.Invoke( rectTransform.localPosition );
    }

    #endregion

}

猜你喜欢

转载自blog.csdn.net/qq_39162566/article/details/129268628
今日推荐