简述
最简单的说就是,EventSystem在每帧去input看有没有数据,有的话就对每条数据都遍历所有canvas下的所有graphics,找出所有命中的graphics,选择第一个graphic的gameobject,执行go对应的component的事件处理函数。高度概括伪代码如下:
EventSystem.Update()
// 查看当前是否有输入,如n点触控,touchCount=n (以touch事件处理为例)
for (int i = 0; i < Input.touchCount; i++)
{
List<Graphic> hitResults = new List<Graphic>();
for (int j = 0; j < canvasCount; j++)
{
List<Graphic> graphicsList = canvasList[j].graphics;
for (int z = 0; z < graphicsList.Count; z++)
{
Graphic graphic = graphicsList[z];
if (isHit)
{
hitResults.Add(graphic);
}
}
}
Graphic First = hitResults[0];
GameObject go = First.gameObject;
// 找出go下实现了对应事件的Component,执行其对应回调,如: OnPointerDown
// 根据父子层级关系,构建执行对应的事件链,一直往下穿,直接有Component处理为止
t = go.transform
while (t != null)
{
eventChain.Add(t);
t = t.parent;
}
for(int i=0;i < eventChain.Count; i++){
// 找出gameobject实现了IPointerDownHandler的Component
list<IPointerDownHandler> pList= eventChain[i].GetComponents<IPointerDownHandler>()
for(int j = 0; j<pList.count; p++){
//执行回调
}
//有执行就直接返回
if (pList.count > 0) return
}
}
事件类结构
时序图
建议
- 如若能确定此canvas下的UI不需要响应事件,则可以直接在canvas中去掉GraphicsRaycast组件:
- Image/Text挂的GameObject不需要响应事件时,应把Raycast Target选项去掉,特别是Button类,会带一个Image(背景图)和Text(按钮文字),应把Text的Raycast Target去掉:
- 事件不会透穿,即事件只会被一个GameObject消费掉:
- 执行链会往上查找执行,意味着:如果一个gameobject有一个子gameObject,子gameobject挂了一个Image,如果事件没有被子gameobject处理,那么会往上穿,即使子gameobject的“矩形”不在其父gameobject里。
考虑的优化点:不用canvas的graphics管理,业务层直接将关心的事件注册到EventSystem中,但原生事件相关的东西就不能用了,根据性能考虑是否做这个优化。