之前在網上找了好多方法,總是不適合自己,要麼就是不能用之類的,我用的是2017的版本,在這裡"全面"解析一下.首先建立一個指令碼InventoryItem掛在需要拖拽的Image上,這裡的Image是另一個Image的子物體,因為子的Imget我用來顯示物品的相應物品的圖片,而父的Image我用來顯示格子的圖片,這樣就有了格子裡面的物品,可以用來拖動,而且,格子的標籤我設為"InventoryItemGird",拖拽結束時就可以通過標籤判斷是不是格子.
1. 繼承介面
u3d的ugui有相應的介面來方便我們實現拖拽功能,除了本來就需要繼承的MonoBehaviour外,我們寫的指令碼再繼承三個介面,還得先匯入標頭檔 UnityEngine.EventSystems;
using UnityEngine.EventSystems;public class InventoryItem : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler{} 2. 實現抽象方法
抽象方法同樣是三個
using UnityEngine.EventSystems;public class InventoryItem : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler{ /// <summary> /// 實現介面的OnBeginDrag方法,處理開始拖拽時要做的事情 /// </summary> /// <param name="eventData"></param> public void OnBeginDrag(PointerEventData eventData) { } /// <summary> /// 實現介面的OnDrag方法,處理拖動中要做的事情 /// </summary> /// <param name="eventData"></param> public void OnDrag(PointerEventData eventData) { } /// <summary> /// 實現介面的OnEndDrag方法,處理結束時的方法 /// </summary> /// <param name="eventData"></param> public void OnEndDrag(PointerEventData eventData) { }} 3. 去遮擋
拖拽的時候我們會遇到一個問題,就是拖拽物品的父物體是是一個格子,而格子又位於不同的層,如圖:
我把格子取名為Inventory-Item-gird-00至Inventory-Item-gird-34共20個格子,這裡的第一個格子Inventory-Item-gird-00在其他格子的後面,ui物體越靠下就越顯示在攝像機的越靠外層,所以,當Inventory-Item-gird-00下的物品拖拽到Inventory-Item-gird-34格子的位置時,會被其遮擋我們這裡就在最下面再建立一個隱形而不能用畫布:Canvas,當我們需要拖動物品時就將該物品的父物體設定為Canvas,拖拽物品就會位於最外層,不會被任何ui遮擋,需要注意的是,當拖拽結束我們需要將父物體設定回來,不然會導致原來格子下的Image沒了.
private Transform imageParent;//拖拽物品的父物體 private Canvas imageFather;//擷取最外層的畫布 private Vector2 drugingRectTransform;//正在被拖拽的圖片的原始位置 public void OnBeginDrag(PointerEventData eventData) { imageFather = GameObject.Find("Canvas").GetComponent<Canvas>();//擷取在最外層的格子 drugingRectTransform = myImage.rectTransform.position;//擷取初始位置 imageParent = transform.parent;//擷取父物體的transform transform.SetParent(imageFather.transform);//將物品放在最外層 } /// <summary> /// 將一個物體放在另一個物體下 /// </summary> /// <param name="child">作為子物體</param> /// <param name="parent">作為父物體</param> private void SetParentAndPosition(Transform child, Transform parent)//將child放到parent下作為子物體 { child.SetParent(parent); child.position = parent.position; } 4. 實現穿透
當我們拖拽結束的時候,我們需要判斷滑鼠停留的位置是否是一個格子,如果是格子才有可能吧物品放進去,如果不是就要讓物品還原位置.那麼問題來了,拖拽物品始終在滑鼠位置,也就是說判斷滑鼠停留位置的時候,射線判斷到的永遠是我們正在拖拽的物品.所以我們我讓我們在拖拽中的物品不被滑鼠射線檢測到,也就是使物品實現穿透.這個時候我們的物品位於Canvas下,所以我們只需要讓Canvas實現穿透就行了,先給其添加一個組件:Canvas Group
這個組件下的Blocks Raycasts選項就表示所在物體是否會遮擋滑鼠射線,這時我們就可以通過代碼來控制其開關了.
/// <summary> /// 實現介面的OnBeginDrag方法,處理開始拖拽時要做的事情 /// </summary> /// <param name="eventData"></param> public void OnBeginDrag(PointerEventData eventData) { imageFather = GameObject.Find("Canvas").GetComponent<Canvas>();//擷取在最外層的畫布 drugingRectTransform = myImage.rectTransform.position;//擷取初始位置 imageParent = transform.parent;//擷取父物體的transform transform.SetParent(imageFather.transform);//將物品放在最外層 GameObject.Find("Canvas").GetComponent<CanvasGroup>().blocksRaycasts = false;//射線可以穿透物體 }
至此開始拖拽物品的方法完全實現
5. 物品置入
物品置入前還有一個拖拽方法需要實現:
/// <summary> /// 實現介面的OnDrag方法,處理拖動中要做的事情 /// </summary> /// <param name="eventData"></param> public void OnDrag(PointerEventData eventData) { this.GetComponent<RectTransform>().position=Input.mousePosition;//滑鼠左鍵按住拖拽的時候,物體跟著滑鼠移動 }
最後就是拖拽結束的方法了.最後一個交換物品的方法可以不用管,我寫給自己看的,所有的思路已經很清晰了.
/// <summary> /// 實現介面的OnEndDrag方法,處理結束時的方法 /// </summary> /// <param name="eventData"></param> public void OnEndDrag(PointerEventData eventData) { SetParentAndPosition(transform, imageParent);//複原父物體 transform.position = drugingRectTransform;//原圖歸位 ExchangeOfGoods(eventData);//交換物品 GameObject.Find("Canvas").GetComponent<CanvasGroup>().blocksRaycasts = false;//ui事件穿透:置為不能穿透 } /// <summary> /// 格子內的物品進行交換 /// </summary> /// <param name="eventData">參數PointerEventData用於擷取滑鼠終點位置</param> private void ExchangeOfGoods(PointerEventData eventData) { GameObject go = eventData.pointerCurrentRaycast.gameObject;//擷取滑鼠下的物體 //如果物體不為空白且該物體為物品格子 if (go != null && go.tag.Equals("InventoryItemGird")) { InventoryItemGrid fatherOfThis = this.GetComponentInParent<InventoryItemGrid>();//擷取起點格子的指令碼 InventoryItemGrid fatherOfThat = go.GetComponent<InventoryItemGrid>();//擷取終點格子的指令碼 //如果格子為空白格子 if (fatherOfThat.id == 0) { fatherOfThat.SetId(fatherOfThis.id,fatherOfThis.num);//向終點格子儲存物品 fatherOfThis.ClearInfo();//清空原來格子資訊 } else//兩格子交換物品 { InventoryItemGrid temp = fatherOfThat;//臨時儲存終點格子的指令碼 fatherOfThat.SetId(fatherOfThis.id, fatherOfThis.num);//向終點格子儲存物品 fatherOfThis.SetId(temp.id, temp.num);//向起點格子儲存物品 } } }