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)两个物体间互相交换
大致样子如下:
代码:
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之背包物品拖放的更多相关文章
- Unity——可复用背包工具
Unity可复用背包工具 Demo展示 设计思路 游戏中有非常多的背包样式,比如玩家道具背包,商城,装备栏,技能栏等:每个形式的背包都单独写一份逻辑会非常繁琐,所以需要有一套好用的背包工具: 这些背包 ...
- Unity UGUI —— 无限循环List
还记得大学毕业刚工作的时候是做flash的开发,那时候看到别人写的各种各样的UI组件就非常佩服,后来自己也慢慢尝试着写,发现其实也就那么回事.UI的开发其实技术的成分相对来说不算多,但是一个好的UI是 ...
- Unity UGUI图文混排源码(三) -- 动态表情
这里是根据图文混排源码(二)进一步修改的,其他链接也不贴了,就贴一个链接就好了,第一次看这文章的同学可以先去看看其他几篇文章 Unity UGUI图文混排源码(二):http://blog.csdn. ...
- Unity UGUI图文混排源码(二)
Unity UGUI图文混排源码(一):http://blog.csdn.net/qq992817263/article/details/51112304 Unity UGUI图文混排源码(二):ht ...
- Unity UGUI图文混排源码(一)
Unity UGUI图文混排源码(一):http://blog.csdn.net/qq992817263/article/details/51112304 Unity UGUI图文混排源码(二):ht ...
- Unity UGUI实现图文混排
目前在unity实现图文混排的好像都是通过自定义字体然后在文本获取字符的位置,用图片替换掉图片标签,这样对于支持英文来说,并没有什么影响.然后对于中文来说就是一个相当麻烦的事了,毕竟图文混排多用于游戏 ...
- Unity UGUI
超详细的基础教程传送门:(持续更新中) Unity UGUI之Canvas&EventSystem:http://blog.csdn.net/qq992817263/article/detai ...
- Unity UGUI Layout自动排版组件用法介绍
Unity UGUI布局组件 本文提供全流程,中文翻译. Chinar 坚持将简单的生活方式,带给世人!(拥有更好的阅读体验 -- 高分辨率用户请根据需求调整网页缩放比例) Chinar -- 心分享 ...
- 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 ...
随机推荐
- Spring 源码阅读之BeanFactory
1. BeanFactory 的结构体系如下: 2. XmlBeanFactory ,装载Spring配置信息 package org.springframework.beans.factory.xm ...
- linux下统计目录下所有子目录的大小
du -sh * --exclude=tar |awk '{v=substr($1,length($1),1)}v=="G"{$0="1G "$0}v==&qu ...
- JavaScript设计模式—单例模式
单例模式介绍 系统中被唯一使用的,一个类只有一个实例 单例模式的思路是: 一个类能返回一个对象的引用(并且永远是同一个)和一个获得该实例的方法(静态方法,通常使用 getInstance 名称). 那 ...
- Android 高级UI设计笔记24:Android 夜间模式之 WebView 实现白天 / 夜间阅读模式 (使用JavaScript)
1. 问题引入: 前面我们是使用方法 降低屏幕亮度(不常用) 和 替换theme,两者都是针对Activity的背景进行白天.夜间模式的交换,但是如果我们显示的是Html的内容,这个时候改怎么办? 分 ...
- 20165318 预备作业3 Linux安装及学习
Linux安装及学习 一.VirtualBox和Ubuntu的安装 我安装的是VirtualBox 5.2.6和Ubuntu 16.04 LTS,安装过程按照老师博客中的步骤依次进行,出现了以下几个问 ...
- 【转】使用URL SCHEME启动天猫客户端并跳转到某个商品页面的方法
在项目中遇到了这样一个需求:让用户在手机应用中,点击一个天猫的商品链接(知道商品在PC浏览器里的地址),直接启动天猫的客户端并显示这个商品.以前曾经实现过类似的功能,不过那次是淘宝的商品,天猫和淘宝的 ...
- 【原创】大叔经验分享(52)ClouderaManager修改配置报错
Cloudera Manager中修改配置可能报错: Incorrect string value: '\xE7\xA8\x8B\xE5\xBA\x8F...' for column 'MESSAGE ...
- 学习笔记——线程 Thread
Thread是.net1.0 1.1时出现的 主要了解线程等待.回调.前后台线程区别 1.实例: //定义:public delegate void ThreadStart(); ThreadStar ...
- Redis(一)源码安装
redis 是一款开源的,基于 BSD 许可的,高级键值 (key-value) 缓存 (cache) 和存储 (store) 系统.性能极高,并且支持丰富的数据结构.下面将介绍在ubuntu 14. ...
- Php5.6.31连接sqlserver 2008R2数据库问题sqlsrv(php5.3及以上版本)与mssql(php5.3以前版本)②
Php5.6.31连接sqlserver 2008R2数据库 1.环境配置 Win7(win8.1) 64 +Apache2.4 + PHP5.6.31 + SQL Server 2008 R2数据 ...