• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

梳理Unity EventSystem事件系统调用过程

武飞扬头像
terruig
帮助1

之前写过一个关于Button点击事件怎么被调用的,这次把EventSystem事件系统调用过程总结一下

学新通

在事件系统中,最重要的两个类是EventSystem与StandaloneInputModule,这两个类均继承自基类UIBehavior,而UIBehavior继承了MonoBehavior,因此这两个类是以组件的形式在Unity中执行逻辑的.

  1.  
    public abstract class UIBehaviour : MonoBehaviour
  2.  
    {
  3.  
    protected virtual void Awake()
  4.  
    {}
  5.  
     
  6.  
    protected virtual void OnEnable()
  7.  
    {}
  8.  
     
  9.  
    protected virtual void Start()
  10.  
    {}
  11.  
     
  12.  
    protected virtual void OnDisable()
  13.  
    {}
  14.  
     
  15.  
    protected virtual void OnDestroy()
  16.  
    {}
  17.  
     
  18.  
    /// <summary>
  19.  
    /// Returns true if the GameObject and the Component are active.
  20.  
    /// </summary>
  21.  
    public virtual bool IsActive()
  22.  
    {
  23.  
    return isActiveAndEnabled;
  24.  
    }
  25.  
     
  26.  
    #if UNITY_EDITOR
  27.  
    protected virtual void OnValidate()
  28.  
    {}
  29.  
     
  30.  
    protected virtual void Reset()
  31.  
    {}
  32.  
    #endif
  33.  
    /// <summary>
  34.  
    /// 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.
  35.  
    /// </summary>
  36.  
    protected virtual void OnRectTransformDimensionsChange()
  37.  
    {}
  38.  
     
  39.  
    protected virtual void OnBeforeTransformParentChanged()
  40.  
    {}
  41.  
     
  42.  
    protected virtual void OnTransformParentChanged()
  43.  
    {}
  44.  
     
  45.  
    protected virtual void OnDidApplyAnimationProperties()
  46.  
    {}
  47.  
     
  48.  
    protected virtual void OnCanvasGroupChanged()
  49.  
    {}
  50.  
     
  51.  
    /// <summary>
  52.  
    /// Called when the state of the parent Canvas is changed.
  53.  
    /// </summary>
  54.  
    protected virtual void OnCanvasHierarchyChanged()
  55.  
    {}
  56.  
     
  57.  
    /// <summary>
  58.  
    /// Returns true if the native representation of the behaviour has been destroyed.
  59.  
    /// </summary>
  60.  
    /// <remarks>
  61.  
    /// 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.
  62.  
    /// </remarks>
  63.  
    public bool IsDestroyed()
  64.  
    {
  65.  
    // Workaround for Unity native side of the object
  66.  
    // having been destroyed but accessing via interface
  67.  
    // won't call the overloaded ==
  68.  
    return this == null;
  69.  
    }
  70.  
    }
学新通

首先对于EventSystem,它其中有BaseInputModule m_CurrentInputModule与 List<BaseInputModule> m_SystemInputModules均是管理输入模块的,默认情况下Unity与含有EventSystem的物体一同生成StandaloneInputModule

对于EventSystem中List<BaseInputModule> m_SystemInputModules中的对象添加是在BaseInputModule类中

  1.  
    protected override void OnEnable()
  2.  
    {
  3.  
    base.OnEnable();
  4.  
    m_EventSystem = GetComponent<EventSystem>();
  5.  
    m_EventSystem.UpdateModules();
  6.  
    }
  7.  
     
  8.  
    BaseInputModule中的OnEnable()方法
  9.  
     
  10.  
     
  11.  
    public void UpdateModules()
  12.  
    {
  13.  
    GetComponents(m_SystemInputModules);
  14.  
    for (int i = m_SystemInputModules.Count - 1; i >= 0; i--)
  15.  
    {
  16.  
    if (m_SystemInputModules[i] && m_SystemInputModules[i].IsActive())
  17.  
    continue;
  18.  
     
  19.  
    m_SystemInputModules.RemoveAt(i);
  20.  
    }
  21.  
    }
  22.  
     
  23.  
    EventSystem中的OnEnable()方法
学新通

在BaseInputModule调用EventSystem的UpdateModules方法之后将对List<BaseInputModule> m_SystemInputModules进行填充,而StandaloneInputModule继承自BaseInputModule且StandaloneInputModule与EventSystem在同一个物体上,因此StandaloneInputModule被加入到EventSystem中的List<BaseInputModule> m_SystemInputModules中之后进行轮询访问.

在EventSystem中其内部的Update方法进行帧调用每一个BaseInputModule对应的方法

  1.  
    protected virtual void Update()
  2.  
    {
  3.  
    if (current != this)
  4.  
    return;
  5.  
    TickModules();
  6.  
     
  7.  
    bool changedModule = false;
  8.  
    for (var i = 0; i < m_SystemInputModules.Count; i )
  9.  
    {
  10.  
    var module = m_SystemInputModules[i];
  11.  
    if (module.IsModuleSupported() && module.ShouldActivateModule())
  12.  
    {
  13.  
    if (m_CurrentInputModule != module)
  14.  
    {
  15.  
    ChangeEventModule(module);
  16.  
    changedModule = true;
  17.  
    }
  18.  
    break;
  19.  
    }
  20.  
    }
  21.  
     
  22.  
    // no event module set... set the first valid one...
  23.  
    if (m_CurrentInputModule == null)
  24.  
    {
  25.  
    for (var i = 0; i < m_SystemInputModules.Count; i )
  26.  
    {
  27.  
    var module = m_SystemInputModules[i];
  28.  
    if (module.IsModuleSupported())
  29.  
    {
  30.  
    ChangeEventModule(module);
  31.  
    changedModule = true;
  32.  
    break;
  33.  
    }
  34.  
    }
  35.  
    }
  36.  
     
  37.  
    if (!changedModule && m_CurrentInputModule != null)
  38.  
    m_CurrentInputModule.Process();
  39.  
    }
学新通

这里current为EventSystem类,对于EventSystem一个场景只能存在一个,EventSystem被自身类静态存储      private static List<EventSystem> m_EventSystems = new List<EventSystem>();

对于TickModules方法,该方法进行轮询上述的BaseInputModule的对应方法

  1.  
    private void TickModules()
  2.  
    {
  3.  
    for (var i = 0; i < m_SystemInputModules.Count; i )
  4.  
    {
  5.  
    if (m_SystemInputModules[i] != null)
  6.  
    m_SystemInputModules[i].UpdateModule();
  7.  
    }
  8.  
    }
  1.  
    public override void UpdateModule()
  2.  
    {
  3.  
    if (!eventSystem.isFocused && ShouldIgnoreEventsOnNoFocus())
  4.  
    {
  5.  
    if (m_InputPointerEvent != null && m_InputPointerEvent.pointerDrag != null && m_InputPointerEvent.dragging)
  6.  
    {
  7.  
    ReleaseMouse(m_InputPointerEvent, m_InputPointerEvent.pointerCurrentRaycast.gameObject);
  8.  
    }
  9.  
     
  10.  
    m_InputPointerEvent = null;
  11.  
     
  12.  
    return;
  13.  
    }
  14.  
     
  15.  
    m_LastMousePosition = m_MousePosition;
  16.  
    m_MousePosition = input.mousePosition;
  17.  
    }
学新通

对于EventSystem的Update方法中间部分则是为了确保该输入模块适应当前平台的保障

在EventSystem的Update方法最后执行了每个BaseInputModule的Process方法

  1.  
    public override void Process()
  2.  
    {
  3.  
    if (!eventSystem.isFocused && ShouldIgnoreEventsOnNoFocus())
  4.  
    return;
  5.  
     
  6.  
    bool usedEvent = SendUpdateEventToSelectedObject();
  7.  
     
  8.  
    // case 1004066 - touch / mouse events should be processed before navigation events in case
  9.  
    // they change the current selected gameobject and the submit button is a touch / mouse button.
  10.  
     
  11.  
    // touch needs to take precedence because of the mouse emulation layer
  12.  
    if (!ProcessTouchEvents() && input.mousePresent)
  13.  
    ProcessMouseEvent();
  14.  
     
  15.  
    if (eventSystem.sendNavigationEvents)
  16.  
    {
  17.  
    if (!usedEvent)
  18.  
    usedEvent |= SendMoveEventToSelectedObject();
  19.  
     
  20.  
    if (!usedEvent)
  21.  
    SendSubmitEventToSelectedObject();
  22.  
    }
  23.  
    }
学新通

其中SendUpdateEventToSelectedObject方法将EventSystem中之前被选中的物体执行Selected事件

之后若鼠标可用将执行处理鼠标事件的方法即ProcessMouseEvent

  1.  
    protected void ProcessMouseEvent(int id)
  2.  
    {
  3.  
    var mouseData = GetMousePointerEventData(id);
  4.  
    var leftButtonData = mouseData.GetButtonState(PointerEventData.InputButton.Left).eventData;
  5.  
     
  6.  
    m_CurrentFocusedGameObject = leftButtonData.buttonData.pointerCurrentRaycast.gameObject;
  7.  
     
  8.  
    // Process the first mouse button fully
  9.  
    ProcessMousePress(leftButtonData);
  10.  
    ProcessMove(leftButtonData.buttonData);
  11.  
    ProcessDrag(leftButtonData.buttonData);
  12.  
     
  13.  
    // Now process right / middle clicks
  14.  
    ProcessMousePress(mouseData.GetButtonState(PointerEventData.InputButton.Right).eventData);
  15.  
    ProcessDrag(mouseData.GetButtonState(PointerEventData.InputButton.Right).eventData.buttonData);
  16.  
    ProcessMousePress(mouseData.GetButtonState(PointerEventData.InputButton.Middle).eventData);
  17.  
    ProcessDrag(mouseData.GetButtonState(PointerEventData.InputButton.Middle).eventData.buttonData);
  18.  
     
  19.  
    if (!Mathf.Approximately(leftButtonData.buttonData.scrollDelta.sqrMagnitude, 0.0f))
  20.  
    {
  21.  
    var scrollHandler = ExecuteEvents.GetEventHandler<IScrollHandler>(leftButtonData.buttonData.pointerCurrentRaycast.gameObject);
  22.  
    ExecuteEvents.ExecuteHierarchy(scrollHandler, leftButtonData.buttonData, ExecuteEvents.scrollHandler);
  23.  
    }
  24.  
    }
学新通

GetMousePointerEventData方法是在抽象类PointerInputModule中,该方法进行组装鼠标指针数据,处理这一帧与上一帧的鼠标位置信息,以处理之后的拖拽事件,在组装数据时,先是组装了鼠标左键的数据,组装中还调用了EventSystem的RaycastAll方法,该方法得到该鼠标指针位置下所有射线模块能检测到的物体,之后对于鼠标右键与中键的数据是复制了鼠标左键的数据,之后进行返回该鼠标指针数据.

在得到鼠标指针数据之后ProcessMouseEvent进行各种鼠标事件调用,将调用类型与指针数据中的射线检测到的物体传递给ExecuteEvents,在ExecuteEvents中进行各种事件的触发

总结

EventSystem中保存所有输入模块,在EventSystem的Update方法中进行轮询每个输入模块的TickModules与Process方法,其中TickModules方法将EventSystem类中的保存的选中的对象进行触发Selected事件,保存的选中的对象可在Selected类中进行设置,之后在Process方法调用后进行组装鼠标指针数据,射线检测该指针下的物体,最后在ExecuteEvents中进行各种事件调用

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhfkhkkg
系列文章
更多 icon
同类精品
更多 icon
继续加载