UIEventListener 及其他

原创 2015年11月21日 13:26:43

开篇闲谈

关于标题里为什么带了个其他。因为UIEventListener的功能实现离不开其他功能的支持,然后这篇博客会通过UIEventListener来讲讲NGUI和Unity里的一些东西,东西比较杂就叫做其他吧 ,本篇使用的NGUI版本是3.7.5,老版的未知,新版的应该原理类似。

关于UIEventListener

首先UIEventListener是什么,以及是干嘛用的,首先借用源码里的注释,注释一目了然,不做翻译了。
UIEventListener源码里的注释
NGUI中的UIEventListener使用起来很方便,只需要把UIEventListener这个脚本绑定到一个带有Boxcollider的对象上就可以实现点击,可以实现Button,也可以实现Sprite点击。感觉蛮神奇的,于是乎就来研究研究。P.S.由于以前使用cocos2d-x时养成了看引擎源码的习惯,如今使用同样开源的NGUI,源码为我所用,想看就看,想改就改,感觉棒棒哒!

如何使用UIEventListener

以一个UISprite举例,其他如UIPanel,UIWidget,UILabel同理。
1. 新建一个UISprite并添加一个BoxCollider脚本,设置BoxCollider的Size。这里写图片描述
2. 添加一个测试脚本到UISprite上,源码如下:

    void Start() 
    {
        //绑定点击方法
        UIEventListener.Get(TestBt).onClick = OnClickBt;

        //Lambda表达式
       // UIEventListener.Get(TestBt).onClick = (go) => { Debug.Log("On Click ----->")};
    }

    /// <summary>
    /// 要绑定的点击方法
    /// </summary>
    /// <param name="go"></param>
    void OnClickBt(GameObject go) 
    {
        Debug.Log("On Click ----->");
    }

如上代码所示,通过UIEventListener的Get方法绑定自定义的方法即可,这样当运行程序,点击 TestBt就可以调用OnClickBt方法,打印信息。P.S.1 - 可以在Start里绑定方法,也可以根据需要在其他方法里绑定。2 - 注释的那段代码使用的是Lambda表达式,同样可以实现打印信息功能。关于Lambda表达式,这里就不讲了,自行百度 ->_->

实现原理

通过上面的举例说明,是不是发现UIEventListener使用起来真的很简单,但似这个功能是如何实现的呢?NGUI怎么实现触摸,点击的呢?再具体点就是NGUI怎么判断出我点击或者触摸了这个物体的呢?要知其然,也要知其所以然也。P.S.常常看源码也是棒棒哒 ->_->

首先说说 UICamera
NGUI里有个很重要的脚本就是这个UICamera,因为NGUI里的所有点击事件的触发都是通过这个脚本,关于 UICamera 可以直接看NGUI官网的介绍 —- UICamera是何方神圣
源码里的注释也点明了
UICamera 脚本源码注释

看注释,你会发现有 OnHover,OnPress, OnClick … ….这些方法。然后你去UIEventListener的源码看,会发现也有这些方法名。似不似发现了什么 ->_->
UIEventListener 源码

UICamera点击触摸
其实UICamera里是通过射线检测(Physics.Raycast)来完成点击,触摸事件的检测的。要知其所以然就要看源码了。
这里只以触摸事件为例做个简单介绍,鼠标点击事件类似。
1 .先去看UICamera 中的 Update 方法

        // Process touch events first
        if (useTouch) ProcessTouches ();
        else if (useMouse) ProcessMouse();

很简单,看注释以及方法名字就知道了,ProcessTouches 是处理触摸事件的,ProcessMouse是处理鼠标点击的。

2.再进 ProcessTouches 方法看看。有两个关键点,一个就是调用射线检测方法Raycast,执行射线检测,另一个就是根据射线检测结果,来判断是触摸状态,并调用 ProcessTouch 来处理事件并发送。

// Raycast into the screen
if (!Raycast(currentTouch.pos)) hoveredObject = fallThrough;
if (hoveredObject == null) hoveredObject = mGenericHandler;
currentTouch.last = currentTouch.current;
currentTouch.current = hoveredObject;
lastTouchPosition = currentTouch.pos;

Raycast 代码就是射线检测执行了,根据检测结果返回布尔值。

// Process the events from this touch
ProcessTouch(pressed, unpressed);

这里调用的方法就用来分发事件了。

3.最终操作 射线检测 —– 进 Raycast 方法看,Raycast 方法有点长,就只贴出一段吧


            if (cam.eventType == EventType.World_3D)
            {
                if (Physics.Raycast(ray, out lastHit, dist, mask))
                {
                    lastWorldPosition = lastHit.point;
                    hoveredObject = lastHit.collider.gameObject;

                    if (!list[0].eventsGoToColliders)
                    {
                        Rigidbody rb = FindRootRigidbody(hoveredObject.transform);
                        if (rb != null) hoveredObject = rb.gameObject;
                    }
                    return true;
                }
                continue;
            }

其中cam.eventType就是事件类型,有EventType.World_3D,EventType.UI_3D,是用来对点击,触摸事件进行排序的,看哪个优先,可以参看官网的解释(官网解释要仔细看哦)

The first option on the UICamera, Event Type is what determines how the script sorts whats underneath the mouse and touch events. If it’s set to UI mode, then it’s always based on widget’s depth – just like the draw order. Changing this option to World mode is something you should do if your UICamera is attached to your Main Camera. Doing so will sort the hit objects by their distance to the camera.

然后最终的触摸,点击实现就是 Physics.Raycast 了,这个是Unity自带的射线检测,什么?你不知道射线检测是什么?自己去百度吧 ->_->

4.ProcessTouch 触摸事件分发

if (onPress != null) onPress(currentTouch.pressed, false);
Notify(currentTouch.pressed, "OnPress", false);

第一句就是调用委托,如果有绑定委托,则调用绑定的方法。
第二句Notify就是具体分发事件了。
在 ProcessTouch 方法中会发现多处调用 Notify 如:

Notify(currentTouch.pressed, "OnPress", false);

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

Notify(currentTouch.dragged, "OnDragStart", null);

其中的区别就是传递的 “OnPress”,”OnClick”, “OnDragStart” 等参数,这些参数是不是和上面提到的方法名一样。

5.Notify 方法

/// <summary>
/// Generic notification function. Used in place of SendMessage to shorten the code and allow for more than one receiver.
/// </summary>

    static public void Notify (GameObject go, string funcName, object obj)
    {
        if (mNotifying) return;
        mNotifying = true;

        if (NGUITools.GetActive(go))
        {
            go.SendMessage(funcName, obj, SendMessageOptions.DontRequireReceiver);
            if (mGenericHandler != null && mGenericHandler != go)
            {
                mGenericHandler.SendMessage(funcName, obj, SendMessageOptions.DontRequireReceiver);
            }
        }
        mNotifying = false;
    }

这个方法比较短,但是相当于一个终端,所有的事件都是从此处分发的,具体分发就是调用的Unity中内置的一种传递事件机制。
SendMessage
关于 Unity 的 SendMessage 给两处链接以供参考 SendMessage为何方神圣 SendMessage怎么用

这里简而言之就是可以通过GameObject调用SendMessage来调用GameObject脚本上存在的方法。上面的 “OnPress” “OnClick”等等就是要调用的方法名。

好了,到此处,UICamera 的触摸事件已处理完毕,并发送出去了,接下来看UIEventListener 怎么处理的。

UIEventListener 处理事件
看UIEventListener源码,在UIEventListener定义了很多委托,什么?你不知道委托是啥,赶紧百度去!
这里只拿 onClick 举例说明:

public delegate void VoidDelegate (GameObject go);

这里定义了一个 VoidDelegate 委托,返回值为void,传递的参数是 GameObject

public VoidDelegate onClick;

然后又声明了一个 VoidDelegate 类型的 onClick

    static public UIEventListener Get (GameObject go)
    {
        UIEventListener listener = go.GetComponent<UIEventListener>();
        if (listener == null) listener = go.AddComponent<UIEventListener>();
        return listener;
    }

通过 Get 方法,把 UIEventListener 脚本绑定到了要处理的GameObject上,可以参考上面 TestBt 例子,调用UIEventListener Get 方法的同时已将 TestBt 测试脚本上的 OnClickBt 方法和UIEventListener中的onClick委托绑定。重点绑定委托了

再看 UIEventListener 源码,里面定义了一个名为OnClick的方法(好了又遇见OnClick了),接上文的SendMessage,当触摸或者点击事件发生后,UICamera 中的 Notify 方法会发送很多事件,其中就包括”OnClick”,即此时会调用 UIEventListener 中的 OnClick 方法。再看 OnClick 方法:

void OnClick () { if (onClick != null) onClick(gameObject); }

这个方法很简单,里面就是执行了 onClick 委托,因为此时 onClick 已经绑定了 TestBt 脚本中的 OnClickBt 方法,所以这里执行 onClick 委托,其实是调用的 OnClickBt。至此,@@一个点击事件就完成了@@
P.S.撒花完结 —–

:-( 不会画流程图,谁来教教我—

简述下流程 以 OnClick 为例
UICamera 通过射线检测获取触摸或者点击,然后通过SendMessage分发 OnClick 事件
UIEventListener 收到事件OnClick调用,执行 onClick 委托。因为onClick委托绑定了一个方法,进而会调用这个方法。 ->_->

总结

其实也没啥总结的,就是对 UIEventListener 比较感兴趣,然后就扒出来很多东西(对NGUI UIScrollView的实现也很感兴趣,有机会再扒出来)有些东西需要多了解学习,多看源码,而这些东西,在使用UIEventListener表面上看不到。
此次,需要学习关注的知识点

  1. Unity 射线检测
  2. Unity SendMessage
  3. C# 委托

先掌握上面三个内容吧!P.S.还可以去掌握下Lambda :-)

版权声明:本文为博主原创文章,转载请标明出处 ^M^。

UGUI实现NGUI的UIEventListener功能

在unity中处理UI事件时,习惯了使用NGUI的UIEventListener,虽然UGUI也有AddListener,但是一个组件只能对应一个函数,不能在一个函数中同时处理多个事件,显得有些麻烦 ...

NGUI学习笔记 - 通过UIEventlistener和UIbutton来学习NGUI的消息机制

先来看UIEventlistener脚本首先定义了六个代理类型: public delegate void VoidDelegate (GameObject go); public de...

UNITY之EventTrigger,EventListener

//Demo1 using UnityEngine; using System.Collections; public class EventTriggerTest : MonoBehaviour ...

【小松教你手游开发】【unity实用技能】unity 几种触发事件

1.在button上挂Box Collider,勾上Is Trigger.添加UIButtonMessage,设置Target和FunctionName。 2,.用在GameObject上挂UIE...

NGUI UIEventListener

UIEventListener.cs 对一些常用的事件进行了封装和继承。 一,常用事件: void OnClick ():点击事件; void OnDoubleClick ():二次...

js给eventListener传递参数

项目中有多个img标签需要设置eventListener,其逻辑都是跳转url,考虑传入url值供eventListener中使用switch方式筛选,遇到eventListener传值问题,参考ht...

Button点击事件绑定中如何传递带参数的方法

btn.onClick.AddListener(delegate () { this.OnBtnClick(true); }); 传递一个有参数的方法用于传递参数。这样点击事件就能接收参数了。 ...

关于Unity3D脚本中UIEventListener无法调用的问题

新手刚刚开始接触unity3D 照着网上的教程试着做了一个按钮 在写脚本的过程中却发现无法调用UIEventListener 在网上疯狂找了一圈发现是没有导入NGUI包 百度百科的NGUI词条: ...

NGUI中实时查看UIEventListener的通知对象

NGUI中有好几种方式可以获取按钮的点击事件,我最喜欢用UIEventListener(一行代码搞定),可惜这个脚本在运行时是个黑箱,我不知道它会给哪些脚本发消息。如果某个UI模块是别人写的,脚本调用...
  • lzdidiv
  • lzdidiv
  • 2016年12月02日 17:13
  • 233

UIEventListener是如何工作的及其他

开篇闲谈 关于标题里为什么带了个其他。因为UIEventListener的功能实现离不开其他功能的支持,然后这篇博客会通过UIEventListener来讲讲NGUI和Unity里的一些东西,东...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:UIEventListener 及其他
举报原因:
原因补充:

(最多只允许输入30个字)