【Unity】实现 可拖拽+格子位置互换 的背包系统

真的特别激动,花了两个小时的学习,终于学会了背包格子拖拽的效果,这个效果不仅仅用于背包,还有很多游戏的功能和效果可以基于这里面所包含的技术来实现,非常值得学习!!!

在这里特别感谢M_Studio老师的课程中提供的方法和思路。

特别提醒,在学习该效果之前,请务必预先学习C#的接口以及Unity协程的相关知识!!!

一、UI框架搭建

为了方便演示,这里我只做了一个非常非常简单的UI框架。

1.背包格子容器制作

在这里的话我只使用了一个Panel,然后拉伸成一个较宽的矩形做成容器。

并且给这个Panel添加Horizontal Layout Group组件(主要是为了方便给容器中的格子调整位置,按照你的需求来添加合适的就好。)

2.创建预制体

这里我在Panel中添加了一个Image对象作为格子,改名为item(随便调整一下颜色,看得清楚一些)

然后你需要给这个Image对象添加CanvasGroup组件(后续使用的是射线检测,会用到这个组件)

3.完成预制体的制作

将做好的对象拖放到文件夹当中,预制体制作完成!

4.放置预制体

在容器中多放几个预制体,比如像这样

二、编写脚本

1.创建item的控制脚本

新建一个脚本(我就取名叫做“UIDrag.cs”),然后将这个脚本拖拽给item预制体,这个脚本将控制item预制体实现拖拽效果。

2.创建变量

这里我们需要创建四个变量,用于获取一些必要的组件和保存一些必要的数据。

using System.Collections;
using UnityEngine;
using UnityEngine.EventSystems;

public class UIDrag : MonoBehaviour
{

    public Vector2 originalPosition; //初位置

    public Vector2 offsetPostion; //拖动物体时候的位置差

    private Transform parentTransform; //父级物体的Transform

    private CanvasGroup canvasGroup;

    private void Awake() {
        canvasGroup = GetComponent<CanvasGroup>();
        parentTransform = transform.parent;

        originalPosition = transform.position; //获得初位置的值
    }
}

3.实现接口

我们需要实现三个Unity提供的接口,分别是 IBeginDragHandler, IDragHandler,和IEndDragHandler。

这里我续写代码:

​
using System.Collections;
using UnityEngine;
using UnityEngine.EventSystems;

public class UIDrag : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler
{

    public Vector2 originalPosition; //初位置

    public Vector2 offsetPostion; //拖动物体时候的位置差

    private Transform parentTransform; //父级物体的Transform

    private CanvasGroup canvasGroup;

    private void Awake() {
        canvasGroup = GetComponent<CanvasGroup>();
        parentTransform = transform.parent;

        originalPosition = transform.position; //获得初位置的值
    }
    
    //这下面是实现的接口的方法

    public void OnBeginDrag(PointerEventData eventData)
    {

    }

     public void OnDrag(PointerEventData eventData)
    {

    }

    public void OnEndDrag(PointerEventData eventData)
    {
        
    }    
}

​

4.开始拖拽

在这里我们需要在OnBeginDrag中实现以下几个逻辑

  • 将Item的父级设置为与父级同级(为了暂时的脱离父级容器,也就是那个Panel)

  • 获得鼠标与Item的位置差(为了拖拽的时候不会有鬼畜的抖动)

  • 被拖拽的物体禁用射线阻挡(方便射线射向底部的item)

这里我们继续续写代码:

​
​
using System.Collections;
using UnityEngine;
using UnityEngine.EventSystems;

public class UIDrag : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler
{

    public Vector2 originalPosition; //初位置

    public Vector2 offsetPostion; //拖动物体时候的位置差

    private Transform parentTransform; //父级物体的Transform

    private CanvasGroup canvasGroup;

    private void Awake() {
        canvasGroup = GetComponent<CanvasGroup>();
        parentTransform = transform.parent;

        originalPosition = transform.position; //获得初位置的值
    }
    
    //这下面是实现的接口的方法

    public void OnBeginDrag(PointerEventData eventData)
    {
        //将Item的父级设置为与父级同级
        transform.SetParent(parentTransform.parent);
        //获得鼠标与Item的位置差
        offsetPostion = (Vector2)transform.position - eventData.position;
        //被拖拽的物体禁用射线阻挡,以方便鼠标发射的射线射向底部的UI
        canvasGroup.blocksRaycasts = false;
    }

     public void OnDrag(PointerEventData eventData)
    {

    }

    public void OnEndDrag(PointerEventData eventData)
    {
        
    }    
}

​

​

5.拖拽中

这里我们需要在OnDrag方法中实现以下逻辑

  • 让item跟随鼠标移动

这里继续续写代码:

​
​
​
using System.Collections;
using UnityEngine;
using UnityEngine.EventSystems;

public class UIDrag : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler
{

    public Vector2 originalPosition; //初位置

    public Vector2 offsetPostion; //拖动物体时候的位置差

    private Transform parentTransform; //父级物体的Transform

    private CanvasGroup canvasGroup;

    private void Awake() {
        canvasGroup = GetComponent<CanvasGroup>();
        parentTransform = transform.parent;

        originalPosition = transform.position; //获得初位置的值
    }
    
    //这下面是实现的接口的方法

    public void OnBeginDrag(PointerEventData eventData)
    {
        //将Item的父级设置为与父级同级
        transform.SetParent(parentTransform.parent);
        //获得鼠标与Item的位置差
        offsetPostion = (Vector2)transform.position - eventData.position;
        //被拖拽的物体禁用射线阻挡,以方便鼠标发射的射线射向底部的UI
        canvasGroup.blocksRaycasts = false;
    }

     public void OnDrag(PointerEventData eventData)
    {
        //拖动改变位置
        transform.position = eventData.position+offsetPostion;
    }

    public void OnEndDrag(PointerEventData eventData)
    {
        
    }    
}

​

​

​

6.结束拖拽(重点)

这一步至关重要!!!因为要完成的逻辑特别的多!!!

我们要实现以下几个功能:

  • 恢复被拖拽的Item所处的父级

  • 检测当前射线所射向哪个对象,并进行空值处理

  • 通过射线获得被拖拽的item位于哪个item之上

  • 两个item交换位置

  • 重新设置两个item的初始位置

这里继续续写代码:

​using System.Collections;
using UnityEngine;
using UnityEngine.EventSystems;

public class UIDrag : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler
{

    public Vector2 originalPosition; //初位置

    public Vector2 offsetPostion; //拖动物体时候的位置差

    private Transform parentTransform; //父级物体的Transform

    private CanvasGroup canvasGroup;

    private void Awake() {
        canvasGroup = GetComponent<CanvasGroup>();
        parentTransform = transform.parent;

        originalPosition = transform.position; //获得初位置的值
    }
    
    //这下面是实现的接口的方法

    public void OnBeginDrag(PointerEventData eventData)
    {
        //将Item的父级设置为与父级同级
        transform.SetParent(parentTransform.parent);
        //获得鼠标与Item的位置差
        offsetPostion = (Vector2)transform.position - eventData.position;
        //被拖拽的物体禁用射线阻挡,以方便鼠标发射的射线射向底部的UI
        canvasGroup.blocksRaycasts = false;
    }

     public void OnDrag(PointerEventData eventData)
    {
        //拖动改变位置
        transform.position = eventData.position+offsetPostion;
    }

    public void OnEndDrag(PointerEventData eventData)
    {
        
        //结束拖动后,恢复Item所处的层级
        transform.SetParent(parentTransform);
        //如果不在背包上面或者射线检测到的是空值
        if(eventData.pointerCurrentRaycast.gameObject==null||
        !eventData.pointerCurrentRaycast.gameObject.CompareTag("Item"))
        {
            //允许射线阻挡
            canvasGroup.blocksRaycasts = true;
            //为本Item设置位置
            SetPosition(originalPosition);
        }
        //又如果检测到被拖拽的对象处于另一个item之上
        else if(eventData.pointerCurrentRaycast.gameObject.CompareTag("Item"))
        {
            //获得目标对象
            var target = eventData.pointerCurrentRaycast.gameObject;
            //预存储目标对象的原始位置
            Vector2 newPosition = target.GetComponent<UIDrag>().originalPosition;
            //设置目标Item的位置为自己的初始位置
            target.GetComponent<UIDrag>().SetPosition(originalPosition);
            //设置目标对象的新的原始位置
            target.GetComponent<UIDrag>().originalPosition = originalPosition;
            //设置本item的初始位置为目标Item的原初始位置
            originalPosition = newPosition;
            //允许射线阻挡
            canvasGroup.blocksRaycasts = true;
            //为本Item设置位置
            SetPosition(originalPosition);
        }
    }

    //设置Item位置
    public void SetPosition(Vector2 position)
    {
        //启动协程
        StartCoroutine(Move(position));
    }


    /// <summary>
    /// 协程:平滑移动
    /// </summary>
    /// <param name="position">要移动到的位置</param>
    /// <returns></returns>
    private IEnumerator Move(Vector2 position)
    {
        float elapsed = 0f,duration = 0.3f;
        while(elapsed<duration)
        {
            transform.position =   Vector2.Lerp(transform.position,position,elapsed/duration);
            elapsed+=Time.deltaTime;
            yield return null;
        }
        transform.position = position;
    }

}

​

​

​

​

这样就大功告成啦!

我这里为了做视觉区分,改了一下颜色,现在来预览一下效果:

  • 15
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

君莫愁。

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值