UGUI源码剖析(1):基础架构——UIBehaviour与Graphic的核心职责与生命周期

Unity的UGUI系统,其所有UI组件都构建于一个共享的基础架构之上。这个架构的核心,由两个基类UIBehaviour和Graphic奠定。深入理解这两个类的设计意图、API约定和在UGUI系统中的角色,是剖析所有上层UI组件实现原理的前提。本章将对这两个核心基类的源代码进行深度解读。

1. UIBehaviour:UI组件的生命周期管理与事件回调扩展

UIBehaviour位于UnityEngine.EventSystems命名空间,是所有UI组件的基础行为类。它继承自MonoBehaviour,其主要设计目标是规范化UI组件的生命周期,并为其扩展一套UGUI系统专属的回调接口

1.1 标准生命周期的虚方法化

UIBehaviour将MonoBehaviour的核心生命周期函数,如Awake, OnEnable, Start, OnDisable, OnDestroy,全部重新声明为protected virtual方法。

技术解读:
这一设计的核心目的是为了在深层继承体系中,确保基类逻辑的稳定执行。在C#中,如果子类定义了一个与基类同名的private或protected生命周期方法,它会“隐藏(hide)”而非“覆写(override)”基类的方法,这可能导致基类中重要的初始化或清理逻辑被意外跳过。通过将这些方法virtual化,UIBehaviour强制要求所有子类必须使用override关键字来扩展其行为,从而建立了一个可预测、可维护的生命周期调用链。这对于构建一个庞大而稳定的框架至关重要。

1.2 UGUI专属生命周期回调的引入

UIBehaviour最核心的贡献,是定义了一系列标准MonoBehaviour所没有的、与UI渲染和布局管线深度相关的回调函数。这些回调构成了UI组件与UGUI系统其他部分(如Canvas, LayoutRebuilder)进行通信的基础。

        protected virtual void OnRectTransformDimensionsChange()
        {}

        protected virtual void OnBeforeTransformParentChanged()
        {}

        protected virtual void OnTransformParentChanged()
        {}

        protected virtual void OnDidApplyAnimationProperties()
        {}

        protected virtual void OnCanvasGroupChanged()
        {}

        protected virtual void OnCanvasHierarchyChanged()
        {}
  • protected virtual void OnRectTransformDimensionsChange():
    触发时机:当组件所附加的RectTransform的尺寸(rect.size)发生变化时调用。
    作用:这是UI组件响应布局变化的核心入口。当父容器尺寸改变、锚点设置或布局组件(LayoutGroup)的作用导致自身尺寸变化时,此回调被触发,UI组件可以执行重新计算顶点、调整子对象等操作。
  • protected virtual void OnBeforeTransformParentChanged() / OnTransformParentChanged():
    触发时机:在UI元素的父Transform即将改变之前和改变之后调用。
    作用:这组回调在UGUI中至关重要,因为一个UI元素的有效Canvas是由其在Transform层级树中的父级链决定的。当父对象改变时,组件可能需要从旧的Canvas中注销(unregister),并在归属于新的Canvas后重新注册(register)和刷新状态。
  • protected virtual void OnCanvasGroupChanged():
    触发时机:当层级树中任意一个父级的CanvasGroup组件的属性(如alpha, interactable, blocksRaycasts)发生变化时,该CanvasGroup下的所有子UIBehaviour都会收到此回调。
    作用:允许子元素响应上层CanvasGroup的状态变化,例如,当父级变得不可交互时,子按钮可以改变自己的外观。
  • protected virtual void OnCanvasHierarchyChanged():
    触发时机:当组件所在的Canvas层级结构发生任何可能影响渲染状态的变化时调用(例如,父Canvas被启用/禁用,或嵌套Canvas的排序被覆盖)。
    作用:为组件提供一个捕获上层Canvas状态变更的通用通知机制。

2. Graphic:可视元素的渲染管线与交互接口

Graphic类继承自UIBehaviour,是所有可绘制UI组件(如Image, Text, RawImage)的抽象基类。它定义了UGUI元素从数据到像素的完整渲染管线,以及响应用户输入的交互接口。

2.1 核心职责:网格生成

Graphic类的根本职责,是通过填充顶点数据来定义其视觉形态。这一职责由一个核心的虚方法承担:

/// <summary>
/// 当需要重新生成网格时调用此方法
/// </summary>
/// <param name="vh">顶点助手,用于构建网格数据</param>
protected virtual void OnPopulateMesh(VertexHelper vh)
{
    // 获取经过像素对齐调整后的矩形区域
    // 这确保了UI元素在屏幕上精确对齐像素
    var r = GetPixelAdjustedRect();
    // 创建一个Vector4来存储矩形的边界坐标
    // x:左边界, y:下边界, z:右边界, w:上边界
    var v = new Vector4(r.x, r.y, r.x + r.width, r.y + r.height);
    Color32 color32 = color;
    // 清除之前的所有顶点数据,准备重新构建
    vh.Clear();
    // 添加四个顶点来构成一个矩形(按逆时针顺序)
    // 第一个顶点:左下角 (0,0)
    vh.AddVert(new Vector3(v.x, v.y), color32, new Vector2(0f, 0f));
    // 第二个顶点:左上角 (0,1)
    vh.AddVert(new Vector3(v.x, v.w), color32, new Vector2(0f, 1f));
    // 第三个顶点:右上角 (1,1)
    vh.AddVert(new Vector3(v.z, v.w), color32, new Vector2(1f, 1f));
    // 第四个顶点:右下角 (1,0)
    vh.AddVert(new Vector3(v.z, v.y), color32, new Vector2(1f, 0f));
    // 添加两个三角形来构成矩形
    // 第一个三角形:顶点0-1-2(左下-左上-右上)
    vh.AddTriangle(0, 1, 2);
    // 第二个三角形:顶点2-3-0(右上-右下-左下)
    // 这样就形成了一个完整的矩形网格
    vh.AddTriangle(2, 3, 0);
}
  • protected virtual void OnPopulateMesh(VertexHelper vh
    技术解读:这是Graphic子类必须覆写的核心方法。VertexHelper是一个工具类,用于高效地构建和操作顶点数据列表。子类(如Image)需要在这个回调中,根据自身的属性(如sprite, color, type)和RectTransform的尺寸,计算出所有顶点的位置(position)、颜色(color)、UV坐标(uv0)等信息,并通过vh.AddVert()和vh.AddTriangle()等方法,将其填充到VertexHelper中。这个过程,就是将UI组件的“属性”数据,程序化地转换为可被GPU渲染的“几何”数据的过程。

2.2 性能核心:增量更新的“Dirty”系统

为了避免在每一帧都重新生成所有UI元素的网格,UGUI采用了一套基于“脏标记(Dirty Flag)”的增量更新机制。Graphic类是这套系统的核心执行者。

  • SetVerticesDirty(): 当影响几何形状的属性(如color, rectTransform.rect)变化时调用。
  • SetLayoutDirty(): 当影响布局的属性变化时调用,它会通过LayoutRebuilder标记对应的RectTransform需要重新计算布局。
  • SetMaterialDirty(): 当材质(material)属性变化时调用。

技术解读:当一个Graphic被标记为“Dirty”后,它会被注册到CanvasUpdateRegistry中。Canvas的渲染循环会在适当的阶段(如CanvasUpdate.PreRender),只对那些被注册的“Dirty”元素调用其Rebuild方法,进而触发UpdateGeometry(调用OnPopulateMesh)或UpdateMaterial。这种机制,确保了只有发生变化的UI元素才会被重新计算和提交渲染数据,是UGUI的核心性能优化之一。

2.3 交互接口:射线检测 (Raycasting)

Graphic不仅负责“画”,还负责“感知”。它定义了UI元素如何响应来自EventSystem的射线检测。

  • public virtual bool raycastTarget: 一个布尔属性,用于控制该Graphic是否参与射线检测。
  • public virtual bool Raycast(Vector2 sp, Camera eventCamera):GraphicRaycaster会调用此方法,以确定给定的屏幕点是否命中了该Graphic。默认实现是检查点是否在RectTransform的矩形区域内,同时会向上遍历Transform层级树,检查是否存在ICanvasRaycastFilter(如CanvasGroup)来阻挡射线。

技术解读:关闭不必要元素的raycastTarget属性,可以有效减少GraphicRaycaster在每一帧需要检测的元素数量,是UI性能优化的一个重要手段。通过覆写Raycast方法,可以实现自定义形状的点击检测(例如,只在图片的非透明区域响应点击)。

2.4 与Canvas系统的集成

Graphic通过一系列机制,将自身深度集成到Canvas的渲染和更新管线中。

  • 注册机制: 在OnEnable时,Graphic会通过GraphicRegistry.RegisterGraphicForCanvas将自己注册到所属的Canvas。在OnDisable或OnDestroy时则会注销。这使得Canvas能够维护一个当前所有活动Graphic的列表。
  • 组件缓存: Graphic内部缓存了对rectTransform, canvas, canvasRenderer等必要组件的引用,避免了在运行时频繁调用GetComponent所带来的性能开销。
  • ICanvasElement接口实现: Graphic通过实现ICanvasElement接口,获得了Rebuild, LayoutComplete, GraphicUpdateComplete等回调方法。这使得它可以被CanvasUpdateRegistry统一管理,并以正确的顺序,参与到Canvas在WillRenderCanvases事件驱动的、多阶段(Layout, Rebuild, Render)更新循环中。

通过对UIBehaviour和Graphic这两个基类的源代码级分析,我们揭示了UGUI框架的底层设计原则。UIBehaviour通过虚方法化和扩展MonoBehaviour的生命周期,为所有UI组件建立了统一、可预测的行为规范。Graphic则在此基础之上,定义了一套完整的、从程序化网格生成到增量式渲染更新,再到响应用户输入的渲染与交互管线。

这两个类,是理解所有上层UGUI组件(如Image, Text, Selectable)实现原理的基石。掌握了它们的设计思想和工作机制,就等于掌握了解析整个UGUI系统的钥匙。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值