Unity UI 核心類解析之Graphic

🧱 Unity UI 核心類解析:Graphic 類詳解

一、什么是 Graphic?

在 Unity 的 UI 系統中,Graphic 是一個抽象基類,繼承自 UIBehaviour 并實現了 ICanvasElement 接口。它是所有可以被繪制到屏幕上的 UI 元素的基礎類。

? UI 控件(如按鈕、文字、圖片等),都直接或間接繼承自 Graphic


二、核心作用與職責

Graphic 類主要負責以下幾件事:

職責功能
🎨 視覺渲染控制顏色、材質、紋理、網格生成等
📐 布局更新響應 RectTransform 變化,參與布局系統
🔦 射線檢測控制是否響應點擊/觸摸事件
🔄 臟標記機制優化性能,只在必要時才重新繪制
?? 生命周期管理OnEnable / OnDisable / OnDestroy 等回調處理

三、關鍵屬性詳解

1. 默認材質與紋理

protected static Material s_DefaultUI = null;
protected static Texture2D s_WhiteTexture = null;public static Material defaultGraphicMaterial
{get{if (s_DefaultUI == null)s_DefaultUI = Canvas.GetDefaultCanvasMaterial();return s_DefaultUI;}
}
public virtual Texture mainTexture => s_WhiteTexture;
  • defaultGraphicMaterial:獲取默認的 UI 材質。
  • mainTexture:默認使用白色紋理,用于繪制基礎圖形。

2. 顏色控制

[SerializeField] private Color m_Color = Color.white;
public virtual Color color { get => m_Color; set { SetPropertyUtility.SetColor(ref m_Color, value); SetVerticesDirty();} 
}
  • 支持序列化,方便在 Inspector 中修改。
  • 修改顏色會觸發頂點數據“臟”狀態,表示需要重新繪制。

3. 材質控制

protected Material m_Material;
public virtual Material material
{get{return (m_Material != null) ? m_Material : defaultMaterial;}set{if (m_Material == value)return;m_Material = value;SetMaterialDirty();}
}
  • 支持設置自定義材質,否則使用默認材質。
  • 修改材質也會觸發“臟”狀態。

4. 射線檢測開關

private bool m_RaycastTarget = true;
public virtual bool raycastTarget{get{return m_RaycastTarget;}set{if (value != m_RaycastTarget){if (m_RaycastTarget)GraphicRegistry.UnregisterRaycastGraphicForCanvas(canvas, this);m_RaycastTarget = value;if (m_RaycastTarget && isActiveAndEnabled)GraphicRegistry.RegisterRaycastGraphicForCanvas(canvas, this);}m_RaycastTargetCache = value;}}
  • 控制該 UI 是否能接收點擊事件。
  • 修改此值時會注冊或注銷射線檢測圖層。

四、重建方法(Rebuild)

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

Graphic 實現了ICanvasElement接口

public abstract class Graphic : UIBehaviour, ICanvasElement

Rebuild方法如下

/// <summary>
/// 在 PreRender(預渲染)階段重建圖形的幾何體及其材質。
/// </summary>
/// <param name="update">當前 CanvasUpdate 渲染循環的階段。</param>
/// <remarks>
/// 有關畫布更新循環的更多詳細信息,請參閱 CanvasUpdateRegistry。
/// </remarks>
public virtual void Rebuild(CanvasUpdate update)
{if (canvasRenderer == null || canvasRenderer.cull)return;switch (update){case CanvasUpdate.PreRender:if (m_VertsDirty){UpdateGeometry();m_VertsDirty = false;}if (m_MaterialDirty){UpdateMaterial();m_MaterialDirty = false;}break;}
}

CanvasUpdateRegistry的部分源碼

public class CanvasUpdateRegistry
{private readonly IndexedSet<ICanvasElement> m_LayoutRebuildQueue = new IndexedSet<ICanvasElement>();protected CanvasUpdateRegistry(){//willRenderCanvases是一個Unity內置的靜態事件,它在每一幀中://在所有 Canvas 被渲染之前立即觸發。Canvas.willRenderCanvases += PerformUpdate;}private void PerformUpdate(){CleanInvalidItems();m_PerformingLayoutUpdate = true;m_LayoutRebuildQueue.Sort(s_SortLayoutFunction);for (int i = 0; i <= (int)CanvasUpdate.PostLayout; 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);}}}}
}

五、臟標記機制(SetAllDirty)

為了提高性能,Unity 使用了“臟標記”機制,只有當某些數據發生改變時才會觸發重繪或重新計算。

1.SetAllDirty

public virtual void SetAllDirty(){if (m_SkipLayoutUpdate){m_SkipLayoutUpdate = false;}else{SetLayoutDirty();}if (m_SkipMaterialUpdate){m_SkipMaterialUpdate = false;}else{SetMaterialDirty();}SetVerticesDirty();SetRaycastDirty();}

該方法在下面幾處會調用

protected override void OnTransformParentChanged()
protected override void OnEnable()
protected override void Reset()
protected override void OnDidApplyAnimationProperties()
#if UNITY_EDITORprotected override void OnValidate()
#endif

📋 總結表格

方法名調用時機是否運行時調用是否編輯器專用主要用途
OnTransformParentChangedGameObject 的父級發生變化時? 是? 否處理層級變化,重新注冊 Canvas
OnEnableGameObject 被激活時? 是? 否初始化資源、注冊 UI、觸發重建
Reset首次掛載腳本或點擊 Reset 時? 否? 是設置默認值,重置組件狀態
OnDidApplyAnimationProperties動畫屬性應用后? 是? 否處理動畫驅動的 UI 更新
OnValidateInspector 中字段修改后失去焦點時? 否? 是數據驗證、實時預覽 UI 效果

2.SetLayoutDirty

 public virtual void SetLayoutDirty(){if (!IsActive())return;LayoutRebuilder.MarkLayoutForRebuild(rectTransform);if (m_OnDirtyLayoutCallback != null)m_OnDirtyLayoutCallback();}

以下方法會調用

public virtual void SetAllDirty()
protected override void OnRectTransformDimensionsChange()
  • 作用

    • 告訴布局系統該元素的布局信息已改變,需要重新計算布局。
  • 觸發時機

    • SetAllDirty()
    • OnRectTransformDimensionsChange():尺寸發生變化時
  • 行為

    • 調用 LayoutRebuilder.MarkLayoutForRebuild(rectTransform),加入布局重建隊列
    • 觸發回調 m_OnDirtyLayoutCallback

3.SetMaterialDirty

 public virtual void SetMaterialDirty(){if (!IsActive())return;m_MaterialDirty = true;CanvasUpdateRegistry.RegisterCanvasElementForGraphicRebuild(this);if (m_OnDirtyMaterialCallback != null)m_OnDirtyMaterialCallback();}

以下方法會調用

public virtual void SetAllDirty()
public virtual Material material{get{return (m_Material != null) ? m_Material : defaultMaterial;}set{if (m_Material == value)return;m_Material = value;SetMaterialDirty();}}
  • 作用

    • 表示材質發生了變化,需要重新提交材質到 Canvas 渲染系統。
  • 觸發時機

    • SetAllDirty()
    • 材質屬性被修改(例如 material = new Material(...)
  • 行為

    • 設置標志位 m_MaterialDirty = true
    • 注冊到 CanvasUpdateRegistry,參與圖形重建流程
    • 觸發回調 m_OnDirtyMaterialCallback

4.SetVerticesDirty

public virtual void SetVerticesDirty(){if (!IsActive())return;m_VertsDirty = true;CanvasUpdateRegistry.RegisterCanvasElementForGraphicRebuild(this);if (m_OnDirtyVertsCallback != null)m_OnDirtyVertsCallback();}

以下方法會調用

public virtual Color color { get { return m_Color; } set { if (SetPropertyUtility.SetColor(ref m_Color, value)) SetVerticesDirty(); } 
}
public virtual void SetAllDirty()
protected override void OnRectTransformDimensionsChange()
  • 作用

    • 表示頂點數據(網格)發生了變化,需要重新生成 UI 網格。
  • 觸發時機

    • SetAllDirty()
    • 顏色屬性變更(如 color = Color.red
    • 尺寸變化(通過 OnRectTransformDimensionsChange()
    • 自定義頂點修改(如自定義 Graphic)
  • 行為

    • 設置標志位 m_VertsDirty = true
    • 注冊到 CanvasUpdateRegistry,等待重建
    • 觸發回調 m_OnDirtyVertsCallback

5.SetRaycastDirty

 public void SetRaycastDirty(){if (m_RaycastTargetCache != m_RaycastTarget){if (m_RaycastTarget && isActiveAndEnabled)GraphicRegistry.RegisterRaycastGraphicForCanvas(canvas, this);else if (!m_RaycastTarget)GraphicRegistry.UnregisterRaycastGraphicForCanvas(canvas, this);}m_RaycastTargetCache = m_RaycastTarget;}

以下方法會調用

public virtual void SetAllDirty()
  • 作用

    • 表示是否允許響應射線檢測的狀態發生改變。
  • 觸發時機

    • SetAllDirty()
    • 修改 raycastTarget 屬性時
  • 行為

    • 如果啟用射線檢測,則注冊到 GraphicRegistry
    • 如果禁用,則從 GraphicRegistry 移除
    • 更新緩存值 m_RaycastTargetCache

6.總結

生命周期方法是否調用 SetAllDirty()是否調用其他 Dirty 方法
OnEnable()? 是? 否(但會觸發 SetAllDirty)
Reset()? 是? 否
OnDidApplyAnimationProperties()? 是? 否
OnTransformParentChanged()? 是? 否
OnValidate()(編輯器)? 是? 否
OnRectTransformDimensionsChange()? 否? 是(調用 SetVerticesDirty()SetLayoutDirty()

六、幾何體生成(UpdateGeometry)

這是 Graphic 類最核心的部分之一,它決定了 UI 如何繪制到屏幕上。

關鍵方法:

protected virtual void UpdateGeometry()
{if (useLegacyMeshGeneration)DoLegacyMeshGeneration();elseDoMeshGeneration();
}
1. 新版網格生成(DoMeshGeneration)

使用 VertexHelper 構建頂點數據,并支持 IMeshModifier 修改網格。

private void DoMeshGeneration()
{if (rectTransform != null && rectTransform.rect.width >= 0 && rectTransform.rect.height >= 0)OnPopulateMesh(s_VertexHelper);elses_VertexHelper.Clear(); // clear the vertex helper so invalid graphics dont draw.var components = ListPool<Component>.Get();GetComponents(typeof(IMeshModifier), components);//獲取當前對象是否有IMeshModifier接口,//Text的描邊和陰影都是通過它的ModifyMesh方法實現的for (var i = 0; i < components.Count; i++)((IMeshModifier)components[i]).ModifyMesh(s_VertexHelper);ListPool<Component>.Release(components);s_VertexHelper.FillMesh(workerMesh);canvasRenderer.SetMesh(workerMesh);
}
2. 舊版網格生成(DoLegacyMeshGeneration)

直接操作 Mesh 對象,兼容舊版本邏輯。

private void DoLegacyMeshGeneration()
{if (rectTransform != null && rectTransform.rect.width >= 0 && rectTransform.rect.height >= 0){
#pragma warning disable 618OnPopulateMesh(workerMesh);
#pragma warning restore 618}else{workerMesh.Clear();}var components = ListPool<Component>.Get();GetComponents(typeof(IMeshModifier), components);for (var i = 0; i < components.Count; i++){
#pragma warning disable 618((IMeshModifier)components[i]).ModifyMesh(workerMesh);
#pragma warning restore 618}ListPool<Component>.Release(components);canvasRenderer.SetMesh(workerMesh);
}

實現細節:OnPopulateMesh(VertexHelper vh)

protected virtual void OnPopulateMesh(VertexHelper vh)
{var r = GetPixelAdjustedRect();var v = new Vector4(r.x, r.y, r.x + r.width, r.y + r.height);Color32 color32 = color;vh.Clear();vh.AddVert(new Vector3(v.x, v.y), color32, new Vector2(0f, 0f));vh.AddVert(new Vector3(v.x, v.w), color32, new Vector2(0f, 1f));vh.AddVert(new Vector3(v.z, v.w), color32, new Vector2(1f, 1f));vh.AddVert(new Vector3(v.z, v.y), color32, new Vector2(1f, 0f));vh.AddTriangle(0, 1, 2);vh.AddTriangle(2, 3, 0);
}

這是一個虛方法,提供了默認實現,子類可以重寫實現自己的圖形形狀。


七、生命周期與事件響應

1. 啟用與禁用

protected override void OnEnable()
{base.OnEnable();CacheCanvas();GraphicRegistry.RegisterGraphicForCanvas(canvas, this);#if UNITY_EDITORGraphicRebuildTracker.TrackGraphic(this);
#endifif (s_WhiteTexture == null)s_WhiteTexture = Texture2D.whiteTexture;SetAllDirty();
}protected override void OnDisable()
{
#if UNITY_EDITORGraphicRebuildTracker.UnTrackGraphic(this);
#endifGraphicRegistry.DisableGraphicForCanvas(canvas, this);CanvasUpdateRegistry.DisableCanvasElementForRebuild(this);if (canvasRenderer != null)canvasRenderer.Clear();LayoutRebuilder.MarkLayoutForRebuild(rectTransform);base.OnDisable();
}
  • 啟用時注冊到 Canvas 和重建系統。
  • 禁用時取消注冊并清除緩存。

2. 銷毀

protected override void OnDestroy()
{
#if UNITY_EDITORGraphicRebuildTracker.UnTrackGraphic(this);
#endifGraphicRegistry.UnregisterGraphicForCanvas(canvas, this);CanvasUpdateRegistry.UnRegisterCanvasElementForRebuild(this);if (m_CachedMesh)Destroy(m_CachedMesh);m_CachedMesh = null;base.OnDestroy();
}
  • 清除資源,防止內存泄漏。

3. RectTransform 變化

protected override void OnRectTransformDimensionsChange()
{if (gameObject.activeInHierarchy){// prevent double dirtying...if (CanvasUpdateRegistry.IsRebuildingLayout())SetVerticesDirty();else{SetVerticesDirty();SetLayoutDirty();}}
}
  • 當尺寸變化時觸發頂點或布局更新。

4. Canvas 層級變化

protected override void OnCanvasHierarchyChanged()
{// Use m_Cavas so we dont auto call CacheCanvasCanvas currentCanvas = m_Canvas;// Clear the cached canvas. Will be fetched below if active.m_Canvas = null;if (!IsActive()){GraphicRegistry.UnregisterGraphicForCanvas(currentCanvas, this);return;}CacheCanvas();if (currentCanvas != m_Canvas){GraphicRegistry.UnregisterGraphicForCanvas(currentCanvas, this);// Only register if we are active and enabled as OnCanvasHierarchyChanged can get called// during object destruction and we dont want to register ourself and then become null.if (IsActive())GraphicRegistry.RegisterGraphicForCanvas(canvas, this);}
}
  • 當父級 Canvas 發生變化時,重新注冊自己。

八、性能優化技巧

  1. 避免頻繁調用 SetAllDirty()

    • 只在真正需要更新時才調用。
    • 多個屬性變更后統一調用一次即可。
  2. 利用 IMeshModifierIMaterialModifier

    • 可以擴展圖形外觀,而無需繼承 Graphic
  3. 合理使用 raycastTarget

    • 不需要交互的 UI 應關閉射線檢測,減少性能開銷。
  4. 使用對象池(ListPool)

    • 比如 ListPool<Component>.Get()ListPool<Component>.Release(),避免頻繁 GC。

九、總結

Graphic 類是 Unity UI 系統中最底層、最重要的類之一。它不僅提供了基礎的視覺表現能力,還通過高效的臟標記機制和靈活的擴展接口,使得能夠輕松構建各種復雜的 UI 控件。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/84290.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/84290.shtml
英文地址,請注明出處:http://en.pswp.cn/web/84290.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【Elasticsearch】文檔遷移(Reindex)

文檔遷移 1.為什么要進行 reindex 操作2.Reindex 操作的本質3.實際案例3.1 同集群索引之間的全量數據遷移3.2 同集群索引之間基于特定條件的數據遷移3.2.1 源索引設置檢索條件3.2.2 基于 script 腳本的索引遷移3.2.3 基于預處理管道的數據遷移 3.3 不同集群之間的索引遷移3.4 查…

WordPress 區塊版面配置指南

WordPress 的區塊編輯器(Gutenberg)提供了靈活的版面配置選項&#xff0c;以下是主要配置方法&#xff1a; 基本區塊布局 添加區塊&#xff1a;點擊””按鈕或按”/”鍵快速插入區塊 常用內容區塊&#xff1a; 段落(Paragraph) 標題(Heading) 圖像(Image) 畫廊(Gallery)…

TensorFlow基礎之理解張量

2.理解張量 張量&#xff08;Tensors&#xff09;介紹 張量是物理和工程領域的基礎數學結構。但是過去張量很少在計算機科學里使用。它與離散數學和邏輯學有更多的聯系。隨著機器學習的出現&#xff0c;這種狀態開始顯著的改變&#xff0c;成為連續向量的計算基礎。現代機器學…

Flume 安裝與配置步驟

1.解壓 tar -zxvf apache-flume-1.9.0-bin.tar.gz 2.配置環境變量 vim /etc/profile export FLUME_HOME/home/wang/soft/flume/apache-flume-1.9.0-bin export PATH$PATH:$FLUME_HOME/bin source /etc/profile 3.創建必要的目錄 mkdir -p $FLUME_HOME/conf 4.創建 Flume 配置文…

還原線上 WebView 異常:手機端APP遠程調試

前端調試總被理解為開發階段的事&#xff0c;但在實際項目中&#xff0c;真正困難的調試往往發生在產品上線之后。用戶反饋“看不到內容”、“一直轉圈”、“點了沒反應”&#xff0c;而開發環境無法復現&#xff0c;測試機也正常運行&#xff0c;這時怎么定位、驗證和解決問題…

102頁滿分PPT | 汽車設備制造業企業信息化業務解決方案智能制造汽車黑燈工廠解決方案

這份文檔是一份汽車設備制造業企業信息化業務解決方案&#xff0c;詳細闡述了企業從生產到銷售的全流程信息化建設。針對企業目前手工管理為主、信息化程度低、數據追溯困難等問題&#xff0c;提出了建立統一信息化平臺的目標&#xff0c;涵蓋財務、業務、流程和數據的整合。方…

SQLite 表達式詳解

SQLite 表達式詳解 引言 SQLite 是一個輕量級的數據庫,廣泛用于移動設備和桌面應用程序。SQLite 的表達式是 SQL 語句的核心,它們用于查詢、更新和刪除數據庫中的數據。本文將詳細解釋 SQLite 的各種表達式,并探討它們在數據庫操作中的重要性。 表達式概述 在 SQLite 中…

沉浸式AI交互數字人技術解析

360智匯云沉浸式AI交互數字人支持開發者靈活接入和私有化部署大模型服務&#xff0c;構建面向業務場景的實時音視頻交互能力。系統集成了360智匯云自研的沉浸式AI交互數字人引擎與高性能 RTC 模塊&#xff0c;保障音視頻傳輸過程中的低延遲、高穩定性和高并發承載能力&#xff…

HarmonyOS 評論回復彈窗最佳實踐

HarmonyOS 評論回復彈窗最佳實踐 前言 在移動應用開發中&#xff0c;評論回復功能是一個常見且重要的交互場景。本文將詳細介紹如何在 HarmonyOS 中實現一個功能完善的評論回復彈窗&#xff0c;包括彈窗選型、富文本編輯、軟鍵盤適配等關鍵技術點。 功能概述 我們要實現的評…

Git 回退操作詳解:帶示例的“小白”指南

前言 在日常開發中&#xff0c;我們難免會遇到&#xff1a; 改錯代碼&#xff1a;推送之前才發現某些行根本就不該動提交錯誤&#xff1a;commit 信息打錯、提交到錯誤分支想回到之前版本&#xff1a;測試時發現之前版本是好的&#xff0c;需要回去查看 這就需要用到 Git 的…

redux以及react-redux

1.redux案例完整版 上一篇文章我們是沒有action文件&#xff0c;直接在countre組件與store以及reducer直接進行狀態的改變以及展示。 下面我們加上action文件&#xff0c;我們就不能直接通過dispatch傳&#xff0c;而是通過調用action里面的函數講我們傳入的參數變成action這種…

idea中配置svn及提交提示“未檢測到更改”提示

首先要安裝TortoiseSVN&#xff0c;選command line client tools&#xff1b; 在idea中&#xff0c;文件->設置->Subversion->如下圖 文件->設置->目錄映射->如下圖 初次導入到svn&#xff0c; 輸入服務器上的svn地址&#xff0c;等待成功即可&#xff1b;…

vue 實現dot-dropdown

<template><div class"app-container"><div class"mt30"></div><el-row :gutter"20"><!-- title --><!-- <div class"modt-box">導航管理</div> --><el-col :span"2&q…

使用 mysql2/promise 模塊返回以后,使用 await 返回數據總結

SELECT 返回結構 const [rows, fields] await db.query(SELECT * FROM folders);返回&#xff1a; rows: 是一個數組&#xff0c;包含所有查到的記錄。fields: 是字段的結構定義&#xff08;列信息&#xff09;&#xff0c;一般不用。 rows 是一個數組&#xff0c;包含所有…

Manus Metagloves pro高精度+無漂移+低延遲 ,重構VR/XR手部交互方式

manus metagloves pro是一款專為動畫制作、虛擬現實及游戲開發打造的高精度無線動作捕捉手套。采用先進的Quantum追蹤技術&#xff0c;實現毫米級動作捕捉&#xff0c;精準還原手指細節&#xff0c;顯著提升創作效率與交互真實感。 MANUS Metagloves Pro解鎖動捕 / 機器人 / XR…

Uniapp插件改造指南:如何讓vue-plugin支持HarmonyOS5原生能力?

一、分層架構設計 采用通用邏輯與平臺實現分離的三層結構&#xff1a; uni-plugin-harmony ├── common # 跨平臺通用層 │ ├── interfaces # 能力接口抽象&#xff08;如Scanner.ets&#xff09; │ └── utils # 工具類 ├── harmony …

P1040 [NOIP 2003 提高組] 加分二叉樹 題解

題目描述 設一個 n n n 個節點的二叉樹 tree \text{tree} tree 的中序遍歷為 ( 1 , 2 , 3 , … , n ) (1,2,3,\ldots,n) (1,2,3,…,n)&#xff0c;每個節點都有一個分數&#xff08;均為正整數&#xff09;。任一棵子樹 subtree \text{subtree} subtree&#xff08;包含 tr…

【Golang面試題】Data Race 問題怎么檢測?

Go Race Detector 深度指南&#xff1a;原理、用法與實戰技巧 一、什么是數據競爭&#xff1f; 在并發編程中&#xff0c;數據競爭發生在兩個或多個 goroutine 同時訪問同一內存位置&#xff0c;且至少有一個是寫操作時。這種競爭會導致不可預測的行為和極其難以調試的問題。…

257. 二叉樹的所有路徑(js)

257. 二叉樹的所有路徑——DFS 回溯&#xff08;js&#xff09; 題目描述解題思路完整代碼時間復雜度分析 題目描述 257. 二叉樹的所有路徑 解題思路 題意理解 給定一棵二叉樹&#xff0c;要求返回所有從根節點到葉子節點的路徑&#xff0c;路徑以字符串形式表示&#xff0c…

自動化文檔生成工具(親測可運行)

本文介紹了一個用Java編寫的自動化文檔生成工具&#xff0c;通過讀取開發清單文本自動生成格式規范的Word文檔。該工具的主要特點包括&#xff1a; 采用Apache POI庫處理Word文檔&#xff0c;支持多級標題和段落自動生成實現中文數字轉換功能&#xff0c;將編號轉換為"一、…