最近有個卡牌裝備的問題,實現可以將卡牌在裝備框內移動縮放,並且可以將卡牌拖動到其他的裝備框進行交換,或者卸下操作。
一、讓圖片跟隨鼠標移動
要讓圖片隨鼠標拖拽移動,首先需要接收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))
{
//處理交互事件
}
}
}
}