Unity UGUI圖片拖拽與區域限制

最近有個卡牌裝備的問題,實現可以將卡牌在裝備框內移動縮放,並且可以將卡牌拖動到其他的裝備框進行交換,或者卸下操作。

一、讓圖片跟隨鼠標移動

要讓圖片隨鼠標拖拽移動,首先需要接收IBeginDragHandler, IDragHandler,IEndDragHandler,中的拖拽事件。

一般項目中會有一個EventTrigger,像我當前的項目,有實現類似UGUI源碼的UIEventTrigger,統一管理ui的操作。

例如,OnDrag(PointerEventData eventData),OnBeginDrag,OnEndDrag,這三個拖拽事件。

1.我們需要拿到PointerEventData中鼠標點擊的屏幕座標position。

2.通過RectTransformUtility.ScreenPointToWorldPointInRectangle,轉換成世界座標,然後做下一步的操作。

3.OnBeginDrag計算被拖拽圖片與拖拽點的offset

public void OnBeginDrag(PointerEventData eventData)
{
        //開始拖拽時獲得拖拽點與被拖拽圖的偏移
        RectTransformUtility.ScreenPointToWorldPointInRectangle(rect, eventData.position, uiCamera, out dragOffset);
}

4.OnDrag計算被拖拽的距離

public void OnDrag(PointerEventData eventData)
{
        Vector3 pos;
        RectTransformUtility.ScreenPointToWorldPointInRectangle(rect, eventData.position, uiCamera, out pos);
        rect.position = pos + dragOffset;
        //判斷拖拽範圍
}

5.OnEndDrag鬆手檢測,滿足卸下,交換需求

public void OnEndDrag(PointerEventData eventData)
{
        //拖拽結束後,檢查擡起點下的交互物體
        //僞代碼 需要被檢查的裝備框 在範圍內爲true
        foreach(var rect in rects)
        {
            if (RectTransformUtility.RectangleContainsScreenPoint(rect, eventData.position, uiCamera))
            {
                //處理交互事件
            }
        }
}

二、限制圖片可移動範圍

拖拽限制參考Unity UI拖拽及拖拽範圍限制功能的實現

參考文章判斷拖拽範圍時利用被拖拽圖與父親的pivot之間的距離計算可以拖拽的移動範圍。(紅色的爲範圍,藍色爲被拖拽體)

而本篇文章利用例外一種方法,利用頂點的左邊限制移動範圍,剛好可以適應策劃的需求,限制ui在範圍內,而這個ui比父節點還要大。

其實就是限制被拖拽圖的頂點在父節點的外面即可,這時只要限制左上和右下,或者左下和右上即可。

具體方法就是判斷頂點的x,y座標,通過RectTransform.GetWorldCorners(corners),可獲得頂點座標。

public void OnDrag(PointerEventData eventData)
{
        Vector3 pos;
        RectTransformUtility.ScreenPointToWorldPointInRectangle(rect, eventData.position, uiCamera, out pos);
        Vector3 lastPos = rect.position;
        rect.position = pos + dragOffset;
        //判斷拖拽範圍
        int state = 0;
        if (!CanDrag(out state))
        {
            Vector3 nowPos = rect.position;
            //state=1 只能左右移動
            rect.position = new Vector3(nowPos.x, lastPos.y, nowPos.z);
            //state=2 只能上下移動
            rect.position = new Vector3(lastPos.x, nowPos.y, nowPos.z);
            //state=3 不能移動
            rect.position = lastPos;
        }
}
    bool CanDrag(out int state)
{
        state = 1;
        rect.GetWorldCorners(corners);
        //判斷頂點與父頂點之間的關係
        //左上點在父左上點右方或者下方
        //右下點在父右下點左方或者上方
        return true;
}

三、拖拽結束後檢測鬆手位置完成各種需求

這裏有多種操作:

卸下(拖動到了空白區域)

交換(拖動到了另外的裝備格子)

裝備(將未裝備的拖動到了格子)

關鍵接口RectTransformUtility.RectangleContainsScreenPoint,判斷擡起座標是否在格子的範圍內

public void OnEndDrag(PointerEventData eventData)
{
        //拖拽結束後,檢查擡起點下的交互物體
        //僞代碼 需要被檢查的裝備框 在範圍內爲true
        foreach(var rect in rects)
        {
            if (RectTransformUtility.RectangleContainsScreenPoint(rect, eventData.position, uiCamera))
            {
                //處理交互事件
            }
        }
}

四、部分代碼

public class DragUI : MonoBehaviour
{
    private RectTransform parentRect;
    private RectTransform rect;
    private Camera uiCamera;
    private UIEventTrigger eventTrigger;
    private Vector3 dragOffset;
    private Vector3[] pCorners;
    private Vector3[] corners;
    private void Start()
    {
        rect = GetComponent<RectTransform>(); //rect = 被拖拽的圖片對應組件
        //uiCamera = 所對應的ui攝像機
        eventTrigger = GetComponent<UIEventTrigger>();
        eventTrigger.onBeginDrag += OnBeginDrag;
        eventTrigger.onDrag += OnDrag;
        eventTrigger.onEndDrag += OnEndDrag;
        parentRect.GetWorldCorners(pCorners);
    }
    public void OnBeginDrag(PointerEventData eventData)
    {
        //開始拖拽時獲得拖拽點與被拖拽圖的偏移
        RectTransformUtility.ScreenPointToWorldPointInRectangle(rect, eventData.position, uiCamera, out dragOffset);
    }
    public void OnDrag(PointerEventData eventData)
    {
        Vector3 pos;
        RectTransformUtility.ScreenPointToWorldPointInRectangle(rect, eventData.position, uiCamera, out pos);
        Vector3 lastPos = rect.position;
        rect.position = pos + dragOffset;
        //判斷拖拽範圍
        int state = 0;
        if (!CanDrag(out state))
        {
            Vector3 nowPos = rect.position;
            //state=1 只能左右移動
            rect.position = new Vector3(nowPos.x, lastPos.y, nowPos.z);
            //state=2 只能上下移動
            rect.position = new Vector3(lastPos.x, nowPos.y, nowPos.z);
            //state=3 不能移動
            rect.position = lastPos;
        }
    }
    bool CanDrag(out int state)
    {
        state = 1;
        rect.GetWorldCorners(corners);
        //判斷頂點與父頂點之間的關係
        //左上點在父左上點右方或者下方
        //右下點在父右下點左方或者上方
        return true;
    }
    public void OnEndDrag(PointerEventData eventData)
    {
        //拖拽結束後,檢查擡起點下的交互物體
        //僞代碼 需要被檢查的裝備框 在範圍內爲true
        foreach(var rect in rects)
        {
            if (RectTransformUtility.RectangleContainsScreenPoint(rect, eventData.position, uiCamera))
            {
                //處理交互事件
            }
        }
    }
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章