最新揹包代碼:html
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 }
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 }
3、實現
如圖是整個揹包的UI層級,每一個Slot和裏面的Item都是Prefab,把腳本掛在InventoryItem上便可實現