UGUI源码分析|01 EventSystem
用于处理点击、键盘输入、触摸等事件,所以实际上仅仅只是InputEventSystem
EventSystem继承自UIBehaviour,而后者又继承自MonoBehaviour,所以EventSystem本质还是一个Mono
代码一开头,定义了两个输入模块
private List<BaseInputModule> m_SystemInputModules = new List<BaseInputModule>();
private BaseInputModule m_CurrentInputModule;
很明显,一个是所有的输入模块,一个是当前使用中的输入模块
在Update中会调用一个Tick方法,去更新所有的模块
private void TickModules()
{
var systemInputModulesCount = m_SystemInputModules.Count;
for (var i = 0; i < systemInputModulesCount; i++)
{
if (m_SystemInputModules[i] != null)
m_SystemInputModules[i].UpdateModule();
}
}
当前输入模块,最开始是null,会在Update的时候去判断用哪一个Module
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;
}
}
实际上Module的继承关系有好几层,最终直接使用的有两种
- StandaloneInputModule 用于PC
- TouchInputModule 用于移动平台
上面两个都继承自PointerInputModule,而它最终继承自BaseInputModule
RayCastAll
public void RaycastAll(PointerEventData eventData, List<RaycastResult> raycastResults)
{
raycastResults.Clear();
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;
module.Raycast(eventData, raycastResults);
}
raycastResults.Sort(s_RaycastComparer);
}
其中RaycasterManager里存放里所有的BaseRayCast类,不过仅仅是一个逻辑类,实际上触摸检测是在基类里自己实现的
BaseRayCast有一个需要子类重写的方法Raycast
UGUI里的关键就是第一个,GraphicRaycaster,这玩意一般要求挂载到Canvas上
核心代码是这一串
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.raycastTarget || graphic.canvasRenderer.cull || graphic.depth == -1)
continue;
if (!RectTransformUtility.RectangleContainsScreenPoint(graphic.rectTransform, pointerPosition, eventCamera, graphic.raycastPadding))
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();
}
遍历Canvas下所有能被检测的Graphic,然后依次判断是否被点击到,并添加到结果里
ExecuteEvents
这是个静态类,用于执行UGUI事件 (例如:IPointerEnterHandler)
public static bool Execute<T>(GameObject target, BaseEventData eventData, EventFunction<T> functor) where T : IEventSystemHandler
target就是目标对象,方法会拿到target上所有需要发送事件通知的组件
GetEventList<T>(target, internalHandlers);
functor是ExecuteEvents里的一个静态属性,看一个具体调用方法
ExecuteEvents.Execute(t.gameObject, currentPointerData, ExecuteEvents.pointerEnterHandler);
意思就是你想执行什么类型的事件
最后就是遍历所有的事件目标,然后执行
public static bool Execute<T>(GameObject target, BaseEventData eventData, EventFunction<T> functor)
{
...
GetEventList<T>(target, internalHandlers);
...
var internalHandlersCount = internalHandlers.Count;
for (var i = 0; i < internalHandlersCount; i++)
{
T arg = (T)internalHandlers[i];
...
functor(arg, eventData);
}
...
}
以上面那个调用方法为例,最终就会变成,这个方法就是我们调用的一个静态方法,handler就是我们自己写的接口,OnPointerEnter就是接口的具体实现了
private static void Execute(IPointerEnterHandler handler, BaseEventData eventData)
{
handler.OnPointerEnter(ValidateEventData<PointerEventData>(eventData));
}