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

三、实现思路

原文地址:https://www.cnblogs.com/QQW123/p/9663609.html

时间: 2024-10-11 00:16:27

Unity — — UGUI之背包物品拖放的相关文章

Unity UGUI —— 无限循环List

还记得大学毕业刚工作的时候是做flash的开发,那时候看到别人写的各种各样的UI组件就非常佩服,后来自己也慢慢尝试着写,发现其实也就那么回事.UI的开发其实技术的成分相对来说不算多,但是一个好的UI是绝对少不了底层组件的支持的.我个人认为UI组件中相对比较复杂的就是List了,所以,这两天实现了一个UGUI的list,写了好几个版本,最终这个版本是相对比较好用的,在这我介绍一下大概思路,一是巩固一下知识做个记录,二是发扬一下分享精神.嘿嘿,大家多多赐教. 写List有两个重点是需要考虑的: 1.

unity ugui消息透传

公司要做一个这东西. A是滑动区域,ScrollRect组件. B是各种选项. C是拾取到鼠标(或触点)的选项. D是拖放区域. 大概要求是这样. 因为B的条目很多,放在A里可以滑动查看.如果要选择一个B,需要长按B,待时间足够之后生产一个新的C.拖动到D区域释放,则给D添加一个节点.其他区域则取消. 如果按住B的时间不够长,又动了鼠标(或触点),则当前滑动操作由A响应,产生A的滑动效果. 这里涉及到一个消息透传的问题. 解释一下自己在做的过程中采用的2个方案以及各自问题. 涉及的函数有以下几个

完美世界第一题:成功放入背包物品个数

#include <iostream> #include <vector> using namespace std; /************************************************************************/ /* 给定物品个数M,背包个数N,每个背包容量k,依次输入物品的重量 当物品放不下背包时,如果还有背包则放入下一背包,如果没有,直接扔掉. 当下一背包中放入物品后,该背包封口即不可再放物品. 用例输入: 5 5 2 4

Unity UGUI Canvas 使用Slider制作角色血条

最近课程实训选修Unity,讲解的是Unity官方案例--SpaceShoot,培训无聊,便想着给游戏角色制作一下血条,以此增加游戏的可玩性. Unity版本:5.4.1 案例模版:太空射击 原文作者:茄阁云云 原文连接:http://www.cnblogs.com/vmoor2016/p/6044941.html 把一个大象装进冰箱里需要三步:①把冰箱门打开:②把大象塞冰箱里:③关上冰箱门. 那么制作一个角色的血条需要几部呢?在这里也划分为三步:①制作血条:②摆放血条:③操作血条. 下面就来给

Unity UGUI 实现简单拖拽功能

说到拖拽,那必然离不开坐标,UGUI 的坐标有点不一样,它有两种坐标,一种是屏幕坐标,还有一种就是 UI 在Canvas内的坐标(暂时叫做ugui坐标),这两个坐标是不一样的,所以拖拽就需要转换. 因为鼠标的移动是屏幕坐标,而 UI 的移动是ugui坐标.转换的方法: RectTransformUtility.ScreenPointToLocalPointInRectangle ( - ); 这个方法可以把屏幕坐标转换成 ugui 坐标.这里我们只需要知道 UI 的坐标和鼠标的坐标是不一样的,他

unity ugui屏幕适配

1.unity分为2D和3D两种模式,分别对应camera组件的Projection选项,如果是Perspective,就是透视投影(3D模式),如果是Orthographic,就是正交投影(2D模式): 2.unity自带的ugui当中Canvas对象下的Canvas组件当中的Render Mode选项为Screen Space - Overlay时,消耗的draw call相对较少: 3.unity自带的ugui当中Canvas对象下的Canvas Scaler组件的Reference Pi

Unity UGUI 下拉菜单

1.新建Buttom 2.panel里面加下拉buttom using UnityEngine; using System.Collections; public class DropDownList : MonoBehaviour { public GameObject panel; void Start () { panel.SetActive (false); }public void HideOrShow() { panel.SetActive (!panel.activeSelf);

Unity UGUI Button 无法点击问题一例

理论上,只要一个按钮自己这一层或者子节点有一个控件勾选了RaycastTarget,并且按钮为Interactable的.并且不被其他可以点击的控件阻挡,那么这个按钮就可以被点击 在调一个界面时发现一个诡异的问题,有个按钮死活无法点击,EventSystem无法检测到.旁边的一个按钮却是ok的 对比了下,发现两者的区别是: 可点击的 x轴旋转-180 不可点击 无旋转 将不可点击的旋转-180,发现可以点击了 排查父节点,发现父节点挂了个Image为了做表现,旋转了180. 既然确定问题是旋转导

Unity UGUI事件接口概述

UGUI 系统虽然提供了很多封装好的组件,但是要实现一些特定的功能还是显得非常有限,这时候就需要使用事件接口来完成UI功能的实现.比如我们想实现鼠标移动到图片上时自动显示图片的文字介绍,一般思路会想到写个射线来检测,但其实这样的检测UGUI已经替我们完成了,我们只需要实现检测到目标对象后所要执行的代码即可! UI 组件都是基于 UGUI 封装好的类和接口以及一些 Editor 文件来进行封装制作供开发者使用的,开发者利用这些封装好的工具,只需更专注于功能开发即可.UGUI 的代码是开源的,开发者