【Unity】实现鼠标拖拽的各种方法
Unity中实现鼠标拖拽
前言
起因是想写一个类似王权的Demo,然后发现自己对应拖拽这个操作没有系统地归纳与理解一下,于是去看了一下文章、API和视频,然后有了这一篇文
在这里,只讨论鼠标的相关拖拽方式,不讨论移动端也就是屏幕点击拖拽的实现
(以后有必要的话把PC->移动专门弄一下好了)
鼠标拖拽这件事在游戏中并不少见,而我们想要通过Unity实现它也并不困难
本文从基本的坐标系对齐,再到API应用,最后到接口调用。从三个角度小结的如何在Unity中实现”鼠标拖拽”以及相关优劣性
参考的视频:
Unity常用的三种拖拽方法 来自Joe
坐标系判定
个人认为是最最基本的做法了,甚至有了很愚蠢的造轮子的感觉
核心做法很简单:
对于鼠标: 判定鼠标在不在物品上面->判定鼠标是否点击->根据是否点击来修改bool->改变物品
对于物品: 实时更新上述的bool值->如果在“被拖动”的状态,则更新坐标
这里要注意的是鼠标所在的屏幕坐标系和物品所在的世界坐标系的转换,使用内置的ScreenToWorldPoint()
就可以轻松解决这件事
贴一下代码,挂在物品上的,其实可以略过
1 | public class Drag2DSpirte : MonoBehaviour |
两个坐标系
鼠标所处的坐标系叫作屏幕坐标系,是以像素点来支撑的一个屏幕坐标系
左下角是(0,0),右上角为(Screen.width,Screen.height)
而我们的Transform组件中的position所在的坐标系,叫世界坐标系
(当一个物体存在父物体的时候,它会以相对坐标系来描述其位置)
可以利用一些方法来转换二者
OnMouseDrag
刚才说过上一种方式就是造轮子了,现在把轮子呈现一遍
1 | private void OnMouseDrag() |
1 |
|
利用接口实现
事实上我个人觉得利用接口实现,接口写到太多了,在不用快速修复的情况下,一个个方法打的还累人
常用的接口包括:
IDragHandler
IBeginDragHandler
IEndDragHandler
接口中主要的方法是:
OnBeginDrag
OnDrag
OnEndDrag
OnDrop
这里需要注意的是,需要有OnDrag
,才能正常使用的OnBeginDrag
和OnEndDrag
拖拽物品代码如下:
1 | public class DragByInterface : MonoBehaviour,IDragHandler,IBeginDragHandler,IEndDragHandler |
放置物品的槽的代码如下:
1 | public class EndDragInSlot : MonoBehaviour , IDropHandler |
说实话并不觉得接口比OnMouseDrag以及后续的EventTrigger高级或者简单多少,起码现在我是没有看到它的优势。
除此之外,还有以下几个地方要处理:
1.检测不到放置位置:
实质上是因为鼠标拿起我们拖拽的物品的时候,该物品挡住了检测的激光射线,解决这个问题只需要在编辑器中为它加入一个canvasGroup,然后通过canvasGroup.blocksRaycasts
控制激光是否能穿过它即可
在Begin的时候改为false,End的时候改回来
文档中的解释:blocksRaycasts为true的时候,会把这个物品当作一个Collider来看待
2.”移动”的第二种实现
利用每帧移动和 += 对位移量进行计算,这一部分在代码中体现为:rectTransform.anchoredPosition += eventData.delta /canvas.scaleFactor
rectTransform.anchoredPosition是获得图片“相对于”锚点的位置信息
eventData.delta 是 对“上一次更新、上一次Update”而言的,用户拖拽这个对象所移动的2D位置坐标信息
因此利用+=就可以产生“移动”的效果
3.canvas.scaleFactor
在2中,我们看到代码中有canvas.scaleFactor,它的存在是为了修正位置的
因为我们没有采用“直接更新坐标”的方法来移动物品,因此UI画布的尺寸大小会影响到二者位置的判定,很明显的一个表现就是鼠标位置和物品位置“逐渐不同步”,在UI画布的Scale不为1的时候,就会出现在这种情况
因此,我们大大方方地为更新量除以相应的UI画布尺寸大小的系数即可
反正就写上吧,你尺寸是1的时候这个除以也不会影响什么对吧
EvenTrigger
我的评价是:舒服
EvenTrigger和按钮的OnClick()一样,当我们在Unity编辑器里面加入它的时候,只需要利用编辑器界面就能完成各个情况、各个方法的调用
代码层面需要做的,就是写一个都是Public的脚本给它调用即可
1 | public class DrayByEventTrigger : MonoBehaviour |
在这里,我们在判定鼠标拖拽的时候调用DragMenthod,而在结束拖拽的时候调用EndDragMethod就好了,简单方便,在编辑器里面就可以选择
(这里就是在我们想要拖到的对象(Image1)中添加了EventTrigger,然后挂上了对应的方法)
甚至可以说你写好了让策划测试去拖都可以
小结
在Unity中实现拖拽与放开的检测的方法有很多,事实上除了这里写的三种。什么利用激光射线检测、利用位置检测等等,各种方式层出不穷。
但是万变不离其宗的是,对于鼠标位置、鼠标响应者两个逻辑的编写,以及对于拿起和放置这两个重要状态的判定。
个人喜欢在只需要简单的拖拽判定的时候,就利用OnMouse系列进行相关功能的编写
而在调用/判定会复杂一些的情况下,则使用EventTrigger进行编写(而且EventTrigger还能干除了拖拽之外的其他事情,格局大了)
至于调用接口的方式,个人不是很喜欢,故不评价