本文共 4000 字,大约阅读时间需要 13 分钟。
当我们在场景中创建任意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负责确定目标对象,目标对象负责接收事件并处理,然后一个完整的事件系统就有了。
根据第一节中的说明,EventSystem和BaseInputModule是粘在一个对象上的,这两个模块在EventSystem对象上可以直接看到。那么,BaseRaycaster模块呢。
其实射线检测,肯定是从摄像机发起的,那么BaseRaycaster模块也一定和摄像机关系一定不简单。 对于UI模块,在Canvas对象下我们可以看到GraphicRaycaster组件。如果Canvas的渲染模式是SceenSpace-Overlay,那么我们是看不到Camera组件的。所以应该是GraphicRaycaster会对UI不同的渲染模式做特殊处理。因为有GraphicRaycaster组件的原因,Canvas上的所有UI对象,都可以接受输入模块发出的事件,具体事件的处理请看下面UGUI添加事件的方式。场景中的非UI对象,如果想要接收输入模块的事件,一样的道理,也需要给摄像机挂上一个射线检测组件。PhysicsRaycaster, Physics2Draycaster这两个组件分别是用于3D和2D的场景。当然,还需要场景的对象挂了collider射线才检测的到。
其实官方对射线检测也是做了说明的,如果不详读手册是不会发现的,这里是传送门: 如果场景中只有一个射线检测源: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.我们以PointerClick为例, 用于某点点击事件, 其他事件类型可以根据相同的办法调用。之所以使用PointerClick为例,是因为相比其他事件类型,它还有一个特殊的实现方式。
1.创建ObjectClick脚本,继承MonoBehaviour和IpointerClickHandler接口:
using System;using System.Collections;using System.Collections.Generic;using UnityEngine;using UnityEngine.EventSystems;public class ObjectClick : MonoBehaviour, IPointerClickHandler { public void OnPointerClick(PointerEventData eventData) { Debug.Log("$click test"); //throw new NotImplementedException(); }}
2.创建一个名为testObject的空对象,并将ObjectClick附加到testObject上。
3.启动并点击testObject。1.创建一个UI控件如Panel、Image等,命名为UIPanel。
2.创建c#脚本TestClick,并附加到UIPanel上。using System.Collections;using System.Collections.Generic;using UnityEngine;public class TestClick : MonoBehaviour{ public void onTestClick() { Debug.Log("$$onTestClick"); }}
3.控件添加EventTrigger组件,” Add New” -> 选择” PointerClick”。将UIPanel对象拖拽到触发者位置。然后点击”No Function”选择我们写在Test脚本中的OnTestClick事件。
4.启动并点击UIPanel。在日常的开发中,可能需要动态的变更绑定的事件。那么我们如何使用代码控制绑定触发事件呢?
1.代码控制。ScriptControl.cs脚本如下:
public class ScriptControl : MonoBehaviour { // Use this for initialization void Start () { EventTrigger trigger = this.gameObject.GetComponent(); if(trigger == null) { trigger = this.gameObject.AddComponent (); } //实例化triggers trigger.triggers = new List (); //定义需要绑定的事件类型。并设置回调函数 EventTrigger.Entry entry = new EventTrigger.Entry(); //设置事件类型 entry.eventID = EventTriggerType.PointerClick; //设置回调函数 entry.callback = new EventTrigger.TriggerEvent(); UnityAction callback = new UnityAction (onScriptControl); entry.callback.AddListener(callback); trigger.triggers.Add(entry); } public void onScriptControl(BaseEventData eventData) { Debug.Log("$onScriptControl test"); }}
2.创建一个GameObject对象,将脚本绑定到该对象。
3.运行并点击该对象。针对Click事件还存在一种特殊方式:ugui系统中官方提供了一种Button控件。Button封装了官方提供的一套OnClick事件。操作完全类似于方式二。便不详述了。