UGUI源码:
https://bitbucket.org/Unity-Technologies/ui/downloads/?tab=tags
首先下载一份UGUI源码,这里我下载的版本是5.3.2f1。然后找到Text.cs,里面有方法OnPopulateMesh,这个方法会修改文字的顶点。而图文混排,涉及到顶点数据的修改。因此,我们的重点就是对这个方法进行修改,这里给出一个最简单的重写版本,它和普通的text是一样的。Text的渲染过程是由TextGenerator产生顶点数据,配合字体产生的贴图最终显示在屏幕上。
1 using System.Collections.Generic; 2 using System.Text.RegularExpressions; 3 using System.Text; 4 using UnityEngine.EventSystems; 5 using System; 6 using UnityEngine; 7 using UnityEngine.UI; 8 9 //下划线<material=underline c=#ffffff h=1 n=*** p=***>blablabla...</material> 10 public class RichTextTest : Text { 11 12 private FontData fontData = FontData.defaultFontData; 13 14 protected RichTextTest() 15 { 16 fontData = typeof(Text).GetField("m_FontData", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).GetValue(this) as FontData; 17 } 18 19 readonly UIVertex[] m_TempVerts = new UIVertex[4]; 20 protected override void OnPopulateMesh(VertexHelper toFill) 21 { 22 if (font == null) 23 return; 24 25 // We don't care if we the font Texture changes while we are doing our Update. 26 // The end result of cachedTextGenerator will be valid for this instance. 27 // Otherwise we can get issues like Case 619238. 28 m_DisableFontTextureRebuiltCallback = true; 29 30 Vector2 extents = rectTransform.rect.size; 31 32 var settings = GetGenerationSettings(extents); 33 cachedTextGenerator.Populate(text, settings); 34 35 Rect inputRect = rectTransform.rect; 36 37 // get the text alignment anchor point for the text in local space 38 Vector2 textAnchorPivot = GetTextAnchorPivot(fontData.alignment); 39 Vector2 refPoint = Vector2.zero; 40 refPoint.x = Mathf.Lerp(inputRect.xMin, inputRect.xMax, textAnchorPivot.x); 41 refPoint.y = Mathf.Lerp(inputRect.yMin, inputRect.yMax, textAnchorPivot.y); 42 43 // Determine fraction of pixel to offset text mesh. 44 Vector2 roundingOffset = PixelAdjustPoint(refPoint) - refPoint; 45 46 // Apply the offset to the vertices 47 IList<UIVertex> verts = cachedTextGenerator.verts; 48 float unitsPerPixel = 1 / pixelsPerUnit; 49 //Last 4 verts are always a new line... 50 int vertCount = verts.Count - 4; 51 52 toFill.Clear(); 53 if (roundingOffset != Vector2.zero) 54 { 55 for (int i = 0; i < vertCount; ++i) 56 { 57 int tempVertsIndex = i & 3; 58 m_TempVerts[tempVertsIndex] = verts[i]; 59 m_TempVerts[tempVertsIndex].position *= unitsPerPixel; 60 m_TempVerts[tempVertsIndex].position.x += roundingOffset.x; 61 m_TempVerts[tempVertsIndex].position.y += roundingOffset.y; 62 if (tempVertsIndex == 3) 63 toFill.AddUIVertexQuad(m_TempVerts); 64 } 65 } 66 else 67 { 68 for (int i = 0; i < vertCount; ++i) 69 { 70 int tempVertsIndex = i & 3; 71 m_TempVerts[tempVertsIndex] = verts[i]; 72 m_TempVerts[tempVertsIndex].position *= unitsPerPixel; 73 if (tempVertsIndex == 3) 74 toFill.AddUIVertexQuad(m_TempVerts); 75 } 76 } 77 m_DisableFontTextureRebuiltCallback = false; 78 } 79 }
分析一下上面的代码是怎么实现的:
首先,复制OnPopulateMesh这个方法,发现需要m_TempVerts这个变量,这个变量其实充当临时变量的作用,因此直接复制过来即可。然后提示需要m_FontData这个变量,这个变量是FontData类型的,因此跳到FontData.cs,发现FontData这个东西几乎就是Text在Inspector面板上的东西,但是m_FontData是私有的,这里可以通过反射去获取这个私有变量,又因为序列化的原因,不能同名,所以这里使用变量名fontData。
内部源码分析:
1.unitsPerPixel
意思为每像素单位,即一个像素占几个unity单位。因此可以推出这个cachedTextGenerator.verts是像素单位(cachedTextGenerator.verts赋值给m_TempVerts,然后再做乘法)。那么这个变量的作用是什么呢?可以通过改变Game视图的分辨率,然后打印下,发现随分辨率变化,这个值也在变化,不过文字在矩形框的位置还是不变的,因此可以推测这个是用于自适应的。
2.m_TempVerts
在Text控件中赋值2个字,然后在循环中添加代码:Debug.LogWarning(i + "_" + tempVertsIndex + "_" + m_TempVerts[tempVertsIndex].position);,结果如下图。
可以看到,1个字符有4个顶点,而顶点的排列如下:
3.顶点的生成
通过OnPopulateMesh这个方法可以看到,生成顶点的方法如下。不过生成的顶点要去掉最后四个点,可以打印一下看看最后四个点的位置。至于为什么要这样处理呢,因为TextGenerator这个类是在UnityEngine命名空间下的,所以我们就不得而知了...
Vector2 extents = rectTransform.rect.size; var settings = GetGenerationSettings(extents); cachedTextGenerator.Populate(text, settings); IList<UIVertex> verts = cachedTextGenerator.verts; //Last 4 verts are always a new line... int vertCount = verts.Count - 4;
4.获取字符串的长度
TextGenerator类中的GetPreferredWidth方法可以获取字符串的长度。具体使用方式可以看Text类中的preferredWidth,这个方法得到的宽度同样是像素单位的,因此要做转换处理。