UIEventListener是如何工作的及其他

来源:互联网 发布:拼多多和淘宝哪个好 编辑:程序博客网 时间:2024/06/01 13:01

开篇闲谈

关于标题里为什么带了个其他。因为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 ----->");    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

如上代码所示,通过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();
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

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

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

// Raycast into the screenif (!Raycast(currentTouch.pos)) hoveredObject = fallThrough;if (hoveredObject == null) hoveredObject = mGenericHandler;currentTouch.last = currentTouch.current;currentTouch.current = hoveredObject;lastTouchPosition = currentTouch.pos;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

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

// Process the events from this touchProcessTouch(pressed, unpressed);
  • 1
  • 2
  • 1
  • 2

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

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;            }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

其中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);
  • 1
  • 2
  • 1
  • 2

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

Notify(currentTouch.pressed, "OnPress", false);Notify(currentTouch.pressed, "OnClick", null);Notify(currentTouch.dragged, "OnDragStart", null);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

其中的区别就是传递的 “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;    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

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

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

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

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

public delegate void VoidDelegate (GameObject go);
  • 1
  • 1

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

public VoidDelegate onClick;
  • 1
  • 1

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

    static public UIEventListener Get (GameObject go)    {        UIEventListener listener = go.GetComponent<UIEventListener>();        if (listener == null) listener = go.AddComponent<UIEventListener>();        return listener;    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

通过 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); }
  • 1
  • 1

这个方法很简单,里面就是执行了 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 :-)

0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 运动后反胃想吐怎么办 魔域服务器满了怎么办 魔域忘记哪个区怎么办 魔域快捷键锁了怎么办 宝宝走路o型腿怎么办 走路多了腿疼怎么办 孩子与父母相冲怎么办 压片机冲模锈了怎么办 宝宝腹泻10多天怎么办 10个月宝宝腹泻怎么办 5岁宝宝很叛逆怎么办 6岁了不会写字怎么办 宝宝1岁不爱吃饭怎么办 l岁宝宝不吃饭怎么办 1岁宝宝不肯吃饭怎么办 10岁儿童不吃饭怎么办 2周岁宝宝不吃饭怎么办 9个月小孩不吃饭怎么办 孩子被老师打了怎么办 孩子说老师打她怎么办 孩子的数学太差怎么办 2岁宝宝老要喝水怎么办 分手了还想她怎么办 5岁宝宝不会说话怎么办 2岁半宝宝说话晚怎么办 7岁儿童发烧39度怎么办 感冒发烧怎么办简单的退烧方法 生完孩子没奶水怎么办 梦见让狐狸咬了怎么办 1岁宝宝不吃辅食怎么办 母乳不够宝宝不吃奶粉怎么办 吃母乳的宝宝不吃奶粉怎么办 宝宝吃母乳不吃奶粉怎么办 1岁婴儿不吃辅食怎么办 不喝奶瓶的宝宝怎么办 母乳不足宝宝不吃奶粉怎么办 4岁宝宝注意力不集中怎么办 孩子上课不专心听讲怎么办 小孩上课不认真听讲怎么办 一年级孩子上课不认真听讲怎么办 打了孩子很自责怎么办