最新背包代码:
Unity3D — — UGUI之简易背包
Unity版本:2017.3
功能:用UGUI实现简单的背包物品拖放/交换功能
一、简介
在UGUI下,物品的拖放脚本实现主要依赖于UnityEngine.EventSystems下的三个接口 IBeginDragHandler, IDragHandler, IEndDragHandler; 其次还有IPointerEnterHandler,IPointerExitHandler
等接口来实现鼠标移入移出等操作的监控,同时引用这些接口后,对应的方法也是必须要实现的
简单介绍下这几个方法:
官方API解释:PointerEventData - - Event payload associated with pointer (mouse / touch) events.
Drag类:
OnBeginDrag(PointerEventData eventData) :当点击物体后开始执行此方法
OnDrag(PointerEventData eventData) :在拖拽中过程中执行
OnEndDrag(PointerEventData eventData) :拖拽结束时执行(松开鼠标的那下)
Pointer类:
OnPointerEnter(PointerEventData eventData) :当鼠标进入时执行
其余的类似OnPointerExit方法基本类似
Drop类:(待研究)
IDropHandler下的OnDrop(PointerEventData eventData)
这个方法笔者没有过多研究,由于拖放物品结束后的功能(交换/摧毁等)由EndDrag方法实现了,未发现OnDrop的具体用法
二、功能实现
注:由于未导入具体的物品信息,因此目前只是实现简单的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 }
三、实现
如图是整个背包的UI层级,每个Slot和里面的Item都是Prefab,把脚本挂在InventoryItem上即可实现