UI EventSystem事件监听
Unity5.0 EventSystem事件系统的详细说明
一、EventSystem对象的说明
当我们在场景中创建任一UI对象后,Hierarchy面板中都可以看到系统自动创建了对象EventSystem,可以看到该对象下有三个组件:EventSystem、StandaloneInputModule、TouchInputModule,后面两个组件都继承自BaseInputModule。
EventSystem组件主要负责处理输入、射线投射以及发送事件。一个场景中只能有一个EventSystem组件,并且需要BaseInputModule类型组件的协助才能工作。EventSystem在一开始的时候会把自己所属对象下的BaseInputModule类型组件加到一个内部列表,并且在每个Update周期通过接口UpdateModules接口调用这些基本输入模块的UpdateModule接口,然后BaseInputModule会在UpdateModule接口中将自己的状态修改成'Updated',之后BaseInputModule的Process接口才会被调用。
BaseInputModule是一个基类模块,负责发送输入事件(点击、拖拽、选中等)到具体对象。EventSystem下的所有输入模块都必须继承自BaseInputModule组件。StandaloneInputModule和TouchInputModule组件是系统提供的标准输入模块和触摸输入模块,我们可以通过继承BaseInputModule实现自己的输入模块。
除了以上两个组件,还有一个很重要的组件通过EventSystem对象我们看不到,它是BaseRaycaster组件。BaseRaycaster也是一个基类,前面说的输入模块要检测到鼠标事件必须有射线投射组件才能确定目标对象。系统实现的射线投射类组件有PhysicsRaycaster, Physics2DRaycaster, GraphicRaycaster。这个模块也是可以自己继承BaseRaycaster实现个性化定制。
总的来说,EventSystem负责管理,BaseInputModule负责输入,BaseRaycaster负责确定目标对象,目标对象负责接收事件并处理,然后一个完整的事件系统就有了。
另外,其实这些说明官方都有提供,这里也就是把英文译成了中文,并整理下,加上自己的理解,有问题的地方请各路神仙多多指教。
官方文档在这里:
http://docs.unity3d.com/ScriptReference/EventSystems.EventSystem.html
二、UGUI中的事件系统
根据第一节中的说明,EventSystem和BaseInputModule是粘在一个对象上的,这两个模块在EventSystem对象上可以直接看到。那么,BaseRaycaster模块呢。。。
其实射线检测,肯定是从摄像机发起的,那么BaseRaycaster模块也一定和摄像机关系一定不简单。
对于UI模块,在Canvas对象下我们可以看到GraphicRaycaster组件。如果Canvas的渲染模式是SceenSpace-Overlay,那么我们是看不到Camera组件的。所以应该是GraphicRaycaster会对UI不同的渲染模式做特殊处理。
因为有GraphicRaycaster组件的原因,Canvas上的所有UI对象,都可以接受输入模块发出的事件,具体事件的处理在第四节说明。
三、场景对象中使用事件系统
场景中的非UI对象,如果想要接收输入模块的事件,一样的道理,也需要给摄像机挂上一个射线检测组件。PhysicsRaycaster, Physics2Draycaster这两个组件分别是用于3D和2D的场景。当然,还需要场景的对象挂了collider射线才检测的到。
其实官方对射线检测也是做了说明的,如果不详读手册是不会发现的,这里是传送门:
http://docs.unity3d.com/Manual/Raycasters.html
如果场景中只有一个射线检测源:When a Raycaster is present and enabled in the scene it will be used by the EventSystem whenever a query is issued from an InputModule.
如果场景中有多个射线检测源:If multiple Raycasters are used then they will all have casting happen against them and the results will be sorted based on distance to the elements.
四、响应事件
1、输入模块可以检测到的事件
StandaloneInputModule和TouchInputModule两个组件会检测一些输入操作,以事件的方式(message系统)通知目标对象,那么这两个组件支持的事件主要有以下:
- IPointerEnterHandler - OnPointerEnter - Called when a pointer enters the object
- IPointerExitHandler - OnPointerExit - Called when a pointer exits the object
- IPointerDownHandler - OnPointerDown - Called when a pointer is pressed on the object
- IPointerUpHandler - OnPointerUp - Called when a pointer is released (called on the original the pressed object)
- IPointerClickHandler - OnPointerClick - Called when a pointer is pressed and released on the same object
- IInitializePotentialDragHandler - OnInitializePotentialDrag - Called when a drag target is found, can be used to initialise values
- IBeginDragHandler - OnBeginDrag - Called on the drag object when dragging is about to begin
- IDragHandler - OnDrag - Called on the drag object when a drag is happening
- IEndDragHandler - OnEndDrag - Called on the drag object when a drag finishes
- IDropHandler - OnDrop - Called on the object where a drag finishes
- IScrollHandler - OnScroll - Called when a mouse wheel scrolls
- IUpdateSelectedHandler - OnUpdateSelected - Called on the selected object each tick
- ISelectHandler - OnSelect - Called when the object becomes the selected object
- IDeselectHandler - OnDeselect - Called on the selected object becomes deselected
- IMoveHandler - OnMove - Called when a move event occurs (left, right, up, down, ect)
- ISubmitHandler - OnSubmit - Called when the submit button is pressed
- ICancelHandler - OnCancel - Called when the cancel button is pressed
只要目标对象的mono脚本实现了以上接口,那么输入模块会将检测到的事件通过这些接口通知给目标对象。参考:http://docs.unity3d.com/Manual/SupportedEvents.html
如果你自定义了自己的输入模块,那么以上这些事件肯定是不能用的了。
2、接收输入事件的方式
1)、自行继承接口实现监听
在mono脚本中继承输入模块提供的事件接口,如下图。接口的定义方式也可以查下官方手册,http://docs.unity3d.com/ScriptReference/EventSystems.IBeginDragHandler.html 这边有每一个接口的定义方式,放心大胆地点进去。另外,添加ObjChooseEvent组件的对象,一定要有Collider哦。

2)、 通过EventTrigger组件监听事件
这是一个官方组件。在需要监听事件的对象上,挂上这个组件,然后在Inspector面板展开配置,你会看到这个组件提供了所有输入模块支持的事件类型的监听,如下图。

这种方式的优点是,当你选中一个你要监听的类型,你可以为这个事件类型添加多个监听接口,统一管理,可以清楚的知道到底哪些地方响应了这个事件呢。如果是继承Interface的方式,它将会分散在N个脚本里,一旦出现问题,那查起来一定会很酸爽。
但是这种通过配置的方式,一旦项目多人协作,项目的复杂度起来,这种拖来拽去的配置终究是会有很多问题的,比如某个组件删除,比如响应接口改了个名字~~都会导致配置丢失,而问题又不能及时发现。又或者程序的监听接口因为某些条件而不同。所以也许你会需要第三种方式。
3)、动态添加EventTrigger组件或者修改组件

其实http://www.cnblogs.com/zou90512/p/3995932.html 这位同学的博客对这三种方法都做了很详细的说明。
只不过EventTrigger对外提供的接口不是很友好,导致我们需要添加一个监听,仿佛绕了N了山路弯弯,看着就心情不愉快……反而是这位博主后面说的Button的Click事件的实现方式有点意思……如果项目有需要,也许我们也可以这么做……
五、EventSystem组件提供的一些有意思的接口
其实文档都有http://docs.unity3d.com/ScriptReference/EventSystems.EventSystem.html 只是也许你没有注意。
点击EventSystem对象,你可以看到运行时候的一些详细数据:

变量:
firstSelectedGameObject:这个值可以在面板设置,如果你需要游戏在启动的时候自动选中某个对象,需要鼠标的那一下点击。
currentSelectedGameObject:当前选中的对象,你可以通过这个值判断当前是否鼠标点击在对象上,因为也许你有拖动摄像机的功能,但是你又不喜欢点击某些对象的时候这个功能又被响应,所以通过这个变量判断是一个很好的办法。
接口:
IsPointerOverGameObject:当前鼠标是否在事件系统可以检测的对象上。
SetSelectedGameObject:这个接口也许你会忽略,但是它很棒。因为你点击场景对象的时候,如果不调用这个接口,你的对象是收不到OnSelect事件的,currentSelectedGameObject的值也不会被设置的,必须在点击事件里调用这个接口设置选中对象!
Ex:
public void OnPointerClick(PointerEventData eventData)
{
print ("OnPointerClick...");
currEvent.SetSelectedGameObject(gameObject);
}
不用在场景里找EventSystem对象,EventSystem组件有一个current静态变量,它就是你要的对象,直接EventSystem.current即可使用。
///监听脚本
*********************************************************************************/
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
using UnityEngine.EventSystems;
public class EventDo : MonoBehaviour, IPointerEnterHandler, IPointerExitHandler, IPointerDownHandler, IPointerUpHandler, IPointerClickHandler,
IBeginDragHandler, IInitializePotentialDragHandler, IDragHandler, IEndDragHandler, IDropHandler, IUpdateSelectedHandler, IScrollHandler,
ISelectHandler, IDeselectHandler, IMoveHandler, ISubmitHandler, ICancelHandler
{
public delegate void VoidDelegate(GameObject go, PointerEventData eventData);
public delegate void BaseEventDataDelegate(GameObject go, BaseEventData eventData);
public delegate void AxisEventDataDelegate(GameObject go, AxisEventData eventData);
public VoidDelegate onClick;
public VoidDelegate onDown;
public VoidDelegate onUp;
public VoidDelegate onEnter;
public VoidDelegate onExit;
public VoidDelegate onBeginDrag;//开始拖动
public VoidDelegate onDrag;//拖动
public VoidDelegate onDrop;//放下
public VoidDelegate onEndDrag;//结束拖动
public VoidDelegate onInitializePotentialDrag;//没拖动
public VoidDelegate onScroll;//滑动
public BaseEventDataDelegate onSelect;//选中
public BaseEventDataDelegate onDeselect;//不选中
public BaseEventDataDelegate onUpdateSelect;//选中状态
public BaseEventDataDelegate onCancel;//取消
public BaseEventDataDelegate onSubmit;//确认
public AxisEventDataDelegate onMove;//鼠标移动
public static EventDo Get(GameObject go)
{
if (null == go)
{
Debug.Log(go + "幺幺零 this NULL");
return null;
}
EventDo listener = go.GetComponent<EventDo>();
if (listener == null) listener = go.AddComponent<EventDo>();
return listener;
}
/// <summary>
/// 点击
/// </summary>
/// <param name="eventData"></param>
public void OnPointerClick(PointerEventData eventData)
{
if (onClick != null) onClick(gameObject, eventData);
}
public void OnPointerDown(PointerEventData eventData)
{
if (onDown != null) onDown(gameObject, eventData);
}
public void OnPointerUp(PointerEventData eventData)
{
if (onUp != null) onUp(gameObject, eventData);
}
public void OnPointerEnter(PointerEventData eventData)
{
if (onEnter != null) onEnter(gameObject, eventData);
}
public void OnPointerExit(PointerEventData eventData)
{
if (onExit != null) onExit(gameObject, eventData);
}
/// <summary>
/// 拖动
/// </summary>
/// <param name="eventData"></param>
public void OnBeginDrag(PointerEventData eventData)
{
if (onBeginDrag != null) onBeginDrag(gameObject, eventData);
}
public void OnDrag(PointerEventData eventData)
{
if (onDrag != null) onDrag(gameObject, eventData);
}
public void OnDrop(PointerEventData eventData)
{
if (onDrop != null) onDrop(gameObject, eventData);
}
public void OnEndDrag(PointerEventData eventData)
{
if (onEndDrag != null) onEndDrag(gameObject, eventData);
}
public void OnInitializePotentialDrag(PointerEventData eventData)
{
if (onInitializePotentialDrag != null) onInitializePotentialDrag(gameObject, eventData);
}
public void OnScroll(PointerEventData eventData)
{
if (onScroll != null) onScroll(gameObject, eventData);
Debug.Log("OnScroll");
}
/// <summary>
/// 选中
/// </summary>
/// <param name="eventData"></param>
public void OnSelect(BaseEventData eventData)
{
if (onSelect != null) onSelect(gameObject, eventData);
}
public void OnDeselect(BaseEventData eventData)
{
if (onDeselect != null) onDeselect(gameObject, eventData);
}
public void OnUpdateSelected(BaseEventData eventData)
{
if (onUpdateSelect != null) onUpdateSelect(gameObject, eventData);
}
/// <summary>
/// OnMove
/// </summary>
/// <param name="eventData"></param>
public void OnMove(AxisEventData eventData)
{
if (onMove != null) onMove(gameObject, eventData);
}
/// <summary>
/// OnSubmit
/// </summary>
/// <param name="eventData"></param>
public void OnCancel(BaseEventData eventData)
{
if (onCancel != null) onCancel(gameObject, eventData);
}
public void OnSubmit(BaseEventData eventData)
{
if (onSubmit != null) onSubmit(gameObject, eventData);
}
}
//UGUI穿透3D世界判断
public bool isPointUI(){
PointerEventData eventDataCurrnt = new PointerEventData (EventSystem.current);
eventDataCurrnt .position = new Vector2(Input.mousePosition.x,Input.mousePosition.y);
List<RaycastResult> results= new List<RaycastResult>();
EventSystem.current.RaycastAll(eventDataCurrnt ,results);
return results.Count >=1;//Panel自带一个RaycastResult
}
/// <summary>
/// Whether touch down or mouse button down over UI GameObject.
/// </summary>
public static bool IsClickDownOverUI()
{
#if UNITY_EDITOR
if (Input.GetMouseButtonDown(0))
#else
if (Input.touchCount == 1 && Input.GetTouch(0).phase == TouchPhase.Began)
#endif
{
#if UNITY_EDITOR
// if (EventSystem.current.IsPointerOverGameObject())
#else
if (EventSystem.current.IsPointerOverGameObject(Input.GetTouch(0).fingerId))
#endif
{
return true;
}
}
return false;
}
/// <summary>
/// Whether touch up or mouse button up over UI GameObject.
/// </summary>
public static bool IsClickUpOverUI()
{
#if UNITY_EDITOR
if (Input.GetMouseButtonUp(0))
#else
if (Input.touchCount == 1 && Input.GetTouch(0).phase == TouchPhase.Ended)
#endif
{
#if UNITY_EDITOR
// if (EventSystem.current.IsPointerOverGameObject())
#else
if (EventSystem.current.IsPointerOverGameObject(Input.GetTouch(0).fingerId))
#endif
{
return true;
}
}
return false;
}
UI EventSystem事件监听的更多相关文章
- UI事件监听的击穿
什么是UI事件监听的击穿 在游戏视图中,有两个UI界面叠在一起的时候,单击一个空白处,却触发了被覆盖在下层了UI界面中的单击事件,这就是单击击穿了上层界面. 假设场景中放置了一个箱子,单击箱子会触发一 ...
- .NET事件监听机制的局限与扩展
.NET中把“事件”看作一个基本的编程概念,并提供了非常优美的语法支持,对比如下C#和Java代码可以看出两种语言设计思想之间的差异. // C#someButton.Click += OnSomeB ...
- NavigationView的头部的事件监听
现在App的UI设计中Drawerlayout+NavigationView是一个比较常用的设计了,而以前我一般只是在Navigation中的menu(即下部的item中)添加事件监听,而今天碰到一个 ...
- 百度编辑器的内容改变事件监听bug
先贴上我的初始化代码,可能是用法问题冤枉了百度编辑器,如果是我的用法有问题欢迎大侠们指正 <!DOCTYPE type> <html> <head> <met ...
- 利用JavaFx开发RIA桌面应用-事件监听
1 事件监听 最近利用javaFX开发桌面客户端,碰到需要给各种UI控件添加事件监听,在这里做一个简单的小结,供日后参考. 2 分类处理 在JavaGUI 和Android中,事件通常通过实现list ...
- React.js 小书 Lesson9 - 事件监听
作者:胡子大哈 原文链接:http://huziketang.com/books/react/lesson9 转载请注明出处,保留原文链接和作者信息. 在 React.js 里面监听事件是很容易的事情 ...
- Java GUI 事件监听
现在使用的仍是AWT的事件模型.涉及到3类对象: Event Source:事件源,即事件发生所在的组件 Event:事件,封装了此次事件的相关信息 Event Listener:事件监听器,监听事件 ...
- WebView使用详解(二)——WebViewClient与常用事件监听
登录|注册 关闭 启舰 当乌龟有了梦想…… 目录视图 摘要视图 订阅 异步赠书:Kotlin领衔10本好书 免费直播:AI时代,机器学习如何入门? 程序员8 ...
- springBoot高级:自动配置分析,事件监听,启动流程分析,监控,部署
知识点梳理 课堂讲义 02-SpringBoot自动配置-@Conditional使用 Condition是Spring4.0后引入的条件化配置接口,通过实现Condition接口可以完成有条件的加载 ...
随机推荐
- ACM-ICPC2018沈阳网络赛 Lattice's basics in digital electronics(模拟)
Lattice's basics in digital electronics 44.08% 1000ms 131072K LATTICE is learning Digital Electron ...
- CLR via C# 第五章学习记录(更新中)
1.设置全局溢出检查,项目属性->生成->高级->检测运算上溢/下溢 2.局部使用溢出检测 Byte b = ; b = ));// 不检测溢出 checked// 检测溢出代码段 ...
- Scrapy:Python的爬虫框架【转摘】
网络爬虫,是在网上进行数据抓取的程序,使用它能够抓取特定网页的HTML数据.虽然我们利用一些库开发一个爬虫程序,但是使用框架可以大大提高效率,缩短开发时间.Scrapy是一个使用Python编写的,轻 ...
- python读写xlsx
1使用openpyxl库读写excel xlrd和xlwt处理的是xls文件,单个sheet最大行数是65535,如果有更大需要的,建议使用openpyxl函数,最大行数达到1048576. 如果数 ...
- 2018杭电多校第三场1003(状态压缩DP)
#include<bits/stdc++.h>using namespace std;const int mod =1e9+7;int dp[1<<10];int cnt[1& ...
- ue4 tags 与 cast
父类设置tags,子类默认自动添加这个tags 子类可以强行删除父类的tags,这时如果把子类cast为父类,一样找不到这个tags 综上,要找到一个actor的tags,那么这个actor上就一定要 ...
- codevs3002 石子归并3
3002 石子归并 3 题目描述 Description 有n堆石子排成一列,每堆石子有一个重量w[i], 每次合并可以合并相邻的两堆石子,一次合并的代价为两堆石子的重量和w[i]+w[i+1].问安 ...
- express使用session会话
前几周在学习nodejs基础,发现nodejs没有session会话变量,导致登录注册实现多了很多步骤.现在记录一下express中会话变量用法 安装express-session方法: 1.cnpm ...
- jar包冲突问题
这两天在启动一个新项目的时候,项目一直启动不了,报StackOverFlow; java.util.concurrent.ExecutionException: java.lang.StackOver ...
- 4.高级数据过滤 ---SQL
一.AND操作符 要通过不止一个列进行过滤,可以使用A ND操作符给WHERE子句附加条件. SELECT prod_id, prod_price, prod_name FROM Products ; ...