Unity3D中OnPointerDown与OnBeginDrag的恐怖

时间:2016-03-16 22:23:50

标签: unity3d touch unity3d-5 unity3d-ui

我担心单指移动代码中OnPointerDownOnBeginDrag之间的差异。

(在使用物理raycaster的最新Unity范例中:最后,Unity将正确地忽略UI层上的触摸。

所以从2015年开始你必须做的是:

  1. 忘掉传统的InputTouches系统废话,甚至Unity承认完全没有意义的废话 - 因为它们不起作用

  2. 添加一个空的游戏对象,通常是BoxCollider2D,可能比屏幕大。使图层“绘制”(物理设置,它与任何内容交互)

  3. 只需添加到相机, 2D或3D物理raycaster 。事件掩盖了你的“绘图”层。

  4. enter image description here

    执行如下脚本并将其置于“绘图”。

    (提示 - 不要忘记简单地在场景中添加EventSystem。奇怪的是,Unity在某些情况下不会自动为您执行此操作,但Unity会在其他情况下自动为您执行此操作,因此它是如果你忘记了会很烦人!)

    但是这是问题

    使用OnPointerDownOnBeginDrag(以及匹配的结束调用)之间必须有一些细微差别。 (您可以在以下代码示例中交换操作。)

    自然,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;
        }
      }
    

2 个答案:

答案 0 :(得分:15)

我首先要说InputTouches并不蹩脚。它们仍然很有用,是touch之前在移动设备上检查OnPointerDown的最佳方法和OnBeginDrag出现了。 OnMouseDown()你可以称之为糟糕,因为它没有针对移动设备进行优化。对于初学者,只需开始来学习Unity,InputTouches是他们的选择。

至于您的问题,OnPointerDownOnBeginDrag 相同。虽然他们几乎做同样的事情,但他们被实施以不同的方式执行。下面我将介绍其中的大部分内容:

OnPointerDown: 当屏幕上有按下/触摸时(当触摸屏上有按下或手指按下时)调用

OnPointerUp: 释放按下/触摸时(释放单击或从触摸屏移除手指)时调用

OnBeginDrag: 在拖动开始之前调用一次(当手指/鼠标在第一次时移动时向下移动时)

OnDrag: 当用户在屏幕上拖动时(手指/鼠标在触摸屏上移动时)反复调用

OnEndDrag: 拖动停止时调用(当手指/鼠标在触摸屏上没有更长时移动)。

OnPointerDown OnBeginDrag OnEndDrag

如果OnPointerUp 未被调用,

OnPointerDown。如果OnEndDrag 未被调用,OnBeginDrag。它就像C ++中的花括号,C#,你打开它&#39; {&#39;并且你关闭它&#39; } &#39;。

差异: 当手指/鼠标在触摸屏上时,OnPointerDown将被称为一次立即。在鼠标移动或手指在屏幕上移动之后不会发生任何其他事情,然后OnBeginDrag将被称为一次,然后是OnDrag。

这些用于执行高级用法,例如自定义用户界面中未包含的控件。

何时使用每个人:

1。当您必须在屏幕上实施简单点击按钮时,例如,向上,向下,拍摄按钮, >需要OnPointerDown来检测触摸。这适用于Sprite Images。

2。当您必须实施自定义切换开关并希望它逼真时,玩家可以向左拖动/向右/向上/向下到切换,然后您需要OnPointerDownOnBeginDragOnDragOnEndDragOnPointerUp。您需要按此顺序编写代码,以在屏幕上显示平滑 Sprite / Texture转换。一些切换开关用于点击,它将切换。有些人喜欢通过制作它看起来很逼真,所以你必须拖动它才能切换它。

3。此外,如果您想实施可拖动的通用可重复使用弹出窗口,您还需要使用这5个函数(OnPointerDownOnBeginDragOnDragOnEndDragOnPointerUp)。 首先检测何时有单击(OnPointerDown),检查以确保单击的Sprite是您要移动的正确的Sprite。等待玩家移动(OnBeginDrag)他们的手指/鼠标。一旦他们开始拖动,也许你可以调用带有while 循环的协同程序功能,它将开始移动精灵并在该协程内,你可以平滑的运动使用Time.deltaTime或任何其他首选方法的精灵。

由于OnBeginDrag被调用一次,因此它是开始 协程的好地方。 当玩家继续拖动精灵时,OnDrag将被重复称为 。使用OnDrag功能获取搜索程序的当前位置,并将其更新为Vector3 已经正在运行的协程将用于更新 Sprite的位置。当播放器停止在屏幕上移动他们的手指/鼠标时,OnEndDrag会被调用,您可以boolean变量并告诉协程停止更新雪碧的位置。然后,当玩家释放他们的手指(OnPointerUp)时,您可以使用StopCoroutine函数停止协程。

由于OnBeginDrag我们能够在等待拖动结束时拖动开始时启动协同程序。 感觉 开始 {/ 1}}中的协同程序,因为这意味着每次玩家触摸屏幕,协程将启动

如果没有OnPointerDown,我们必须使用OnBeginDrag变量来使协程在每次调用的boolean函数中只启动一次,否则将会有coroutine在任何地方运行且意外将发生精灵的移动。

4. 当你想确定玩家移动手指的时间。这方面的例子是名为 Fruit Ninja 的着名游戏。让我们说你想确定播放器在屏幕上滑动的距离。

首先,等待直到调用OnDrag等待再次调用OnPointerDown,然后您就可以获得当前< / strong>手指在OnBeginDrag函数内的位置,因为在手指开始移动之前调用了OnBeginDrag。释放手指后,将调用OnBeginDrag。然后你可以再次获得手指的当前位置。您可以使用这些两个 位置来检查手指移动了多远减去它们。

如果您决定使用OnEndDrag作为获取手指的第一个 位置的地方,您将获得错误结果,因为如果播放器向右滑动,则等待向左滑动,然后再次等待每次滑动后,向上滑动 而不释放手指,您唯一的良好结果是首次刷卡右键滑动)。 向上滑动将包含无效值,因为OnPointerDown 调用时获得的第一个值是您仍然使用的值。这是因为玩家从未将手指从屏幕上移开,所以因此OnPointerDown 从不再次称为 和第一个旧<强>旧价值仍然存在。

但是当您使用OnPointerDown代替OnBeginDrag时,此问题将消失,因为当手指停止移动时,{{1} }当被再次开始移动时,OnPointerDown再次被调用,导致第一个位置覆盖

答案 1 :(得分:1)

The difference is that OnBeginDrag doesn't get called until the touch/mouse has moved a certain minimum distance, the drag threshold. You can set the drag threshold on the Event System component.

This is necessary for when you have a hierarchy of objects with different ways of handling input, especially scrollviews. Imagine you have a scrollview with a vertical stack of cells, each with a button in it. When the touch first starts on one of the buttons, we don't know whether the user is tapping a button or dragging the scrollview. It isn't until the touch gets dragged for the drag threshold that we know it is a drag and not a tap.