写点什么

游戏背包系统,“Inventory Pro 插件”,研究学习 ----- 妈妈再也不用担心我不会做背包了(Unity3D)

  • 2022 年 7 月 25 日
  • 本文字数:12002 字

    阅读完需:约 39 分钟

游戏背包系统,“Inventory Pro插件”,研究学习-----妈妈再也不用担心我不会做背包了(Unity3D)

推荐阅读

一、前言

发现就喜欢研究这些插件,为什么呢,因为方便快捷啊。基本不用研究源代码怎么实现的,只要会有就行了。但是,光这样也不行,还是要多去看看底层代码是怎么实现的,还有人家的框架是怎么搭的。要不说 Unity3D 入门容易,提升难呢,因为提升全是靠苦功夫,去研究底层代码。算了,不絮叨了

二、参考文章

Unity3D 装备系统学习Inventory Pro 2.1.2 总结Unity3D 装备系统学习Inventory Pro 2.1.2 基础篇

三、正文

先上一张效果图



下载链接:https://github.com/764424567/Unity-plugin/tree/master/Menu/Unity3D-S-Inventory无效了记得跟博主说一声哈

1、总体结构

物品相关

  • 非 UI 相关 InventoryItem 物品体系类,具体如装备,消耗品,商店物品等

  • UI 相关 InventoryUIItemWrapper 物品体系

  • UIItem 的 UI 包装的 Item 继承体系

  • ItemCollection 这样的类,因为简单的增、删和改肯定是逃不了,复杂的如交换,容器间的交换等操作

UI 窗口相关

  • UIWindow 体系的窗口类,具体有角色,银行,技能,店铺等窗口

  • InventoryUIDialog 系统下的对话框类,具体有确认框,买卖,通用提示

  • 特殊窗口(非继承体系窗口),如上下菜单,通知窗口等

管理相关类

  • 配置管理

  • InvertoryManager

  • ItemManger

  • 数据库操作


其它


应该是一些辅助类,有 UI 部分的,事件辅助,定义接口等等吧,这部分还没有深入去阅读应该也是挺复杂的

剩下部分

一些第三方插件,Unity3D 特性及 Edit 扩展等等


具体类图


2、使用教程


示范项目


在你的 Assets/Demos/Scenes 你会发现这些演示场景,这些演示场景会包含所有特性,一定要仔细的看一下哦


建立一个新的项目

  • 选择 1(自动)打开设置向导,它可以发现在 Tools/Inventory Pro/Setup wizard 对于错误“没有管理者发现的对象”;单击确定按钮和一个管理对象命名为"_managers"将自动添加到你的场景里。

  • 选择 2(手动)创建一个空的游戏对象将 inventorymanager 组成,你可以找到在 库存/经理/ inventorymanager 你会得到几个管理器组件包括 inventorymanager,inventorysettingsmanager 和更多的,我们不需要现在。


数据库建立


itemmanager 包含所有项目的项目数据库,你可以创建管理每个场景,并使用不同的数据库,每一个场景。


1. 去你的项目窗口中,找到一个地方,你想创建项目数据库。

2. 右键单击(或点击创建,或者去创建/ 库存亲 /项目数据库)来创建一个新的项目数据库。

3. 一旦创建的数据库将在你的项目中选定。

4. 将项目拖到 itemmanager 的试题数据库槽。现在也让我们创建 语言数据库 ,做完全相同的操作,并将它分配给 inventorymanager 的 lang 。


配置设置


首先打开主编辑器可以发现在 工具/库存亲/主编。当你打开编辑会给你一个错误消息的第一时间(见下图)。


这是因为库存亲需要知道它应该保存新项目。单击“设置”按钮,选择一个文件夹 文件夹里面你的项目。

现在让我们打开整合经理再次看到我们必须配置。

第一个选项卡“UI”包含用于渲染 UI 的基本要素。为了做到这一点需要至少 2 个项目的 项目按钮预置 Item Button Prefab 和 UIROOT。

这个项目按钮预置是的预置 /槽包含的项目。根是包含所有用户界面窗口的 UI 画布。

几个演示器可以用来快速上手。你可以在库存/演示/资产/用户界面/ ui_prefabs / ui_item_pfb 找到默认的包装

并新建一个 UGUI,把 UI Canvas 赋值给 GUI ROOT。


3、Demo 解析

GettingStarted.unity


首先说一下 Demo1 的功能,其实很简单主要是建立起来 Inventory Pro 的运行环境,首先项目的 Demo 是 3d 的所以创建项目时,选择是 3D 工程。


运行环境中,使用标准插件库建立一个第三方视角跟随的角色,角色可以在 Panel 中自由的移动跑跳;然后才是 Inventroy Pro 的基础配置,主要是引入 Setting,在 Setting 中进行一些基础的配置。


具体的运行后的界面如下图所示


设置一个角色

第一步在 Scene 中添加一个 Panel,然后把它设置大点,不然角色会掉下去



第二步,找到图中的角色 prefab 然后直接拖到场景中,reset 一下即可



第三方视角相机跟随


第三方视角相机跟随,也是按照标准过程进行


1,删除原来的 MainCarmar 摄像机


2,从 Asset 中拖拽我们需要的 Prefab 到场景中来



第三步,设置相机的 Target 为我们的控制角色,这里拖拽即可


最后是装备系统配置创建


基础环境创建好了,下面我们需要创建下装备系统的自身的基础环境了,涉及到了 Srcript,Manage 文件夹中的四大基础类


装备系统配置类,


装备系统管理类


Item 管理类(工厂可能不准确,欢迎指正)


装备数据 Asset 类


Demo1 中其实要实现的就两步


第一步,创建空游戏对象,配置 InventorySetting 类


第二步,初始化游戏 Item 数据 Asset

4、实例

Demo1:使用插件来实现一个装备窗口


功能点:


  • 1、实现了两个窗口,通过点击键盘 I 来,打开或者关闭窗口也就是 Toggle 功能

  • 2、装备窗口中的物品栏空格数量动态生成可控,可以在属性窗口手动配置

  • 3、窗口具有拖拽功能

  • 4、窗口物品具有拖拽,及窗口间拖拽

  • 5、可以在窗口使用物品的功能,物品有消耗扇形显示功能


具体效果图如下所示:


插件使用

1、具体在 UGUI 中的 Canvas 中创建一个 InventoryWindow


2、在 InventoryWindow 下创建空 GameObject 并命名 Container,赋予 Grid LayOut 插件


3、给 InventoryWindow 添加 InventoryUI 组件,插件将自动添加 WindowUI 也就是通用窗口辅助插件


4、添加拖拽功能组件 DraggableWindow,这样窗口就有了拖拽功能了


至此简单的点击 I 键可以打开和关闭的装备窗口做好了

5、总结

最后总结下实现通用窗口的三个类,分别是 WindowHelper 文件夹下的,UIWindow,UIWindowPage 和 DraggableWindow



1、DraggableWindow 有就是拖拽窗口的组件,这里还是比较赞的,也是插件编程的简单例子,这里学过 UGui 的同学都知道要实现拖拽功能实现 IBeginDragHandler 和 IDargHandler 接口即可,原理很简单, 源码如下


using UnityEngine;using System.Collections;using UnityEngine.EventSystems; namespace Devdog.InventorySystem{    [AddComponentMenu("InventorySystem/UI Helpers/DraggableWindow")]    public partial class DraggableWindow : MonoBehaviour, IBeginDragHandler, IDragHandler    {        public float dragSpeed = 1.0f;         private Vector2 dragOffset;          public void OnBeginDrag(PointerEventData eventData)        {            if (InventorySettingsManager.instance.isUIWorldSpace)                dragOffset = transform.position - eventData.worldPosition;                       else                dragOffset = new Vector2(transform.position.x, transform.position.y) - eventData.position;        }         void IDragHandler.OnDrag(PointerEventData eventData)        {            transform.position = new Vector3(eventData.position.x + dragOffset.x * dragSpeed, eventData.position.y + dragOffset.y * dragSpeed, 0.0f);        }    }}
复制代码


2、UIWindow 这个类是窗口的公共类,先上类图主要功能点在类图上标注了,这里就不废话了,主要就是控制的窗口的显示关闭,及组合动画效果比较难的是实现了类似组合窗口的功能(这部分有后有机会再仔细分析)



源码就不全上了,上点有亮点的部分如下:


public virtual void Hide(){    if (isVisible == false)        return;     isVisible = false;     if (OnHide != null)        OnHide();     if (hideAudioClip != null)        InventoryUIUtility.AudioPlayOneShot(hideAudioClip);     if (hideAnimation != null)    {        animator.enabled = true;        animator.Play(hideAnimation.name);         if (hideCoroutine != null)        {            StopCoroutine(hideCoroutine);                           }         hideCoroutine = _Hide(hideAnimation);        StartCoroutine(hideCoroutine);    }    else    {        animator.enabled = false;        SetChildrenActive(false);    }}  /// <summary>/// Hides object after animation is completed./// </summary>/// <param name="animation"></param>/// <returns></returns>protected virtual IEnumerator _Hide(AnimationClip animation){    yield return new WaitForSeconds(animation.length + 0.1f);     // Maybe it got visible in the time we played the animation?    if (isVisible == false)    {        SetChildrenActive(false);        animator.enabled = false;    }}
复制代码


以上部分是通过协程实现的具有延时效果的动画关闭窗口的代码,有代表意义。


3、UIWindowPage 类,该类是 UIWindow 的子类,在 UIWindow 有一个 Page 的集合用于组合显示 UIWindowPage,这块 Demo1 中没有涉及到该功能这里就不仔细分析了,等后面的例子中出现了再研究,亮点代码如下:


/// <summary>/// Container that olds the items, if any./// </summary>public RectTransform itemContainer;public UIWindow windowParent { get; set; } public override void Awake(){    base.Awake();     windowParent = transform.parent.GetComponentInParent<UIWindow>();    if (windowParent == null)        Debug.LogWarning("No UIWindow found in parents", gameObject);     // Register our page with the window parent    windowParent.AddPage(this);} public override void Show(){    if(isEnabled == false)    {        Debug.LogWarning("Trying to show a disabled UIWindowPage");        return;    }     base.Show();     windowParent.NotifyPageShown(this);}
复制代码


这里 UIWindow 和 UIWindowPage 本身是继承关系,然又彼此引用,代码可读性有些差了,作者这里通过 Page 类中 Awake 和 Show 来做父类的初始化和调用,也是一种方法,我觉得还行(请高手拍砖)。


总体来说目前的 UIWinow 和 UIWindowPage 更像是容器 Panel 或者 Group 不像是窗口,等以后的 Demo 中有复杂的再学习吧。


4、如何通过键盘唤起窗口


这个比较简单用到了 U3D 的输入输出模块,关键代码如下:


/// <summary>/// Keys to toggle this window/// </summary>public KeyCode[] keyCombination; public virtual void Update(){    if (keyCombination.Length == 0)        return;     bool allDown = true;    foreach (var key in keyCombination)    {        if (Input.GetKeyDown(key) == false)        {            allDown = false;        }    }     if (allDown)        Toggle(); }
复制代码

Demo2:通用窗口的具体实现

功能点:


  • 1、实现了两个窗口,通过点击键盘 I 来,打开或者关闭窗口也就是 Toggle 功能

  • 2、装备窗口中的物品栏空格数量动态生成可控,可以在属性窗口手动配置

  • 3、窗口具有拖拽功能

  • 4、窗口物品具有拖拽,及窗口间拖拽

  • 5、可以在窗口使用物品的功能,物品有消耗扇形显示功能

  • 6、通用窗口的类体系结构


具体的插件使用和功能已经在上篇中说明了这里就不多说了


1、本篇重点分析 6 通用窗口的类体系结构,类组织和类图如下所示:



类的继承体系结构这里就说了,在第一篇有可以自行查阅



类的引用关系、核心字段和方法已经在类图中标记的很清楚,用简单的几句话说明下,装备窗口中的每个格子是由一个空格子具有背景的 UIItem 和 InventoryItemBase Model 组成的,而整个装备窗口是一个 InventoryUI,该类继承了 ItemCollectionBase 类,也就是说它是具有一组 UIItem 的装备集合窗口,添加上 UIWindow 组件、DraggableWindow 就具有了普通窗口的拖拽移动和显示关闭的功能了。


2、装备窗口中的物品栏空格数量动态生成可控,可以在属性窗口手动配置


如何实现动态装备窗口主要有两个核心技术:一个是 UI 中的自适应排列,也就是 Grid layout Group 组件;一个是 U3D 的 prefab 实例化技术


动态初始化 Cell 数据核心代码如下


protected virtual void FillUI()        {            if (manuallyDefineCollection == false)            {                items = new InventoryUIItemWrapperBase[initialCollectionSize];
// Fill the container on startup, can add / remove later on for (uint i = 0; i < initialCollectionSize; i++) { items[i] = CreateUIItem<InventoryUIItemWrapper>(i, itemButtonPrefab != null ? itemButtonPrefab : InventorySettingsManager.instance.itemButtonPrefab); } } else { for (uint i = 0; i < items.Length; i++) { items[i].itemCollection = this; items[i].index = i; } } }
protected T CreateUIItem<T>(uint i, GameObject prefab) where T : InventoryUIItemWrapperBase { T item = GameObject.Instantiate<GameObject>(prefab).GetComponent<T>(); item.transform.SetParent(container); item.transform.localPosition = new Vector3(item.transform.localPosition.x, item.transform.localPosition.y, 0.0f); item.itemCollection = this; item.transform.localScale = Vector3.one; item.index = i; return item; }
复制代码


是不是很简单 initailCollectionSize 是 InventoryUI 基类的一个共有 field 也就是说这个装备格子的数量,这个可以根据自己设计的装备窗口手动设置,然后根据这个循环调用 CreateUIItem<T>泛型方法通过,GameObject.Instantiate 动态实例化预设装备格子对象,并设置 Parent 和位置

Demo3:通用窗口的具体实现可拖拽功能

窗口间物品的拖拽

自己在学习的过程中,虽然读了源码过了几天来写教程,还是有点不清楚,不能亲车熟路(这也许就是读与写的区别吧),给自己提出了几个问题,在重新去翻代码之前先给自己提出几个问题:


  • 1、拖拽的事件发起者应该是那个类?

  • 2、拖拽的事件的 Drag 是如何确定下方的 UI 元素的?

  • 3、拖拽后的逻辑操作,应该由哪个类来承接?

  • 4、窗口与窗口之间的拖拽,既有 Drag 又有 Drop,如何更加合理的解决这个问题?

  • 5、窗口间物品拖拽的以及同窗口物品拖拽的逻辑流程是什么?


A1 拖拽的事件发起者应该是那个类?:拖拽的发起者必须是 UI 元素这点是必须的,目前涉及的 UI 元素有两种一种是窗口容器,一种数据装备格元素,显然装备格元素更有优势,因为少了一层定位逻辑判断,Drag 事件直接发起,还可以做 Move 的相关逻辑,这里 Inventory Pro2 也确实是怎么做的(这里初次接触 UGUI 的同学可能要去学习下相关的事件系统,这里就不多说了),这里 InventoryUIItemWrapper 就是装备格的基类,这里继承了 UGUI 的相关 UI 事件,IBeginDragHandler, IEndDragHandler, IDragHandler, IPointerUpHandler, IPointerDownHandler, IPointerEnterHandler, IPointerExitHandler,这么多接口也预示着代码并不简单



这些就是接口实现函数,把他们都弄明白了也就明白了如何实现拖拽



A5 窗口间物品拖拽的以及同窗口物品拖拽的逻辑流程是什么?:先回答这个问题比较合理,在过去的 winform 的拖拽中并没有这么多接口可以实现,但是我相信拖拽操作的本身逻辑应该是不变的,也就是几个步骤,


  • 1)在物品上点击鼠标左键(记录鼠标点击的元素)->

  • 2)在鼠标不 up,且 move 事件中确认了拖拽开始(Drag 事件) –>

  • 3) mouse Move 事件中获得鼠标下的元素->

  • 4)mouse up 事件触发 Drop,判断鼠标位置及鼠标下元素是否可以 drop 如果可以进行 Drop 逻辑至此,这个拖拽操作结束


技术原型就是这么简单。下面看看 Uintiy3d ugui Inventory Pro 是如何实现的,又读了一遍代码,深深的有体会到了一把,“原理很简单,现实很残酷”,这还是在 ugui 为我们做了一些封装的情况下,这里其实涉及的函数其实有 5 个


  • OnPointerEnter:确定了点击了那个 UI 元素,对应 1)

  • OnBeginDrag:开始拖拽,对应 2)

  • OnDrag:拖拽中,对应 3)

  • OnEndDrag:结束拖拽,对应 4)

  • OnPointExit:清空选中元素,恢复默认值


具体代码比较多这里不再展开说了,这里庆幸的是,Inventory Pro 对拖拽的逻辑进行了封装,在 InventoryUIItemWrapper 中接口实现函数中,主要做的参数舒适化,主要关于 UI 的逻辑代码封装在了 InventoryUIUtility 类中,


以下是主要接口实现函数的代码


public virtual void OnBeginDrag(PointerEventData eventData)        {            if (itemCollection == null)                return;
if (item != null && eventData.button == PointerEventData.InputButton.Left && itemCollection.canDragInCollection) { // Create a copy var copy = GameObject.Instantiate<InventoryUIItemWrapper>(this); copy.index = index; copy.itemCollection = itemCollection;
var copyComp = copy.GetComponent<RectTransform>(); copyComp.SetParent(InventorySettingsManager.instance.guiRoot); copyComp.transform.localPosition = new Vector3(copyComp.transform.localPosition.x, copyComp.transform.localPosition.y, 0.0f); copyComp.sizeDelta = GetComponent<RectTransform>().sizeDelta;
InventoryUIUtility.BeginDrag(copy, (uint)copy.index, itemCollection, eventData); // Make sure they're the same size, copy doesn't handle this. } }
public virtual void OnDrag(PointerEventData eventData) { if (item != null && itemCollection != null && itemCollection.canDragInCollection) // Can only drag existing item InventoryUIUtility.Drag(this, index, itemCollection, eventData); }
public virtual void OnEndDrag(PointerEventData eventData) { if (item != null && itemCollection != null && itemCollection.canDragInCollection) { var lookup = InventoryUIUtility.EndDrag(this, index, itemCollection, eventData);
// Didn't end on a button or used wrong key. if (lookup == null) return;
if (lookup.endOnButton) { // Place on a slot lookup.startItemCollection.SwapOrMerge((uint)lookup.startIndex, lookup.endItemCollection, (uint)lookup.endIndex); } else if (lookup.startItemCollection.useReferences) { lookup.startItemCollection.SetItem((uint)lookup.startIndex, null); lookup.startItemCollection[lookup.startIndex].Repaint(); } else if(InventoryUIUtility.clickedUIElement == false) { TriggerDrop(); } } }
复制代码


以下是 InventoryUIUtility 类封装的静态函数代码


public static InventoryUIDragLookup BeginDrag(InventoryUIItemWrapper toDrag, uint startIndex, ItemCollectionBase collection, PointerEventData eventData)        {            if (draggingItem != null)            {                Debug.LogWarning("Item still attached to cursor, can only drag one item at a time", draggingItem.gameObject);                return null; // Can only drag one item at a time            }
if (eventData.button != PointerEventData.InputButton.Left) return null;

draggingItem = toDrag; //draggingButtonCollection = collection;
// Canvas group allows object to ignore raycasts. CanvasGroup group = draggingItem.gameObject.GetComponent<CanvasGroup>(); if(group == null) group = draggingItem.gameObject.AddComponent<CanvasGroup>();
group.blocksRaycasts = false; // Allows rays to go through so we can hover over the empty slots. group.interactable = false;
var lookup = new InventoryUIDragLookup(); lookup.startIndex = (int)startIndex; lookup.startItemCollection = collection;
return lookup; }
public static void Drag(InventoryUIItemWrapper toDrag, uint startSlot, ItemCollectionBase handler, PointerEventData eventData) { if(eventData.button == PointerEventData.InputButton.Left) draggingItem.transform.position = new Vector3(eventData.position.x, eventData.position.y, 0.0f); }
public static InventoryUIDragLookup EndDrag(InventoryUIItemWrapper toDrag, uint startSlot, ItemCollectionBase handler, PointerEventData eventData) { if(eventData.button == PointerEventData.InputButton.Left) { var lookup = new InventoryUIDragLookup(); lookup.startIndex = (int)draggingItem.index; lookup.startItemCollection = draggingItem.itemCollection;
if (hoveringItem != null) { lookup.endIndex = (int)hoveringItem.index; lookup.endItemCollection = hoveringItem.itemCollection; }
Object.Destroy(draggingItem.gameObject); // No longer need it
draggingItem = null; //draggingButtonCollection = null;
return lookup; }
return null; }
/// <summary> /// When the cursor enters an item /// </summary> public static void EnterItem(InventoryUIItemWrapper item, uint slot, ItemCollectionBase handler, PointerEventData eventData) { hoveringItem = item; //hoveringItemCollection = handler; }
/// <summary> /// When the cursor exits an item /// </summary> /// <param name="item"></param> /// <param name="slot">The slot is the IButtonHandler index not the inventory index.</param> /// <param name="handler"></param> /// <param name="eventData"></param> public static void ExitItem(InventoryUIItemWrapper item, uint slot, ItemCollectionBase handler, PointerEventData eventData) { hoveringItem = null; //hoveringItemCollection = null; }
复制代码


A2 拖拽的事件的 Drag 是如何确定下方的 UI 元素的?


这里 DragDrop 中的元素由 OnPointerEnter 来触发获得(有点像 Mouse Move 事件),具体保存在 InventoryUIUtility 类的一个静态变量中


public static InventoryUIItemWrapper hoveringItem { get; private set; }
复制代码


A3 拖拽后的逻辑操作,应该由哪个类来承接?


也就是 Drop 的操作由谁来完成,首先回忆下职责 InventoryUIItemWrapper 类负责事件的触发,InventoryUIUtility 类负责 UI 相关的逻辑(选中,射线,坐标系统)


再看一遍 OnDragEnd 函数,具体的 Drop 逻辑是有 Drop 的后查找的 lookup 集合类(装备格集合基类 ItemCollectionBase)来处理的,具体又有交换\合并两个逻辑,触发代码如下:


if (lookup.endOnButton){     // Place on a slot     lookup.startItemCollection.SwapOrMerge((uint)lookup.startIndex, lookup.endItemCollection, (uint)lookup.endIndex);}
复制代码


当然还有一种复杂的逻辑,就是扔掉物品的操作,这个是有具体的 Item 装备模型类(InventoryItemBase)来处理,核心代码在 TriggerDrop 方法中来调用,具体如下:


public override void TriggerDrop(bool useRaycast = true)        {            if (item == null || itemCollection.canDropFromCollection == false)                return;
if(item.isDroppable == false) { InventoryManager.instance.lang.itemCannotBeDropped.Show(item.name, item.description); return; }
Vector3 dropPosition = InventorySettingsManager.instance.playerObject.transform.position; Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition); RaycastHit hit; if (Physics.Raycast(ray, out hit, InventorySettingsManager.instance.maxDropDistance, InventorySettingsManager.instance.layersWhenDropping)) { dropPosition = hit.point; } else { return; // Couldn't drop item }
var s = InventorySettingsManager.instance; if (useRaycast && s.showConfirmationDialogWhenDroppingItem && s.showConfirmationDialogMinRarity.ID <= item.rarity.ID) { // Not on a button, drop it var tempItem = item; // Capture list stuff var msg = InventoryManager.instance.lang.confirmationDialogDrop; s.confirmationDialog.ShowDialog(msg.title, msg.message, s.defaultDialogPositiveButtonText, s.defaultDialogNegativeButtonText, item, (dialog) => { ItemCollectionBase startCollection = tempItem.itemCollection; uint startIndex = tempItem.index;
var d = tempItem.Drop(dropPosition); if (d != null) { startCollection[startIndex].Repaint(); } }, (dialog) => { //Debug.Log("No clicked"); }); } else { var d = item.Drop(dropPosition); if (d != null) { Repaint(); } } }
复制代码


A4 窗口与窗口之间的拖拽,既有 Drag 又有 Drop,如何更加合理的解决这个问题?


这个问题比较绕,其实也涉及到了问题 2,其实无论怎么拖拽也就是两个东西,一个是被拖拽的物体 from,一个是要放的地方 to,这里其实都是窗口中的格子,只要有了这两个格子类也就确定了 from 和 to 的容器,比较特殊的一种情况也就是 from 和 to 两个容器相等,也就是同窗口拖拽了,具体这些对象 InventoryUIUtilty 类中都做了封装,还是很赞的具体代码如下:


public class InventoryUIDragLookup        {            public int startIndex = -1;            public ItemCollectionBase startItemCollection;
public int endIndex = -1; public ItemCollectionBase endItemCollection;
public bool endOnButton { get { return endItemCollection != null; } } }

#region Variables
private static InventoryUIItemWrapper draggingItem; public static InventoryUIItemWrapper hoveringItem { get; private set; } public static bool isDraggingItem { get { return draggingItem != null; } }
public static bool clickedUIElement { get { return EventSystem.current.IsPointerOverGameObject(); } }

public static bool isFocusedOnInput { get { if (EventSystem.current.currentSelectedGameObject != null) if (EventSystem.current.currentSelectedGameObject.GetComponent<UnityEngine.UI.InputField>() != null) return true;
return false; } }
#endregion
复制代码


复杂的物品拖拽逻辑总结完毕,再次向我们印证了,从 helloworld 到现实是多么的困难,实际的情况可能更复杂比如要加入动画效果,要做网络延时验证,数据同步等等吧

发布于: 刚刚阅读数: 3
用户头像

Unity3D软件工程师 2019.10.31 加入

Unity3D软件工程师,专注于虚拟仿真开发和VR开发。

评论

发布
暂无评论
游戏背包系统,“Inventory Pro插件”,研究学习-----妈妈再也不用担心我不会做背包了(Unity3D)_游戏开发_恬静的小魔龙_InfoQ写作社区