UIGU源码分析11:Canvas和CanvasUpdateRegistry

源码11:Canvas和CanvasUpdateRegistry

Runtime类图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Wnyg7g0A-1647096526003)(D:\UnityProjectSpace\BlogRecord\UGUI源码分析\Image\watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2RtazE3NzcxNTUyMzA0,size_16,color_FFFFFF,t_70.png)]

Canvas

Canvas 画布 UGUI的重要组件。 UGUI源码里并没有给Canvas源码 他是在UnityEngine里,这里反射看下源码。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-b4zcV57n-1647096506318)(D:\UnityProjectSpace\BlogRecord\UGUI源码分析\Image\image-20220310232122002-16470964181041.png)]

Canvas本身提供给引擎调整的参数是上面那些。基本是调整渲染模式 层级 显示等等。

源码里有几个比较重要的属性要记录下

   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();
   }

CanvasUpdateRegistry

CanvasUpdateRegistry(画布更新注册处)是一个单例,它是UGUI与Canvas之间的中介,继承了ICanvasElement接口的组件都可以注册到它,它监听了Canvas即将渲染的事件,并调用已注册组件的Rebuild等方法。


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>

除了最后一个枚举项,其他五个项分别代表了布局的三个阶段和渲染的两个阶段。


/// <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 :所有需要在Canvas上展示的元素都应该继承这个接口。

ICanvasElement里有一个主要的方法Rebuild,表示元素的重建。CanvasUpdate是一个表示重建流程的枚举。Rebuild传入CanvasUpdate,也就是通知CanvasElement应该执行重建的哪一个流程。


CanvasUpdateRegistry 维护了两个索引

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

anvasUpdateRegistry的构造函数:

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

willRenderCanvases是Canvas的静态事件,事件是一种特殊的委托,在渲染所有的Canvas之前,抛出willRenderCanvases事件,继而调用CanvasUpdateRegistry的PerformUpdate方法。


 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);
        }
  • 从两个序列中删除不可用的元素 CleanInvalidItems();
  • 布局更新开始
  • 对m_LayoutRebuildQueue依据父对象的数量进行排序
  • 分别以PreLayout,Layout,PostLayout的参数顺序调用每一个元素的Rebuild方法
  • 调用所有元素的LayoutComplete方法
  • 清除布局重建序列中的所有元素
  • 布局更新结束
  • 完成布局后,调用组件的修剪方法
  • 图形更新开始
  • 以PreRender,LatePreRender的参数顺序调用每一个元素的Rebulid方法
  • 调用所有元素的GraphicUpdateComplete方法
  • 清除图形重建序列中的所有元素
  • 图形更新结束
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值