之前写过一个关于Button点击事件怎么被调用的,这次把EventSystem事件系统调用过程总结一下
图来自 UGUI源码分析:EventSystem事件系统_Vin129的博客-CSDN博客
在事件系统中,最重要的两个类是EventSystem与StandaloneInputModule,这两个类均继承自基类UIBehavior,而UIBehavior继承了MonoBehavior,因此这两个类是以组件的形式在Unity中执行逻辑的.
public abstract class UIBehaviour : MonoBehaviour
{
protected virtual void Awake()
{}
protected virtual void OnEnable()
{}
protected virtual void Start()
{}
protected virtual void OnDisable()
{}
protected virtual void OnDestroy()
{}
///
/// Returns true if the GameObject and the Component are active.
///
public virtual bool IsActive()
{
return isActiveAndEnabled;
}
#if UNITY_EDITOR
protected virtual void OnValidate()
{}
protected virtual void Reset()
{}
#endif
///
/// This callback is called if an associated RectTransform has its dimensions changed. The call is also made to all child rect transforms, even if the child transform itself doesn't change - as it could have, depending on its anchoring.
///
protected virtual void OnRectTransformDimensionsChange()
{}
protected virtual void OnBeforeTransformParentChanged()
{}
protected virtual void OnTransformParentChanged()
{}
protected virtual void OnDidApplyAnimationProperties()
{}
protected virtual void OnCanvasGroupChanged()
{}
///
/// Called when the state of the parent Canvas is changed.
///
protected virtual void OnCanvasHierarchyChanged()
{}
///
/// Returns true if the native representation of the behaviour has been destroyed.
///
///
/// When a parent canvas is either enabled, disabled or a nested canvas's OverrideSorting is changed this function is called. You can for example use this to modify objects below a canvas that may depend on a parent canvas - for example, if a canvas is disabled you may want to halt some processing of a UI element.
///
public bool IsDestroyed()
{
// Workaround for Unity native side of the object
// having been destroyed but accessing via interface
// won't call the overloaded ==
return this == null;
}
}
首先对于EventSystem,它其中有BaseInputModule m_CurrentInputModule与 List
对于EventSystem中List
protected override void OnEnable()
{
base.OnEnable();
m_EventSystem = GetComponent();
m_EventSystem.UpdateModules();
}
BaseInputModule中的OnEnable()方法
public void UpdateModules()
{
GetComponents(m_SystemInputModules);
for (int i = m_SystemInputModules.Count - 1; i >= 0; i--)
{
if (m_SystemInputModules[i] && m_SystemInputModules[i].IsActive())
continue;
m_SystemInputModules.RemoveAt(i);
}
}
EventSystem中的OnEnable()方法
在BaseInputModule调用EventSystem的UpdateModules方法之后将对List
在EventSystem中其内部的Update方法进行帧调用每一个BaseInputModule对应的方法
protected virtual void Update()
{
if (current != this)
return;
TickModules();
bool changedModule = false;
for (var i = 0; i < m_SystemInputModules.Count; i++)
{
var module = m_SystemInputModules[i];
if (module.IsModuleSupported() && module.ShouldActivateModule())
{
if (m_CurrentInputModule != module)
{
ChangeEventModule(module);
changedModule = true;
}
break;
}
}
// no event module set... set the first valid one...
if (m_CurrentInputModule == null)
{
for (var i = 0; i < m_SystemInputModules.Count; i++)
{
var module = m_SystemInputModules[i];
if (module.IsModuleSupported())
{
ChangeEventModule(module);
changedModule = true;
break;
}
}
}
if (!changedModule && m_CurrentInputModule != null)
m_CurrentInputModule.Process();
}
这里current为EventSystem类,对于EventSystem一个场景只能存在一个,EventSystem被自身类静态存储 private static List
对于TickModules方法,该方法进行轮询上述的BaseInputModule的对应方法
private void TickModules()
{
for (var i = 0; i < m_SystemInputModules.Count; i++)
{
if (m_SystemInputModules[i] != null)
m_SystemInputModules[i].UpdateModule();
}
}
public override void UpdateModule()
{
if (!eventSystem.isFocused && ShouldIgnoreEventsOnNoFocus())
{
if (m_InputPointerEvent != null && m_InputPointerEvent.pointerDrag != null && m_InputPointerEvent.dragging)
{
ReleaseMouse(m_InputPointerEvent, m_InputPointerEvent.pointerCurrentRaycast.gameObject);
}
m_InputPointerEvent = null;
return;
}
m_LastMousePosition = m_MousePosition;
m_MousePosition = input.mousePosition;
}
对于EventSystem的Update方法中间部分则是为了确保该输入模块适应当前平台的保障
在EventSystem的Update方法最后执行了每个BaseInputModule的Process方法
public override void Process()
{
if (!eventSystem.isFocused && ShouldIgnoreEventsOnNoFocus())
return;
bool usedEvent = SendUpdateEventToSelectedObject();
// case 1004066 - touch / mouse events should be processed before navigation events in case
// they change the current selected gameobject and the submit button is a touch / mouse button.
// touch needs to take precedence because of the mouse emulation layer
if (!ProcessTouchEvents() && input.mousePresent)
ProcessMouseEvent();
if (eventSystem.sendNavigationEvents)
{
if (!usedEvent)
usedEvent |= SendMoveEventToSelectedObject();
if (!usedEvent)
SendSubmitEventToSelectedObject();
}
}
其中SendUpdateEventToSelectedObject方法将EventSystem中之前被选中的物体执行Selected事件
之后若鼠标可用将执行处理鼠标事件的方法即ProcessMouseEvent
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(leftButtonData.buttonData.pointerCurrentRaycast.gameObject);
ExecuteEvents.ExecuteHierarchy(scrollHandler, leftButtonData.buttonData, ExecuteEvents.scrollHandler);
}
}
GetMousePointerEventData方法是在抽象类PointerInputModule中,该方法进行组装鼠标指针数据,处理这一帧与上一帧的鼠标位置信息,以处理之后的拖拽事件,在组装数据时,先是组装了鼠标左键的数据,组装中还调用了EventSystem的RaycastAll方法,该方法得到该鼠标指针位置下所有射线模块能检测到的物体,之后对于鼠标右键与中键的数据是复制了鼠标左键的数据,之后进行返回该鼠标指针数据.
在得到鼠标指针数据之后ProcessMouseEvent进行各种鼠标事件调用,将调用类型与指针数据中的射线检测到的物体传递给ExecuteEvents,在ExecuteEvents中进行各种事件的触发
总结
EventSystem中保存所有输入模块,在EventSystem的Update方法中进行轮询每个输入模块的TickModules与Process方法,其中TickModules方法将EventSystem类中的保存的选中的对象进行触发Selected事件,保存的选中的对象可在Selected类中进行设置,之后在Process方法调用后进行组装鼠标指针数据,射线检测该指针下的物体,最后在ExecuteEvents中进行各种事件调用
如果觉得文章有用请关注B站EOE组合 谢谢喵