相关组件和类

EventSystem



1.负责InputModule的切换(因为现在游戏大部分都只有一个StanaloneInputModule,所以切换这部分可以先不考虑)。

2.负责InputModule的激活与反激活。

3.负责Tick整个事件系统。

4.更新InputModule,处理失焦和记录鼠标位置。

5.记录一个Selected对象。

StandaloneInputModule



1.处理输入的鼠标或触摸事件,进行事件的分发。

2.激活和反激活时负责初始化(选择对象,鼠标位置)和清理无效数据(选择对象、pointerData)。

3.不直接使用Input获取数据,而使用一个MonoBehaviour进行封装,提供切换Input的能力(例如游戏进入了反转模式,点左下角时希望右上角有反应。那么重写一个对应的脚本,在进入这个模式时切换Input脚本就可以)。

Raycaster





1.找到所有被射线检测成功的对象,选排序后第一个对象进行事件分发。

Input类

1.负责获取和封装外部的输入信息,如点击、重力感应等。

2.BaseInput提供和Input类一样的能力,是对Input对象的封装,接口名字都一样,方便输入系统的切换。

Touch类

1.Touch类是一个Touch行为(在屏幕上按下,抬起的过程算一个Touch行为)某一时刻的数据。

2.Touch类包含的信息。

主要分3部分:

<1>每一个Touch行为从开始到结束,有一个唯一Id

<2>当前这个Touch行为所处在的阶段,一共5个阶段

    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的情况。

所以在将GetTouch获取的数据作为EventSystem的输入数据时,需要将这些特殊情况考虑进去。

运行流程

总体流程

一次StandaloneInputModule.Process处理流程

以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作为举例。

GraphicRaycaster的射线检测

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的特殊情况处理这些可以先略过,把握住核心思路之后再看这些部分。

Unity事件系统EventSystem简析的更多相关文章

  1. Unity5中新的Shader体系简析

    一.Unity5中新的Shader体系简析 Unity5和之前的书写模式有了一定的改变.Unity5时代的Shader Reference官方文档也进一步地变得丰满. 主要需要了解到的是,在原来的Un ...

  2. Entitas实现简析

    Entitas实现简析   这里主要讲Entitas的执行原理,不讲Entitas的代码生成方面. ECS简介   ECS(实体-组件-系统)是一种常用于游戏开发的架构模式.   实体: 实体只是一个 ...

  3. 简析.NET Core 以及与 .NET Framework的关系

    简析.NET Core 以及与 .NET Framework的关系 一 .NET 的 Framework 们 二 .NET Core的到来 1. Runtime 2. Unified BCL 3. W ...

  4. 简析 .NET Core 构成体系

    简析 .NET Core 构成体系 Roslyn 编译器 RyuJIT 编译器 CoreCLR & CoreRT CoreFX(.NET Core Libraries) .NET Core 代 ...

  5. RecycleView + CardView 控件简析

    今天使用了V7包加入的RecycleView 和 CardView,写篇简析. 先上效果图: 原理图: 这是RecycleView的工作原理: 1.LayoutManager用来处理RecycleVi ...

  6. Java Android 注解(Annotation) 及几个常用开源项目注解原理简析

    不少开源库(ButterKnife.Retrofit.ActiveAndroid等等)都用到了注解的方式来简化代码提高开发效率. 本文简单介绍下 Annotation 示例.概念及作用.分类.自定义. ...

  7. PHP的错误报错级别设置原理简析

    原理简析 摘录php.ini文件的默认配置(php5.4): ; Common Values: ; E_ALL (Show all errors, warnings and notices inclu ...

  8. Android 启动过程简析

    首先我们先来看android构架图: android系统是构建在linux系统上面的. 所以android设备启动经历3个过程. Boot Loader,Linux Kernel & Andr ...

  9. Android RecycleView + CardView 控件简析

    今天使用了V7包加入的RecycleView 和 CardView,写篇简析. 先上效果图: 原理图: 这是RecycleView的工作原理: 1.LayoutManager用来处理RecycleVi ...

随机推荐

  1. 12627 - Erratic Expansion——[递归]

    Piotr found a magical box in heaven. Its magic power is that if you place any red balloon inside it ...

  2. P1070 东风谷早苗

    题目描述 在幻想乡,东风谷早苗是以高达控闻名的高中生宅巫女.某一天,早苗终于入手了最新款的钢达姆模型.作为最新的钢达姆,当然有了与以往不同的功能了,那就是它能够自动行走,厉害吧(好吧,我自重).早苗的 ...

  3. Nginx的三种应用场景介绍

    配置虚拟主机 就是在一台服务器启动多个网站. 如何区分不同的网站: 1.域名不同 2.端口不同 1.1. 通过端口区分不同虚拟机 Nginx的配置文件: /usr/local/nginx/conf/n ...

  4. 基于JWT的Token登录认证(一)

    1.JWT简介 JSON Web Token(缩写 JWT),是目前最流行的跨域认证解决方案. session登录认证方案:用户从客户端传递用户名.密码等信息,服务端认证后将信息存储在session中 ...

  5. Vue学习笔记-目录结构

    1.采用脚手架构建的项目基本目录结构 可能会有些许差别,但是大致基本目录都差不多 2.项目入口(index.html,main.js,App.vue) 一般情况下,我们都习惯性将 index.html ...

  6. WebGPU学习(十):介绍“GPU实现粒子效果”

    大家好,本文介绍了"GPU实现粒子效果"的基本思想,并推荐了相应的学习资料. 本文学习webgpu-samplers->computeBoids示例,它展示了如何用compu ...

  7. 本地项目推送到coding

      当我们本地新建了一个项目,需要放到coding上维护时,按照下面步骤即可做到. 1.先在coding上新建一个项目,并完成初始化. 2.进入到本地项目的目录下 //初始化本地仓库 a:git in ...

  8. DEVOPS技术实践_07:Jenkins 管道工作

    一 简介 由于在公司构建很多工作,都是使用的maven工作构建的,这种构建方式很大缺点就是所有的工作都需要一步一步的配置,当项目较多,需求变动时,需要很大的精力去修改配置,很费劲 Pipeline即为 ...

  9. C# event 事件

    事件第二篇:https://www.cnblogs.com/FavoriteMango/p/11731485.html 曾经面试碰到一道设计题: 现有一个人,一群鸟,人有一把手枪,当人开枪时,所有的鸟 ...

  10. `docker数据持久化volume和bind mounts两种方式

    将数据从宿主机到容器的三种方式: ,volumes:docker管理宿主机文件系统的一部分(/var/lib/docker/volumes)保存数据的最佳方式 ,bind mounts 将宿主机上的任 ...