unity拖拽背包物体,并交换两个物体的信息

前几天看了一个教程,背包物品交换。由于教程中使用的是ngui,很多代码需要使用UGUI重新写,因此苦恼了几天。

我遇到了以下问题,在此详细罗列,以记录错误的原因和修改的方法,供日后阅读查找。

一、背包中的物品在拖拽之后放入指定的空格子(问题1:不知道该怎么获得指定的空格子;问题3:更新格子内的物品信息时找不到该物品);
二、背包中的物品在拖拽之后放入原来的格子(问题2:少了两行代码);
三、背包中的物品在拖拽之后,与另一个物品交换位置,并将自身信息更新在新的格子中(问题3:更新格子内的物品信息时找不到该物品;问题4:被交换的物体还在原来的位置待着)。

解决这些问题所参考的资料(原创者:不在同一频道上的呆子)

交换背包格子中的物体

要实现的功能是:
1.在开始拖拽时,保证被拖拽的UI对象(以下称该对象为A)始终显示在其他UI的上方;
2.在拖拽时,保证A在屏幕中始终在鼠标的位置处;
3.拖拽结束,释放鼠标之后,A存放在目标格子中(即,把目标格子设置为A的父物体)。分为三种情况:(1)如果目标格子中没有物体且不是A的原位置,则直接放入;(2)如果目标格子是原位置,则视觉上不做更改;(3)如果目标格子中有物品B,则A与B交换位置,并交换父物体。

unity拖放UI对象需要引入命名空间和接口:

using UnityEngine.EventSystems;
public class InventoryItem : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler,IPointerEnterHandler,IPointerExitHandler

主要实现以下三个接口,实现方法分别是:

    public void OnBeginDrag(PointerEventData eventData) { }
    public void OnDrag(PointerEventData eventData) { }
    public void OnEndDrag(PointerEventData eventData) { }

问题1:不知道该怎么获得指定的空格子:
解决方法:让被拖拽的物体屏蔽射线检测,这样就能获得鼠标悬停下的UI对象

transform.GetComponent<Image>().raycastTarget = false;

问题2:将A放入原格子:

//nowParent是A被拖拽前的父物体
 transform.SetParent(nowParent);
 transform.localPosition = Vector3.zero;

问题:4:被交换的物体还在原来的位置待着
这是因为将被拖拽的物体A的位置重新设置,但是没有将被交换的物品B的位置重新设置。只需要加上一行语句即可:

Transform tmpItem = dropTarget.transform;

tmpItem.localPosition = Vector3.zero;

交换物品后更新格子内的物品信息

要实现的功能是:
在交换完成后,相应的背包格子内部关于物品的信息也要进行更新。

问题3:更新格子内的物品信息时找不到该物品

这是因为在调用另一个脚本时,一个临时变量为空导致的。在拖拽A时,A的原来的父物体找不到A,导致报空指针,因而出错。为了解决这个问题,需要将A的父亲进行更新(如果与B交换,则B的父亲需要同时更新)

dropTarget.transform.SetParent(nowParent);

完整的脚本如下:

在这里插入图片描述
InventoryItemGrid是背包的物品格子,InventoryItemCount是记录该格子中的物品数量。InventoryItem(Clone)是背包内的物品。

InventoryItem脚本挂载在每一个背包物品上面:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
/// <summary>
/// 管理背包格子内的物品
/// </summary>
public class InventoryItem : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler,IPointerEnterHandler,IPointerExitHandler
{
    public Image sprite;
    /// <summary>
    /// 物品id
    /// </summary>
    private int itemId;
    /// <summary>
    /// 鼠标指针是否悬浮于物品上方
    /// </summary>
    private bool isHover = false;

    /// <summary>
    /// 图标的RectTransform组件
    /// </summary>
    private RectTransform rectTransform;
    /// <summary>
    /// Canvas组件,用于确定拖拽的缩放因子
    /// </summary>
    private Canvas canvas;

    /// <summary>
    /// 物体的目前父物体
    /// </summary>
    private Transform nowParent;
    
    private void Start()
    {
        rectTransform = GetComponent<RectTransform>(); // 获取图标的RectTransform组件
        canvas = GetComponentInParent<Canvas>(); // 获取父级Canvas组件

        //originalPosition = rectTransform.anchoredPosition; // 记录图标的初始位置
    }

    private void Update()
    {
        if (isHover)
        {
            if (Input.GetMouseButtonDown(1))
            {
                bool success = EquipmentPanel._instance.Dress(itemId);
                if (success)
                {
                    transform.parent.GetComponent<InventoryItemGrid>().MinusNumber();
                }
            }
        }
    }

    public void SetId(int id)
    {
        ObjectInfo info = ObjectsInfo._instance.GetObjectInfoById(id);

        //更新显示
        sprite.name = info.iconName;
    }

    public void SetIconName(int id, string iconName)
    {
        sprite.sprite = Resources.Load<Sprite>(Path.itemPath+iconName);
        itemId = id;
    }

    public void OnBeginDrag(PointerEventData eventData)
    {
        if (transform.GetComponent<Image>().raycastTarget)
        {
            //让被拖拽的物体屏蔽射线检测,这样就能获得鼠标悬停下的UI对象
            transform.GetComponent<Image>().raycastTarget = false;
        }
        nowParent = transform.parent;  //nowparent为被拖拽物体的当前父物体        

        transform.SetParent(canvas.transform);//将当前拖拽的物体放在canvas下
    }

    public void OnDrag(PointerEventData eventData)
    {
        rectTransform.anchoredPosition += eventData.delta / canvas.scaleFactor; // 根据鼠标移动更新图标位置
    }

    public void OnEndDrag(PointerEventData eventData)
    {
        //用于存放鼠标当前悬停位置下的物体
        GameObject dropTarget = eventData.pointerCurrentRaycast.gameObject;

        if (dropTarget != null)
        {
            if (dropTarget.CompareTag(S_Tags.inventoryItemGrid)) // 当拖放到了一个空的格子里面
            {
                if (dropTarget.transform == nowParent)
                {
                    transform.SetParent(nowParent);
                }
                else
                {
                    // 处理拖拽到空格子的逻辑
                    InventoryItemGrid oldSlot = nowParent.GetComponent<InventoryItemGrid>();
                    transform.SetParent(dropTarget.transform);
                    ResetLocalPosition();
                    InventoryItemGrid newSlot = dropTarget.GetComponent<InventoryItemGrid>();
                    //int oldId = oldSlot.itemId;
                    //int oldCount = oldSlot.itemCount;
                    newSlot.SetId(oldSlot.itemId, oldSlot.itemCount);
                    oldSlot.CleanInfo();
                }
            }
            else if (dropTarget.CompareTag(S_Tags.inventoryItem)) // 当拖放到了一个有物品的格子里面
            {
                // 处理拖拽到有物品的格子里面的逻辑
                InventoryItemGrid slot1 = nowParent.GetComponent<InventoryItemGrid>();
                InventoryItemGrid slot2 = dropTarget.transform.parent.GetComponent<InventoryItemGrid>();

                Transform tmpItem = dropTarget.transform;
                Transform tmpParent = dropTarget.transform.parent;

                //交换父亲
                //将被拖拽的物体的父亲更新为当前指针悬停位置物品的父亲
                transform.SetParent(tmpParent);
                //将指针悬停位置物品的父亲更新为被拖拽物品的原父亲
                dropTarget.transform.SetParent(nowParent);

                int id = slot1.itemId;
                int num = slot1.itemCount;
                slot1.SetId(slot2.itemId, slot2.itemCount);
                slot2.SetId(id, num);
                tmpItem.localPosition = Vector3.zero;
            }
            else
            {
                transform.SetParent(nowParent);
            }
        }
        else
        {
            transform.SetParent(nowParent);
        }
        transform.GetComponent<Image>().raycastTarget = true;

        ResetLocalPosition();
    }
    /// <summary>
    /// 被拖拽物体返回原位置
    /// </summary>
    private void ResetLocalPosition()
    {
        transform.localPosition = Vector3.zero;
        transform.SetAsFirstSibling();
    }

    /// <summary>
    /// 鼠标指针进入时
    /// </summary>
    /// <param name="eventData"></param>
    public void OnPointerEnter(PointerEventData eventData)
    {
        InventoryDes._instance.ShowDes(itemId);
        isHover = true;
    }

    /// <summary>
    /// 鼠标指针退出时
    /// </summary>
    /// <param name="eventData"></param>
    public void OnPointerExit(PointerEventData eventData)
    {
        InventoryDes._instance.HideDes();
        isHover = false;
    }
}

InventoryItemGrid 挂载在每一个背包格子上

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

/// <summary>
/// 管理背包格子
/// </summary>
public class InventoryItemGrid : MonoBehaviour
{
    /// <summary>
    /// 表示当前背包格子里面存储了什么物品.默认为0,表示当前背包格子中什么也没有
    /// </summary>
    public int itemId = 0;
    /// <summary>
    /// 表示当前背包格子中的物品的数量。默认为0.
    /// </summary>
    public int itemCount = 0;
    /// <summary>
    /// 当前背包格子中的物品的信息
    /// </summary>
    private ObjectInfo info = null;
    
    private Text txt_itemCount;

    private void Start()
    {
        txt_itemCount = transform.Find("InventoryItemCount").GetComponent<Text>();
    }
    /// <summary>
    /// 当背包格子中的物品发生变化时(包括类型、图标、数量等),就更新背包格子中关于物品的显示。
    /// </summary>
    /// <param name="id">物品的属性:id</param>
    /// <param name="num">物品的数量,默认为1</param>
    public void SetId(int id, int num = 1)
    {
        itemId = id;
        //先得到物品信息
        info = ObjectsInfo._instance.GetObjectInfoById(id);
        //更新背包格子中的显示
        InventoryItem item = GetComponentInChildren<InventoryItem>();
        //用于检测是哪个格子失去了子物体
        if (item == null)
        {
            print(transform.name);
        }
        item.SetIconName(id, info.iconName);//info.iconName是从txt文件中解析得来的

        txt_itemCount.gameObject.SetActive(true);
        itemCount = num;
        txt_itemCount.text = itemCount.ToString();
        txt_itemCount.gameObject.transform.SetAsLastSibling();
    }

    /// <summary>
    /// 背包格子中的物体数目增加,相应的显示数字也应当变化
    /// </summary>
    /// <param name="num">物品增加的数量</param>
    public void PlusNumber(int num = 1)
    {
        itemCount += num;
        txt_itemCount.text = itemCount.ToString();
    }

    /// <summary>
    /// 用于减去背包中物品的数量
    /// </summary>
    /// <param name="num"></param>
    /// <return>是否减量成功</return>
    public bool MinusNumber(int num = 1)
    {
        if (itemCount >= num)
        {
            itemCount -= num;
            txt_itemCount.text = itemCount.ToString();
            if (itemCount == 0)
            {
                //清空物品格子
                CleanInfo();
                //销毁物品
                Destroy(GetComponentInChildren<InventoryItem>().gameObject);
            }
            return true;
        }
        return false;
    }

    /// <summary>
    /// 清空背包格子中的物品信息
    /// </summary>
    public void CleanInfo()
    {
        itemId = 0;
        info = null;
        itemCount = 0;
        txt_itemCount.gameObject.SetActive(false);
    }
}

其中ObjectsInfo用于读取txt文本文件(记录了物品的各个属性)。

  • 34
    点赞
  • 37
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值