UGUI事件模块

24k words

Desc:

Unity3D公开了大部分UGUI系统源码,除了渲染和网格合并算法外(Canvas和CanvasRenderer),UGUI内核源码将UGUI系统分为输入事件、动画、核心渲染三部分

输入事件源码:

UGUI系统将输入事件模块分为四部分,即事件数据模块(EventData)、输入事件捕获模块(InputModules)、射线碰撞检测模块(Raycasters)、事件逻辑处理及回调模块(EventHandle…)

事件数据模块:

作用:
主要定义并且存储了事件发生时的位置、与事件对应的部分、事件的位移大小、触发事件的输入类型及事件的设备信息。事件数据模块主要是为了获取数据,提供数据服务

BaseEventData:
作为PointerEventData和AxisEventData的基类,提供了几个常用的接口,其子类PointerEventData是最常用的事件数据。代码并不多,大部分都是数据定义

代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
/// <summary>
/// A class that contains the base event data that is common to all event types in the new EventSystem.
/// </summary>
public class BaseEventData : AbstractEventData
{
private readonly EventSystem m_EventSystem;
public BaseEventData(EventSystem eventSystem)
{
m_EventSystem = eventSystem;
}

/// <summary>
/// >A reference to the BaseInputModule that sent this event.
/// </summary>
public BaseInputModule currentInputModule
{
get { return m_EventSystem.currentInputModule; }
}

/// <summary>
/// The object currently considered selected by the EventSystem.
/// </summary>
public GameObject selectedObject
{
get { return m_EventSystem.currentSelectedGameObject; }
set { m_EventSystem.SetSelectedGameObject(value, this); }
}
}

AxisEventData:
提供滚轮的方向信息

代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/// <summary>
/// Event Data associated with Axis Events (Controller / Keyboard).
/// </summary>
public class AxisEventData : BaseEventData
{
/// <summary>
/// Raw input vector associated with this event.
/// </summary>
public Vector2 moveVector { get; set; }

/// <summary>
/// MoveDirection for this event.
/// </summary>
public MoveDirection moveDir { get; set; }

public AxisEventData(EventSystem eventSystem)
: base(eventSystem)
{
moveVector = Vector2.zero;
moveDir = MoveDirection.None;
}
}

PointerEventData:
作为数据类的核心类,存储了大部分事件系统逻辑需要的数据,包括按下的位置、松开和按下的时间差、拖拽的位移差、点击的物体等,承载了所有输入事件需要的数据。意义是存储数据并为逻辑部分做好准备

输入事件捕获模块

由BaseInputModule、PointerInputModule、StandaloneInputModule、TouchInputModule四个类组成

BaseInputModule

BaseInputModule类是抽象类,提供必需的空接口和基本变量

代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
[NonSerialized]
protected List<RaycastResult> m_RaycastResultCache = new List<RaycastResult>();

private AxisEventData m_AxisEventData;

private EventSystem m_EventSystem;
private BaseEventData m_BaseEventData;

protected BaseInput m_InputOverride;
private BaseInput m_DefaultInput;

...

protected override void OnEnable()
{
base.OnEnable();
m_EventSystem = GetComponent<EventSystem>();
m_EventSystem.UpdateModules();
}

protected override void OnDisable()
{
m_EventSystem.UpdateModules();
base.OnDisable();
}

/// <summary>
/// Process the current tick for the module.
/// </summary>
public abstract void Process();

...

/// <summary>
/// Should the module be activated.
/// </summary>
public virtual bool ShouldActivateModule()
{
return enabled && gameObject.activeInHierarchy;
}

/// <summary>
/// Called when the module is deactivated. Override this if you want custom code to execute when you deactivate your module.
/// </summary>
public virtual void DeactivateModule()
{}

/// <summary>
/// Called when the module is activated. Override this if you want custom code to execute when you activate your module.
/// </summary>
public virtual void ActivateModule()
{}

/// <summary>
/// Update the internal state of the Module.
/// </summary>
public virtual void UpdateModule()
{}

/// <summary>
/// Check to see if the module is supported. Override this if you have a platform specific module (eg. TouchInputModule that you do not want to activate on standalone.)
/// </summary>
/// <returns>Is the module supported.</returns>
public virtual bool IsModuleSupported()
{
return true;
}

PointerInputModule

在BaseInputModule的基础上拓展了关于点位置的输入逻辑,增加了输入的类型和状态

1
2
3
4
5
protected bool GetPointerData(int id, out PointerEventData data, bool create)
protected virtual MouseState GetMousePointerEventData()
protected virtual MouseState GetMousePointerEventData(int id)
protected virtual void ProcessMove(PointerEventData pointerEvent)
protected virtual void ProcessDrag(PointerEventData pointerEvent)

StandaloneInputModule和TouchInputModule类继承PointerInputModule类,从父类开始向不同的方向拓展

StandaloneInputModule

向标准键盘鼠标输入方向拓展

代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
当有触碰事件发生会执行这部分代码,ProcessMouseEvent\

/// <summary>
/// Process all mouse events.
/// </summary>
protected void ProcessMouseEvent(int id)
{
var mouseData = GetMousePointerEventData(id);
var leftButtonData = mouseData.GetButtonState(PointerEventData.InputButton.Left).eventData;

m_CurrentFocusedGameObject = leftButtonData.buttonData.pointerCurrentRaycast.gameObject;

// Process the first mouse button fully
ProcessMousePress(leftButtonData);
ProcessMove(leftButtonData.buttonData);
ProcessDrag(leftButtonData.buttonData);

// Now process right / middle clicks
ProcessMousePress(mouseData.GetButtonState(PointerEventData.InputButton.Right).eventData);
ProcessDrag(mouseData.GetButtonState(PointerEventData.InputButton.Right).eventData.buttonData);
ProcessMousePress(mouseData.GetButtonState(PointerEventData.InputButton.Middle).eventData);
ProcessDrag(mouseData.GetButtonState(PointerEventData.InputButton.Middle).eventData.buttonData);

if (!Mathf.Approximately(leftButtonData.buttonData.scrollDelta.sqrMagnitude, 0.0f))
{
var scrollHandler = ExecuteEvents.GetEventHandler<IScrollHandler>(leftButtonData.buttonData.pointerCurrentRaycast.gameObject);
ExecuteEvents.ExecuteHierarchy(scrollHandler, leftButtonData.buttonData, ExecuteEvents.scrollHandler);
}
}

protected void ProcessTouchPress(PointerEventData pointerEvent, bool pressed, bool released)
{
var currentOverGo = pointerEvent.pointerCurrentRaycast.gameObject;

// PointerDown notification
if (pressed)
{
pointerEvent.eligibleForClick = true;
pointerEvent.delta = Vector2.zero;
pointerEvent.dragging = false;
pointerEvent.useDragThreshold = true;
pointerEvent.pressPosition = pointerEvent.position;
pointerEvent.pointerPressRaycast = pointerEvent.pointerCurrentRaycast;

DeselectIfSelectionChanged(currentOverGo, pointerEvent);

if (pointerEvent.pointerEnter != currentOverGo)
{
// send a pointer enter to the touched element if it isn't the one to select...
HandlePointerExitAndEnter(pointerEvent, currentOverGo);
pointerEvent.pointerEnter = currentOverGo;
}

// search for the control that will receive the press
// if we can't find a press handler set the press
// handler to be what would receive a click.
var newPressed = ExecuteEvents.ExecuteHierarchy(currentOverGo, pointerEvent, ExecuteEvents.pointerDownHandler);

// didnt find a press handler... search for a click handler
if (newPressed == null)
newPressed = ExecuteEvents.GetEventHandler<IPointerClickHandler>(currentOverGo);

// Debug.Log("Pressed: " + newPressed);

float time = Time.unscaledTime;

if (newPressed == pointerEvent.lastPress)
{
var diffTime = time - pointerEvent.clickTime;
if (diffTime < 0.3f)
++pointerEvent.clickCount;
else
pointerEvent.clickCount = 1;

pointerEvent.clickTime = time;
}
else
{
pointerEvent.clickCount = 1;
}

pointerEvent.pointerPress = newPressed;
pointerEvent.rawPointerPress = currentOverGo;

pointerEvent.clickTime = time;

// Save the drag handler as well
pointerEvent.pointerDrag = ExecuteEvents.GetEventHandler<IDragHandler>(currentOverGo);

if (pointerEvent.pointerDrag != null)
ExecuteEvents.Execute(pointerEvent.pointerDrag, pointerEvent, ExecuteEvents.initializePotentialDrag);

m_InputPointerEvent = pointerEvent;
}

// PointerUp notification
if (released)
{
// Debug.Log("Executing pressup on: " + pointer.pointerPress);
ExecuteEvents.Execute(pointerEvent.pointerPress, pointerEvent, ExecuteEvents.pointerUpHandler);

// Debug.Log("KeyCode: " + pointer.eventData.keyCode);

// see if we mouse up on the same element that we clicked on...
var pointerUpHandler = ExecuteEvents.GetEventHandler<IPointerClickHandler>(currentOverGo);

// PointerClick and Drop events
if (pointerEvent.pointerPress == pointerUpHandler && pointerEvent.eligibleForClick)
{
ExecuteEvents.Execute(pointerEvent.pointerPress, pointerEvent, ExecuteEvents.pointerClickHandler);
}
else if (pointerEvent.pointerDrag != null && pointerEvent.dragging)
{
ExecuteEvents.ExecuteHierarchy(currentOverGo, pointerEvent, ExecuteEvents.dropHandler);
}

pointerEvent.eligibleForClick = false;
pointerEvent.pointerPress = null;
pointerEvent.rawPointerPress = null;

if (pointerEvent.pointerDrag != null && pointerEvent.dragging)
ExecuteEvents.Execute(pointerEvent.pointerDrag, pointerEvent, ExecuteEvents.endDragHandler);

pointerEvent.dragging = false;
pointerEvent.pointerDrag = null;

// send exit events as we need to simulate this on touch up on touch device
ExecuteEvents.ExecuteHierarchy(pointerEvent.pointerEnter, pointerEvent, ExecuteEvents.pointerExitHandler);
pointerEvent.pointerEnter = null;

m_InputPointerEvent = pointerEvent;
}
}

TouchInputModule

向触摸控板输入方向拓展

代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
针对按下和松开时进行相关的处理

/// <summary>
/// Process all touch events.
/// </summary>
private void ProcessTouchEvents()
{
for (int i = 0; i < input.touchCount; ++i)
{
Touch touch = input.GetTouch(i);

if (touch.type == TouchType.Indirect)
continue;

bool released;
bool pressed;
var pointer = GetTouchPointerEventData(touch, out pressed, out released);

ProcessTouchPress(pointer, pressed, released);

if (!released)
{
ProcessMove(pointer);
ProcessDrag(pointer);
}
else
RemovePointerData(pointer);
}
}

射线碰撞检测模块

主要的工作是从摄像机的屏幕位置上进行射线碰撞检测并获取碰撞结果,将结果返回给事件处理逻辑类,交由事件处理模块处理
主要包含三个类,分别用于2D射线碰撞检测、3D射线碰撞检测、GraphicRaycaster图形射线碰撞检测
2D射线碰撞检测和3D射线碰撞检测相对比较简单,通过射线的方式进行碰撞检测,区别在于2D射线碰撞检测结果里预留了点位进行碰撞检测,以便在后面的碰撞结果排序时,以这个层级顺序为依据进行排序,而3D射线碰撞检测结果则是以距离大小为依据进行排序的

Physics Raycaster
用于检测3D物理元素,并且保存被射线检测到的物体数据,具体逻辑在(管理射线检测)那块写了

Physics2DRaycaster
继承自 PhysicsRaycaster,实现功能和方式基本一致,只不过用于检测2D物体

GraphicRaycaster类为UGUI元素点位检测的类,被放在Core渲染块里,主要针对ScreenSpaceOverlay模式下的输入点位进行碰撞检测,因为这个模式下的检测并不依赖于UI元素,而是通过遍历所有可点击的UGUI元素来进行检测比较,从而判断该响应哪个元素的,因此GraphicRaycaster类是比较特殊

代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
/// <summary>
/// Perform a raycast into the screen and collect all graphics underneath it.
/// </summary>
[NonSerialized] static readonly List<Graphic> s_SortedGraphics = new List<Graphic>();
private static void Raycast(Canvas canvas, Camera eventCamera, Vector2 pointerPosition, IList<Graphic> foundGraphics, List<Graphic> results)
{
// Necessary for the event system
int totalCount = foundGraphics.Count;
for (int i = 0; i < totalCount; ++i)
{
Graphic graphic = foundGraphics[i];

// -1 means it hasn't been processed by the canvas, which means it isn't actually drawn
if (graphic.depth == -1 || !graphic.raycastTarget || graphic.canvasRenderer.cull)
continue;

if (!RectTransformUtility.RectangleContainsScreenPoint(graphic.rectTransform, pointerPosition, eventCamera))
continue;

if (eventCamera != null && eventCamera.WorldToScreenPoint(graphic.rectTransform.position).z > eventCamera.farClipPlane)
continue;

if (graphic.Raycast(pointerPosition, eventCamera))
{
s_SortedGraphics.Add(graphic);
}
}

s_SortedGraphics.Sort((g1, g2) => g2.depth.CompareTo(g1.depth));
totalCount = s_SortedGraphics.Count;
for (int i = 0; i < totalCount; ++i)
results.Add(s_SortedGraphics[i]);

s_SortedGraphics.Clear();
}

所有碰撞检测结果都是RaycastResult类,承载了所有碰撞检测的结果,包括距离,世界点位,屏幕点位,2D层级次序和碰撞物体等,为后面的事件处理提供数据上的依据

1
2
3
4
5
6
7
8
9
10
11
12
RaycastResult.cs

public BaseRaycaster module;
public float distance;
public float index;
public int depth;
public int sortingLayer;
public int sortingOrder;
public Vector3 worldPosition;
public Vector3 worldNormal;
public Vector2 screenPosition;

事件逻辑处理模块

主要集中在EventSystem类中,其他类都只是对它进行辅助
EventInterfaces类、EventTrigger类、EventTriggerType类定义了事件回调函数,ExecuteEvents类编写了所有执行事件的回调接口
IPointerEnterHandler:
void OnPointerEnter(PointerEventData eventData);

IPointerExitHandler:
void OnPointerExit(PointerEventData eventData);

IPointerDownHandler:
void OnPointerDown(PointerEventData eventData);

IPointerUpHandler:
void OnPointerUp(PointerEventData eventData);

IPointerClickHandler:
void OnPointerClick(PointerEventData eventData);

IBeginDragHandler:
void OnBeginDrag(PointerEventData eventData);

IInitializePotentialDragHandler:
void OnInitializePotentialDrag(PointerEventData eventData);

IDragHandler:
void OnDrag(PointerEventData eventData);

IEndDragHandler:
void OnEndDrag(PointerEventData eventData);

IDropHandler:
void OnDrop(PointerEventData eventData);

IScrollHandler:
void OnScroll(PointerEventData eventData);

IUpdateSelectedHandler:
void OnUpdateSelected(BaseEventData eventData);

ISelectHandler:
void OnSelect(BaseEventData eventData);

IDeselectHandler:
void OnDeselect(BaseEventData eventData);

IMoveHandler:
void OnMove(AxisEventData eventData);

ISubmitHandler:
void OnSubmit(BaseEventData eventData);

ICancelHandler:
void OnCancel(BaseEventData eventData);

。。。。

参考链接
EventSystem类基本都是在处理由射线碰撞检测后引起的各类事件。比如,判断事件是否成立。若成立,则发起事件回调,若不成立,则继续论调检查,等待事件的发生.EventSystem本身被设计成事件系统不同模块之间通信的管理者和推进者,主要功能如下:

管理哪个游戏对象被选中

1
2
3
4
5
6
7
8
9
10
11
public void SetSelectedGameObject(GameObject selected, BaseEventData pointer)
{
......//省略部分代码
//通知之前被选中取消选中
ExecuteEvents.Execute(m_CurrentSelected, pointer, ExecuteEvents.deselectHandler);
m_CurrentSelected = selected;
//通知当前物体被选中
ExecuteEvents.Execute(m_CurrentSelected, pointer, ExecuteEvents.selectHandler);
m_SelectionGuard = false;
}

管理正在使用的输入模块

根据需要更新所有输入模块

代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
//系统输入模块
private List<BaseInputModule> m_SystemInputModules = new List<BaseInputModule>();
//当前输入模块
private BaseInputModule m_CurrentInputModule;


protected virtual void Update()
{
//遍历m_SystemInputModules,如果其中的Module不为null,则调用UpdateModule方法
TickModules();

//遍历m_SystemInputModules判断其中的输入模块是否支持当前平台
//如果支持并且可以激活,则将其赋值给当前输入模块并Break
bool changedModule = false;
var systemInputModulesCount = m_SystemInputModules.Count;
for (var i = 0; i < systemInputModulesCount; i++)
{
var module = m_SystemInputModules[i];
if (module.IsModuleSupported() && module.ShouldActivateModule())
{
if (m_CurrentInputModule != module)
{
ChangeEventModule(module);
changedModule = true;
}
break;
}
}

//如果上面没找到符合条件的模块,则使用第一个支持当前平台的模块
if (m_CurrentInputModule == null)
{
for (var i = 0; i < systemInputModulesCount; i++)
{
var module = m_SystemInputModules[i];
if (module.IsModuleSupported())
{
ChangeEventModule(module);
changedModule = true;
break;
}
}
}

//如果当前模块没有发生变化并且当前模块不为空
if (!changedModule && m_CurrentInputModule != null)
m_CurrentInputModule.Process();
}

private void TickModules()
{
var systemInputModulesCount = m_SystemInputModules.Count;
for (var i = 0; i < systemInputModulesCount; i++)
{
if (m_SystemInputModules[i] != null)
m_SystemInputModules[i].UpdateModule();
}
}

管理射线检测

EventSystem中,还有一个非常重要的函数RaycastAll(),主要是获取目标。它被PointerInputModule类调用,大致来说是当鼠标设备可用或触摸板被使用时调用
它首先获取所有的BaseRaycast对象,然后调用它的Raycast方法,用以获取屏幕某个点下的所有目标,最后对得到的结果进行排序,大部分情况都是根据深度(Depth)进行排序,在一些情况下也会使用距离(Distance)、排序顺序(SortingOrder,如果是UI元素则是根据Canvas面板的Sort order值,3D物体默认是0)或者排序层级(Sorting Layer)等作为排序依据。

代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
EventSystem.cs
public void RaycastAll(PointerEventData eventData, List<RaycastResult> raycastResults)
{
raycastResults.Clear();
//获取BaseRaycast对象
var modules = RaycasterManager.GetRaycasters();
var modulesCount = modules.Count;
for (int i = 0; i < modulesCount; ++i)
{
var module = modules[i];
if (module == null || !module.IsActive())
continue;
//调用Raycast方法,
module.Raycast(eventData, raycastResults);
}
raycastResults.Sort(s_RaycastComparer);
}

private static int RaycastComparer(RaycastResult lhs, RaycastResult rhs)
{
if (lhs.module != rhs.module)
{
var lhsEventCamera = lhs.module.eventCamera;
var rhsEventCamera = rhs.module.eventCamera;

// 深度排序
if (lhsEventCamera != null && rhsEventCamera != null && lhsEventCamera.depth != rhsEventCamera.depth)
{
// need to reverse the standard compareTo
if (lhsEventCamera.depth < rhsEventCamera.depth)
return 1;
if (lhsEventCamera.depth == rhsEventCamera.depth)
return 0;

return -1;
}
if (lhs.module.sortOrderPriority != rhs.module.sortOrderPriority)
return rhs.module.sortOrderPriority.CompareTo(lhs.module.sortOrderPriority);

if (lhs.module.renderOrderPriority != rhs.module.renderOrderPriority)
return rhs.module.renderOrderPriority.CompareTo(lhs.module.renderOrderPriority);
}

// 排序顺序
if (lhs.sortingLayer != rhs.sortingLayer)
{
// Uses the layer value to properly compare the relative order of the layers.
var rid = SortingLayer.GetLayerValueFromID(rhs.sortingLayer);
var lid = SortingLayer.GetLayerValueFromID(lhs.sortingLayer);
return rid.CompareTo(lid);
}

/// <summary>
/// The SortingOrder for the hit object.
/// </summary>
/// <remarks>
/// For Graphic elements this will be the values from that graphics Canvas
/// For 3D objects this will always be 0.
/// For 2D objects if a SpriteRenderer is attached to the same object as the hit collider that SpriteRenderer sortingOrder will be used.
/// </remarks>
// public int sortingOrder;
if (lhs.sortingOrder != rhs.sortingOrder)
return rhs.sortingOrder.CompareTo(lhs.sortingOrder);

// comparing depth only makes sense if the two raycast results have the same root canvas (case 912396)
if (lhs.depth != rhs.depth && lhs.module.rootRaycaster == rhs.module.rootRaycaster)
return rhs.depth.CompareTo(lhs.depth);

if (lhs.distance != rhs.distance)
return lhs.distance.CompareTo(rhs.distance);

return lhs.index.CompareTo(rhs.index);
}

BaseRaycaster.cs
/// <summary>
/// Raycast against the scene.
/// </summary>
/// <param name="eventData">Current event data.</param>
/// <param name="resultAppendList">List of hit Objects.</param>
public abstract void Raycast(PointerEventData eventData, List<RaycastResult> resultAppendList);


PhysicsRaycaster.cs
/// <summary>
/// Returns a ray going from camera through the event position and the distance between the near and far clipping planes along that ray.
/// </summary>
/// <param name="eventData">The pointer event for which we will cast a ray.</param>
/// <param name="ray">The ray to use.</param>
/// <param name="distanceToClipPlane">The distance between the near and far clipping planes along the ray.</param>
/// <returns>True if the operation was successful. false if it was not possible to compute, such as the eventPosition being outside of the view.</returns>
protected bool ComputeRayAndDistance(PointerEventData eventData, ref Ray ray, ref float distanceToClipPlane)
{
if (eventCamera == null)
return false;

var eventPosition = Display.RelativeMouseAt(eventData.position);
if (eventPosition != Vector3.zero)
{
// We support multiple display and display identification based on event position.
int eventDisplayIndex = (int)eventPosition.z;

// Discard events that are not part of this display so the user does not interact with multiple displays at once.
if (eventDisplayIndex != eventCamera.targetDisplay)
return false;
}
else
{
// The multiple display system is not supported on all platforms, when it is not supported the returned position
// will be all zeros so when the returned index is 0 we will default to the event data to be safe.
eventPosition = eventData.position;
}

// Cull ray casts that are outside of the view rect. (case 636595)
if (!eventCamera.pixelRect.Contains(eventPosition))
return false;

ray = eventCamera.ScreenPointToRay(eventPosition);
// compensate far plane distance - see MouseEvents.cs
float projectionDirection = ray.direction.z;
distanceToClipPlane = Mathf.Approximately(0.0f, projectionDirection)
? Mathf.Infinity
: Mathf.Abs((eventCamera.farClipPlane - eventCamera.nearClipPlane) / projectionDirection);
return true;
}


public override void Raycast(PointerEventData eventData, List<RaycastResult> resultAppendList)
{
Ray ray = new Ray();
float distanceToClipPlane = 0;
if (!ComputeRayAndDistance(eventData, ref ray, ref distanceToClipPlane))
return;

int hitCount = 0;

if (m_MaxRayIntersections == 0)
{
if (ReflectionMethodsCache.Singleton.raycast3DAll == null)
return;


<!--
var raycastAllMethodInfo = typeof(Physics).GetMethod("RaycastAll", new[] {typeof(Ray), typeof(float), typeof(int)});
if (raycastAllMethodInfo != null)
raycast3DAll = (RaycastAllCallback)Delegate.CreateDelegate(typeof(RaycastAllCallback), raycastAllMethodInfo); -->
m_Hits = ReflectionMethodsCache.Singleton.raycast3DAll(ray, distanceToClipPlane, finalEventMask);
hitCount = m_Hits.Length;
}
else
{
if (ReflectionMethodsCache.Singleton.getRaycastNonAlloc == null)
return;

if (m_LastMaxRayIntersections != m_MaxRayIntersections)
{
m_Hits = new RaycastHit[m_MaxRayIntersections];
m_LastMaxRayIntersections = m_MaxRayIntersections;
}

hitCount = ReflectionMethodsCache.Singleton.getRaycastNonAlloc(ray, m_Hits, distanceToClipPlane, finalEventMask);
}

if (hitCount > 1)
System.Array.Sort(m_Hits, (r1, r2) => r1.distance.CompareTo(r2.distance));

if (hitCount != 0)
{
for (int b = 0, bmax = hitCount; b < bmax; ++b)
{
var result = new RaycastResult
{
gameObject = m_Hits[b].collider.gameObject,
module = this,
distance = m_Hits[b].distance,
worldPosition = m_Hits[b].point,
worldNormal = m_Hits[b].normal,
screenPosition = eventData.position,
index = resultAppendList.Count,
sortingLayer = 0,
sortingOrder = 0
};
resultAppendList.Add(result);
}
}
}

Physics2DRaycaster.cs
/// <summary>
/// Raycast against 2D elements in the scene.
/// </summary>
public override void Raycast(PointerEventData eventData, List<RaycastResult> resultAppendList)
{
Ray ray = new Ray();
float distanceToClipPlane = 0;
if (!ComputeRayAndDistance(eventData, ref ray, ref distanceToClipPlane))
return;

int hitCount = 0;

if (maxRayIntersections == 0)
{
if (ReflectionMethodsCache.Singleton.getRayIntersectionAll == null)
return;

m_Hits = ReflectionMethodsCache.Singleton.getRayIntersectionAll(ray, distanceToClipPlane, finalEventMask);
hitCount = m_Hits.Length;
}
else
{
if (ReflectionMethodsCache.Singleton.getRayIntersectionAllNonAlloc == null)
return;

if (m_LastMaxRayIntersections != m_MaxRayIntersections)
{
m_Hits = new RaycastHit2D[maxRayIntersections];
m_LastMaxRayIntersections = m_MaxRayIntersections;
}

hitCount = ReflectionMethodsCache.Singleton.getRayIntersectionAllNonAlloc(ray, m_Hits, distanceToClipPlane, finalEventMask);
}

if (hitCount != 0)
{
for (int b = 0, bmax = hitCount; b < bmax; ++b)
{
var sr = m_Hits[b].collider.gameObject.GetComponent<SpriteRenderer>();

var result = new RaycastResult
{
gameObject = m_Hits[b].collider.gameObject,
module = this,
distance = Vector3.Distance(eventCamera.transform.position, m_Hits[b].point),
worldPosition = m_Hits[b].point,
worldNormal = m_Hits[b].normal,
screenPosition = eventData.position,
index = resultAppendList.Count,
sortingLayer = sr != null ? sr.sortingLayerID : 0,
sortingOrder = sr != null ? sr.sortingOrder : 0
};
resultAppendList.Add(result);
}
}
}

执行事件

代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
ExecuteEvent.cs

public static bool Execute<T>(GameObject target, BaseEventData eventData, EventFunction<T> functor) where T : IEventSystemHandler
{
/从对象池中取出一个IEventSystemHandler类型的元素
var internalHandlers = s_HandlerListPool.Get();
//获取指定对象(target)的事件,并保存在internalHandlers中
GetEventList<T>(target, internalHandlers);
// if (s_InternalHandlers.Count > 0)
// Debug.Log("Executinng " + typeof (T) + " on " + target);

for (var i = 0; i < internalHandlers.Count; i++)
{
T arg;
try
{
arg = (T)internalHandlers[i];
}
catch (Exception e)
{
var temp = internalHandlers[i];
Debug.LogException(new Exception(string.Format("Type {0} expected {1} received.", typeof(T).Name, temp.GetType().Name), e));
continue;
}

try
{
functor(arg, eventData);
}
catch (Exception e)
{
Debug.LogException(e);
}
}

var handlerCount = internalHandlers.Count;
s_HandlerListPool.Release(internalHandlers);
return handlerCount > 0;
}