Arzie100 发表于 2022-9-28 12:45

Unity UGUI update优化

问题出现的原因:
GraphicRaycaster.get_eventCamera();
Graphic.get_canvasRenderer();优化方式:

GraphicRaycaster.get_eventCamera()

这个函数使用来获取当前UI的摄像机的,但是实际上相机都是与各个Canvas绑定的,按理来说一旦初始化之后,只要游戏内没有更改过相机,那么就应该一直是原来的相机。
继承重写Canvas中的eventCamera
public class YHGraphicRaycaster : GraphicRaycaster
{
      public Camera TargetCamera;

      public override Camera eventCamera
      {
            get
            {
                if (TargetCamera == null)
                {
                  TargetCamera = base.eventCamera;
                }
                return TargetCamera;
            }
      }
}在不修改游戏内UI相机的情况下,可以这样优化,替换原来的Canvas脚本(可以从源码中修改,可能会出现意外的bug),这个操作是Unity为了兼容性做的动态获取摄像机,在项目中如果可以确定,可以不必动态获取
Graphic.get_canvasRenderer()

这个函数Unity本身是有对应的缓存操作的。在优化的方向上只能通过对调用次数的减少,来增加效率从源码分析上看到,,其实UGUI在获取到很多点击控件的时候会对其进行排序。


如果项目中只需要响应最顶层的UI响应,可以值返回最顶层的UI 如果涉及到UI穿透,对应最顶层下的UI也是不必要排序的,因为这里只是每个Canvas下面的UI元素排序,再收集完各个Canvas的点击元素之后,UGUI会再进行一次排序,最后选择最上层的UI元素
static readonly List<Graphic> s_SortedGraphics = new List<Graphic>();
      private static void Raycast(Canvas canvas, Camera eventCamera, Vector2 pointerPosition, IList<Graphic> foundGraphics, List<Graphic> results)
      {
            int totalCount = foundGraphics.Count;
            Graphic upGraphic = null;
            int upIndex = -1;
            for (int i = 0; i < totalCount; ++i)
            {
                Graphic graphic = foundGraphics;
                int depth = graphic.depth;
                if (depth == -1 || !graphic.raycastTarget || graphic.canvasRenderer.cull)
                  continue;

                if (!RectTransformUtility.RectangleContainsScreenPoint(graphic.rectTransform, pointerPosition, eventCamera))
                  continue;

                if (eventCamera != null && eventCamera.WorldToScreenPoint(graphic.rectTransform.position).z > eventCamera.farClipPlane)
                  continue;

                if (graphic.Raycast(pointerPosition, eventCamera))
                {
                  s_SortedGraphics.Add(graphic);
                  if (depth > upIndex)
                  {
                        upIndex = depth;
                        upGraphic = graphic;
                  }
                }
            }
            if (upGraphic != null)
                results.Add(upGraphic);
      }因为,这个方法是没有重载的,所以需要在脚本中添加另外一个Raycast方法的重写。(不用修改,直接复制就可以了) 经过这样的优化,可以减少depth,和CanvasRenderer的调用,从而减少耗时
在项目中,会有一些Get_Canvas的耗时,如果可以接受,可以不用优化 因为再缓存中对Canvas会进行判空的操作,如果没有对应的Canvas上的修改,可以不用判空直接在Awake中赋值,减少判空的耗时 相对应的CanvasRender也可以修改判空的逻辑修改。 对应的可以重载Image中的Awake,直接获取组件上的CanvasRender组件 并且对应的depth也可以使用CanvasRender.absoluteDepth属性直接获取,不做缓存
如果需要优化这一部分,可以在YHGraphicRaycaster脚本中添加对应的获取方法:
/// <summary>
      /// 获取层级;
      /// </summary>
      /// <param name="gra"></param>
      /// <returns></returns>
      public static int GetGraphicDepth(Graphic graphic)
      {
            if (graphic is YHImage)
            {
                return(graphic as YHImage).depth;
            }
            return graphic.depth;
      }

      /// <summary>
      /// 获取CanvasRenderer;
      /// </summary>
      /// <param name="gra"></param>
      /// <returns></returns>
      public static CanvasRenderer GetGraphicCanvasRenderer(Graphic graphic)
      {
            if (graphic is YHImage)
            {
                return (graphic as YHImage).canvasRenderer;
            }
            return graphic.canvasRenderer;
      }然后将对应脚本中根据Graphic中获取depth和canvasRenderer的地方都替换为这两种方法
经过这些优化,可以修改对应的Raycast方法,来减少depth的调用次数; 这里添加一个Raycast2方法,因为对应Graphic中RaycastTarget如果没有勾选,就不用判断depth相关的操作所以:
/// <summary>
      /// Perform a raycast into the screen and collect all graphics underneath it.
      /// </summary>
      private static void Raycast2(Canvas canvas, Camera eventCamera, Vector2 pointerPosition, IList<Graphic> foundGraphics, List<Graphic> results)
      {
            int totalCount = foundGraphics.Count;
            Graphic upGraphic = null;
            int upIndex = -1;
            for (int i = 0; i < totalCount; ++i)
            {
                Graphic graphic = foundGraphics;
                //不响应点击事件的就不要执行了;
                if (!graphic.raycastTarget) continue;
                //层级;
                int depth = GetGraphicDepth(graphic);
                if (depth == -1 ||GetGraphicCanvasRenderer(graphic).cull)
                  continue;

                if (!RectTransformUtility.RectangleContainsScreenPoint(graphic.rectTransform, pointerPosition, eventCamera))
                  continue;

                if (eventCamera != null && eventCamera.WorldToScreenPoint(graphic.rectTransform.position).z > eventCamera.farClipPlane)
                  continue;

                if (graphic.Raycast(pointerPosition, eventCamera))
                {
                  if (depth > upIndex)
                  {
                        upIndex = depth;
                        upGraphic = graphic;
                  }
                }
            }
            if (upGraphic != null)
                results.Add(upGraphic);
页: [1]
查看完整版本: Unity UGUI update优化