当前位置:   article > 正文

untiy UGUI源码分析(3)Text_unity text 源码

unity text 源码

直接分析Graphic源码不太直观 ,我们从顶层分析Text控件的运作机制这样可以知道Graphic的运作机制了,首先Text继承关系如下

public class Text : MaskableGraphic, ILayoutElement

public abstract class MaskableGraphic : Graphic, IClippable, IMaskable, IMaterialModifier

我们需要分析Text控件何时会刷新,可以肯定地是Graphic使用了脏标记模式(游戏编程

模式里面有这个设计模式),每当Text的一些属性发生改变后不会立即更新属性,而是将这些变化的属性打上标记,并在某一个时刻一起更新。

鉴于这些类比较复杂我们通过一个简单的行为去分析Text内部的运行机制。我们首先考虑当Text控件的内容发生变化时,Text控件到底执行了那些方法。通过下面代码可以看出,当Text控件的内容发生变化时会将顶点(vertex)以及布局(layout)打上标记。

  1. public virtual string text
  2. {
  3. get
  4. {
  5. return m_Text;
  6. }
  7. set
  8. {
  9. if (String.IsNullOrEmpty(value))
  10. {
  11. if (String.IsNullOrEmpty(m_Text))
  12. return;
  13. m_Text = "";
  14. SetVerticesDirty();
  15. }
  16. else if (m_Text != value)
  17. {
  18. m_Text = value;
  19. SetVerticesDirty();
  20. SetLayoutDirty();
  21. }
  22. }
  23. }

我们继续分析SetVerticesDirty方法。这个方法跳到Graphic类里面来了,这个方法首先设置标志位,然后CanvasUpdateRegistry.RegisterCanvasElementForGraphicRebuild 方法会向一个队列中添加一个元素,到这里Text的部分就分析完了。但是不对啊,Text控件的内容什么时候渲染更新呢,我们继续分析CanvasUpdateRegistry

return m_GraphicRebuildQueue.AddUnique(element);

  1. public virtual void SetVerticesDirty()
  2. {
  3. if (!IsActive())
  4. return;
  5. m_VertsDirty = true;
  6. CanvasUpdateRegistry.RegisterCanvasElementForGraphicRebuild(this);
  7. if (m_OnDirtyVertsCallback != null)
  8. m_OnDirtyVertsCallback();
  9. }

接下来我们分析 CanvasUpdateRegistry 类,他的构造函数如下会向Canvas中的willRenderCanvases静态事件添加一个回调,由于Canvas没有源码,这里我推测performUpdate这个回调会每一帧都执行更新。performUpdate这个回调的功能很简单就是根据从m_GraphicRebuildQueue以及m_LayoutRebuildQueue两个队列中取出元素,并执行每一个元素的Rebuild方法,Rebuild方法会更具不同的枚举类型进行不同的操作。Rebuild完成后执行LayoutComplete()GraphicUpdateComplete()方法。

  1. public enum CanvasUpdate
  2. {
  3. Prelayout,
  4. Layout,
  5. PostLayout,
  6. PreRender,
  7. LatePreRender,
  8. MaxUpdateValue,
  9. }
  10. protected CanvasUpdateRegistry()
  11. {
  12. Canvas.willRenderCanvases += PerformUpdate;
  13. }
  14. private void PerformUpdate()
  15. {
  16. UISystemProfilerApi.BeginSample(UISystemProfilerApi.SampleType.Layout);
  17. CleanInvalidItems();
  18. m_PerformingLayoutUpdate = true;
  19. m_LayoutRebuildQueue.Sort(s_SortLayoutFunction);
  20. for (int i = 0; i <= (int)CanvasUpdate.PostLayout; i++)
  21. {
  22. UnityEngine.Profiling.Profiler.BeginSample(m_CanvasUpdateProfilerStrings[i]);
  23. for (int j = 0; j < m_LayoutRebuildQueue.Count; j++)
  24. {
  25. var rebuild = m_LayoutRebuildQueue[j];
  26. try
  27. {
  28. if (ObjectValidForUpdate(rebuild))
  29. rebuild.Rebuild((CanvasUpdate)i);
  30. }
  31. catch (Exception e)
  32. {
  33. Debug.LogException(e, rebuild.transform);
  34. }
  35. }
  36. UnityEngine.Profiling.Profiler.EndSample();
  37. }
  38. for (int i = 0; i < m_LayoutRebuildQueue.Count; ++i)
  39. m_LayoutRebuildQueue[i].LayoutComplete();
  40. m_LayoutRebuildQueue.Clear();
  41. m_PerformingLayoutUpdate = false;
  42. UISystemProfilerApi.EndSample(UISystemProfilerApi.SampleType.Layout);
  43. UISystemProfilerApi.BeginSample(UISystemProfilerApi.SampleType.Render);
  44. // now layout is complete do culling...
  45. UnityEngine.Profiling.Profiler.BeginSample(m_CullingUpdateProfilerString);
  46. ClipperRegistry.instance.Cull();
  47. UnityEngine.Profiling.Profiler.EndSample();
  48. m_PerformingGraphicUpdate = true;
  49. for (var i = (int)CanvasUpdate.PreRender; i < (int)CanvasUpdate.MaxUpdateValue; i++)
  50. {
  51. UnityEngine.Profiling.Profiler.BeginSample(m_CanvasUpdateProfilerStrings[i]);
  52. for (var k = 0; k < m_GraphicRebuildQueue.Count; k++)
  53. {
  54. try
  55. {
  56. var element = m_GraphicRebuildQueue[k];
  57. if (ObjectValidForUpdate(element))
  58. element.Rebuild((CanvasUpdate)i);
  59. }
  60. catch (Exception e)
  61. {
  62. Debug.LogException(e, m_GraphicRebuildQueue[k].transform);
  63. }
  64. }
  65. UnityEngine.Profiling.Profiler.EndSample();
  66. }
  67. for (int i = 0; i < m_GraphicRebuildQueue.Count; ++i)
  68. m_GraphicRebuildQueue[i].GraphicUpdateComplete();
  69. m_GraphicRebuildQueue.Clear();
  70. m_PerformingGraphicUpdate = false;
  71. UISystemProfilerApi.EndSample(UISystemProfilerApi.SampleType.Render);

经过上面的分析我们继续来看,Text的Rebuild方法,首先判断脏标记,并执行相应的更新。

  1. public virtual void Rebuild(CanvasUpdate update)
  2. {
  3. if (canvasRenderer == null || canvasRenderer.cull)
  4. return;
  5. switch (update)
  6. {
  7. case CanvasUpdate.PreRender:
  8. if (m_VertsDirty)
  9. {
  10. UpdateGeometry();
  11. m_VertsDirty = false;
  12. }
  13. if (m_MaterialDirty)
  14. {
  15. UpdateMaterial();
  16. m_MaterialDirty = false;
  17. }
  18. break;
  19. }
  20. }

综上所述,我们可以很清楚的知道Text控件某些属性改变后的更新过程。

1.当某些属性改变时,可能时图像(Graphic)或者布局(layout)Text会进行脏标记,并向 CanvasUpdateRegistry 类中的相应队列中添加一个元素(把Text的引用添加进去,Text继承了ICanvasElement接口)。

2.CanvasUpdateRegistry 每一帧会调用PerformUpadate方法,该方法会遍历队列中的元素,并执行ICanvasElement接口的rebuild方法(Text重写了rebuild方法实际执行的时Text的rebuild方法)

3.最后在Text的rebuild方法中会根据脏标记,去更新Text的一些属性。

到这里Text的更新循环已经分析完毕了,现在继续分析Text是如何更新自己的属性,并且渲染到屏幕上。我们只分析Text的Geometry(几何可以说就是顶点)部分的更新,在rebuild中执行了UpdateGeometry方法,这个方法调用几个方法后,最终执行了DoMeshGeneration

方法。s_VertexHelper是一个工具类的实例,可以帮助生成UI的Mesh。然后DoMeshGeneration方法会执行OnPopulateMesh方法向s_VertexHelper更新Mesh,最后通过CanvasRenderer(可以在unity面板找到)组件进行渲染。

  1. private void DoMeshGeneration()
  2. {
  3. if (rectTransform != null && rectTransform.rect.width >= 0 && rectTransform.rect.height >= 0)
  4. OnPopulateMesh(s_VertexHelper);
  5. else
  6. s_VertexHelper.Clear(); // clear the vertex helper so invalid graphics dont draw.
  7. var components = ListPool<Component>.Get();
  8. GetComponents(typeof(IMeshModifier), components);
  9. for (var i = 0; i < components.Count; i++)
  10. ((IMeshModifier)components[i]).ModifyMesh(s_VertexHelper);
  11. ListPool<Component>.Release(components);
  12. s_VertexHelper.FillMesh(workerMesh);
  13. canvasRenderer.SetMesh(workerMesh);
  14. }
  15. protected override void OnPopulateMesh(VertexHelper toFill)
  16. {
  17. if (font == null)
  18. return;
  19. // We don't care if we the font Texture changes while we are doing our Update.
  20. // The end result of cachedTextGenerator will be valid for this instance.
  21. // Otherwise we can get issues like Case 619238.
  22. m_DisableFontTextureRebuiltCallback = true;
  23. Vector2 extents = rectTransform.rect.size;
  24. var settings = GetGenerationSettings(extents);
  25. cachedTextGenerator.PopulateWithErrors(text, settings, gameObject);
  26. // Apply the offset to the vertices
  27. IList<UIVertex> verts = cachedTextGenerator.verts;
  28. float unitsPerPixel = 1 / pixelsPerUnit;
  29. int vertCount = verts.Count;
  30. // We have no verts to process just return (case 1037923)
  31. if (vertCount <= 0)
  32. {
  33. toFill.Clear();
  34. return;
  35. }
  36. Vector2 roundingOffset = new Vector2(verts[0].position.x, verts[0].position.y) * unitsPerPixel;
  37. roundingOffset = PixelAdjustPoint(roundingOffset) - roundingOffset;
  38. toFill.Clear();
  39. if (roundingOffset != Vector2.zero)
  40. {
  41. for (int i = 0; i < vertCount; ++i)
  42. {
  43. int tempVertsIndex = i & 3;
  44. m_TempVerts[tempVertsIndex] = verts[i];
  45. m_TempVerts[tempVertsIndex].position *= unitsPerPixel;
  46. m_TempVerts[tempVertsIndex].position.x += roundingOffset.x;
  47. m_TempVerts[tempVertsIndex].position.y += roundingOffset.y;
  48. if (tempVertsIndex == 3)
  49. toFill.AddUIVertexQuad(m_TempVerts);
  50. }
  51. }
  52. else
  53. {
  54. for (int i = 0; i < vertCount; ++i)
  55. {
  56. int tempVertsIndex = i & 3;
  57. m_TempVerts[tempVertsIndex] = verts[i];
  58. m_TempVerts[tempVertsIndex].position *= unitsPerPixel;
  59. if (tempVertsIndex == 3)
  60. toFill.AddUIVertexQuad(m_TempVerts);
  61. }
  62. }
  63. m_DisableFontTextureRebuiltCallback = false;
  64. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/人工智能uu/article/detail/897980
推荐阅读
相关标签
  

闽ICP备14008679号