一、项目描述
二 、背包格子容器制作 通过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;
}
}