#UGUI Text组件扩展描边

起因

最近在帮助项目组扩展一些UI组件,以方便项目的开发,前面就扩展了Text组件的图文混排https://blog.csdn.net/tc3819171/article/details/84504014。我们都知道游戏里的文字如果是单纯的什么都没加就显示出来,会不怎么好看,而加描边,加阴影都有助于这个文字能更美观一点。UGUI给我们提供了一个outline的组件是内置的描边的组件,也能满足我们的需求,但是我在用framedebug检查的时候发现问题#UGUI Text组件扩展描边_第1张图片#UGUI Text组件扩展描边_第2张图片这里附上NGUI与UGUI描边的对比图,我们可以发现UGUI的顶点的数量比NGUI的顶点数量多了了一半,而两个实现的方式都是一样的,都是通过多绘制4次当前的文本然后做偏移才达到有描边的效果,但是为什么UGUI的顶点数会多那么多,这点让我很好奇,所以就下了UGUI的源码来看了下https://bitbucket.org/Unity-Technologies/ui/downloads/?tab=downloads
#UGUI Text组件扩展描边_第3张图片单从算法上看没有看出什么特别的地方,最后调试了下发现原来重写ModifyMesh方法的时候,得到的顶点数就不对,每个字应该都是一个面片4个顶点两个三角形就可以构成这个面片了,而ModifyMesh里得到的顶点确实6个顶点,我这里简单解释下为什么6个顶点能构成一个面片,4个顶点也能构成。
#UGUI Text组件扩展描边_第4张图片三角形(A1,A2,A3)和(B1,B2,B3)如果我们把这6个点全部都发给GPU让GPU去绘制的话那么会把重复的点发过去,造成带宽的浪费,而且模型中很多这种共边的三角形,所以人们就用一个顶点索引buffer来代替原来的方式,使用索引buffer的话我们就只需要发(A1,A2,A3,B1)这四个顶点,同时我们生成(A1,A2,A3),(B1,A2,A3)的索引,这样GPU就可以根据这些索引生成三角形,而不会造成冗余。这个应该算是UGUI底层的bug,UGUI在OnPopulateMesh生成顶点的时候是按照4个顶点一个面片来生成的,但是在ModifyMesh重写顶点的时候却没有按照4个顶点返回而是自己又把冗余的两个顶点返回了。

实现

我们扩展下Text组件,把描边这个功能嵌套进Text组件里,而不需要再写个脚本专门处理描边,而且在Text组件内部一次性处理完,而不是分成OnPopulateMesh处理一次,ModifyMesh又处理一次,这样效果更好。

if (roundingOffset != Vector2.zero)
        	{
            	for (int i = 0; i < vertCount; ++i)
            	{
                	int tempVertsIndex = i & 3;
                	rVertex[tempVertsIndex] = verts[i];
                	rVertex[tempVertsIndex].position *= unitsPerPixel;
                	rVertex[tempVertsIndex].position.x += roundingOffset.x;
                	rVertex[tempVertsIndex].position.y += roundingOffset.y;
                	if (tempVertsIndex == 3)
                    	toFill.AddUIVertexQuad(rVertex);
            	}
        	}
			else
			{
				for (int i = 0; i < verts.Count-4; i++)
				{
					int tempVertsIndex = i & 3;
                    rVertex[tempVertsIndex] = verts[i];
                    rVertex[tempVertsIndex].position.x += roundingOffset.x;
                    rVertex[tempVertsIndex].position.y += roundingOffset.y;
                    rVertex[tempVertsIndex].position *= unitsPerPixel;
                    rVertex[tempVertsIndex].uv1 = Vector2.zero;
                    if (m_OutLine && tempVertsIndex == 3)
                    {
						ApplyShadowZeroAlloc(ref rVertex, m_OutLineColor, m_OutLineOffsetX, m_OutLineOffsetY, toFill);
                        ApplyShadowZeroAlloc(ref  rVertex, m_OutLineColor, m_OutLineOffsetX, -m_OutLineOffsetY, toFill);
                        ApplyShadowZeroAlloc(ref  rVertex, m_OutLineColor, -m_OutLineOffsetX, m_OutLineOffsetY, toFill);
                        ApplyShadowZeroAlloc(ref rVertex, m_OutLineColor, -m_OutLineOffsetX, -m_OutLineOffsetY, toFill);
                        toFill.AddUIVertexQuad(rVertex);
                    }
				}
			}
private void ApplyShadowZeroAlloc(ref UIVertex[] rVertex, Color rEffectColor, float rEffectDistanceX, float rEffectDistanceY, VertexHelper rHelper)
    {
        for (int i = 0; i < rVertex.Length; i++)
        {
            Vector3 rPosition = rVertex[i].position;
            rPosition.x += rEffectDistanceX;
            rPosition.y += rEffectDistanceY;
            rVertex[i].position = rPosition;
            rVertex[i].color = rEffectColor;
        }
        rHelper.AddUIVertexQuad(rVertex);
        for (int i = 0; i < rVertex.Length; i++)
        {
            Vector3 rPosition = rVertex[i].position;
            rPosition.x -= rEffectDistanceX;
            rPosition.y -= rEffectDistanceY;
            rVertex[i].color = color;
            rVertex[i].position = rPosition;
        }
    }

以上就是outline的实现效果如下
#UGUI Text组件扩展描边_第5张图片

新思路

我觉得绘制4次也是很消耗,感觉没什么必要我们也看到过3D模型经常需要有个描边的效果,其实他们的实现方法很简单就是在第二次绘制模型的时候把顶点外扩一个几个像素,然后达到这个效果的,所以为什么我们不可以借用下这个思路。这样也就只用绘制一次了。

结束语

我会不定期更新自己在项目中遇到的问题以及解决方式,如果有兴趣的朋友可以关注我的博客和git https://github.com/LongTimeEnjoy/ExtensionUI

你可能感兴趣的:(Unity,UGUI)