UGUI使用教程(十四)Text组件

发表于2018-01-27
评论0 3.9k浏览
Text是UGUI里非常常用的一个组件,可以根据字符串显示文字。但其实Text的代码并没有很多,因为大部分逻辑是在TextGenerator里实现的,而TextGenerator是UnityEngine命名空间下的类。为了让更多人可以使用Text组件,本篇文章就给大家介绍下Text组件的使用方法。

按照惯例,附上UGUI源码下载地址

虽然TextGenerator的实现被Unity官方藏起来了,但是我们可以借助TextMesh来管中窥豹。
新建一个3D Text,如图:

我们看到它为每个字幕画了一个矩形框(两个三角形),由此我们可猜测TextGenerator根据font文件里每一个字的大小和偏移量创建四个顶点和两个三角形,并按照font文件里每个字所在的位置设置uv坐标。

这就是TextGenerator的Populate方法,它在Text的OnPopulateMesh方法中被调用到。
protected override void OnPopulateMesh(VertexHelper toFill)  
{  
    if (font == null)  
        return;  
    // We don't care if we the font Texture changes while we are doing our Update.  
    // The end result of cachedTextGenerator will be valid for this instance.  
    // Otherwise we can get issues like Case 619238.  
    m_DisableFontTextureRebuiltCallback = true;  
    Vector2 extents = rectTransform.rect.size;  
    var settings = GetGenerationSettings(extents);  
    cachedTextGenerator.Populate(text, settings);  
    Rect inputRect = rectTransform.rect;  
    // get the text alignment anchor point for the text in local space  
    Vector2 textAnchorPivot = GetTextAnchorPivot(m_FontData.alignment);  
    Vector2 refPoint = Vector2.zero;  
    refPoint.x = (textAnchorPivot.x == 1 ? inputRect.xMax : inputRect.xMin);  
    refPoint.y = (textAnchorPivot.y == 0 ? inputRect.yMin : inputRect.yMax);  
    // Determine fraction of pixel to offset text mesh.  
    Vector2 roundingOffset = PixelAdjustPoint(refPoint) - refPoint;  
    // Apply the offset to the vertices  
    IList<UIVertex> verts = cachedTextGenerator.verts;  
    float unitsPerPixel = 1 / pixelsPerUnit;  
    //Last 4 verts are always a new line...  
    int vertCount = verts.Count - 4;  
    toFill.Clear();  
    if (roundingOffset != Vector2.zero)  
    {  
        for (int i = 0; i < vertCount; ++i)  
        {  
            int tempVertsIndex = i & 3;  
            m_TempVerts[tempVertsIndex] = verts[i];  
            m_TempVerts[tempVertsIndex].position *= unitsPerPixel;  
            m_TempVerts[tempVertsIndex].position.x += roundingOffset.x;  
            m_TempVerts[tempVertsIndex].position.y += roundingOffset.y;  
            if (tempVertsIndex == 3)  
                toFill.AddUIVertexQuad(m_TempVerts);  
        }  
    }  
    else  
    {  
        for (int i = 0; i < vertCount; ++i)  
        {  
            int tempVertsIndex = i & 3;  
            m_TempVerts[tempVertsIndex] = verts[i];  
            m_TempVerts[tempVertsIndex].position *= unitsPerPixel;  
            if (tempVertsIndex == 3)  
                toFill.AddUIVertexQuad(m_TempVerts);  
        }  
    }  
    m_DisableFontTextureRebuiltCallback = false;  
}  

Text继承自MaskableGraphic,后者继承自Graphic,在Text里重写了Graphic的OnPopulateMesh方法(参考UGUI内核大探究(七)Graphic),在重建图像的时候被调用到(UpdateGeometry方法)。

OnPopulateMesh方法里,根据用户的设置生成了一个TextGenerationSettings,然后调用了TextGenerator的Populate方法,生成mesh的顶点、顶点颜色、三角形和UV坐标。然后根据alignment获取textAnchorPivot(文本锚点轴心),并计算出偏移量(例如左对齐需要空出来左边)。最后遍历TextGenerator的顶点数组,将它们的位置除以pixelsPerUnit(像素每单元)并加上偏移量(如果有的话),得到的结果填到toFill(VertexHelper类型,Graphic用它来创建Mesh)里面。

另外在OnDisable、OnEnable(调用时机参见Untiy3D组件小贴士(一)OnEnabled与OnDisabled)或者修改font的时候,会调用FontUpdateTracker的UntrackText和TrackText方法。FontUpdateTracker是个静态类,维护了一个Dictionary<Font, List<Text>>类型的变量m_Tracked。TrackText里将Text加入List,并且如果之前Dictionary是空的,UntrackText会将Text移出List。FontUpdateTracker会监听Font静态类的textureRebuilt事件,回调RebuildForFont。RebuildForFont方法会根据传入的font找到List<Text>,然后调用每一个Text的FontTextureChanged方法(UpdateGeometry更新几何形状重新计算Mesh或SetAllDirty重建布局、顶点和材质)。

再补充一点关于FontData的代码。作为一个配置类,FontData本身没有什么可说的,但是它继承了ISerializationCallbackReceiver接口,这个接口需要实现OnBeforeSerialize和OnAfterDeserialize两个方法。这是两个很有趣的方法(虽然不是线程安全的,需要小心使用),它们分别会在序列化之前和反序列化之后调用。FontData的OnAfterDeserialize里便在反序列化之后,限定了m_FontSize、m_MinSize和m_MaxSize在0到300之间。

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引

0个评论