最新背包代码:

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)两个物体间互相交换

大致样子如下:

代码:

 using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using UnityEngine.EventSystems;
using UnityEngine.UI; public class InventoryItem : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler,IPointerEnterHandler
{
private Transform originalSlot;
private GameObject parent;
private GameObject item;
private float x_item;
private float y_item;
private Vector2 itemSize;
private bool isDragging = false; //默认设为false,否则OnPointerEnter每帧都会调用,会有bug /// <summary>
/// 添加CanvasGroup组件,在物品拖动时blocksRaycasts设置为false;
/// 让鼠标的Pointer射线穿过Item物体检测到UI下层的物体信息
/// </summary>
private CanvasGroup itemCanvasGroup; public string objectTag=null; private void Start()
{
itemCanvasGroup = this.GetComponent<CanvasGroup>();
item = this.transform.gameObject; x_item = item.GetComponent<Image>().GetPixelAdjustedRect().width; //Image的初始长宽
y_item = item.GetComponent<Image>().GetPixelAdjustedRect().height;
parent = GameObject.FindGameObjectWithTag("SlotGrid");
} public void OnPointerEnter(PointerEventData eventData)
{
//当鼠标在最外层时(移出背包,Canvas外)
//让物品回到原位
if(eventData.pointerCurrentRaycast.depth== && isDragging==true)
{
SetOriginalPos(this.gameObject);
return;
} //Debug.Log(eventData.pointerCurrentRaycast.depth);
objectTag = eventData.pointerCurrentRaycast.gameObject.tag;
Debug.Log("Raycast = "+objectTag); if(objectTag!=null && isDragging==true)
{ if (objectTag == Tags.InventorySlot) //如果是空格子,则放置Item
{
SetCurrentSlot(eventData);
}
else if (objectTag == Tags.InventoryItem) //交换物品
{
SwapItem(eventData);
}
else //如果都不是则返回原位
{
SetOriginalPos(this.gameObject);
}
}
} //把Item回归到原来位置
public void SetOriginalPos(GameObject gameobject)
{ gameobject.transform.SetParent(originalSlot);
gameobject.GetComponent<RectTransform>().anchoredPosition = originalSlot.GetComponent<RectTransform>().anchoredPosition;
itemCanvasGroup.blocksRaycasts = true;
} //交换两个物体
//由于拖放中,正被拖放的物体没有Block RayCast
//具体思路:
//1.记录当前射线照射到的物体(Item2)
//2.获取Item2的parent的位置信息,并把item1放过去
//3.把Item2放到Item1所在的位置
public void SwapItem(PointerEventData eventData)
{
GameObject targetItem = eventData.pointerCurrentRaycast.gameObject; //下面这两个方法不可颠倒,否则执行顺序不一样会出bug
//BUG:先把Item2放到了Item1的位置,此时Item1得到的位置信息是传递后的Item2的(原本Item1的位置)
//因此会把Item1也放到Item2下,变成都在原本Item1的Slot内
SetCurrentSlot(eventData);
SetOriginalPos(targetItem);
} //设置Item到当前鼠标所在的Slot
public void SetCurrentSlot(PointerEventData eventData)
{
//如果Slot为空
if (eventData.pointerCurrentRaycast.gameObject.tag==Tags.InventorySlot)
{
Transform currentSlot= eventData.pointerCurrentRaycast.gameObject.transform;
this.transform.SetParent(currentSlot);
//如果只是transform position,图片会默认在左上角顶点处的Anchor
//因此这里用anchoredPosition让Item图片填充满Slot
this.GetComponent<RectTransform>().anchoredPosition = currentSlot.GetComponent<RectTransform>().anchoredPosition;
}
else if(eventData.pointerCurrentRaycast.gameObject.tag == Tags.InventoryItem)
{
Transform currentSlot = eventData.pointerCurrentRaycast.gameObject.transform.parent;
this.transform.SetParent(currentSlot);
this.GetComponent<RectTransform>().anchoredPosition = currentSlot.GetComponent<RectTransform>().anchoredPosition;
}
} public void OnBeginDrag(PointerEventData eventData)
{
originalSlot = this.GetComponent<Transform>().parent; //每次拖拽开始前记录初始位置
isDragging = true;
itemCanvasGroup.blocksRaycasts = true;
item.transform.SetParent(parent.transform, false); // 将item设置到当前UI层级的最下面(最表面,防止被同一层级的UI覆盖)
item.transform.SetAsLastSibling(); item.GetComponent<RectTransform>().SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, x_item);
item.GetComponent<RectTransform>().SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, y_item);
} public void OnDrag(PointerEventData eventData)
{
itemCanvasGroup.blocksRaycasts = false;
DragPos(eventData);
//OnPointerEnter(eventData);
} public void OnEndDrag(PointerEventData eventData)
{
OnPointerEnter(eventData);
itemCanvasGroup.blocksRaycasts = true;
isDragging = false;
} //获取鼠标当前位置,并赋给item
private void DragPos(PointerEventData eventData)
{
RectTransform RectItem = item.GetComponent<RectTransform>();
Vector3 globalMousePos;
if (RectTransformUtility.ScreenPointToWorldPointInRectangle(item.transform as RectTransform, eventData.position, eventData.pressEventCamera, out globalMousePos))
{
RectItem.position = globalMousePos;
}
}

InventoryItem

Unity官方的的实现代码:

官方的的代码实现的是拖动物体时生成一个新的Gamobject

下面是官方代码加自己的一些注释

     public bool dragOnSurfaces = true;

     private GameObject m_DraggingIcon;
private RectTransform m_DraggingPlane; public void OnBeginDrag(PointerEventData eventData)
{
//找到有Canvas组件的物体
var canvas = FindInParents<Canvas>(gameObject); if (canvas == null)
return; //We have clicked something that can be dragged.
// What we want to do is create an icon for this.
//给实例化的新GameObject命名
m_DraggingIcon = new GameObject(this.name);
//放到指定路径
m_DraggingIcon.transform.SetParent(canvas.transform, false); //Move the transform to the end of the local transform list.
//Puts the panel to the front as it is now the last UI element to be drawn.
m_DraggingIcon.transform.SetAsLastSibling(); //给新GameObject添加<Image>组件
var image = m_DraggingIcon.AddComponent<Image>();
//把当前脚本所挂载的物体的图片赋给新GameObject
image.sprite = GetComponent<Image>().sprite;
image.SetNativeSize(); if (dragOnSurfaces)
m_DraggingPlane = transform as RectTransform;
else
m_DraggingPlane = canvas.transform as RectTransform; SetDraggedPosition(eventData);
} public void OnDrag(PointerEventData data)
{
if (m_DraggingIcon != null)
SetDraggedPosition(data);
} private void SetDraggedPosition(PointerEventData data)
{ if (dragOnSurfaces && data.pointerEnter != null && data.pointerEnter.transform as RectTransform != null)
m_DraggingPlane = data.pointerEnter.transform as RectTransform; var rt = m_DraggingIcon.GetComponent<RectTransform>();
Vector3 globalMousePos;
if (RectTransformUtility.ScreenPointToWorldPointInRectangle(m_DraggingPlane, data.position, data.pressEventCamera, out globalMousePos))
{
rt.position = globalMousePos;
rt.rotation = m_DraggingPlane.rotation;
}
} public void OnEndDrag(PointerEventData eventData)
{
if (m_DraggingIcon != null)
Destroy(m_DraggingIcon);
} //实现不断往相应的上层parent查找所需组件
//Component: Base class for everything attached to GameObjects.
static public T FindInParents<T>(GameObject go) where T : Component
{
//如果go为null,返回null
if (go == null) return null; //查找go身上相应组件(Canvas)
//找到后返回comp
var comp = go.GetComponent<T>();
if (comp != null)
return comp; //查找t的parent
//循环查找,不断往上层找parent,直到找到相应组件(Canvas)
Transform t = go.transform.parent;
while (t != null && comp == null) //t有上层parent && 第1步里未找到组件
{
comp = t.gameObject.GetComponent<T>();
t = t.parent;
}
return comp;
}

UnityAPI手册内的代码

三、实现

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

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

  1. Unity——可复用背包工具

    Unity可复用背包工具 Demo展示 设计思路 游戏中有非常多的背包样式,比如玩家道具背包,商城,装备栏,技能栏等:每个形式的背包都单独写一份逻辑会非常繁琐,所以需要有一套好用的背包工具: 这些背包 ...

  2. Unity UGUI —— 无限循环List

    还记得大学毕业刚工作的时候是做flash的开发,那时候看到别人写的各种各样的UI组件就非常佩服,后来自己也慢慢尝试着写,发现其实也就那么回事.UI的开发其实技术的成分相对来说不算多,但是一个好的UI是 ...

  3. Unity UGUI图文混排源码(三) -- 动态表情

    这里是根据图文混排源码(二)进一步修改的,其他链接也不贴了,就贴一个链接就好了,第一次看这文章的同学可以先去看看其他几篇文章 Unity UGUI图文混排源码(二):http://blog.csdn. ...

  4. Unity UGUI图文混排源码(二)

    Unity UGUI图文混排源码(一):http://blog.csdn.net/qq992817263/article/details/51112304 Unity UGUI图文混排源码(二):ht ...

  5. Unity UGUI图文混排源码(一)

    Unity UGUI图文混排源码(一):http://blog.csdn.net/qq992817263/article/details/51112304 Unity UGUI图文混排源码(二):ht ...

  6. Unity UGUI实现图文混排

    目前在unity实现图文混排的好像都是通过自定义字体然后在文本获取字符的位置,用图片替换掉图片标签,这样对于支持英文来说,并没有什么影响.然后对于中文来说就是一个相当麻烦的事了,毕竟图文混排多用于游戏 ...

  7. Unity UGUI

    超详细的基础教程传送门:(持续更新中) Unity UGUI之Canvas&EventSystem:http://blog.csdn.net/qq992817263/article/detai ...

  8. Unity UGUI Layout自动排版组件用法介绍

    Unity UGUI布局组件 本文提供全流程,中文翻译. Chinar 坚持将简单的生活方式,带给世人!(拥有更好的阅读体验 -- 高分辨率用户请根据需求调整网页缩放比例) Chinar -- 心分享 ...

  9. Curved UI - VR Ready Solution To Bend Warp Your Canvas 1.7,1.8,2.2,2.3 四种版本压缩包(Unity UGUI曲面插件),可以兼容VRTK

    Curved UI - VR Ready Solution To Bend Warp Your Canvas 1.7,1.8,2.2,2.3 四种版本压缩包(Unity UGUI曲面插件) 可以兼容V ...

随机推荐

  1. 解密虚拟 DOM——snabbdom 核心源码解读

    本文源码地址:https://github.com/zhongdeming428/snabbdom 对很多人而言,虚拟 DOM 都是一个很高大上而且远不可及的专有名词,以前我也这么认为,后来在学习 V ...

  2. swift的Hashable

    Conforming to the Hashable Protocol To use your own custom type in a set or as the key type of a dic ...

  3. 2879. [NOI2012]美食节【费用流】

    Description CZ市为了欢迎全国各地的同学,特地举办了一场盛大的美食节.作为一个喜欢尝鲜的美食客,小M自然不愿意错过这场盛宴.他很快就尝遍了美食节所有的美食.然而,尝鲜的欲望是难以满足的.尽 ...

  4. 1877. [SDOI2009]晨跑【费用流】

    Description Elaxia最近迷恋上了空手道,他为自己设定了一套健身计划,比如俯卧撑.仰卧起坐等 等,不过到目前为止,他 坚持下来的只有晨跑. 现在给出一张学校附近的地图,这张地图中包含N个 ...

  5. Jenkins构建Python项目失败

     Console Output 提示:'Python' 不是内部或外部命令,也不是可运行的程序 定位原因:python.exe 不在jenkins执行用户的PATH里面 解决:构建的时候Python命 ...

  6. Linux 无法连接网络排查方法

    .hosts文件增加 127.0.0.1 对localhost的解析. .检查/etc/resove.cnf dns配置是否正确 .route命令检查是否有默认路由,没有就 route add 网段 ...

  7. svn 查找指定文件和后缀变化

    有时候需要批量查找一些包含特定关键字文件名和后缀的文件,先把所有变化文件通多svn diff命令 输出到一个文件,然后使用如下命令: grep -i 'data*.xml' change.txt |a ...

  8. 垂直方向兼容显示的内容多少的情况样式Flex布局

    使用flex弹性布局,无论里面的元素显示几个,都会居中显示,父元素设置成如下样式 display: flex; flex-direction: column; justify-content: cen ...

  9. AWR报告中Top 10 Foreground Events存在”reliable message”等待事件的处理办法

    操作系统版本:HP-UNIX B.11.31 数据库版本:11.2.0.4 RAC (一) 问题概要 (1)在AWR报告的Top 10 Foreground Events中发现reliable mes ...

  10. html移动应用 input 标签 清除按钮功能如何实现(不触发键盘)

    有个需求是:输入框有文本的时候就显示清除按钮,没有文本则隐藏清除按钮,点击清除按钮不能影响键盘弹出的状态. 网上有css实现自动显示和隐藏清除按钮的方案,但是考虑到兼容性,我们还是使用js来实现. c ...