Análisis del código fuente UIGU 11: Canvas y CanvasUpdateRegistry

Código fuente 11: Canvas y CanvasUpdateRegistry

Diagrama de clases en tiempo de ejecución

(Error en la transferencia de la imagen del enlace externo. El sitio de origen puede tener un mecanismo anti-leeching. Se recomienda guardar la imagen y cargarla directamente (img-Wnyg7g0A-1647096526003) (D:\UnityProjectSpace\BlogRecord\UGUI source code Analysis\Image \watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10, text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2RtazE3NzcxNTUyMzA0,size_16,color_FFFFFF,t_70.png)]

Lienzo

Canvas es un componente importante de UGUI. El código fuente de Canvas no se proporciona en el código fuente de UGUI. Está en UnityEngine. Echemos un vistazo al código fuente aquí.

(Error en la transferencia de la imagen del enlace externo. El sitio de origen puede tener un mecanismo anti-leeching. Se recomienda guardar la imagen y cargarla directamente (img-b4zcV57n-1647096506318) (D:\UnityProjectSpace\BlogRecord\UGUI Source Code Analysis\Image \image-20220310232122002-16470964181041.png)]

Los parámetros que el propio Canvas proporciona al motor para su ajuste son los anteriores. Básicamente, es para ajustar la visualización del nivel del modo de renderizado, etc.

Hay varios atributos importantes en el código fuente que deben registrarse.

   public delegate void WillRenderCanvases();
  // 公有事件,在CanvasUpdateRegistry.cs的构造函数里,为willRenderCanvases事件添加了一个监听PerformUpdate。
	//在渲染(所有)Canvas之前会抛出willRenderCanvases事件,继而调用到CanvasUpdateRegistry.cs的PerformUpdate方法
   public static event WillRenderCanvases willRenderCanvases;
   public static event WillRenderCanvases preWillRenderCanvases;
   
   //强制更新所有Canvas, 当Canvas需要重新绘制时调用,可在外部主动调用
   public static void ForceUpdateCanvases()
   {
       SendPreWillRenderCanvases();
       SendWillRenderCanvases();
   }

Registro de actualización de lienzo

CanvasUpdateRegistry (registro de actualización de lienzo) es un singleton. Es el intermediario entre UGUI y Canvas. Los componentes que heredan la interfaz ICanvasElement se pueden registrar en él. Escucha el evento que Canvas está a punto de renderizar y llama a Rebuild del componente registrado. y otros métodos.


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>

Excepto el último elemento de enumeración, los otros cinco elementos representan las tres etapas de diseño y las dos etapas de representación, respectivamente.


/// <summary>
/// This is an element that can live on a Canvas.
/// </summary>
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();
}

ICanvasElement: todos los elementos que deben mostrarse en Canvas deben heredar esta interfaz.

Existe un método principal Rebuild en ICanvasElement, que representa la reconstrucción de elementos. CanvasUpdate es una enumeración que representa el proceso de reconstrucción. La reconstrucción pasa en CanvasUpdate, que notifica a CanvasElement qué proceso de reconstrucción debe ejecutarse.


CanvasUpdateRegistry mantiene dos índices

//需要布局重建的元素的队列
private readonly IndexedSet<ICanvasElement> m_LayoutRebuildQueue = new IndexedSet<ICanvasElement>();
//存需要Graphic重建的队列
private readonly IndexedSet<ICanvasElement> m_GraphicRebuildQueue = new IndexedSet<ICanvasElement>();

Constructor de anvasUpdateRegistry:

   protected CanvasUpdateRegistry()
   {
   		Canvas.willRenderCanvases += PerformUpdate;
   }

willRenderCanvases es un evento estático de Canvas. El evento es un delegado especial. Antes de renderizar todo Canvas, se lanza el evento willRenderCanvases y luego se llama al método PerformUpdate de CanvasUpdateRegistry.


 private void PerformUpdate()
        {
        
       	 //开始布局重建
            UISystemProfilerApi.BeginSample(UISystemProfilerApi.SampleType.Layout);
            
            //清除无用的元素(为null 或者Destroy)
            CleanInvalidItems();

            m_PerformingLayoutUpdate = true;
		
			//先根据父对象的数量进行重新排序
            m_LayoutRebuildQueue.Sort(s_SortLayoutFunction);

			//根据布局的三个阶段 进行Rebuild
            for (int i = 0; i <= (int)CanvasUpdate.PostLayout; i++)
            {
                UnityEngine.Profiling.Profiler.BeginSample(m_CanvasUpdateProfilerStrings[i]);

                for (int j = 0; j < m_LayoutRebuildQueue.Count; j++)
                {
                    var rebuild = m_LayoutRebuildQueue[j];
                    try
                    {
                        if (ObjectValidForUpdate(rebuild))
                            rebuild.Rebuild((CanvasUpdate)i);
                    }
                    catch (Exception e)
                    {
                        Debug.LogException(e, rebuild.transform);
                    }
                }
                UnityEngine.Profiling.Profiler.EndSample();
            }

			//重建完毕 调用所有元素LayoutComplete
            for (int i = 0; i < m_LayoutRebuildQueue.Count; ++i)
                m_LayoutRebuildQueue[i].LayoutComplete();
			//清空布局队列
            m_LayoutRebuildQueue.Clear();
            m_PerformingLayoutUpdate = false;
            UISystemProfilerApi.EndSample(UISystemProfilerApi.SampleType.Layout);
            UISystemProfilerApi.BeginSample(UISystemProfilerApi.SampleType.Render);
			
			//调用元素的裁剪方法(继承了IClipper接口)
            // now layout is complete do culling...
            UnityEngine.Profiling.Profiler.BeginSample(m_CullingUpdateProfilerString);
            ClipperRegistry.instance.Cull();
            UnityEngine.Profiling.Profiler.EndSample();

            m_PerformingGraphicUpdate = true;
			根据渲染的的两个阶段 进行Rebuild
            for (var i = (int)CanvasUpdate.PreRender; i < (int)CanvasUpdate.MaxUpdateValue; i++)
            {
                UnityEngine.Profiling.Profiler.BeginSample(m_CanvasUpdateProfilerStrings[i]);
                for (var k = 0; k < m_GraphicRebuildQueue.Count; k++)
                {
                    try
                    {
                        var element = m_GraphicRebuildQueue[k];
                        if (ObjectValidForUpdate(element))
                            element.Rebuild((CanvasUpdate)i);
                    }
                    catch (Exception e)
                    {
                        Debug.LogException(e, m_GraphicRebuildQueue[k].transform);
                    }
                }
                UnityEngine.Profiling.Profiler.EndSample();
            }
            
			//调用GraphicUpdateComplete
            for (int i = 0; i < m_GraphicRebuildQueue.Count; ++i)
                m_GraphicRebuildQueue[i].GraphicUpdateComplete();
			
			//清空布局队列
            m_GraphicRebuildQueue.Clear();
            m_PerformingGraphicUpdate = false;
            UISystemProfilerApi.EndSample(UISystemProfilerApi.SampleType.Render);
        }
  • Eliminar elementos no disponibles de ambas secuenciasCleanInvalidItems();
  • Comienza la actualización del diseño
  • Ordene m_LayoutRebuildQueue según la cantidad de objetos principales
  • Llame al método Rebuild de cada elemento en el orden de los parámetros de PreLayout, Layout y PostLayout.
  • Llame al método LayoutComplete de todos los elementos.
  • Borrar todos los elementos en la secuencia de reconstrucción del diseño
  • Actualización de diseño completada
  • Después de terminar el diseño, llame al método de recorte del componente.
  • Comienza la actualización de gráficos
  • Llame al método Rebulid de cada elemento en orden con los parámetros de PreRender y LatePreRender.
  • Llame al método GraphicUpdateComplete de todos los elementos.
  • Borrar todos los elementos en la secuencia de reconstrucción de gráficos.
  • Actualización de gráficos finalizada.

Supongo que te gusta

Origin blog.csdn.net/NippyLi/article/details/123452074
Recomendado
Clasificación