UGUI源码(四)Text

Text.cs

//部分源码
public class Text : MaskableGraphic, ILayoutElement
{
	//Text 组件在 Inspector 面板上显示的属性
    [SerializeField] private FontData m_FontData = FontData.defaultFontData;

	//TextGenerator是文本生成器,文字的大部分功能都是由这个类实现的
	//Caches vertices, character info, and line info for memory friendlyness.
	public TextGenerator cachedTextGenerator
    {
    	get { return m_TextCache ?? (m_TextCache = (m_Text.Length != 0 ? new TextGenerator(m_Text.Length) : new TextGenerator())); }
    }

    //字体的贴图保存在Font.material.mainTexture中,通过基类Graphic.UpdateMaterial更新材质(同Image)
    public override Texture mainTexture
    {
        get
        {
            if (font != null && font.material != null && font.material.mainTexture != null)
                return font.material.mainTexture;

            if (m_Material != null)
                return m_Material.mainTexture;

            return base.mainTexture;
        }
    }
    
	readonly UIVertex[] m_TempVerts = new UIVertex[4];
	
	//更新网格(核心函数)
	//扩展:图文混排主要是修改源码中这部分逻辑实现
    protected override void OnPopulateMesh(VertexHelper toFill)
    {
        if (font == null)
            return;
        
        m_DisableFontTextureRebuiltCallback = true;

        Vector2 extents = rectTransform.rect.size;

		//获取字体生成所需的生成配置
        var settings = GetGenerationSettings(extents);

		//将使用生成配置为字符串生成顶点和其他数据。
        cachedTextGenerator.PopulateWithErrors(text, settings, gameObject);

		//生成的顶点数组。
        IList<UIVertex> verts = cachedTextGenerator.verts;
        // (float)m_FontData.fontSize / font.fontSize
        float unitsPerPixel = 1 / pixelsPerUnit;
        //默认字符最后一个字符是 '/n'
        int vertCount = verts.Count - 4;

        Vector2 roundingOffset = new Vector2(verts[0].position.x, verts[0].position.y) * unitsPerPixel;
        roundingOffset = PixelAdjustPoint(roundingOffset) - roundingOffset;
        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;
    }

	//获取字体生成所需的配置对象
	public TextGenerationSettings GetGenerationSettings(Vector2 extents)
    {
        var settings = new TextGenerationSettings();

        settings.generationExtents = extents;
        if (font != null && font.dynamic)
        {
            settings.fontSize = m_FontData.fontSize;
            settings.resizeTextMinSize = m_FontData.minSize;
            settings.resizeTextMaxSize = m_FontData.maxSize;
        }

        settings.textAnchor = m_FontData.alignment;
        settings.alignByGeometry = m_FontData.alignByGeometry;
        settings.scaleFactor = pixelsPerUnit;
        settings.color = color;
        settings.font = font;
        settings.pivot = rectTransform.pivot;
        settings.richText = m_FontData.richText;
        settings.lineSpacing = m_FontData.lineSpacing;
        settings.fontStyle = m_FontData.fontStyle;
        settings.resizeTextForBestFit = m_FontData.bestFit;
        settings.updateBounds = false;
        settings.horizontalOverflow = m_FontData.horizontalOverflow;
        settings.verticalOverflow = m_FontData.verticalOverflow;

        return settings;
    }
}

Shadow 与 Outline

字体阴影和描边效果:在OnPopulateMesh()更新网格之后,会遍历实现了IMeshModifier的组件,调用ModifyMesh方法修改Mesh。Shadow和Outline都实现了IMeshModifier接口,具体实现参考UGUI源码(二)Graphic.cs中的DoMeshGeneration()

Shadow.cs

//部分源码
public class Shadow : BaseMeshEffect
{

    public override void ModifyMesh(VertexHelper vh)
    {
        if (!IsActive())
            return;

        var output = ListPool<UIVertex>.Get();
        //获取当前顶点数据
        vh.GetUIVertexStream(output);

		//复制一组当前顶点,设置顶点偏移(effectDistance)和颜色(effectColor)
        ApplyShadow(output, effectColor, 0, output.Count, effectDistance.x, effectDistance.y);
        vh.Clear();
        //将新增之后的新顶点数据存入网格数据中
        vh.AddUIVertexTriangleStream(output);
        ListPool<UIVertex>.Release(output);
    }
    
    protected void ApplyShadow(List<UIVertex> verts, Color32 color, int start, int end, float x, float y)
    {
        ApplyShadowZeroAlloc(verts, color, start, end, x, y);
    }

	//复制了一组顶点存入verts中
	protected void ApplyShadowZeroAlloc(List<UIVertex> verts, Color32 color, int start, int end, float x, float y)
    {
        UIVertex vt;

        var neededCapacity = verts.Count + end - start;
        if (verts.Capacity < neededCapacity)
            verts.Capacity = neededCapacity;

        for (int i = start; i < end; ++i)
        {
            vt = verts[i];
            verts.Add(vt);

            Vector3 v = vt.position;
            v.x += x;
            v.y += y;
            vt.position = v;
            var newColor = color;
            if (m_UseGraphicAlpha)
                newColor.a = (byte)((newColor.a * verts[i].color.a) / 255);
            vt.color = newColor;
            verts[i] = vt;
        }
    }
}

Outline.cs

描边效果,继承自Shadow,只是在原来顶点基础上增加了4倍的顶点
使用Outline组件,三角面是原始网格三角面的四倍,不建议使用

//源码
public class Outline : Shadow
{
    public override void ModifyMesh(VertexHelper vh)
    {
        if (!IsActive())
            return;

        var verts = ListPool<UIVertex>.Get();
        vh.GetUIVertexStream(verts);

        var neededCpacity = verts.Count * 5;
        if (verts.Capacity < neededCpacity)
            verts.Capacity = neededCpacity;

		//生成4倍的顶点数据
        var start = 0;
        var end = verts.Count;
        ApplyShadowZeroAlloc(verts, effectColor, start, verts.Count, effectDistance.x, effectDistance.y);

        start = end;
        end = verts.Count;
        ApplyShadowZeroAlloc(verts, effectColor, start, verts.Count, effectDistance.x, -effectDistance.y);

        start = end;
        end = verts.Count;
        ApplyShadowZeroAlloc(verts, effectColor, start, verts.Count, -effectDistance.x, effectDistance.y);

        start = end;
        end = verts.Count;
        ApplyShadowZeroAlloc(verts, effectColor, start, verts.Count, -effectDistance.x, -effectDistance.y);

        vh.Clear();
        vh.AddUIVertexTriangleStream(verts);
        ListPool<UIVertex>.Release(verts);
    }
}

Outline组件慎用,使用shader实现

Shader "UI/Outline"
{
	Properties
	{
		[PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {}
        _Color ("Tint", Color) = (1,1,1,1)

        _StencilComp ("Stencil Comparison", Float) = 8
        _Stencil ("Stencil ID", Float) = 0
        _StencilOp ("Stencil Operation", Float) = 0
        _StencilWriteMask ("Stencil Write Mask", Float) = 255
        _StencilReadMask ("Stencil Read Mask", Float) = 255

        _Outline ("Outline", Range(0, 1)) = 0
        _OutlineColor ("Outline Color", Color) = (1, 1, 1, 1)
        _OutlineAlphaMul ("OutlineAlphaMul", Range(0, 5)) = 2
	}
    
	SubShader
	{
		Tags
        { 
            "Queue"="Transparent" 
            "IgnoreProjector"="True" 
            "RenderType"="Transparent" 
            "PreviewType"="Plane"
            "CanUseSpriteAtlas"="True"
        }

        Stencil
        {
            Ref [_Stencil]
            Comp [_StencilComp]
            Pass [_StencilOp]
            ReadMask [_StencilReadMask]
            WriteMask [_StencilWriteMask]
        }

        Cull Off
        Lighting Off
        ZWrite Off
        ZTest [unity_GUIZTestMode]
        Blend SrcAlpha OneMinusSrcAlpha


		Pass
		{
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag

			
			#include "UnityCG.cginc"
            #include "UnityUI.cginc"

			struct appdata
			{
				float4 vertex : POSITION;
                float4 color : COLOR;
				float2 uv : TEXCOORD0;
                UNITY_VERTEX_INPUT_INSTANCE_ID
			};

			struct v2f
			{
				float4 vertex : SV_POSITION;
                fixed4 color : COLOR;
                float2 uv : TEXCOORD0;
                float4 worldPosition : TEXCOORD1;
                UNITY_VERTEX_OUTPUT_STEREO
			};

            fixed4 _Color;
            fixed4 _TextureSampleAdd;
            float4 _ClipRect;
			
			v2f vert (appdata v)
			{
				v2f o;
                UNITY_SETUP_INSTANCE_ID(v);
                UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
				o.vertex = UnityObjectToClipPos(v.vertex);
                o.color = v.color * _Color;
				o.uv = v.uv;
                o.worldPosition = v.vertex;
				
				return o;
			}

            sampler2D _MainTex;
            float4 _MainTex_TexelSize;
            float _Outline;
            fixed4 _OutlineColor;
            float _OutlineAlphaMul;
			
			fixed4 frag (v2f i) : SV_Target
			{
                fixed2 up = i.uv + _MainTex_TexelSize.xy * float2(0, 1) * _Outline;
                fixed2 down = i.uv + _MainTex_TexelSize.xy * float2(0, -1) * _Outline;
                fixed2 left = i.uv + _MainTex_TexelSize.xy * float2(-1, 0) * _Outline;
                fixed2 right = i.uv + _MainTex_TexelSize.xy * float2(1, 0) * _Outline;
                fixed2 ul = i.uv + _MainTex_TexelSize.xy * float2(-1, 1) * _Outline;
                fixed2 ur = i.uv + _MainTex_TexelSize.xy * float2(1, 1) * _Outline;
                fixed2 dl = i.uv + _MainTex_TexelSize.xy * float2(-1, -1) * _Outline;
                fixed2 dr = i.uv + _MainTex_TexelSize.xy * float2(1, -1) * _Outline;

                fixed4 upColor = tex2D(_MainTex, up) + _TextureSampleAdd;
                fixed4 downColor = tex2D(_MainTex, down) + _TextureSampleAdd;
                fixed4 leftColor = tex2D(_MainTex, left) + _TextureSampleAdd;
                fixed4 rightColor = tex2D(_MainTex, right) + _TextureSampleAdd;
                fixed4 ulColor = tex2D(_MainTex, ul) + _TextureSampleAdd;
                fixed4 urColor = tex2D(_MainTex, ur) + _TextureSampleAdd;
                fixed4 dlColor = tex2D(_MainTex, dl) + _TextureSampleAdd;
                fixed4 drColor = tex2D(_MainTex, dr) + _TextureSampleAdd;
                fixed4 texColor = (tex2D(_MainTex, i.uv) + _TextureSampleAdd) * i.color;

              
                float alphaSum = upColor.a + downColor.a + leftColor.a + rightColor.a + ulColor.a + urColor.a + dlColor.a + drColor.a + texColor.a;
                float alphaAva = alphaSum / 9;
                float isEdge = _Outline > 0 && alphaSum > 0;

                    
                fixed4 edgeColor = texColor * alphaAva + _OutlineColor * (1 - alphaAva);
                edgeColor.a = saturate(alphaAva * _OutlineAlphaMul);

                fixed4 color = edgeColor * isEdge + texColor * (1 - isEdge);
                color.a *= UnityGet2DClipping(i.worldPosition.xy, _ClipRect);

            #if !defined(UNITY_COLORSPACE_GAMMA)
                color.rgb = LinearToGammaSpace(color.rgb);
            #endif
                return color;
			}
			ENDCG
		}
	}
}

UGUI源码(五)VertexHelper.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值