Unity3D中OnPointerDown与OnBeginDrag的恐怖

Fat*_*tie 4 touch unity-game-engine unity3d-ui

我担心单指移动代码OnPointerDown与之间的区别OnBeginDrag.

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

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

  1. 忘记垃圾传统InputTouches系统废话甚至Unity承认是完全没有意义的废话 - 因为他们不工作

  2. 添加一个空的游戏对象,通常是BoxCollider2D,可能比屏幕大.使图层"绘制"(物理设置,它与任何东西互动)

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

在此输入图像描述

做一个像下面这样的脚本并把它放在"画画"上.

(提示 - 不要忘记简单地添加一个EventSystem场景.奇怪的是,Unity在某些情况下不会自动为你做这件事,但Unity会在其他情况下自动为你做这件事,所以如果你忘记了它会很烦人!)

但这是问题所在.

在使用OnPointerDownvs 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);
    }
  }
Run Code Online (Sandbox Code Playgroud)

这是一个方便的事情.使用鲜为人知但精致的3D场景保存输入相同的内容

pointerCurrentRaycast

这是怎么...注意到优秀

data.pointerCurrentRaycast.worldPosition
Run Code Online (Sandbox Code Playgroud)

打电话给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;
    }
  }
Run Code Online (Sandbox Code Playgroud)

Pro*_*mer 18

我想通过说开始InputTouches没有crappy.They仍然有用的并且是检查的最佳方式touch在移动设备上之前OnPointerDownOnBeginDrag走了过来.OnMouseDown()你可以打电话给蹩脚,因为它没有针对手机进行优化.对于初学者谁刚开始学习团结,Input并且Touches是他们的选择.

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

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

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

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

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

OnEndDrag:当拖动停止时(当手指/鼠标不再在触摸屏上移动时调用).

OnPointerDown对比OnBeginDragOnEndDrag

OnPointerUp若叫OnPointerDown 尚未调用.OnEndDrag若叫OnBeginDrag 尚未调用.就像C++,C#中的花括号一样,你将它打开 ' { '并关闭它' } '.

的差异性:OnPointerDown将被调用一次,并立即当手指/鼠标在触摸屏上.在鼠标移动或手指在屏幕上移动之后,其他任何事情都不会发生,然后OnBeginDrag会被OnDrag 调用一次.

这些用于执行高级用法,例如具有未包含在Unity中的控件的自定义 UI.

什么时候使用每个:

1.当您必须在屏幕上实现简单的单击按钮(例如,向上,向下,拍摄按钮)时,您需要OnPointerDown检测触摸.这适用于Sprite Images.

2.当你要实现自定义切换开关,你希望它是真实的,这样玩家可以拖动左/右或向上/向下拨动它,那么你需要的OnPointerDown, OnBeginDrag,OnDrag,OnEndDrag,OnPointerUp.您需要按此顺序编写代码,以在屏幕上实现平滑的 Sprite/Texture转换.一些切换开关被点击,它将切换.有些人喜欢通过制作它使它看起来很逼真,所以你必须拖动它才能切换它.

3.此外,当你想实现一个通用的可重复使用的弹出窗口可拖动,你还需要使用这些5个功能(OnPointerDown, OnBeginDrag,OnDrag,OnEndDrag,OnPointerUp).首先检测何时有click(OnPointerDown),检查以确保单击的Sprite是您要移动的正确的Sprite.等待玩家移动(OnBeginDrag)他们的手指/鼠标.一旦他们开始拖动,也许你可以使用while 循环来调用一个coroutine函数,它将开始移动Sprite并在该coroutine中,你可以使用或任何其他首选方法平滑 Sprite的移动Time.deltaTime.

由于OnBeginDrag被调用一次,它是一个很好的地方开始协同程序.随着玩家继续拖动Sprite,OnDrag将被重复调用.使用该OnDrag函数获取finder的当前位置,并将其更新Vector3运行的coroutine 将用于更新 Sprite 的位置.当玩家停止在屏幕上移动他们的手指/鼠标时,OnEndDrag会调用并且您可以boolean变量并告诉协程停止更新 Sprite的位置.然后,当玩家释放他们的手指(OnPointerUp)时,您可以使用StopCoroutine功能停止协程.

因为OnBeginDrag我们能够在等待拖动结束时一旦拖动就开始协程.它不会使,以启动该协程中OnPointerDown,因为这意味着每当玩家触摸屏,协同程序将启动.

如果没有OnBeginDrag,我们必须使用boolean变量使coroutine在OnDrag每次调用的函数中只启动一次,否则会在各处运行coroutine并且会发生Sprite的意外移动.

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

首先,等待直到OnPointerDown被调用,等待直到再次OnBeginDrag被调用,那么就可以得到当前内手指的位置OnBeginDrag功能,因为OnBeginDrag手指开始移动之前被调用.手指松开后, OnEndDrag被叫.然后你可以再次获得手指的当前位置.您可以使用这两个 位置通过减去它们来检查手指移动的距离.

如果您决定使用OnPointerDown作为获取手指第一个 位置的地方,您将得到错误的结果,因为如果玩家向右滑动,然后等待向左滑动然后再次等待并在每次滑动后向上滑动 而不释放他们的手指,你唯一的结果就是第一次滑动(向右滑动).在左边向上轻扫将有无效的值,因为你有当第一个值被称为是你的价值仍在使用.这是因为玩家永远不会被删除从屏幕上手指这么因此,是从来没有一次和第一旧旧的价值仍然存在.OnPointerDownOnPointerDown

但是,当你使用OnBeginDrag,而不是OnPointerDown,这个问题会消失,因为当手指停止移动,OnEndDrag被调用,它再次开始转动时,OnBeginDrag 被称为再次导致第一位置覆盖新的.

  • 我意识到我的问题有点不切实际,所以我已经把它关闭了!@Programmer在这里提出的观点...***"如果没有调用OnBeginDrag,OnEndDrag将不会被调用.它就像花括号......"***等,似乎是这个QA中的关键洞察力.谢谢,全部! (3认同)