1.负责InputModule的切换(由于如今游戏大部分都只有一个StanaloneInputModule,因此切换这部分能够先不考虑)。
2.负责InputModule的激活与反激活。
3.负责Tick整个事件系统。
4.更新InputModule,处理失焦和记录鼠标位置。
5.记录一个Selected对象。canvas
1.处理输入的鼠标或触摸事件,进行事件的分发。
2.激活和反激活时负责初始化(选择对象,鼠标位置)和清理无效数据(选择对象、pointerData)。
3.不直接使用Input获取数据,而使用一个MonoBehaviour进行封装,提供切换Input的能力(例如游戏进入了反转模式,点左下角时但愿右上角有反应。那么重写一个对应的脚本,在进入这个模式时切换Input脚本就能够)。缓存
1.找到全部被射线检测成功的对象,选排序后第一个对象进行事件分发。函数
1.负责获取和封装外部的输入信息,如点击、重力感应等。
2.BaseInput提供和Input类同样的能力,是对Input对象的封装,接口名字都同样,方便输入系统的切换。测试
1.Touch类是一个Touch行为(在屏幕上按下,抬起的过程算一个Touch行为)某一时刻的数据。
2.Touch类包含的信息。
主要分3部分:
<1>每个Touch行为从开始到结束,有一个惟一Id
<2>当前这个Touch行为所处在的阶段,一共5个阶段3d
public enum TouchPhase { Began = 0,//按下 Moved = 1,//正在移动 Stationary = 2,//静止,但没有结束 Ended = 3,//离开 Canceled = 4//黑屏等其余因素致使的结束 }
<3>当前位置,移动距离等信息。
3.Touch行为在绝大多数状况下都是由Began开始,Ended或Canceled结束。
可是咱们并无监听Touch阶段修改的能力(没找到相关的接口),只能经过Input.GetTouch接口在某一时间点(如update中)来循环获取Touch信息。而后经过FingerId,phase来还原一个完整的Touch行为。但这样会有一个问题,经过GetTouch获取的Touch信息多是不完整的,如:
1.在一个Touch拖动的过程当中开始循环调用GetTouch,那么咱们获得的Touch就会不是由Began开始的,而是由Moved开始的。
2.在帧数很低,且在一帧内连续点击屡次时,可能出现相同FingerId的Touch没有经过Ended结束,而后又直接Began的状况。code
因此在将GetTouch获取的数据做为EventSystem的输入数据时,须要将这些特殊状况考虑进去。orm
以Touch举例
tips:
1.PointerEventData能够理解为对Touch行为的进一步封装,记录了Touch行为信息,如开始位置等,且在此基础上增长了射线检测结果等信息。每个PointerEventData的生命周期基本上和Touch行为相同。由pressed开始(对应Touch的Began,如缓存中没有对应fingerId的PointerEventData,则新建一个),released结束(对应Touch的Ended或Canceled,从缓存中移除该PointerEventData)。固然对于特殊状况要特殊处理(如上面提到的没有由Began开始的Touch等)。
2.Process主要的工做就是维护PointerEventData的数据,同时根据PointerEventData发出事件。
3.对事件脚本的查找是向上查找的,如C是B的子节点,B是A的子节点。射线检测的结果是C。那么会按C->B->A的顺序去查找可响应该事件的对象。对象
这里简单说一下GraphicRaycaster做为举例。blog
1.GraphicRaycaster是检测同gameobject下canvas中包含的全部Graphic元素是否被射线击中的脚本。
2.Graphic在Onenable,OnDisable,OnBeforeTransformParentChanged,OnTransformParentChanged,OnCanvasHierarchyChanged这几个时间点把本身加入或移除一个以canvas为键值的graphic集合的字典中。
3.具体检测过程:
<1>先从缓存中获取该Canvas下全部的Graphic对象。
<2>处理多显示器问题,先作一波坐标转换。
<3>根据BlockingObjects,对游戏中的3D或2D对象作一次射线检测,保存离相机最近的对象的距离,以后用于对结果的过滤。
<4>先经过RectangleContainsScreenPoint判断射线击中点是否在Graphic的RectTransform中,再经过Graphic自身的Raycast函数进行进一步的检测(检测CanvasGroup,Active状态等)。
<5>最后再作一些测试,如反转剔除,遮挡测试等。排序
1.游戏中全部的Raycaster都进行一次射线检测,获取当前射线击中的全部物体,统一进行排序,选排序后的第一个对象做为射线检测的结果。
2.排序规则
不一样Racaster下:
camera.depth
Raycaster.sortOrderPriority 针对ScreenSpaceOverlay
Raycaster.renderOrderPriority 针对ScreenSpaceOverlay
相同Racaster下:
sortingLayer
sortingOrder
depth
distance
index
在手机上按这个方式操做。
其中A上的脚本继承了IPointerEnterHandler,IPointerExitHandler,IPointerDownHandler,IPointerUpHandler,IPointerClickHandler,IDragHandler接口。
B上的脚本继承了IPointerEnterHandler,IPointerExitHandler, IDropHandler接口。
这一系列操做中事件的触发主要依赖于对这几个变量的设置和断定。
①pointerPress:按下时射线击中对象或向上查找的某一挂有继承了IPointerDownHandler或IPointerClickHandler脚本的对象。
②pointerDrag:按下时射线击中对象或向上查找的某一挂有继承了IDragHandler脚本的对象。
③pointerEnter:当前Touch位置发出的射线击中的第一个物体。
④pointerCurrentRaycast:当前位置发出射线的计算结果,包括当前击中的物体等信息。
1.按下
一次完整的touch行为的开始,新生成一个PointerEventData加入缓存中
<1>记录pressPosition,用于开始拖动的断定。
<2>由于当前按下的位置在A上,因此设置pointerEnter为A。且A上的脚本继承了IPointerEnterHandler接口,因此执行A上的PointerEnter函数。
<3>由于当前按下的位置在A上,且A上的脚本继承了IProinterDownHandler、IPointerDragHandler接口,因此设置pointerPress和pointerDrag为A。用于对后续抬起时的Click等事件作断定。同时执行PointerDown函数。
2.拖动
在拖动距离超过Threshold以前什么都不作。超事后开始不停的执行pointerDrag(A)对象上的OnDrag函数。
3.离开A
在离开A时,pointerEnter对象由A变为了null,因此执行pointerEnter(A)对象上的PointerExit函数。
4.拖动
同2。
5.进入B
在进入B时,pointerEnter对象由null变为了B,因此执行pointerEnter(B)上的PointerEnter函数。
6.抬起
touch行为的结束,从缓存中移除这个PointerEventData
<1>执行pointerPress(A)对象上的PointerUp函数。
<2>因为抬起时射线击中的对象是B,而不是pointerPress(A)对象。因此不执行pointerPress(A)对象上的OnClick函数,而执行B上的OnDrop函数。
<3>执行B上的PointExit函数。
1.简单来讲EventSysetm的处理过程就是循环获取Touch数据。根据Touch数据来推测完整的Touch行为,来维护对应的PointerEventData,在此基础上进行事件的计算和分发。 2.EventSystem的代码量比较少但特殊处理的地方还挺多的,毕竟一个完善的系统,全部状况都得考虑到位。因此阅读代码时能够先看最核心的Process相关的代码(Touch和Mouse先选一个),像InputModule切换、BaseInput的处理、Touch的特殊状况处理这些能够先略过,把握住核心思路以后再看这些部分。