100个 Unity小游戏系列一【Unity】实现 可拖拽+格子位置互换 的背包系统

一、项目描述

二 、背包格子容器制作 通过Horizontal Layout Group组件里面放了几个image组件

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

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

2.2 创建item预制体

这里我在Panel中添加了一个Item对象作为格子,颜色调整为随机颜色

2.3 item对象添加CanvasGroup组件

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

2.4 预制体坐坐完成

2.5 ui框架的最终效果

三 、编写脚本

3.1 创建item的控制脚本

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

3.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.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)
    {
        
    }    
}
 
​

3.4 开始拖拽OnBeginDrag接口实现

在这里我们需要在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)
    {
        
    }    
}
 
​
 

3.5 拖拽中OnDrag接口实现

这里我们需要在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)
    {
        
    }    
}

3.6 IEndDragHandler结束拖拽
这一步至关重要!!!因为要完成的逻辑特别的多!!!

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

恢复被拖拽的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;
    }
 
}

四、 源码

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值