Unity — — UGUI之背包物品拖放

最新背包代码:

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     }
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手册内的代码

 

三、实现

 

如图是整个背包的UI层级,每个Slot和里面的Item都是Prefab,把脚本挂在InventoryItem上即可实现

 

转载于:https://www.cnblogs.com/QQW123/p/9663609.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值