我担心单指运动代码中OnPointerDown
和OnBeginDrag
之间的区别。
(在最新的使用物理光线投射器的Unity范例中:因此,最终,Unity将正确忽略UI层上的触摸。
因此,从2015年开始,您必须做的是:
Input
或Touches
系统,它们是毫无意义的废话,不起作用像下面这样编写脚本并放上它。
(提示-不要忘记简单地在场景中添加一个
EventSystem
。奇怪的是,Unity在某些情况下不会自动为您执行此操作,但在其他情况下Unity会自动为您执行此操作,因此,如果您忘记了,那会很烦人!)但是这是问题。
在使用
OnPointerDown
和OnBeginDrag
(以及匹配的结束调用)之间必须有一些细微的区别。 (您可以只交换以下代码示例中的操作。)自然,Unity在这方面不提供任何指导。以下代码精美地拒绝了流浪者,并且完美地忽略了您的UI层(感谢Unity!最后!),但是我对这两种方法之间的区别(开始拖动V.开始触摸)感到迷惑不解,无论如何我都找不到逻辑上的区别在单元测试中两者之间。
答案是什么?
/*
general movement of something by a finger.
*/
using UnityEngine;
using System.Collections;
using UnityEngine.EventSystems;
public class FingerMove:MonoBehaviour,
IPointerDownHandler,
IBeginDragHandler,
IDragHandler,
IPointerUpHandler,
IEndDragHandler
{
public Transform moveThis;
private Camera theCam;
private FourLimits thingLimits;
private Vector3 prevPointWorldSpace;
private Vector3 thisPointWorldSpace;
private Vector3 realWorldTravel;
public void Awake()
{
theCam = Camera.main or whatever;
}
public void OnMarkersReady() // (would be EVENT DRIVEN for liveness)
{
thingLimits = Grid.liveMarkers. your motion limits
}
private int drawFinger;
private bool drawFingerAlreadyDown;
public void OnPointerDown (PointerEventData data)
{
Debug.Log(" P DOWN " +data.pointerId.ToString() );
}
public void OnBeginDrag (PointerEventData data)
{
Debug.Log(" BEGIN DRAG " +data.pointerId.ToString() );
if (drawFingerAlreadyDown == true)
{
Debug.Log(" IGNORE THAT DOWN! " +data.pointerId.ToString() );
return;
}
drawFinger = data.pointerId;
drawFingerAlreadyDown=true;
prevPointWorldSpace = theCam.ScreenToWorldPoint( data.position );
}
public void OnDrag (PointerEventData data)
{
Debug.Log(" ON DRAG " +data.pointerId.ToString() );
if (drawFingerAlreadyDown == false)
{
Debug.Log(" IGNORE THAT PHANTOM! " +data.pointerId.ToString() );
}
if ( drawFinger != data.pointerId )
{
Debug.Log(" IGNORE THAT DRAG! " +data.pointerId.ToString() );
return;
}
thisPointWorldSpace = theCam.ScreenToWorldPoint( data.position );
realWorldTravel = thisPointWorldSpace - prevPointWorldSpace;
_processRealWorldtravel();
prevPointWorldSpace = thisPointWorldSpace;
}
public void OnEndDrag (PointerEventData data)
{
Debug.Log(" END DRAG " +data.pointerId.ToString() );
if ( drawFinger != data.pointerId )
{
Debug.Log(" IGNORE THAT UP! " +data.pointerId.ToString() );
return;
}
drawFingerAlreadyDown = false;
}
public void OnPointerUp (PointerEventData data)
{
Debug.Log(" P UP " +data.pointerId.ToString() );
}
private void _processRealWorldtravel()
{
if ( Grid. your pause concept .Paused ) return;
// potential new position...
Vector3 pot = moveThis.position + realWorldTravel;
// almost always, squeeze to a limits box...
// (whether the live screen size, or some other box)
if (pot.x < thingLimits.left) pot.x = thingLimits.left;
if (pot.y > thingLimits.top) pot.y = thingLimits.top;
if (pot.x > thingLimits.right) pot.x = thingLimits.right;
if (pot.y < thingLimits.bottom) pot.y = thingLimits.bottom;
// kinematic ... moveThis.position = pot;
// or
// if pushing around physics bodies ... rigidbody.MovePosition(pot);
}
}
这是一件方便的事情。使用鲜为人知但精致的3D场景,用相同的内容保存键入内容pointerCurrentRaycast
这是...如何注意到优秀
data.pointerCurrentRaycast.worldPosition
致电Unity。public class FingerDrag .. for 3D scenes:MonoBehaviour,
IPointerDownHandler,
IDragHandler,
IPointerUpHandler
{
public Transform moveMe;
private Vector3 prevPointWorldSpace;
private Vector3 thisPointWorldSpace;
private Vector3 realWorldTravel;
private int drawFinger;
private bool drawFingerAlreadyDown;
public void OnPointerDown (PointerEventData data)
{
if (drawFingerAlreadyDown == true)
return;
drawFinger = data.pointerId;
drawFingerAlreadyDown=true;
prevPointWorldSpace = data.pointerCurrentRaycast.worldPosition;
// in this example we'll put it under finger control...
moveMe.GetComponent<Rigidbody>().isKinematic = false;
}
public void OnDrag (PointerEventData data)
{
if (drawFingerAlreadyDown == false)
return;
if ( drawFinger != data.pointerId )
return;
thisPointWorldSpace = data.pointerCurrentRaycast.worldPosition;
realWorldTravel = thisPointWorldSpace - prevPointWorldSpace;
_processRealWorldtravel();
prevPointWorldSpace = thisPointWorldSpace;
}
public void OnPointerUp (PointerEventData data)
{
if ( drawFinger != data.pointerId )
return;
drawFingerAlreadyDown = false;
moveMe.GetComponent<Rigidbody>().isKinematic = false;
moveMe = null;
}
private void _processRealWorldtravel()
{
Vector3 pot = moveMe.position;
pot.x += realWorldTravel.x;
pot.y += realWorldTravel.y;
moveMe.position = pot;
}
}
最佳答案
我首先要说Input
和Touches
并不糟糕,它们仍然有用,并且是在touch
和OnPointerDown
出现之前在移动设备上检查OnBeginDrag
的最佳方法。您可以将OnMouseDown()
称为crappy,因为它并未针对移动设备进行优化。对于刚开始学习Unity的初学者,他们可以选择Input
和Touches
。
至于您的问题,OnPointerDown
和OnBeginDrag
是,而不是。尽管它们几乎做相同的事情,但是它们被实现为以不同的方式执行。下面我将描述其中的大多数:OnPointerDown
:
当屏幕上有按下/触摸时(在触摸屏上单击或按下手指时)调用OnPointerUp
:
释放按下/触摸(释放点击或从触摸屏上移开手指)时调用OnBeginDrag
:
在开始拖动之前,将其称为一次(当手指/鼠标向下移动的第一个时间时)OnDrag
:
当用户在屏幕上拖动时(当手指/鼠标在触摸屏上移动时)反复调用OnEndDrag
:
当拖动停止时调用(当手指/鼠标不再在触摸屏上移动时,不再调用时调用)。
OnPointerDown
与 OnBeginDrag
和 OnEndDrag
如果尚未调用OnPointerUp
,则将调用,而不调用。如果尚未调用OnPointerDown
,则将调用,而不调用。就像C++,C#中的花括号一样,您打开,将其' {',然后关闭,将其'} '。
差异:
当手指/鼠标在触摸屏上时,OnPointerDown将被称为,一旦被命名为,则立即被称为。除非有鼠标移动或手指在屏幕上移动,否则什么都不会发生,一旦后跟OnDrag,则OnEndDrag
将称为。
这些是为进行高级使用而设计的,例如带有未包含在Unity中的控件的自定义 UI。
每次使用时:
1. 当您必须执行简单单击按钮时(例如,屏幕上的向上,向下,拍摄按钮),您仅需要OnBeginDrag
来检测触摸。这应该适用于Sprite图片。
2. 如果您必须实现自定义切换开关,并且希望它是现实的,以便玩家可以向左/向右或向上/向下拖动到切换,则需要OnBeginDrag
,OnPointerDown
, OnPointerDown
,OnBeginDrag
,OnDrag
。您需要以此顺序编写代码,以在屏幕上显示平滑 Sprite / Texture过渡。一些切换开关被设置为单击,它将切换。某些人喜欢通过使其变得逼真来使它看起来逼真,因此您必须拖动以便将切换到。
3. 同样,当您要实现可拖拽的通用可重用弹出窗口 时,还需要使用这5个函数(OnEndDrag
,OnPointerUp
,OnPointerDown
,OnBeginDrag
,OnDrag
)。
首先检测是否有一个click(OnEndDrag
),检查以确保单击的Sprite是您要移动的正确的Sprite。等待玩家移动(OnPointerUp
)他们的手指/鼠标。一旦他们开始拖动,也许您可以使用OnPointerDown
调用协程函数循环,它将开始移动Sprite,并在该协程内部,您可以使用OnBeginDrag
或其他任何首选方法平滑 Sprite的运动。
由于while
曾经被调用过,因此它是开始和协程的好地方。
当玩家继续拖动Sprite时,Time.deltaTime
将重复称为 。使用OnBeginDrag
函数获取查找器的当前位置,并将其更新为OnDrag
,已运行的协程的协程将用于更新,将Sprite的位置更新。当播放器停止在屏幕上移动手指/鼠标的时,会调用OnDrag
,您可以Vector3
变量并告诉协程停止更新的Sprite位置。然后,当玩家释放手指(OnEndDrag
)时,您可以使用StopCoroutine函数停止协程。
由于有了boolean
,我们可以在拖动开始后就开始协程,而等待拖动结束。 不会使成为,以来启动OnPointerUp
中的协程,因为这意味着每次玩家触摸屏幕时,协程将是来启动。
没有OnBeginDrag
,我们必须使用OnPointerDown
变量使协程仅在每次调用的OnBeginDrag
函数中启动一次,否则协程将在各处运行,并且Sprite会发生意外移动。
4. 当您想确定玩家移动手指多长时间时。例如,著名游戏水果忍者。假设您要确定玩家在屏幕上滑动的距离。
首先,等待直到调用boolean
,再次等待直到调用OnDrag
,然后您可以获取OnPointerDown
函数内手指的当前位置,因为在手指开始移动之前调用了OnBeginDrag
。释放手指后,将调用OnBeginDrag
。然后,您可以再次获取手指的当前位置。您可以使用和这两个位置来检查减去后手指移动了多远。
如果您决定使用OnBeginDrag
作为获取手指的第一个 位置的位置,则会得到错误的结果,因为如果播放器向右滑动,则会向左滑动 再次等待,然后向上滑动 ,而在每次滑动之后不松开手指,唯一的良好结果是第一次滑动(正确的swipe)。左侧上的和向上滑动的将具有无效的值,因为在OnEndDrag
为时获得的第一个值称为,是您仍在上使用的值。这是因为玩家从未将手指从屏幕上移开,因此因此,OnPointerDown
不再是,从再也称为,不再是,并且第一个旧旧值仍然存在。
但是,当您使用OnPointerDown
而不是OnPointerDown
时,此问题将变成消失了,因为当手指停止移动时,将调用OnBeginDrag
,并在其再次开始移动时再次调用OnPointerDown
,从而导致第一个位置被覆盖使用新的。