Unity — — UGUI之揹包物品拖放

最新揹包代碼:html

Unity3D — — UGUI之簡易揹包

Unity版本:2017.3canvas

功能:用UGUI實現簡單的揹包物品拖放/交換功能ide

1、簡介post

在UGUI下,物品的拖放腳本實現主要依賴於UnityEngine.EventSystems下的三個接口 IBeginDragHandler,  IDragHandler,  IEndDragHandler; 其次還有IPointerEnterHandler,IPointerExitHandler
等接口來實現鼠標移入移出等操做的監控,同時引用這些接口後,對應的方法也是必需要實現的this

簡單介紹下這幾個方法:url

 

官方API解釋:PointerEventData - - Event payload associated with pointer (mouse / touch) events.spa

Drag類:code

OnBeginDrag(PointerEventData eventData)  :當點擊物體後開始執行此方法orm

OnDrag(PointerEventData eventData)       :在拖拽中過程當中執行htm

OnEndDrag(PointerEventData eventData)     :拖拽結束時執行(鬆開鼠標的那下)

 

Pointer類:

OnPointerEnter(PointerEventData eventData)  :當鼠標進入時執行

其他的相似OnPointerExit方法基本相似

 

Drop類:(待研究)

IDropHandler下的OnDrop(PointerEventData eventData)

這個方法筆者沒有過多研究,因爲拖放物品結束後的功能(交換/摧毀等)由EndDrag方法實現了,未發現OnDrop的具體用法

 

2、功能實現

注:因爲未導入具體的物品信息,所以目前只是實現簡單的GameObject間的拖放關係

    (1)揹包內物體的拖放,並在結束後指定到相應的格子下

    (2)當物品未在格子內或者超出揹包範圍時,歸位到本來的位置

    (3)兩個物體間互相交換

大體樣子以下:

代碼:

  1 using System.Collections;
  2 using System.Collections.Generic;
  3 using UnityEngine;
  4 using UnityEditor;
  5 using UnityEngine.EventSystems;
  6 using UnityEngine.UI;
  7 
  8 public class InventoryItem : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler,IPointerEnterHandler
  9 {
 10     private Transform originalSlot;
 11     private GameObject parent;
 12     private GameObject item;
 13     private float x_item;
 14     private float y_item;
 15     private Vector2 itemSize;
 16     private bool isDragging = false;    //默認設爲false,不然OnPointerEnter每幀都會調用,會有bug
 17     
 18     /// <summary>
 19     /// 添加CanvasGroup組件,在物品拖動時blocksRaycasts設置爲false;
 20     /// 讓鼠標的Pointer射線穿過Item物體檢測到UI下層的物體信息
 21     /// </summary>
 22     private CanvasGroup itemCanvasGroup;
 23 
 24     public string objectTag=null;
 25 
 26     private void Start()
 27     {
 28         itemCanvasGroup = this.GetComponent<CanvasGroup>();
 29         item = this.transform.gameObject;
 30 
 31         x_item = item.GetComponent<Image>().GetPixelAdjustedRect().width;    //Image的初始長寬
 32         y_item = item.GetComponent<Image>().GetPixelAdjustedRect().height;
 33         parent = GameObject.FindGameObjectWithTag("SlotGrid");
 34     }
 35 
 36     public void OnPointerEnter(PointerEventData eventData)
 37     {
 38         //當鼠標在最外層時(移出揹包,Canvas外)
 39         //讓物品回到原位
 40         if(eventData.pointerCurrentRaycast.depth==0 && isDragging==true)
 41         {
 42             SetOriginalPos(this.gameObject);
 43             return;
 44         }
 45 
 46         //Debug.Log(eventData.pointerCurrentRaycast.depth);
 47         objectTag = eventData.pointerCurrentRaycast.gameObject.tag;
 48         Debug.Log("Raycast = "+objectTag);
 49 
 50        
 51        if(objectTag!=null && isDragging==true)
 52        {
 53             
 54             if (objectTag == Tags.InventorySlot)   //若是是空格子,則放置Item
 55             {
 56                 SetCurrentSlot(eventData);
 57             }
 58             else if (objectTag == Tags.InventoryItem)      //交換物品
 59             {
 60                 SwapItem(eventData);
 61             }
 62             else   //若是都不是則返回原位
 63             {
 64                 SetOriginalPos(this.gameObject);
 65             }
 66        }
 67     }
 68 
 69     //把Item迴歸到原來位置
 70     public void SetOriginalPos(GameObject gameobject)
 71     {
 72         
 73         gameobject.transform.SetParent(originalSlot);
 74         gameobject.GetComponent<RectTransform>().anchoredPosition = originalSlot.GetComponent<RectTransform>().anchoredPosition;
 75         itemCanvasGroup.blocksRaycasts = true;
 76     }
 77     
 78     //交換兩個物體
 79     //因爲拖放中,正被拖放的物體沒有Block RayCast
 80     //具體思路:
 81         //1.記錄當前射線照射到的物體(Item2)
 82         //2.獲取Item2的parent的位置信息,並把item1放過去
 83         //3.把Item2放到Item1所在的位置
 84     public void SwapItem(PointerEventData eventData)
 85     {
 86         GameObject targetItem = eventData.pointerCurrentRaycast.gameObject;
 87 
 88         //下面這兩個方法不可顛倒,不然執行順序不同會出bug
 89         //BUG:先把Item2放到了Item1的位置,此時Item1獲得的位置信息是傳遞後的Item2的(本來Item1的位置)
 90         //所以會把Item1也放到Item2下,變成都在本來Item1的Slot內
 91         SetCurrentSlot(eventData);      
 92         SetOriginalPos(targetItem);
 93     }
 94 
 95     //設置Item到當前鼠標所在的Slot
 96     public void SetCurrentSlot(PointerEventData eventData)
 97     {
 98         //若是Slot爲空
 99         if (eventData.pointerCurrentRaycast.gameObject.tag==Tags.InventorySlot)
100         {
101             Transform currentSlot= eventData.pointerCurrentRaycast.gameObject.transform;
102             this.transform.SetParent(currentSlot);
103             //若是隻是transform position,圖片會默認在左上角頂點處的Anchor
104             //所以這裏用anchoredPosition讓Item圖片填充滿Slot
105             this.GetComponent<RectTransform>().anchoredPosition = currentSlot.GetComponent<RectTransform>().anchoredPosition;
106         }
107         else if(eventData.pointerCurrentRaycast.gameObject.tag == Tags.InventoryItem)
108         {
109             Transform currentSlot = eventData.pointerCurrentRaycast.gameObject.transform.parent;
110             this.transform.SetParent(currentSlot);
111             this.GetComponent<RectTransform>().anchoredPosition = currentSlot.GetComponent<RectTransform>().anchoredPosition;
112         }
113     }
114 
115     public void OnBeginDrag(PointerEventData eventData)
116     {
117         originalSlot = this.GetComponent<Transform>().parent;       //每次拖拽開始前記錄初始位置
118         isDragging = true;
119         itemCanvasGroup.blocksRaycasts = true;
120         item.transform.SetParent(parent.transform, false);
121        
122         // 將item設置到當前UI層級的最下面(最表面,防止被同一層級的UI覆蓋)
123         item.transform.SetAsLastSibling();      
124         
125         item.GetComponent<RectTransform>().SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, x_item);
126         item.GetComponent<RectTransform>().SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, y_item);
127     }
128 
129     public void OnDrag(PointerEventData eventData)
130     {
131         itemCanvasGroup.blocksRaycasts = false;
132         DragPos(eventData);
133         //OnPointerEnter(eventData);
134     }
135 
136     public void OnEndDrag(PointerEventData eventData)
137     {
138         OnPointerEnter(eventData);
139         itemCanvasGroup.blocksRaycasts = true;
140         isDragging = false;
141     }
142 
143     //獲取鼠標當前位置,並賦給item
144     private void DragPos(PointerEventData eventData)
145     {
146         RectTransform RectItem = item.GetComponent<RectTransform>();
147         Vector3 globalMousePos;
148         if (RectTransformUtility.ScreenPointToWorldPointInRectangle(item.transform as RectTransform, eventData.position, eventData.pressEventCamera, out globalMousePos))
149         {
150             RectItem.position = globalMousePos;
151         }
152     }
InventoryItem

 

Unity官方的的實現代碼:

官方的的代碼實現的是拖動物體時生成一個新的Gamobject

下面是官方代碼加本身的一些註釋

 1     public bool dragOnSurfaces = true;
 2 
 3     private GameObject m_DraggingIcon;
 4     private RectTransform m_DraggingPlane;
 5 
 6     public void OnBeginDrag(PointerEventData eventData)
 7     {
 8         //找到有Canvas組件的物體
 9         var canvas = FindInParents<Canvas>(gameObject);
10 
11         if (canvas == null)
12             return;
13 
14         //We have clicked something that can be dragged.
15         // What we want to do is create an icon for this.
16         //給實例化的新GameObject命名
17         m_DraggingIcon = new GameObject(this.name);
18         //放到指定路徑
19         m_DraggingIcon.transform.SetParent(canvas.transform, false);
20 
21         //Move the transform to the end of the local transform list.
22         //Puts the panel to the front as it is now the last UI element to be drawn.
23         m_DraggingIcon.transform.SetAsLastSibling();
24 
25         //給新GameObject添加<Image>組件
26         var image = m_DraggingIcon.AddComponent<Image>();
27         //把當前腳本所掛載的物體的圖片賦給新GameObject
28         image.sprite = GetComponent<Image>().sprite;
29         image.SetNativeSize();
30 
31 
32         if (dragOnSurfaces)
33             m_DraggingPlane = transform as RectTransform;
34         else
35             m_DraggingPlane = canvas.transform as RectTransform;
36 
37 
38         SetDraggedPosition(eventData);
39     }
40 
41     public void OnDrag(PointerEventData data)
42     {
43         if (m_DraggingIcon != null)
44             SetDraggedPosition(data);
45     }
46 
47     private void SetDraggedPosition(PointerEventData data)
48     {
49 
50         if (dragOnSurfaces && data.pointerEnter != null && data.pointerEnter.transform as RectTransform != null)
51             m_DraggingPlane = data.pointerEnter.transform as RectTransform;
52 
53         var rt = m_DraggingIcon.GetComponent<RectTransform>();
54         Vector3 globalMousePos;
55         if (RectTransformUtility.ScreenPointToWorldPointInRectangle(m_DraggingPlane, data.position, data.pressEventCamera, out globalMousePos))
56         {
57             rt.position = globalMousePos;
58             rt.rotation = m_DraggingPlane.rotation;
59         }
60     }
61 
62     public void OnEndDrag(PointerEventData eventData)
63     {
64         if (m_DraggingIcon != null)
65             Destroy(m_DraggingIcon);
66     }
67 
68     //實現不斷往相應的上層parent查找所需組件
69      //Component: Base class for everything attached to GameObjects.
70     static public T FindInParents<T>(GameObject go) where T : Component
71     {
72         //若是go爲null,返回null
73         if (go == null) return null;
74 
75         
76         //查找go身上相應組件(Canvas)
77         //找到後返回comp
78         var comp = go.GetComponent<T>();
79         if (comp != null)
80             return comp;
81 
82         //查找t的parent
83         //循環查找,不斷往上層找parent,直到找到相應組件(Canvas)
84         Transform t = go.transform.parent;
85         while (t != null && comp == null)       //t有上層parent && 第1步裏未找到組件
86         {
87             comp = t.gameObject.GetComponent<T>();
88             t = t.parent;
89         }
90         return comp;
91     }
UnityAPI手冊內的代碼

 

3、實現

 

如圖是整個揹包的UI層級,每一個Slot和裏面的Item都是Prefab,把腳本掛在InventoryItem上便可實現

相關文章
相關標籤/搜索