uGUI之Rebuild

UI的Rebuild.

一、基础

Canvas 静态方法

  • Canvas.willRenderCanvases,事件 Canvas 渲染之前被调用

  • Canvas.ForceUpdateCanvases

  • Canvas.ForceUpdateCanvases

CanvasRenderer

GUI的渲染器。依赖于Canvas.

  1. int absoluteDepth, 相对于Root Canvas的Render' depth
  2. Vector2 clippingSoftness , 应用于Render的softness(软) 裁剪。
  3. bool cull { get; set; }
  4. bool cullTransparentMesh { get; set; }
  5. CanvasRenderer.DisableRectClipping
  6. CanvasRenderer.EnableRectClipping(Rect rect);

二、UGUI Rebuild设计

作用是监听Canvas.willRenderCanvases,在渲染Canvas之前, 更新Mesh、Material、Color、Cull等。 CanvasUpdateRegistry管理执行逻辑。 Rebuild对象是 继承ICanvasElement接口

  • 接口。
public interface ICanvasElement
{
    /// <summary>
    /// Rebuild the element for the given stage.
    /// </summary>
    /// <param name="executing">The current CanvasUpdate stage being rebuild.</param>
    void Rebuild(CanvasUpdate executing);

    /// <summary>
    /// Get the transform associated with the ICanvasElement.
    /// </summary>
    Transform transform { get; }

    /// <summary>
    /// Callback sent when this ICanvasElement has completed layout.
    /// </summary>
    void LayoutComplete();

    /// <summary>
    /// Callback sent when this ICanvasElement has completed Graphic rebuild.
    /// </summary>
    void GraphicUpdateComplete();

    /// <summary>
    /// Used if the native representation has been destroyed.
    /// </summary>
    /// <returns>Return true if the element is considered destroyed.</returns>
    bool IsDestroyed();
}
  • 监听事件
protected CanvasUpdateRegistry()
{
    Canvas.willRenderCanvases += PerformUpdate;
}
  • 主要的回调监听处理方式。

处理步骤枚举定义

public enum CanvasUpdate
{
    /// <summary>
    /// Called before layout.
    /// </summary>
    Prelayout = 0,
    /// <summary>
    /// Called for layout.
    /// </summary>
    Layout = 1,
    /// <summary>
    /// Called after layout.
    /// </summary>
    PostLayout = 2,
    /// <summary>
    /// Called before rendering.
    /// </summary>
    PreRender = 3,
    /// <summary>
    /// Called late, before render.
    /// </summary>
    LatePreRender = 4,
    /// <summary>
    /// Max enum value. Always last.
    /// </summary>
    MaxUpdateValue = 5
}
  • PerformUpdate主要步骤归纳

    1. 第一个步骤:移除无用对象。CleanInvalidItems();

    2. LayoutRebuild列表对象 1. 对null值的ICanvasElement进行移除 2. 对IsDestroyed()ICanvasElement进行移除,并回调LayoutComplete

    3. GraphicRebuild列表对象 1. 对null值的ICanvasElement进行移除 2. 对IsDestroyed()ICanvasElement进行移除,并回调LayoutComplete

    4. 第二步骤:对Layout列表执行PrelayoutLayoutPostLayout。也就是Layout的前中后三个子步骤,最后执行LayoutComplete回调。 这有个判断需要对象是UnityEngine.Object对象

    5. Rebuild(CanvasUpdate.Prelayout)

    6. Rebuild(CanvasUpdate.Layout)

    7. Rebuild(CanvasUpdate.PostLayout)

    8. LayoutComplete

    9. 第三个步骤:执行Cull。ClipperRegistry.Cull()

    10. 第四步骤:对Graphic列表执行PreRenderLatePreRender。也就是Layout的前中后三个子步骤,最后执行GraphicUpdateComplete回调。 这有个判断需要对象是UnityEngine.Object对象

    11. Rebuild(CanvasUpdate.PreRender)

    12. Rebuild(CanvasUpdate.LatePreRender)

    13. GraphicUpdateComplete

  • 统计主要接口和被使用的对象。

  1. LayoutRebuild, 监听Layout Rebuild有两个方法。 作用一样,区别是将返回值返回。
  2. void RegisterCanvasElementForLayoutRebuild(ICanvasElement element), 1. ScrollRectBar 2. ScrollRect 3. Slider 4. Toggle
  3. bool TryRegisterCanvasElementForLayoutRebuild(ICanvasElement element) 1. LayoutRebuilder
  4. GraphicRebuild,监听 Graphic Rebuild,有两个方法。作用一样,区别是将返回值返回。
  5. void RegisterCanvasElementForGraphicRebuild(ICanvasElement element) 1. Graphic 2. InputField
  6. bool TryRegisterCanvasElementForGraphicRebuild(ICanvasElement element) 1. 没有用到
  7. UnRegisterCanvasElementForRebuild, 取消监听 LayoutRebuild和GraphicRebuild。

具体监听对象的实现。

Layout部分。

LayoutRebuilder, > 这是一个中间件。 组件通过调用 MarkLayoutForRebuild,然后内部通过一系列过滤,然后通过TryRegisterCanvasElementForLayoutRebuild进行注册。Rebuild回调再管理派发回调。

  • MarkLayoutForRebuild 调用
    1. Graphic
    2. AspectRatioFitter
    3. ContentSizeFitter
    4. HorzontalOrVertcalLayoutGroup
    5. LayoutElement
    6. LayoutGroup
    7. ScrollRect

注册 void MarkLayoutForRebuild(RectTransform rect)。

  1. 对自己/父节点不包含有效的ILayoutGroup,且自己不包含有效的ILayoutController。的组件,执行Return。
  2. 对自己/父节点包含有效的ILayoutGroup,或者且自己包含有效的ILayoutController。执行MarkLayoutRootForRebuild(layoutRoot);,使用包含ILayoutGroup的组件进行添加Rebuild 注册CanvasUpdateRegistry.TryRegisterCanvasElementForLayoutRebuild(rebuilder)

回调

  1. Rebuild, 这里只处理CanvasUpdate.Layout 状态。
  2. 水平计算,ILayoutElement.CalculateLayoutInputHorizontal(), 对于含有ILayoutElement/ILayoutGroup递归Child, 在执行自己。
  3. 水平设置,ILayoutController.SetLayoutHorizontal(), ILayoutControllerILayoutSelfControllerScrollRect
  4. 竖直计算,ILayoutElement.CalculateLayoutInputVertical(), 对于含有ILayoutElement/ILayoutGroup递归Child, 在执行自己。
  5. 竖直设置,ILayoutController.SetLayoutVertical(), ILayoutControllerILayoutSelfControllerScrollRect
  6. LayoutComplete, xx空缺。
  7. GraphicUpdateComplete 空缺。

Cull 部分。

ClipperRegistry

注册/取消注册裁剪

public static void Register(IClipper c)、public static void Unregister(IClipper c)

  1. RectMask2D

GraphicRebuild 部分

InputField、Graphic

Graphic

  • Rebuid > 只处理了CanvasUpdate.PreRender状态。

    1. m_VertsDirty 变化时,执行Mesh的生成。
      // Text、Image、RawImage时, useLegacyMeshGeneration为true。
      if (useLegacyMeshGeneration)
      {
          DoLegacyMeshGeneration();
      }
      else
      {
          DoMeshGeneration();
      }
      //...
      //填充顶点、颜色、uv等信息。
      OnPopulateMesh(s_VertexHelper);
      //...
      GetComponents(typeof(IMeshModifier), components);
    
      //通知监听者。主要有 BashMeshEffect、Outline、PositionAsUV1、Shadow。
      for (var i = 0; i < components.Count; i++)
          ((IMeshModifier)components[i]).ModifyMesh(s_VertexHelper);
    
      // 填充和设置顶点。
      s_VertexHelper.FillMesh(workerMesh);
      canvasRenderer.SetMesh(workerMesh);
    1. m_MaterialDirty 变换时,执行
      canvasRenderer.materialCount = 1;
      canvasRenderer.SetMaterial(materialForRendering, 0);
      canvasRenderer.SetTexture(mainTexture);


文章作者: Yonggang Long
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Yonggang Long !
 上一篇
2022-08-10 Yonggang Long
下一篇 
2022-08-10 Yonggang Long
  目录