UICamera的触摸事件

转载自:    http://dsqiu.iteye.com/blog/1971866

记得刚开始用NGUI的时候,就有心思要去琢磨下UICamera,那个时候NGUI还是2.6的版本,现在已经到了3.0.3f,NGUI更新真的很强劲, 当然改动也挺大的,特性也越来越多了。之前本来研究下UICamera最后还是放弃了,因为UICamera的代码太复杂了,很凌乱,也就放下去了,这几天重新翻看了下,发现UICamera的可读性太强了,代码的组织逻辑很强,完全可以当做文本来从上到下来阅读,所以才会有这篇文章。

       UICamera做了很多有优化,新增了一些特性,之前可能觉得NGUI只是做一个工具,现在越来越完美了,少废话,下面把看到的亮点呈上。

ClickNotification

/// <summary>
  /// Whether the touch event will be sending out the OnClick notification at the end.
  /// </summary>

  public enum ClickNotification
  {
    None,
    Always,
    BasedOnDelta,
  }

      ClickNotification定义了OnClick响应的条件,后面也定义了ClickNotification变量 public ClickNotification clickNotification = ClickNotification.Always;

       ClickNotification.None: 不响应OnClick事件

       ClickNotification.Always:总是响应OnClick事件

       ClickNotification.BaseOnDelta:依据移动的delta的距离判断是否响应OnClick函数,如果移动距离大于float click  = isMouse ? mouseClickThreshold : touchClickThreshold;则不响应OnClick事件

下面这部分代码是当响应了OnDrag事件就把currentTouch.clickNotification = ClickNotification.None;就不在会响应OnClick事件了。

bool isDisabled = (currentTouch.clickNotification == ClickNotification.None);
            Notify(currentTouch.dragged, "OnDrag", currentTouch.delta);
            isDragging = false;

            if (isDisabled)
            {
              // If the notification status has already been disabled, keep it as such
              currentTouch.clickNotification = ClickNotification.None;
            }
            else if (currentTouch.clickNotification == ClickNotification.BasedOnDelta && click < mag)
            {
              // We've dragged far enough to cancel the click
              currentTouch.clickNotification = ClickNotification.None;
            }

 然后再执行OnClick和OnDoubleClick事件先判断条件currentTouch.clickNotification != ClickNotification.None 是否成立:

// If the touch should consider clicks, send out an OnClick notification
          if (currentTouch.clickNotification != ClickNotification.None)
          {
            float time = Time.realtimeSinceStartup;

            Notify(currentTouch.pressed, "OnClick", null);

            if (currentTouch.clickTime + 0.35f > time)
            {
              Notify(currentTouch.pressed, "OnDoubleClick", null);
            }
            currentTouch.clickTime = time;
          }

 EventType

public enum EventType
  {
    World,	// Perform a Physics.Raycast and sort by distance to the point that was hit.
    UI,		// Perform a Physics.Raycast and sort by widget depth.
  }

        这个很简单就是定义当前射线和碰撞体碰撞的判断标准,如果是UI则以Depth来判断,如果是World是以实际距离来判断。

/// <summary>
  /// List of all active cameras in the scene.
  /// </summary>
 
  static public List<UICamera> list = new List<UICamera>();

        UICamera在初始化的时候会被加入 mList这个链表中,然后对链表进行排序,根据相机的深度,深度值越小的相机排位靠前,最靠前的相机为场景的主UICamera,然后只有只有主UICamera才会去监测场景中的事件,其他的UICamera并不执行监测任务。UICamera利用Unity的Raycast去监测事件发生的对象,因为发射出去的Ray对象必须碰撞到Collider才会有反应,所以NGUI中所有需要响应事件的控件均需要添加Collider,同时Ray只会碰撞到深度最小的Collider,Ray射线的最大深度为rangeDistance,当这个值为-1时则发射深度和相机深度一样,主UICamera每一帧都会主动去发射Ray检测鼠标此时触碰到的对象并将其记录在对应的鼠标按键事件中,这是能监测到OnHover这个动作的关键(当然只有在useMouse为true时才会有此操作)。

        在游戏场景初始化阶段,每个UICamera都会根据平台义useMouse、useTouch、useKeyboard和useController 这些属性,分别对应的是能否在场景使用鼠标、触摸屏、键盘以及摇杆。

public bool useMouse = true;

  public bool useTouch = true;

  public bool allowMultiTouch = true;

  public bool useKeyboard = true;

  public bool useController = true;

MouseOrTouch

/// <summary>
  /// Ambiguous mouse, touch, or controller event.
  /// </summary>

  public class MouseOrTouch
  {
    public Vector2 pos;				// Current position of the mouse or touch event
    public Vector2 delta;			// Delta since last update
    public Vector2 totalDelta;		// Delta since the event started being tracked

    public Camera pressedCam;		// Camera that the OnPress(true) was fired with

    public GameObject current;		// The current game object under the touch or mouse
    public GameObject pressed;		// The last game object to receive OnPress
    public GameObject dragged;		// The last game object to receive OnDrag

    public float clickTime = 0f;	// The last time a click event was sent out

    public ClickNotification clickNotification = ClickNotification.Always;
    public bool touchBegan = true;
    public bool pressStarted = false;
    public bool dragStarted = false;
  }

       MouseOrTouch是一个很重要的类,是一个事件的结构体,然后就定义了不同平台的事件,记录Camera监测的事件:MouseOrTouch只是记录“鼠标”等的移动的“物理”信息——位置,移动距离等,只有鼠标是否按下只有在Update中每帧监测。

       下面定义不同平台的事件,例如鼠标事件,mMouse记录鼠标左键,右键和中键的事件(因为鼠标这里只记录鼠标的三个按键,所以mMouse才是有三个元素,现在明白为啥了吧)。

// Mouse events
  static MouseOrTouch[] mMouse = new MouseOrTouch[] { new MouseOrTouch(), new MouseOrTouch(), new MouseOrTouch() };

  // The last object to receive OnHover
  static GameObject mHover;

  // Joystick/controller/keyboard event
  static MouseOrTouch mController = new MouseOrTouch();

  // Used to ensure that joystick-based controls don't trigger that often
  static float mNextEvent = 0f;

  // List of currently active touches
  static Dictionary<int, MouseOrTouch> mTouches = new Dictionary<int, MouseOrTouch>();

 currentTouch

/// <summary>
  /// ID of the touch or mouse operation prior to sending out the event. Mouse ID is '-1' for left, '-2' for right mouse button, '-3' for middle.
  /// </summary>

  static public int currentTouchID = -1;

  /// <summary>
  /// Current touch, set before any event function gets called.
  /// </summary>

  static public MouseOrTouch currentTouch = null;

       currentTouch这个变量是整个UICamera中控制事件监测的关键所在,记录了当前事件的触发对象和一些其他诸如position位置、dealta时间、totaldealta总时间等属性,然后用currentTouchID记录当前事件的类型,这些类型包括鼠标事件、键盘控制器事件以及触摸屏事件。

ProcessTouch

       ProcessTouch这个函数就是根据currentTouch来针对不同的情况响应不同的函数,被ProcessMouse,ProcessTouch和ProcessOthers调用,如ProcessMouse,分别捕获鼠标三个按键的状态,然后调用ProcessTouch来响应:

// Process all 3 mouse buttons as individual touches
    if (useMouse)
    {
      for (int i = 0; i < 3; ++i)
      {
        bool pressed = Input.GetMouseButtonDown(i);
        bool unpressed = Input.GetMouseButtonUp(i);
  
        currentTouch = mMouse[i];
        currentTouchID = -1 - i;
  
        // We don't want to update the last camera while there is a touch happening
        if (pressed) currentTouch.pressedCam = currentCamera;
        else if (currentTouch.pressed != null) currentCamera = currentTouch.pressedCam;
  
        // Process the mouse events
        ProcessTouch(pressed, unpressed);
      }

 其他

        UICamera还提供其他一些“特性”,能够让开发者实现更多的功能(就不解释了吧, 有注释):

/// <summary>
  /// If 'true', once a press event is started on some object, that object will be the only one that will be
  /// receiving future events until the press event is finally released, regardless of where that happens.
  /// If 'false', the press event won't be locked to the original object, and other objects will be receiving
  /// OnPress(true) and OnPress(false) events as the touch enters and leaves their area.
  /// </summary>

  public bool stickyPress = true;

/// <summary>
  /// If set, this game object will receive all events regardless of whether they were handled or not.
  /// </summary>

  static public GameObject genericEventHandler;

  /// <summary>
  /// If events don't get handled, they will be forwarded to this game object.
  /// </summary>

  static public GameObject fallThrough;

最后,NGUI一共支持一下事件:

void OnHover (bool isOver) – Sent out when the mouse hovers over the collider or moves away from it. Not sent on touch-based devices.
void OnPress (bool isDown) – Sent when a mouse button (or touch event) gets pressed over the collider (with ‘true’) and when it gets released (with ‘false’, sent to the same collider even if it’s released elsewhere).
void OnClick() — Sent to a mouse button or touch event gets released on the same collider as OnPress. UICamera.currentTouchID tells you which button was clicked.
void OnDoubleClick () — Sent when the click happens twice within a fourth of a second. UICamera.currentTouchID tells you which button was clicked.
void OnSelect (bool selected) – Same as OnClick, but once a collider is selected it will not receive any further OnSelect events until you select some other collider.
void OnDrag (Vector2 delta) – Sent when the mouse or touch is moving in between of OnPress(true) and OnPress(false).
void OnDrop (GameObject drag) – Sent out to the collider under the mouse or touch when OnPress(false) is called over a different collider than triggered the OnPress(true) event. The passed parameter is the game object of the collider that received the OnPress(true) event.
void OnInput (string text) – Sent to the same collider that received OnSelect(true) message after typing something. You likely won’t need this, but it’s used by UIInput
void OnTooltip (bool show) – Sent after the mouse hovers over a collider without moving for longer than tooltipDelay, and when the tooltip should be hidden. Not sent on touch-based devices.
void OnScroll (float delta) is sent out when the mouse scroll wheel is moved.
void OnKey (KeyCode key) is sent when keyboard or controller input is used.

       最近由于项目要用到FastGUI,然后手上的FastGUI不支持NGUI(NGUI变动太大了),然后自己要升级下FastGUI,就要更多的掌握NGUI的原理,所以才会一直不断的写一些文章。写文章主要是记录下自己从中看到的东西,当然D.S.Qiu最喜欢和大家分享,希望能对读者有帮助,哪怕只有一个人,D.S.Qiu也会很兴奋的,因为很多次D.S.Qiu都不打算写的(文章写的太烂,没有深度,逻辑差,每次都要熬夜等),但当我看到别人文章的亮点时,我就觉得自己还是可以分享些的。

       今天把FastGUI 兼容到了NGUi3.0.3f,还增加一些功能,然后要写一个文档给美术的同事,我感觉头就大了,感觉如果要我口述一定能让听者完全明白,但是写起来就完全不着调,所以觉得D.S.Qiu的文字很渣,马上就是凌晨1:30,睡觉,晚安!

        如果您对D.S.Qiu有任何建议或意见可以在文章后面评论,或者发邮件(gd.s.qiu@gmail.com)交流,您的鼓励和支持是我前进的动力,希望能有更多更好的分享。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 在Unity中,可以通过一些方法来实现拖拽UI范围的限制。 首先,你可以使用RectTransform组件来获取UI元素的位置和大小信息。通过调整RectTransform的anchoredPosition属性,可以改变UI元素的位置。 其次,你可以编写一个脚本来实现拖拽功能。在拖拽脚本中,可以使用鼠标或触摸事件来控制UI元素的位置。在开始拖拽时,记录下鼠标或触摸的初始位置。然后,在拖拽过程中,根据鼠标或触摸的移动距离,计算出UI元素的新位置。 为了限制UI元素的拖拽范围,你可以在拖拽脚本中添加一些判断条件。例如,可以使用RectTransform的边界信息来判断UI元素是否超出了指定的范围。如果超出了范围,可以将UI元素的位置锁定在边界上。 另外,你还可以使用Mathf库中的一些函数来限制UI元素的位置。例如,可以使用Mathf.Clamp函数来约束UI元素的位置在指定的范围内。 最后,在制作拖拽UI时,你可以将拖拽脚本添加到UI元素的父节点上,以实现拖拽整个UI元素的效果。 ### 回答2: 在Unity中,要实现拖拽UI范围的限制,可以按照以下步骤进行操作: 首先,获取需要拖拽的UI对象的RectTransform组件,通过代码或者拖拽的方式获取到该组件的引用。 然后,在UI对象所在的脚本中添加一个拖拽的方法,可以是鼠标点击和移动时触发的方法。在该方法中,通过Input.mousePosition获取到鼠标当前的位置。 接着,通过Camera的ScreenToWorldPoint方法,将屏幕坐标转换为世界坐标。这样可以得到鼠标在世界坐标中的位置。 接下来,通过RectTransform的anchoredPosition属性,获取UI对象相对于父级容器的位置。可以通过修改该属性的值,来改变UI对象的位置。 在修改anchoredPosition属性之前,可以先判断鼠标当前位置是否在限定的范围内。可以通过检查当前位置的X和Y坐标是否在限定范围内,来判断是否需要对anchoredPosition属性进行修改。 如果需要限制范围,可以使用Mathf.Clamp方法,将鼠标位置的X和Y坐标进行限制,使其在设定的范围内。 最后,将修改后的anchoredPosition重新赋值给UI对象的RectTransform组件,即可完成拖拽UI范围的限制。 需要注意的是,上述方法仅适用于使用RectTransform组件进行布局的UI对象。对于非RectTransform的UI对象,可能需要使用其他方式来实现拖拽范围的限制。 ### 回答3: 在Unity中,我们可以通过一些方法来限制UI拖拽的范围。以下是使用Unity提供的RectTransform组件和事件响应来实现的常见方法: 1. 获取UI元素的RectTransform组件。 首先,需要获取需要拖拽的UI元素的RectTransform组件。可以通过代码获取该组件,或者在Unity编辑器中手动将该组件添加到UI元素上。 2. 设置拖拽的触发事件。 在UI元素上添加拖拽触发事件,例如鼠标按下和拖拽的事件。可以使用Unity的EventTrigger组件或者编写代码来添加这些事件。 3. 实现拖拽的逻辑。 在拖拽事件的响应函数中,可以通过获取鼠标的位置来计算拖拽的距离。然后,根据需要限制的范围,对UI元素的位置进行适当的调整。 4. 限制拖拽的范围。 可以使用RectTransform组件的anchoredPosition属性来设置UI元素的位置。根据UI元素需要被限制的范围,我们可以通过判断UI元素的目标位置是否超出限制范围,并进行相应的调整。 例如,如果要限制UI元素只能在一个矩形区域内拖拽,可以通过比较UI元素的目标位置和限制范围的最大最小值来限制位置。在拖拽事件中,可以根据鼠标的位置计算出UI元素的目标位置,然后使用Mathf.Clamp函数将目标位置限制在合适的范围内。 通过以上的步骤,我们可以在Unity中实现对UI拖拽的范围限制。根据具体的需求和场景,可以进行相应的调整和扩展。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值