Unity用鼠标拖拽UI,UI跟随鼠标移动

该文章介绍了如何在Unity中实现UI元素的鼠标拖拽功能,并限制其在Canvas范围内移动。通过实现IBeginDragHandler,IDragHandler,IEndDragHandler接口,计算偏移量和坐标限制,确保UI元素在预定区域内拖动。提供了相关源代码示例。

Unity用鼠标拖拽UI,UI跟随鼠标移动


效果

先上效果
请添加图片描述


一、原理

继承几个拖拽的接口 IBeginDragHandler, IDragHandler,IEndDragHandler
计算下偏移量,转换下坐标系
限制下可拖拽的范围,我设置的是canvas的大小

二、源码

using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEngine;
using UnityEngine.EventSystems;

namespace HHQ
{
    /// <summary>
    /// 拖拽ui(限制拖拽范围)
    /// </summary>
    public class LimitUIDrag : MonoBehaviour, IBeginDragHandler, IDragHandler,IEndDragHandler
    {
        /// <summary>
        /// 限制的区域
        /// </summary>
        private RectTransform limitContainer;

        private Canvas canvas;

        private  RectTransform rt;

        // 位置偏移量
        Vector3 offset = Vector3.zero;

        // 最小、最大X、Y坐标
        float minX, maxX, minY, maxY;

        void Start()
        {
            rt = GetComponent<RectTransform>();
            canvas = GetComponentInParent<Canvas>();
            limitContainer = canvas.GetComponent<RectTransform>();
        }

        /// <summary>
        /// 开始拖拽
        /// </summary>
        /// <param name="eventData"></param>
        public void OnBeginDrag(PointerEventData eventData)
        {
            if (eventData.button != PointerEventData.InputButton.Left)
                return;
            if (RectTransformUtility.ScreenPointToWorldPointInRectangle(rt, eventData.position, eventData.enterEventCamera, out Vector3 globalMousePos))
            {
                // 计算偏移量
                offset = rt.position - globalMousePos;
                // 设置拖拽范围
                SetDragRange();
                //EventDispatcher.GameEvent.DispatchEvent(101);
            }
        }

        /// <summary>
        /// 拖拽中
        /// </summary>
        /// <param name="eventData"></param>
        public void OnDrag(PointerEventData eventData)
        {
            if (eventData.button != PointerEventData.InputButton.Left)
                return;
            // 将屏幕空间上的点转换为位于给定RectTransform平面上的世界空间中的位置
            if (RectTransformUtility.ScreenPointToWorldPointInRectangle(rt, eventData.position, eventData.pressEventCamera, out Vector3 globalMousePos))
            {
                rt.position = DragRangeLimit(globalMousePos + offset);
            }
        }
        /// <summary>
        /// 拖拽结束
        /// </summary>
        /// <param name="eventData"></param>
        /// <exception cref="NotImplementedException"></exception>
        public void OnEndDrag(PointerEventData eventData)
        {
            //EventDispatcher.GameEvent.DispatchEvent(103);
        }

        // 设置最大、最小坐标
        void SetDragRange()
        {
            // 最小x坐标 = 容器当前x坐标 - 容器轴心距离左边界的距离 + UI轴心距离左边界的距离
            minX = limitContainer.position.x
                   - limitContainer.pivot.x * limitContainer.rect.width * canvas.scaleFactor
                   + rt.rect.width * canvas.scaleFactor * rt.pivot.x;
            // 最大x坐标 = 容器当前x坐标 + 容器轴心距离右边界的距离 - UI轴心距离右边界的距离
            maxX = limitContainer.position.x
                   + (1 - limitContainer.pivot.x) * limitContainer.rect.width * canvas.scaleFactor
                   - rt.rect.width * canvas.scaleFactor * (1 - rt.pivot.x);

            // 最小y坐标 = 容器当前y坐标 - 容器轴心距离底边的距离 + UI轴心距离底边的距离
            minY = limitContainer.position.y
                   - limitContainer.pivot.y * limitContainer.rect.height * canvas.scaleFactor
                   + rt.rect.height * canvas.scaleFactor * rt.pivot.y;

            // 最大y坐标 = 容器当前x坐标 + 容器轴心距离顶边的距离 - UI轴心距离顶边的距离
            maxY = limitContainer.position.y
                   + (1 - limitContainer.pivot.y) * limitContainer.rect.height * canvas.scaleFactor
                   - rt.rect.height * canvas.scaleFactor * (1 - rt.pivot.y);
        }

        // 限制坐标范围
        Vector3 DragRangeLimit(Vector3 pos)
        {
            pos.x = Mathf.Clamp(pos.x, minX, maxX);
            pos.y = Mathf.Clamp(pos.y, minY, maxY);
            return pos;
        }

       
    }
}

总结

欢迎大佬多多来给萌新指正,欢迎大家来共同探讨。
如果各位看官觉得文章有点点帮助,跪求各位给点个“一键三连”,谢啦~

声明:本博文章若非特殊注明皆为原创原文链接
https://blog.youkuaiyun.com/Wrinkle2017/article/details/130885091
————————————————————————————————

💢💢版权声明

版权声明:本博客为非营利性个人原创
所刊登的所有作品的著作权均为本人所拥有
本人保留所有法定权利,违者必究!
对于需要复制、转载、链接和传播博客文章或内容的
请及时和本博主进行联系
对于经本博主明确授权和许可使用文章及内容的
使用时请注明文章或内容出处并注明网址
转载请附上原文出处链接及本声明

<think>好的,我现在要帮用户解决在Unity中实现鼠标悬停出现UI界面并跟随物体移动的问题。首先,我需要理解用户的需求。他们可能是在做一个3D游戏,想让玩家把鼠标移到某个物体上时显示一个UI界面,比如显示物品信息,并且这个UI跟随物体移动。 首先,我得考虑如何检测鼠标悬停。在Unity中,常用的方法有几种。对于3D物体,可以用OnMouseEnter和OnMouseExit事件,但这种方法需要物体有Collider组件。如果项目用的是Unity的新输入系统,可能需要使用射线检测,这样更灵活,特别是对于需要处理大量交互的情况。 接下来是UI跟随物体的问题。UI通常是在屏幕空间的Canvas上,所以需要将物体的3D坐标转换为屏幕坐标。这里可以用Camera.WorldToScreenPoint方法。然后设置UI元素的位置,可能需要根据UI的大小调整偏移量,避免遮挡或者位置不合适。 然后要考虑UI的显示和隐藏。当鼠标进入物体时激活UI,离开时隐藏。这里可能需要一个协程来检测鼠标是否还在物体上方,或者直接使用事件触发。 另外,用户可能希望UI平滑跟随,所以可能需要每帧更新位置,比如在LateUpdate中,这样确保物体的移动UI的位置也被更新。 还需要注意性能问题,比如避免每帧都进行射线检测,但如果是悬停检测,可能需要在Update中持续检测,或者使用事件驱动的方式。 可能的步骤是: 1. 创建UI元素,比如一个Panel,设置好布局,并初始设置为隐藏。 2. 编写脚本挂在目标物体上,检测鼠标悬停。 3. 在悬停时,激活UI,并在每帧更新UI的位置到物体所在屏幕位置。 4. 处理鼠标离开时隐藏UI。 可能会遇到的问题包括坐标转换不正确,UI出现在屏幕外,或者性能开销。这时候需要调试WorldToScreenPoint的结果,确保摄像机正确,以及UI的锚点设置合适。比如,如果物体的位置在摄像机后面,转换后的z可能是负的,这时候需要处理这种情况,避免UI显示异常。 另外,如果物体在移动,可能需要将UI的位置更新放在LateUpdate中,确保在物体移动之后更新UI的位置,避免抖动。 可能还需要考虑多个物体同时存在的情况,确保只有一个UI被显示,或者分别跟踪各自的UI。 总结下来,步骤大概是检测悬停事件,转换坐标,控制UI显示和位置更新。需要测试各个部分是否正常工作,比如事件触发是否正确,坐标转换是否准确,UI的显隐控制是否及时。</think>在Unity中实现鼠标悬停触发UI显示并跟随物体移动,需分步骤完成。以下是详细实现方案: --- ### 一、基础准备 1. **创建UI元素** - 在Canvas下添加`Panel`或`Image`作为悬浮UI - 设置初始状态为隐藏:`SetActive(false)` 2. **目标物体设置** - 确保3D物体挂载`Collider`组件(Box Collider/Mesh Collider) --- ### 二、核心代码实现 ```csharp using UnityEngine; public class HoverFollowUI : MonoBehaviour { public GameObject hoverUI; // 拖拽UI对象到Inspector public Vector3 offset = new Vector3(0, 1, 0); // UI与物体的偏移量 private Camera mainCamera; void Start() { mainCamera = Camera.main; hoverUI.SetActive(false); } void Update() { if (hoverUI.activeSelf) { // 将物体世界坐标转为屏幕坐标 Vector3 screenPos = mainCamera.WorldToScreenPoint(transform.position + offset); hoverUI.transform.position = screenPos; } } void OnMouseEnter() { hoverUI.SetActive(true); } void OnMouseExit() { hoverUI.SetActive(false); } } ``` --- ### 三、优化方案 1. **使用射线检测(适合复杂场景)** ```csharp void Update() { Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition); RaycastHit hit; if (Physics.Raycast(ray, out hit)) { if (hit.collider.gameObject == this.gameObject) { hoverUI.SetActive(true); // 更新UI位置 Vector3 screenPos = mainCamera.WorldToScreenPoint(transform.position + offset); hoverUI.transform.position = screenPos; } else { hoverUI.SetActive(false); } } } ``` 2. **平滑跟随(避免抖动)** ```csharp public float smoothSpeed = 5f; void Update() { if (hoverUI.activeSelf) { Vector3 targetPos = mainCamera.WorldToScreenPoint(transform.position + offset); hoverUI.transform.position = Vector3.Lerp( hoverUI.transform.position, targetPos, smoothSpeed * Time.deltaTime ); } } ``` --- ### 四、常见问题解决 1. **UI位置偏移异常** - 调整`offset`参数或检查摄像机投影模式(Perspective/Orthographic) - 确保Canvas渲染模式为`Screen Space - Overlay` 2. **UI闪烁问题** - 将位置更新代码放在`LateUpdate()`中 - 增加防抖阈值检测 --- ### 五、扩展功能 1. **动态内容显示** ```csharp public Text infoText; // UI中的Text组件 void OnMouseEnter() { infoText.text = $"物体名称:{gameObject.name}"; hoverUI.SetActive(true); } ``` 2. **多物体共存处理** - 使用对象池管理多个UI实例 - 通过Tag/Layer区分不同物体类型 --- 通过以上步骤,可实现基础到进阶的鼠标悬停UI跟随效果。需根据具体项目需求调整参数和检测逻辑。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值