【unity】几种常用的拖拽方法(内置方法 + 接口 + Event Trigger组件)

news2024/11/16 5:28:29

前言

在Unity中实现拖拽的方法有多种,以下是几种常见的方法和它们的优缺点

1. 鼠标按键的点击事件

Input.GetMouseButtonDown和Input.GetMouseButtonUp 方法可以监测用户鼠标按键的点击事件,通过检测鼠标按钮的状态来实现拖拽效果。用户通过鼠标进行拖拽操作。

1.1 优点:

  • 简单易懂,是最通用的实现拖拽方法之一。
  • 适用于所有平台,包括PC和移动设备。

1.2 缺点:

  • 拖拽细节(如拖拽的加速度、加速度的方向等)难以控制。
  • 如果需要控制多个物体的拖拽行为,则需要编写大量的代码逻辑。

2. OnMouseDrag

OnMouseDrag 方法是 Unity 内建的一个组件事件,用于处理鼠标拖拽事件,可以通过在物体上加上事件脚本来实现拖拽。

2.1 优点:

  • 简单明了,易于使用。
  • 对于简单的拖拽需求,非常适用。

2.2 缺点:

  • 只能用于PC平台或者Web平台。
  • 不支持多点触摸和移动设备上的触摸操作。

3. Event Trigger 中的 BeginDrag、OnDrag 和 EndDrag

Event Trigger 是 Unity 中常用的 GUI 事件框架,通过监听不同的事件类型实现拖拽功能,包括 BeginDrag、OnDrag 和 EndDrag 事件。

BeginDrag 事件:用户开始拖拽一个物体时触发该事件;

OnDrag 事件:在拖拽物体时持续调用该事件,可以实现拖拽过程中的反馈等功能;

EndDrag 事件:在用户释放物体时触发该事件,可以在此处理放置、执行等操作。

3.1 优点:

  • 支持多点触摸和移动设备上的触摸操作。
  • 比较容易控制拖拽的操作流程,如速度、拖拽范围等。
  • 可以实现更多基于 GUI 的拖拽效果。

3.2 缺点:

  • 对于非 GUI 元素的拖拽,需要额外的逻辑实现。
  • 开销比较大。

4. 接口实现的 OnBeginDrag、OnDrag 和 OnEndDrag

该方法需要继承 UnityEngine.EventSystems.IDragHandler 接口并实现接口中的方法,从而接收该界面上的物体的拖拽操作。

4.1 优点:

  • 支持多点触摸和移动设备上的触摸操作。
  • 对于非 GUI 元素的拖拽,也很容易实现。

4.2 缺点:

  • 开销较大。
  • 需要手动实现接口中的方法。

综上所述,以上几种实现拖拽效果的方法各有优缺点,需要针对实际需求来选择使用。如果需要快速实现拖拽效果,可以使用
Input.GetMouseButtonDown 和 OnMouseDrag。如果要实现更多的拖拽事件,可以使用 Event Trigger
或者接口实现。

实例

还不懂的也没关系,下面我会举一下实例,更深入的了解他们的用法

1. 鼠标按键的点击事件

新建场景
在这里插入图片描述
我们要做的就是:当游戏运行后,通过鼠标的点击、拖拽、松开等操作,能够自由地将右边的这些人物的零部件,自定义(拖拽)到我们左边的这个人物的外貌上

扩展问题:我们如何让我们处于世界坐标系的2D图片,跟随着处于屏幕坐标系的鼠标一起移动呢?

我们只要将鼠标的屏幕坐标系信息转换为世界坐标系就可以了,可以通过Camera.Main.ScreenToWorldPoint 方法将Screen屏幕坐标系To转换为WorldPoint世界坐标系上的每一个像素点(坐标)

using UnityEngine;

public class Drag2DSprite : MonoBehaviour
{
    [SerializeField] private bool isSelected; // 是否被选中
    private void Update()
    {
        if (isSelected)
        {
            Vector2 cursorPos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
            transform.position = new Vector2(cursorPos.x, cursorPos.y);
        }
    }
    private void OnMouseOver()
    {
        if (Input.GetMouseButtonDown(0))
            isSelected = true; // 被选中
        if (Input.GetMouseButtonUp(0))
            isSelected = false; // 取消选中
    }
}

2. 拖拽内置方法实现

其中就有一个叫做【OnMouseDrag】方法,它可以更方便的可以实现这个案例当中的2D贴图的拖拽

注意:如果我们想使用OnMouseDrag、OnMouseOver、OnMouseEnter、On MouseExit、OnMouseUp等方法,这个对象必须含有Collider组件,之后才能被这些方法所调用

2.1 OnMouseEnter、OnMouseExit例子

还是上面的案例,我们可以通过【OnMouseDrag】来实现,当我们的鼠标进入、或者离开2D贴图时,可以增加相应的放大、缩小功能,来增加一些交互的体验感

private void OnMouseDrag() // 当鼠标拖动时
{
    Vector2 cursorPos = Camera.main.ScreenToWorldPoint(Input.mousePosition); // 将鼠标位置转换为世界坐标
    transform.position = new Vector2(cursorPos.x,cursorPos.y); // 将物体位置设置为鼠标位置
}

private void OnMouseEnter() // 当鼠标进入时
{
    transform.localScale += Vector3.one * 0.07f; // 增加物体的缩放大小
}

private void OnMouseExit() // 当鼠标离开时
{
    transform.LocalScale -= Vector3.one * 0.07f; // 减小物体的缩放大小
}

2.2 OnMouseUp例子

在这里插入图片描述

我们要做的就是,将下方的三张图通过拖拽来进行正确的匹配,我们首先选中可拖拽的这三张图片,因为由于我们之后会用到OnMouseDrag等方法,我们首先去添加BoxCollider2D组件

代码实现

private Vector2 startPos; // 起始位置
[SerializeField] private Transform correctTrans; // 黑色图像
[SerializeField] private bool isFinished; // 是否完成
private void Start()
{
    startPos = transform.position; // 记录起始位置
}
private void OnMouseDrag()
{
    if (!isFinished) // 如果还未完成
    {
        transform.position = new Vector2(Camera.main.ScreenToWorldPoint(Input.mousePosition).x,
            Camera.main.ScreenToWorldPoint(Input.mousePosition).y); // 鼠标拖拽时移动物体
    }
}
private void OnMouseUp()
{
    if (Mathf.Abs(transform.position.x - correctTrans.position.x) <= 0.5f &&
        Mathf.Abs(transform.position.y - correctTrans.position.y) <= 0.5f) // 如果移动到了正确位置
    {
        transform.position = new Vector2(correctTrans.position.x, correctTrans.position.y); // 将物体移动到正确位置
        isFinished = true; // 标记为已完成
    }
    else // 如果没有移动到正确位置
    {
        transform.position = new Vector2(startPos.x, startPos.y); // 将物体移回起始位置
    }
}

结果
在这里插入图片描述

UI拖拽方法

我们刚才说了OnMouseDrag(等拖拽内置方法),一般适用于2D图片贴图和3D场景当中,如果遇到UI图片呢,是不会去使用OnMouseDrag等方法的

Event Trigger组件

新建ui场景
在这里插入图片描述
通过添加Event Trigger组件来实现

在这里插入图片描述
按下【Add New Event Type】添加新的事件类型,根据游戏中不同的事件类型,来实现不同的交互效果,不同的事件类型,包括了鼠标指针的进入、离开、按下、松开、点击,还有我们将会去使用到的Drag拖拽
还有我们拖拽结束后的EndDrag事件
在这里插入图片描述
书写代码方法

private Vector3 startPos; // 初始位置

private void Start()
{
    startPos = transform.position; // 记录初始位置
}
public void DragMethod()
{
    transform.position = Input.mousePosition; // 将物体位置设置为鼠标位置
}
public void EndDragMethod()
{
    Gameobject slotGO = Gameobject.Find("SLot"); // 查找名为 "SLot" 的物体
    float dist = Vector3.Distance(transform.position, slotGO.transform.position); // 计算物体与 "SLot" 之间的距离
    if (dist <= 100)
        transform.position = slotGO.transform.position; // 如果距离小于等于 100,则将物体位置设置为 "SLot" 的位置
    else
        transform.position = startPos; // 否则将物体位置设置为初始位置
}

在刚才的Event Trigger上挂载对应的方法
在这里插入图片描述
运行效果
在这里插入图片描述

2. 疑问

这时候有人可能会有疑问了,前面不是说鼠标的位置信息是屏幕坐标系,而我们现在图片是UI图片并不是在同一坐标系,为什么我们可以用等号直接来写呢?
如果我们的UI模式是Screenspace Overlay模式下,我们可以直接的将鼠标位置信息直接赋值给我们的transition.position(如果想使用RectTransformUtility.ScreenPointToLocalPointInRectangle也是可以的)

3. 问题

  • 首先:一般情况下我们会很少去使用GameObject.Find方法(如果需要找很多类似的游戏对象)
    原因之一就是因为这个方法是在所有游戏对象中,通过名字挨个去查找满足这个名字要求的游戏对象,我们有可能会有很多个【槽】也有很多个物品,那么这将会是一件非常消耗性能的地方
  • 第二就是如果你的同事修改了你这个对象的名字,那这个方法对这个项目可能就会造成不必要的隐患
  • 还有就是Vector3.Distancel的计算方法也非常消耗性能,这就引出了本文章的最后一种方法,通过接口来实现UI对象的拖拽、点击等操作

接口

首先我们需要去引用UnityEngine.EventSystems命名空间

接着我们开始使用EventSystem下提供的拖拽接口,有关Drag的接口,有IBeginDrag、IEndDrag、IDrag等接口

注意:OnBeginDrag和OnEndDrag的使用必须依赖OnDragHandler,而反过来,OnDragHandler却可以单独使用

1. IBeginDrag、IEndDrag、IDrag接口实现拖拽

1.1 扩展

我们可以通过rectTrans.AnchoredPosition的方式,获取这个UI图片「相对于」Anchor锚点的(参考)位置坐标信息,等于形参变量「eventData.delta」,这里的eventData.deltal的类型是Vector2结构体类型,表示的是自从上一次更新、上一次Update(Since Last Update可以理解为每一帧)用户拖着这个对象所移动的2D位置坐标信息通过+=的方式的累加(赋值),在拖拽的过程当中,赋值给RectTrans组件当中的anchoredPosition,如果你对这个方法有所迟疑呢,我们还可以去使用之前的transform.position=input.mousePosition来实现

1.2 槽的实现

我们拖拽的这个物品是否在【槽内】还是在【槽外】,所以放下物品这一个操作接口IDropHandler应该在写【槽Slot】这个游戏对象中,而不是写在我们的这个物品脚本上,因为我们的物品随时是可以Drop的,但是只有当我们的物品在【槽内】Drop的时候呢,那才有意义

1.2.1 槽的代码实现

using UnityEngine;
using UnityEngine.EventSystems;

public class Slot:MonoBehaviour,IDropHandler
{
	// 在拖拽结束时调用,将拖拽的物体的位置设置为当前物体的位置
	public void OnDrop(PointerEventData eventData)	
	{
		eventData.pointerDrag.GetComponent<RectTransform>().anchoredPosition = GetComponent<RectTransform>().anchoredPosition;
	}
}

1.2.2 可能出现的问题

当我们想要点击、想要触发的这个对象时,可能被上一层的U对象呢所遮挡、所覆盖了,会导致我们的鼠标无法被检测到,无法实现我们想要实现的功能。
比如说这里,我们想要实现的是槽这个游戏对象中的OnDrop方法,但是槽本身呢被上方鼠标拖拽的这个UI物品所覆盖、所遮挡了,他无法获取到我们鼠标何时松开Drop的操作,因为他被我们的物品所遮挡

这里介绍一个更为方便的组件,适合管理这一物体,包括他的子物体的所有的UI对象(透明度、可交互、是否遮挡等属性),添加Canvas Group组件

在脚本当中呢,我们首先去获取Canvas Group组件
在这里插入图片描述
当我们开始拖拽时,在OnBeginDrag方法的内部,将这个组件的blocksRaycasts属性设置为false,表示在我们刚开始拖拽的整个过程当中,鼠标不会再去把这个UI物品当作一个阻挡物来看待

1.3 物品代码

using UnityEngine;
using UnityEngine.EventSystems;

public class DragByInterface : MonoBehaviour,IDragHandler,IBeginDragHandler,IEndDragHandler
{
    private RectTransform rectTrans;
    private CanvasGroup canvasGroup;
    private void Start()
    {
        rectTrans = GetComponent<RectTransform>();
        canvasGroup = GetComponent<CanvasGroup>();
    }
    public void OnBeginDrag(PointerEventData eventData)
    {
        // 开始拖拽时,禁用射线检测和设置透明度
        canvasGroup.blocksRaycasts = false;
        canvasGroup.alpha = 0.35f;
    }
    public void OnDrag(PointerEventData eventData)
    {
        // 拖拽时,更新位置
        rectTrans.anchoredPosition += eventData.delta;
        //transform.position Input.mousePosition;//0PTIONAL
    }
    public void OnEndDrag(PointerEventData eventData)
    {
        // 结束拖拽时,启用射线检测和恢复透明度
        canvasGroup.blocksRaycasts = true;
        canvasGroup.alpha = 1f;
    }
}

1.4 效果

在这里插入图片描述

1.5 问题

这里最后提一句,如果鼠标在拖拽的过程当中,并不是和你要拖着这个物品同步,比如说鼠标和你拖拽的这个点的位置偏离过大
在这里插入图片描述

我们需要去检查Cnavasi画布当中Scale的数值呢是否为1,如果这个scalel不是1,那么就会出现鼠标拖拽过程当中不同步的问题,我们需要在EventData.deltal的后面,去除以相应的U画布尺寸大小的系数,这样就可以去解决鼠标拖拽U物品跑偏的这个问题了

public void OnDrag(PointerEventData eventData)
{
	rectTrans.anchoredPosition += eventData.delta / canvas.scaleFactor;
	//transform.position Input.mousePosition;//0PTIONAL
}

2. 单独使用IDragHandler实现拖拽

我们简单的通过接口来实现针对不同的UI面板窗口,进行拖拽的功能实现
在这里插入图片描述
我们希望拖拽的每个面板上方的导航栏会显示在UI的最高层,我们可以去使用panelRectPanel.SetAsLastSibling表示的是:set设置,as为,last最后一个、最下方的Sibling同级最下方的这个位置将它在Hierarchy窗▣中,这个父物体下的顺序设置为最后一个,这样,我们就可以确保它能渲染在最前方

using UnityEngine;
using UnityEngine.EventSystems;

public class Dragwindow : MonoBehaviour, IDragHandler, IPointerDownHandler
{
    private RectTransform panelRectTrans;

    private void Awake()
    {
        // 如果panelRectTrans为空,则设置为父对象的RectTransform组件
        if (panelRectTrans == null)
            panelRectTrans = transform.parent.GetComponent<RectTransform>();
    }
	
	//当用户拖拽物体时,将会触发
    public void OnDrag(PointerEventData eventData)
    {
        // 更新panelRectTrans的anchoredPosition
        panelRectTrans.anchoredPosition += eventData.delta;
    }
	
	//当用户按下物体时,将会触发
    public void OnPointerDown(PointerEventData eventData)
    {
        // 将panelRectTrans设置为最后一个兄弟对象
        panelRectTrans.SetAsLastSibling();
    }
}

效果
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/623634.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

DolphinScheduler3.1.4 集群部署

文章目录 DolphinScheduler3.1.4 集群部署一、解压 dolphinScheduler3.1.4 安装包二、复制 mysql8.0.16 的驱动三、创建mysql数据库用户和权限四、配置 dolphinscheduler_env.sh 文件五、初始化元数据六、配置 install_env.sh 文件七、安装 dolphinScheduler DolphinScheduler3…

2核4G服务器阿里云4M和腾讯云5M性能价格对比

阿里云轻量应用服务器2核4G4M带宽297.98元12个月&#xff0c;腾讯云轻量2核4G5M服务器168元一年&#xff0c;628元3年&#xff0c;2核4G轻量应用服务器阿里云和腾讯云怎么选择&#xff1f;哪个性能比较好&#xff1f;阿腾云分享轻量应用服务器2核4G配置阿里云和腾讯云CPU、带宽…

插件分享 | Headshot ⼀击即中,对指定URL进行漏洞批量扫描

前言&#xff1a;在⼀次真实的攻防场景中&#xff0c;我们发现了⼀个存在 Struts2 漏洞的地址&#xff0c;这个地址在我们通过 Fuzz 获得的⼆级⽬录下&#xff0c;这使得 Goby 的爬⾍没有办法爬取到这⼀个⻚⾯&#xff0c;但是我们通过其它 Struts2 专扫⼯具检测发现这个地址确…

入行软件测试一年了,薪资涨到18K,太强了...

梦的开始 收到了领导的通知&#xff0c;说我这一年来表现的很优秀&#xff0c;准备给我加工资。今天收到工资到账短信&#xff0c;扣掉税&#xff0c;比以往多了3k多&#xff0c;再加上年终奖这些&#xff0c;月薪也到了18k&#xff0c;真的太开心了。 仔细想想这一年来&#…

2023软件测试面试热点问题,你真的了解吗?

收集了2023年所有朋友软件测试的面试题后&#xff0c;我特意整理出了7个高频出现的面试题&#xff0c;一起来看看。 高频问题1&#xff1a;请自我介绍下&#xff1f; 高频问题2&#xff1a;请介绍下最近做过的项目&#xff1f; 高频问题3&#xff1a;请介绍下你印象深刻的bug&a…

MSE播放fragmented mp4 问题记录

一、在使用MSE 播放视频的时候发现firfox能播放,chrome 不能播放 原因&#xff1a;两边要求的fragmented mp4的格式要求不一样 , 参照Transcoding assets for Media Source Extensions - Web APIs | MDN 用ffmpeg 转成 对应的格式 firefox ffmpeg -i mp4-264.mp4 -movflags f…

DatenLord开源产品技术分享 | Xline源码解读 No.2

传统单数据中心解决方案无法满足跨数据中心的场景对性能和一致性的需求。DatenLord推出开源分布式KV存储Xline&#xff0c;针对多数据中心场景&#xff0c;可以实现数据的高性能跨云、跨数据中心共享访问&#xff0c;并且保证数据的一致性。 本期源码解读将聚焦Xline的Lease机…

技术赋能-混流编排功能,助力京东618直播重保 | 京东云技术团队

每每到618、双11这样的大型活动的时候&#xff0c;每天都有几个重要的大v或者品牌直播需要保障。 以往的重点场次监播方式是这么造的&#xff1a; 对每路直播的源流、各档转码流分别起一个ffplay播放窗口&#xff0c;再手动调整尺寸在显示器桌面进行布局&#xff0c;排到一屏…

Vue使用prerender-spa-plugin做预渲染,用于SEO优化相关内容

原因:像vue、react、angular开发的都是spa应用,他只有一个页面index,他们都是内加载,动态加载切换路由的,所以你再多页面百度蜘蛛只能爬到首页 1.解决方案 (1)vue.js官网提供的 SSR(服务端渲染) 这种方案呢学习成本高,对于刚开始的新手来说可能有点难度,基本还得重…

WTN6040-8S语音播报芯片在抽油烟机上的应用- 提升厨房智能化体验

在当今快节奏的生活中&#xff0c;智能家居技术的发展不仅为我们的生活带来了便利&#xff0c;更为我们的家庭安全和舒适提供了全新的解决方案。作为现代厨房的关键设备&#xff0c;油烟机在净化空气、排除异味和保护家庭健康方面起着重要的作用。而加入WTN6040-8S语音播报芯片…

如何配置IP地址

一.自动获取IP 1.dhclient 2.ifconfig 通过这个命令可以查看系统有几块网卡和网卡的IP。 如果您的Linux有多块网卡&#xff0c;那么在Linux中它会显示成eth1, eth2 依此类推 二.手动配置IP 如果您的虚拟机不能自动获取IP&#xff0c;那么只能手动配置&#xff0c;配置方法为&am…

项目管理用什么工具?甘特图给你答案

在项目管理中&#xff0c;项目的实施需要制定一个完善的项目计划。然而&#xff0c;在实际的项目管理工作中&#xff0c;计划变化快&#xff0c;总会产生各种问题和突发状况&#xff0c;导致管理问题层出不穷。 例如&#xff0c;项目团队人数多&#xff0c;团队协作混乱&…

SOP电子作业指导书系统SaaS部署及应用分析

随着互联网技术的不断发展&#xff0c;越来越多的企业开始采用SaaS&#xff08;Software as a Service&#xff09;模式来部署和应用各种软件系统。其中&#xff0c;SOP电子作业指导书系统是一种非常实用的工具&#xff0c;可以帮助企业有效地管理和指导员工的工作。 SOP电子作…

python实现简单的多机并行调度

场景说明 我们有10个任务需要主动发送到3台机器上并行执行&#xff0c;某一台机器执行完成再为此机器分配下一个任务 方案一&#xff1a;消息队列&#xff08;被动调度&#xff09; 此方案可以使用celeryredis实现简单的生产者消费者模型&#xff0c;步骤如下&#xff1a; …

关于单片机的时钟浅谈及STM32F103/F030单片机的内外时钟切换问题

绪论 本文主要讲解单片机的时钟系统的相关知识&#xff0c;并进行超频测试&#xff0c;同时介绍如何在STM32F0单片机上进行内外时钟的切换&#xff0c;在不使用外部晶振或者外部晶振不启动时自动切换内部时钟的方法。 一、杂谈 问题来源于群里的一次问答&#xff1a; 诚然&…

Flatpickr教程:使用JavaScript快速创建一个自定义日期选择器

部分数据来源&#xff1a;ChatGPT 引言 如果您是一个网站开发者&#xff0c;想为自己的网站添加方便易用的日期选择对话框&#xff0c;那么Flatpickr日期选择对话框可能正好符合您的需要。在这篇文章中&#xff0c;我们将详细介绍如何使用Flatpickr日期选择对话框&#xff0c…

容器集群管理工具 Docker Swarm

前言 《了解和使用Docker》中有提到容器编排工具 docker compose &#xff0c;不过只限于单机。如果现在需要搭建一个集群环境&#xff0c;提供了10台服务器用来部署应用以及其依赖的组件&#xff0c;比如5个 Tomcat 应用容器、3个Redis、5个 Mysql、3个 Nginx &#xff0c;你…

Share Creators Ada Liu 与 VNG Christopher. Liu C出席 2023 全球游戏产业峰会

夏日将至&#xff0c;第二十届中国国际数码互动娱乐展览会&#xff08;ChinaJoy&#xff09;将于 2023 年 7 月 28 日至 7 月 31 日在上海新国际博览中心隆重举办。 本届 ChinaJoy 将带来多场重磅主题高端会议&#xff0c;其中全球游戏产业峰会将于 7 月 29 日在上海浦东嘉里大…

网安大佬常用的10大工具

从事网络安全工作&#xff0c;手上自然离不开一些重要的网络安全工具。今天&#xff0c;分享10大网络安全工具。 一、Kali Linux Kali 是一个基于 Debian 的 Linux 发行版。它的目标就是为了简单&#xff1a;在一个实用的工具包里尽可能多的包含渗透和审计工具。Kali 实现了这…

互联网大厂面试必备——1685页《Java 面试突击核心手册,二十大专题,覆盖2000道 Java后端核心面试解析

前言 不论是校招还是社招都避免不了各种面试。笔试&#xff0c;如何去准备这些东西就显得格外重要。不论是笔试还是面试都是有章可循的&#xff0c;我这个有章可循‘说的意思只是说应对技术面试是可以提前准备。 运筹帷幄之后&#xff0c;决胜千里之外!不打毫无准备的仗,我觉…