Unity3d Ugui 25 拖拽自动吸附功能

25 篇文章 1 订阅

一:前言

针对上一节讲解的事件机制https://blog.csdn.net/HexianWHH/article/details/119741189,这节练习一个实际应用。基于IDragHandler事件做一个在指定区域拖动松手自动吸附的功能。

二:效果演示

拖动滑块只能在指定区域内滑动,不能超出边界。松开鼠标以后自动吸附到最近的格子。

 

 

三:制作过程

  1. 添加一个图片作为拖动区域。
  2. 制作一个每一个格子的预制体。
  3. 给第一步的拖动区域添加GridLayoutGroup组件和自己写的GridWindow组件,并设置参数。
  4. 新建一个图片作为滑动块,并且挂上DragMove组件,并设置参数。

四:代码讲解

GridWindow负责创建格子,拥有所有的格子对象。

using System.Collections.Generic;
using UnityEngine;

/// <summary>
/// 所有格子的窗体
/// </summary>
public class GridWindow : MonoBehaviour
{
    /// <summary>
    /// 挂载每一个格子的父物体
    /// </summary>
    [SerializeField]
    Transform parentTrans;
    /// <summary>
    /// 格子的个数
    /// </summary>
    [SerializeField]
    int gridItemCount;
    /// <summary>
    /// 格子物体
    /// </summary>
    [SerializeField]
    GameObject childObj;
    /// <summary>
    /// 所有格子
    /// </summary>
    List<RectTransform> allChildGrid = new List<RectTransform>();
    void Start()
    {
        for (int i = 0; i < gridItemCount; i++)
        {
            var obj = Instantiate(childObj);
            obj.transform.SetParent(parentTrans);
            allChildGrid.Add(obj.GetComponent<RectTransform>());
        }
    }
    private void OnDestroy()
    {
        //删除所有创建的格子
        foreach (var item in allChildGrid)
        {
            Destroy(item.gameObject);
        }
        allChildGrid.Clear();
    }
    /// <summary>
    /// 得到距离指定位置最近的一个格子位置
    /// </summary>
    /// <param name="pos">指定位置</param>
    /// <returns>最近的格子位置</returns>
    public Vector3 GetNearestPos(Vector3 pos)
    {
        float minDis = float.MaxValue;
        Vector3 returnPos = Vector3.zero;
        foreach (var child in allChildGrid)
        {
            var dis = Vector3.Distance(child.position, pos);
            if (dis < minDis)
            {
                minDis = dis;
                returnPos = child.position;
            }
        }
        return returnPos;
    }
}

DragMove拖动的滑块,里面涉及到的变量具体意思

eventData.delta这次滑动的距离

rectTrans.rect和rectTrans.anchoredPosition,以及最小最大边界为什么是这样计算的。可以参照我之前写的文章有详细的解析https://blog.csdn.net/HexianWHH/article/details/118881151

Mathf.Clamp把值限定在最小和最大值范围之内。

using UnityEngine;
using UnityEngine.EventSystems;
/// <summary>
/// 可以拖动的物体
/// </summary>
public class DragMove : MonoBehaviour, IDragHandler,IEndDragHandler
{
    /// <summary>
    /// 可以拖拽的区域
    /// </summary>
    [SerializeField]
    RectTransform dragRangeRect;
    /// <summary>
    /// 所有格子的窗体
    /// </summary>
    [SerializeField]
    GridWindow gridWindow;
    /// <summary>
    /// 当前物体自己的矩形组件
    /// </summary>
    RectTransform rectTrans;
    private void Awake()
    {
        rectTrans = this.GetComponent<RectTransform>();
    }
    public void OnDrag(PointerEventData eventData)
    {
        //拖动区域的最小边界
        var minPos = 0.5f * (rectTrans.rect.size - dragRangeRect.rect.size);
        //拖动区域的最大边界
        var maxPos = 0.5f * (dragRangeRect.rect.size - rectTrans.rect.size);
        //拖动以后的距离
        var movePos = rectTrans.anchoredPosition + eventData.delta;
        //把滑块限定在拖动区域内部
        var curPos = new Vector2(Mathf.Clamp(movePos.x, minPos.x, maxPos.x), Mathf.Clamp(movePos.y, minPos.x, maxPos.y));
        rectTrans.anchoredPosition = curPos;
    }

    public void OnEndDrag(PointerEventData eventData)
    {
        //查找距离该点最近的格子的位置
        var pos = gridWindow.GetNearestPos(rectTrans.position);
        rectTrans.position = pos;
    }
}

  • 8
    点赞
  • 37
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
你可以使用Unity的协程(Coroutine)实现这个功能。具体步骤如下: 1. 在物体到达指定位置后,使用协程等待一段时间,让它停留在当前位置。 2. 在协程中,使用物体的Transform组件使其上升到指定高度。 下面是一个简单的示例代码: ```csharp public class ObjectController : MonoBehaviour { public float moveSpeed = 5f; // 物体移动速度 public float waitTime = 2f; // 停留时间 public float riseHeight = 2f; // 上升高度 private Vector3 targetPosition; // 目标位置 private bool isMoving = false; // 是否正在移动 void Start() { // 初始化目标位置 targetPosition = new Vector3(5f, 0f, 0f); } void Update() { // 检测是否需要移动 if (!isMoving && transform.position != targetPosition) { // 开始移动 StartCoroutine(MoveAndRise()); } } IEnumerator MoveAndRise() { isMoving = true; // 计算移动方向和距离 Vector3 direction = (targetPosition - transform.position).normalized; float distance = Vector3.Distance(transform.position, targetPosition); // 移动到目标位置 while (distance > 0.1f) { transform.position += direction * moveSpeed * Time.deltaTime; distance = Vector3.Distance(transform.position, targetPosition); yield return null; } // 等待一段时间 yield return new WaitForSeconds(waitTime); // 上升到指定高度 float currentHeight = 0f; while (currentHeight < riseHeight) { transform.position += Vector3.up * moveSpeed * Time.deltaTime; currentHeight += moveSpeed * Time.deltaTime; yield return null; } isMoving = false; } } ``` 在这个示例中,我们定义了三个公共变量,分别是物体移动速度、停留时间和上升高度。在Start()函数中初始化目标位置,然后在Update()函数中检测是否需要移动。如果物体没有正在移动且没有到达目标位置,就启动协程MoveAndRise()来移动物体。 在MoveAndRise()协程中,我们先计算移动方向和距离,并使用while循环来移动物体到目标位置。然后使用yield return new WaitForSeconds()来等待一段时间。最后使用while循环来上升物体到指定高度。注意,我们在while循环中使用了moveSpeed变量来控制移动速度和上升速度。 希望这个示例对你有所帮助!

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值