常用的三种拖拽方法(内置方法 + 接口 + Event Trigger组件)

news2024/11/23 15:26:08

前言

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

  1. Input.GetMouseButtonDown

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

优点:

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

缺点:

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

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

优点:

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

缺点:

  • 只能用于PC平台或者Web平台。
  • 不支持多点触摸和移动设备上的触摸操作。
  1. Event Trigger 中的 BeginDrag、OnDrag 和 EndDrag

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

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

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

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

优点:

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

缺点:

  • 对于非 GUI 元素的拖拽,需要额外的逻辑实现。
  • 开销比较大。
  1. 接口实现的 OnBeginDrag、OnDrag 和 OnEndDrag

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

优点:

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

缺点:

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

综上所述,以上几种实现拖拽效果的方法各有优缺点,需要针对实际需求来选择使用。如果需要快速实现拖拽效果,可以使用 Input.GetMouseButtonDown 和 OnMouseDrag。如果要实现更多的拖拽事件,可以使用 Event Trigger 或者接口实现。
新建场景
在这里插入图片描述
我们要做的就是:当游戏运行后
通过鼠标的点击、拖拽、松开等操作
能够自由地将右边的这些人物的零部件
自定义(拖拽)到我们左边的这个人物的外貌上

因为如果我们想使用OnMouseOver、OnMouseEnter、.,On MouseExit等方法的实现的条件呢
这个对象必须With the Collider”含有Collider组件,之后才能被这些方法所调用
在这里插入图片描述
我们只要将鼠标的屏幕坐标系信息转换为世界坐标系就可以了
我们可以通过Camera.Main.ScreenToWorldPoint
方法的名字呢也很直接,说的就是将
Screen屏幕坐标系To转换为WorldPointt世界坐标系上的每一个像素点(坐标)

代码实现

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;
	}
}

拖拽内置方法实现

其中就有一个叫做【OnMouseDrag】方法呢
更方便的可以实现这个案例当中的2D贴图的拖拽
当我们的鼠标进入、或者离开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;
}

OnMouseDrag等方法呢
我会在2D游戏当中,那些非U类型的2D贴图

其他游戏

在这里插入图片描述

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

private Vector2 startPos;
[SerializeField]private Transform correctTrans;//Black Image
[SerializeField]private bool isFinished;
private void Start()
{
	startPos = transform.position;
}
private void OnMouseDrag()
{
	if(!isFinished)//isFinished =false
	{
		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场景当中
如果遇到U图片呢
是不会去使用OnMouseDrag等方法的
新建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");
	float dist Vector3.Distance(transform.position,slotGO.transform.position);
	if(dist<=100)
		transform.position slotGO.transform.position;
	else
		transform.position startPos;
}

刚才不是说鼠标的位置信息是屏幕坐标系
而我们现在图片呢由于是U图片并不是在同一坐标系
为什么我们可以用等号直接来写呢
如果U模式是Screenspace Overlay模式下呢
我们可以直接的将鼠标位置信息直接赋值给我们的transition.position
如果想使用RectTransformUtility.ScreenPointToLocalPointInRectangle也是可以的)

在刚才的Event Trigger上挂载对应的方法
在这里插入图片描述
运行效果
在这里插入图片描述
这样就实现了我们的基本功能,但是呢,它存在很多问题
首先:一般情况下我们会很少去使用GameObject.Find方法
(如果需要找很多类似的游戏对象)
原因之一呢是因为
这个方法呢是在所有游戏对象中
通过名字挨个去查找
满足这个名字要求的游戏对象
我们有很多个【槽】也有很多个物品
这将会是一件非常消耗性能的地方

第二呢
如果你可爱的同事修改了你这个对象的名字
那这个方法呢
对这个项目呢
就会造成不必要的隐患

还要呢就是
Vector3.Distancel的计算方法呢也非常消耗性能
这就引出了本视频当中最后一种方法
通过接口来实现UI对象的拖拽、点击等操作

接口

我们可以让脚本实现更多有趣的事件接口
这些接口呢会强制要求实现它的方法
接口的方法
会在进行U交互的特定行为出现后呢,被调用
使用接口需要在冒号的后面:输入我们需要使用的接口
接口一般惯例会是以【I】开头的

【I】呢就表示我们的Interface接口
如果我们想使用IPointerDownHandler接口
直接输入呢还是不行的
因为使用这些接口
首先我们需要去引用UnityEngine.EventSystems命名空间

单单这样呢依旧是会报错的
因为使用接口的【前提】
接口本身它只关注方法的定义
他并不会去关注方法的实现

我们要使用这个接口
那就必须将接口中未实现的这些方法呢
加以实现

接着我们开始使用EventSystem下提供的拖拽接口
我们可以看到有关Drag的接口呢
有IBeginDrag,IEndDrag和IDrag等四个接口

如果BeginDrag和EndDrag在没有IDrag的基础上呢,是不成大器的
如果这里我们再补上OnDragHandler接口呢
那么呢,Console窗口呢就会输出我们想要的效果了

也就是BeginDrag和EndDrag的使用必须依赖OnDragHandler,而反过来却又是可行的,OnDragHandler可以单独使用

首先我们需要去获取当前拖拽的这个游戏对象,就是我们这个UI物品的RectTrans组件
我们通过rectTrans.AnchoredPosition的方式
获取这个UI图片「相对于」Anchor锚点的(参考)位置坐标信息
等于形参变量「eventData.delta」
这里的eventData.deltal呢
首先它的类型呢,是Vector2结构体类型

表示的是
自从上一次更新、上一次Update
(Since Last Update可以理解为每一帧)
用户拖着这个对象所移动的2D位置坐标信息
通过+=的方式的累加(赋值)
(这里的「+=」表示A=A+1,也可以写作A+=1;与委托的「+=」不同
在拖拽的过程当中
赋值给RectTrans组件当中的anchoredPosition
如果你对这个方法有所迟疑呢
我们还可以去使用之前的transform.position=input.mousePosition:来实现

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

代码

using UnityEngine;
using UnityEngine.EventSystems;

public class Slot:MonoBehaviour,IDropHandler
{
	public void OnDrop(PointerEventData eventData)	
	{
		Debug.Log("Droooo0000oooop");
		eventData.pointerDrag.GetComponent<RectTransform>().anchoredPosition = GetComponent<RectTransform>().anchoredPosition;
	}
}

这里呢就要提到我们在制作U过程当中,经常会犯
那就是我们想要点击、想要触发的这个对象可能呢
被上一层的U对象呢所遮挡、所覆盖了
所以呢会导致我们的鼠标
无法被检测到,无法实现我们想要实现的功能
比如说这里
我们想要实现的是槽这个游戏对象中的OnDrop方法

比如说这里
我们想要实现的是槽这个游戏对象中的OnDrop方法
但是槽本身呢
被上方鼠标拖拽的这个UI物品所覆盖、所遮挡了
他无法获取到我们鼠标何时松开Drop的操作
因为他被我们的物品所遮挡

这里呢介绍一个更为方便的组件
适合管理这一物体
包括他的子物体的所有的UI对象(透明度、可交互、是否遮挡等属性)
添加Canvas Group组件
Group:表示这一个组合,这一个整体
管理这一个整体的一个组件
在脚本当中呢,我们首先去获取Canvas Group组件
在这里插入图片描述
当我们开始拖拽时
在OnBeginDrag方法的内部
将这个组件的blocksRaycasts属性设置为false
表示在我们刚开始拖拽的整个过程当中
鼠标不会再去把这个U物品当作一个碰撞物
当作一个阻挡物来看待

当我们拖拽这个物品到槽的上方时呢
鼠标能够顺利的去忽略这个UI物品
如果松开这个物品能够成功的去调用OnDrop方法

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;
	}
}

效果
在这里插入图片描述

问题

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

我们还需要去检查
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
}

其他

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

using UnityEngine;
using UnityEngine.EventSystems;

public class Dragwindow MonoBehaviour,IDragHandler,IPointerDownHandler
{
	//[SerializeField]private RectTransform panelRectTrans;//0PTIONAL
	private RectTransform panelRectTrans;
	
	private void Awake()
	{
		if (panelRectTrans =null)
			panelRectTrans transform.parent.GetComponent<RectTransfgrm>();
	}
	public void OnDrag(PointerEventData eventData)
	{
		panelRectTrans.anchoredPosition += eventData.delta;
	}
	public void OnPointerDown(PointerEventData eventData)
	{
		panelRectTrans.SetAsLastsibling();
	}
}

效果
在这里插入图片描述

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

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

相关文章

Web基本概念

一、前言 World Wide Web的简称&#xff0c;是一个由许多互相链接的超文本组成的系统&#xff0c;通过互联网访问 &#xff08;为用户提供信息&#xff09; 静态网页 仅适用于不能经常更改内容的网页&#xff1b; 动态网页 网络编程技术创建的页面&#xff1b;通过在传统的静态…

MongoDB安装与使用

目录 一、MongoDB介绍与安装 什么是MongoDB 为什么要用MongoDB MongoDB下载 MongoDB安装完成 二、MongoDB Compass MongoDB Compass使用 三、使用mongoose连接数据库 使用MongoDB Compass 创建数据库 使用mongoose 连接数据库 每日一课&#xff1a;MongoDB 常用命令…

Vue项目打包dist目录介绍

如下 CSS目录&#xff1a; ① .css文件是项目要用到的css文件,当你做webpack打包的时候&#xff0c;会把所有的css样式打包到这里 ② .css.map文件是一个Source map文件&#xff0c;Source map就是一个信息文件&#xff0c;里面储存着位置信息。也就是说&#xff0c;转换后的代…

ChatGPT扩展系列之使用pandora本地搭建ChatGPT

ChatGPT扩展系列之使用pandora本地搭建ChatGPT 1. 为什么要本地搭建 主要解决使用上的几个痛点,我们可以看一下下面就是我们最常遇到的几个问题,这里我们重点提一下就是我们本地搭建好了之后,我们获取Access Token,这个Token的有效期长达14天,也就是这14天中,我们都不需…

【沐风老师】3DMAX径向对称插件使用方法应解

3DMAX径向对称插件使用教程 3DMAX径向对称插件&#xff0c;允许你对径向结构建模并查看最终结果。它的功能类似于3dMax自带的“对称”修改器&#xff0c;但它可以在三个轴的任意角度径向对象&#xff0c;这可以创造出很多我们意想不到的建模艺术效果&#xff0c;也可以理解它是…

【前端 - HTML】第 6 课 - 表单标签

欢迎来到博主 Apeiron 的博客&#xff0c;祝您旅程愉快 &#xff01; 时止则止&#xff0c;时行则行。动静不失其时&#xff0c;其道光明。 目录 1、缘起 2、表单标签 2.1、input 标签基本使用 2.2、input 标签占位文本 2.3、单选框 radio 2.4、上传文件 2.5、多选框 …

Vue.js中的动态组件和异步组件

Vue.js中的动态组件和异步组件 在Vue.js中&#xff0c;动态组件和异步组件是两个常用的技术&#xff0c;用于处理动态加载和渲染组件的需求。虽然它们都可以实现动态加载和渲染组件的功能&#xff0c;但它们的实现方式和使用方法有所不同。本文将详细介绍Vue.js中的动态组件和…

短视频账号矩阵系统技术开发难度之.框架底层逻辑

申请流程&#xff1a;注册官方开放平台账号----申请服务商-----关联应用----申请权限-----等待审核通过 接入开发------开发功能列表&#xff1a; 数据归纳箱&#xff08;账号数据对比概览内含视频总数、播放总数、点赞总数、分享总数、粉丝总数数据统计概览统计&#xff09;…

CMU15-445 2022 Fall 通关记录 —— Project 0

Project 0 Project #0 - C Primer | CMU 15-445/645 :: Intro to Database Systems (Fall 2022) — 项目 #0 - C 入门 | CMU 15-445/645 :: 数据库系统简介&#xff08;2022 年秋季&#xff09; 前期准备 为完成该项目做的一些准备&#xff1a; 创建个人项目FarewellYi/BusT…

【半监督医学图像分割 2023 CVPR】BCP

【半监督医学图像分割 2023 CVPR】BCP 论文题目&#xff1a;Bidirectional Copy-Paste for Semi-Supervised Medical Image Segmentation 中文题目&#xff1a;双向复制粘贴半监督医学图像分割 论文链接&#xff1a;https://arxiv.org/abs/2305.00673 论文代码&#xff1a;http…

EXCEL和VBA里的通配符和转义符

1 EXCEL里的通配符 1.1 EXCEL里常见通配符 通配符必须是英文半角的&#xff0c;中文输入下的不行&#xff01;* 可代表任意数量的字符&#xff1f; 可代表任一个的字符 1.2 使用举例 EXCEL的查找框&#xff0c;也可以使用 通配符只有部分内置函数可…

如何做架构设计?

也许您对软件设计存在一些疑惑&#xff0c;或者缺乏明确思路&#xff0c;那么本文将非常适合您。 1、设计很重要 我们可以看一下周边的事物&#xff0c;那些好的东西&#xff0c;他们并不会天然存在&#xff0c;都是被设计出来的&#xff0c;因此设计就是创造和改善事物的重要…

JavaSE03_流程控制语句

JavaSE-03 [流程控制语句] 第一章 流程控制 1.1 流程概述 在一个程序执行的过程中&#xff0c;每条语句的执行顺序对程序的结果是由直接影响的&#xff0c;也就是&#xff0c;语句的流程对运行结果有着直接的影响&#xff0c;所以&#xff0c;必须清楚知道每条语句的执行流程…

【Python】Python系列教程-- Python3 命名空间和作用域(二十九)

文章目录 前言作用域全局变量和局部变量global 和 nonlocal关键字 前言 往期回顾&#xff1a; Python系列教程–Python3介绍&#xff08;一&#xff09;Python系列教程–Python3 环境搭建&#xff08;二&#xff09;Python系列教程–Python3 VScode&#xff08;三&#xff09…

Vue.js 中的 v-bind 和 v-on 简写

Vue.js 中的 v-bind 和 v-on 简写 在 Vue.js 中&#xff0c;v-bind 和 v-on 是两个常用的指令&#xff0c;用于绑定属性和事件。在本文中&#xff0c;我们将介绍 v-bind 和 v-on 的简写形式&#xff0c;以及如何使用它们。 v-bind 简写形式 在 Vue.js 中&#xff0c;v-bind 用…

Java 进阶—死锁造成原因及其解决

今天我们来了解一下线程死锁&#xff0c;死锁很好理解&#xff0c;从字面上来看就是锁死了&#xff0c;解不开&#xff0c;在大街上看到一对卧龙凤雏的情侣&#xff0c;怎么说&#xff0c;你们给我锁死&#xff0c;不要分开去霍霍别人 之前我们不是说过&#xff0c;解决线程安…

GoogleTest之创建Mock

目录 MOCK_METHODmock方法的访问属性mock非虚函数mock自由函数Nice/Strict/Naggymock方法简化参数mock具体类的替代方法代理给fake mock是用来模拟对象&#xff0c;隔离边界的一种测试方法&#xff0c;以便在开发阶段不需要依赖第三方或其他依赖项可以进行独立的测试。 MOCK_ME…

MySQL调优系列(六)——查询优化

一、查询慢的原因 查询速率受网络、CPU、IO、上下文切换、系统调用、生成统计信息、锁等待时间等因素影响。 举个常见面试题&#xff1a; 一个表非常非常大&#xff0c;上亿级别的数据&#xff0c;性能会变慢嘛&#xff1f;如果表有索引 答&#xff1a;增删改会变慢。&#xf…

Python可视化分析项目高分课设

今天给大家分享一个基于python的django框架结合爬虫以及数据可视化和数据库的项目&#xff0c;该项目总体来说还是挺不错的&#xff0c;下面针对这个项目做具体介绍。 1&#xff1a;项目涉及技术&#xff1a; 项目后端语言&#xff1a;python 项目页面布局展现&#xff1a;前…

数据结构--队列

文章目录 队列基础队列的实现链表实现环形数组实现tail一直加的问题容量处理 队列的使用 队列基础 queue 是以顺序的方式维护的一组数据集合 相对于链表来说&#xff0c;队列操作数据的位置是固定的只能2端操作。 在一端添加数据&#xff0c;从另一端移除数据。习惯来说&#…