zoukankan      html  css  js  c++  java
  • Unity -点击下传

    问题

    策划需求,打开道具提示浮动弹窗时,点击空白处,需要关闭弹窗,并且其他组件要响应点击操作。

    解决

    基本思路是,背景遮罩响应到点击时间时,执行关闭弹窗,然后再把点击事件从当前节点继续往下发。
    以下代码来自雨松大佬,项目里是用另外的实现方式(直接封装在了按钮组件里面),但原理都是一样的,就不贴项目代码了,不值一贴。
    但是有个特殊的地方,button节点即使没有接收点击的组件也能响应,只要子节点有接收点击的组件,就会自动触发到button的点击事件。区别在于ExcuteEvents.cs中的ExecuteHierarchy和Execute,这两个方法的区别是ExecuteHierarchy会获取父节点组件的事件Handler(通过GetEventHandler),而Execute只会获取当前节点组件的事件Handler。

    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.EventSystems;
     
    public class UITouchPass : MonoBehaviour, IPointerClickHandler,
        IMoveHandler,IPointerDownHandler, IPointerUpHandler,IPointerEnterHandler,ISelectHandler, IDeselectHandler
        , ISubmitHandler, IInitializePotentialDragHandler, IBeginDragHandler, IEndDragHandler, IDragHandler, IScrollHandler
    {
        public virtual void OnPointerClick(PointerEventData eventData)
        {
            PassEvent(eventData, ExecuteEvents.pointerClickHandler);
        }
        public virtual void OnPointerDown(PointerEventData eventData)
        {
            PassEvent(eventData, ExecuteEvents.pointerDownHandler);
            if (Input.GetButtonDown("Submit"))
               ExecuteEvents.Execute(eventData.pointerCurrentRaycast.gameObject, eventData, ExecuteEvents.submitHandler);
        }
     
        public virtual void OnPointerUp(PointerEventData eventData)
        {
            PassEvent(eventData, ExecuteEvents.pointerUpHandler);
        }
     
        public virtual void OnPointerEnter(PointerEventData eventData)
        {
            PassEvent(eventData, ExecuteEvents.pointerEnterHandler);
        }
     
        public virtual void OnSelect(BaseEventData eventData)
        {
            PassEvent(eventData, ExecuteEvents.selectHandler);
        }
     
        public virtual void OnDeselect(BaseEventData eventData)
        {
            PassEvent(eventData, ExecuteEvents.deselectHandler);
        }
     
        public virtual void OnSubmit(BaseEventData eventData)
        {
            PassEvent(eventData, ExecuteEvents.submitHandler);
        }
     
        public virtual void OnMove(AxisEventData eventData)
        {
            PassEvent(eventData, ExecuteEvents.moveHandler);
        }
     
        GameObject CacheGameObject;
        public virtual void OnInitializePotentialDrag(PointerEventData eventData)
        {
            CacheGameObject = PassEvent(eventData, ExecuteEvents.initializePotentialDrag);
        }
        public virtual void OnBeginDrag(PointerEventData eventData)
        {
             PassEvent(eventData, ExecuteEvents.beginDragHandler);
        }
        public virtual void OnDrag(PointerEventData eventData)
        {
            ExecuteEvents.Execute(CacheGameObject, eventData, ExecuteEvents.dragHandler);
        }
        public virtual void OnEndDrag(PointerEventData eventData)
        {
            ExecuteEvents.Execute(CacheGameObject, eventData, ExecuteEvents.endDragHandler);
            CacheGameObject = null;
        }
        public virtual void OnScroll(PointerEventData eventData)
        {
            ExecuteEvents.Execute(CacheGameObject, eventData, ExecuteEvents.scrollHandler);
        }
     
        List<RaycastResult> result = new List<RaycastResult>();
        GameObject PassEvent<T>(BaseEventData data, ExecuteEvents.EventFunction<T> function) where T : IEventSystemHandler
        {
            PointerEventData eventData = data as PointerEventData;
            var pointerGo = eventData.pointerCurrentRaycast.gameObject?? eventData.pointerDrag;
            EventSystem.current.RaycastAll(eventData, result);
            foreach (var item in result)
            {
                var go = item.gameObject;
                if (go != null && go != pointerGo)
                {
                    var excuteGo = ExecuteEvents.GetEventHandler<T>(go);
                    if (excuteGo)
                    {
                        if (excuteGo.TryGetComponent<UITouchPass>(out var __))
                            return null;
                        ExecuteEvents.Execute(excuteGo, data, function);
                        return excuteGo;
                    }
                    else
                    {
                        if(go.TryGetComponent<UnityEngine.UI.Graphic>(out var com))
                        {
                            if (com.raycastTarget) return null;
                        }
                    }
                }
            }
            return null;
        }
    }
    

    原理笔记(都在UGUI开源源码里的EventSystem中)

    事件触发大致流程

    组件继承 Graphic 
    Graphic.OnEnable调用GraphicRegistry.RegisterGraphicForCanvas(canvas, this);注册自己和canvas关联,canvas是父节点离自己最新的激活的cavas组件
    EventSystem.Update执行InputModule.Process
    执行InputModule.ProcessTouchEvents
    执行InputModule.GetTouchPointerEventData
    执行EventSystem.RaycastAll
    执行GraphicRaycaster.Raycast,根据GraphicRegistry.GetGraphicsForCanvas获得所有响应组件,判断是否相交,相交则加入列表返回,最终得到第一个响应的组件
    执行InputModule.ProcessTouchPress
    执行各种ExecuteEvents.Execute
    

    ExcuteEvents.cs 阅读笔记

    GetEventChain(GameObject root, IList<Transform> eventChain)
    获取父节点transform列表,包括自己
    
    GetEventList<T>(GameObject go, IList<IEventSystemHandler> results) where T : IEventSystemHandler
    获取go节点上激活并且实现了IEventSystemHandler的component列表
    
    bool ShouldSendToComponent<T>(Component component) where T : IEventSystemHandler
    component是否激活(必须继承了实现了IEventSystemHandler接口类的类型)
    
    bool Execute<T>(GameObject target, BaseEventData eventD`ata, EventFunction<T> functor) where T : IEventSystemHandler
    收集当前节点component,全部执行一遍
    
    GameObject ExecuteHierarchy<T>(GameObject root, BaseEventData eventData, EventFunction<T> callbackFunction) where T : IEventSystemHandler
    收集当前节点的父节点,包括自己,然后依次执行Execute,直到有个节点执行成功后,返回当前执行的go
    
    bool CanHandleEvent<T>(GameObject go) where T : IEventSystemHandler
    节点上的component是否有可执行的handler
    
    GameObject GetEventHandler<T>(GameObject root) where T : IEventSystemHandler
    依次从自己往父节点寻找,直到找到一个可以执行Handler的父节点,返回父节点,否则返回null
    

    参考

    Unity3D研究院之UI完整透下事件
    UGUI研究院之忽略UI被拦截事件
    UGUI事件传递流程解析
    Unity UGUI —— 鼠标穿透UI问题(Unity官方的解决方法)

  • 相关阅读:
    Eureka 服务的注册和发现
    springcloud 中文文档
    mysql 7 种 join
    通用 mapper
    docker 容器操作( 以 tomcat 为例 )
    linux下安装phpunit简单方法
    图片优化的几个小工具
    安装基准测试工具sysbench
    安装pcntl以实现php多进程
    安装memcache及php的memcached模块
  • 原文地址:https://www.cnblogs.com/nickcan/p/14418138.html
Copyright © 2011-2022 走看看