扩展UGuiText支持文字背景

其实这个需求用TextMeshPro一分钟就能搞定,可TMP对中文动态字体的支持并不完美。针对即时输出的随机化文本,只能扩展Text来实现了。

基本思路

获得输出文本的每个字的位置和宽高数据。这个通过字体渲染时的顶点数据可以拿到。
算法整合每行的最大高度
整合所有行的bounds信息
根据bounds大小来绘制Image

Code

using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.UI;

public class TextBg : BaseMeshEffect
{
    public class CharQuad
    {
        public UIVertex TopLeft { get; set; }
        public UIVertex TopRight { get; set; }
        public UIVertex BottomRight { get; set; }
        public UIVertex BottomLeft { get; set; }

        public float Left { get; set; }
        public float Right { get; set; }
        public float Top { get; set; }
        public float Bottom { get; set; }

        public void Calculate()
        {
            Left = TopLeft.position.x;
            Right = TopRight.position.x;
            Top = TopLeft.position.y;
            Bottom = BottomLeft.position.y;
        }
    }

    public override void ModifyMesh(VertexHelper vh)
    {
        if (!IsActive())
        {
            return;
        }
        int count = vh.currentVertCount;
        if (count <= 0)
        {
            return;
        }
        List<UIVertex> vertices = new List<UIVertex>();
        for (int i = 0; i < count; i++)
        {
            UIVertex vertex = new UIVertex();
            vh.PopulateUIVertex(ref vertex, i);
            vertex.color = Color.red;
            vertices.Add(vertex);
            //vh.SetUIVertex(vertex, i);
        }
        int charlen = count / 4;
#if UNITY_EDITOR
        Debug.LogWarningFormat("text vertices count = {0} charlen = {1}", count, charlen);
#endif
        CharQuad[] charquads = new CharQuad[charlen];
        //根据顶点拓扑顺序
        for (int i = 0; i < charlen; i++)
        {
            CharQuad cquad = new CharQuad();
            cquad.TopLeft = vertices[i * 4];
            cquad.TopRight = vertices[i * 4 + 1];
            cquad.BottomRight = vertices[i * 4 + 2];
            cquad.BottomLeft = vertices[i * 4 + 3];
            cquad.Calculate();
            charquads[i] = cquad;
        }
        //计算text行字符
        //如果字符换行了
        //1.n+1字符的top<=n字符的bottom
        List<CharQuad[]> cquadslist = new List<CharQuad[]>();
        if (charlen == 1)
        {
            cquadslist = new List<CharQuad[]>();
            cquadslist.Add(charquads);
        }
        else
        {
            List<CharQuad> cqlist = new List<CharQuad>();
            cqlist.Add(charquads[0]);
            for (int i = 1; i < charquads.Length; i++)
            {
                CharQuad n0char = charquads[i - 1];
                CharQuad n1char = charquads[i];
                if (n1char.Top <= n0char.Bottom)
                {
                    cquadslist.Add(cqlist.ToArray());
                    cqlist.Clear();
                }
                cqlist.Add(charquads[i]);
            }
            if (cqlist.Count > 0)
            {
                cquadslist.Add(cqlist.ToArray());
            }
        }
        //计算bounds
        //计算text每一行的bound
        /*Vector4[] */bounds = new Vector4[cquadslist.Count];
        for (int i = 0; i < cquadslist.Count; i++)
        {
            CharQuad[] cquads = cquadslist[i];
            float left = float.MaxValue;
            float right = float.MinValue;
            float top = float.MinValue;
            float bottom = float.MaxValue;
            for (int k = 0; k < cquads.Length; k++)
            {
                CharQuad cq = cquads[k];
                if (left > cq.Left)
                {
                    left = cq.Left;
                }
                if (right < cq.Right)
                {
                    right = cq.Right;
                }
                if (top < cq.Top)
                {
                    top = cq.Top;
                }
                if (bottom > cq.Bottom)
                {
                    bottom = cq.Bottom;
                }
            }
#if UNITY_EDITOR
            Debug.LogWarningFormat("text mesh line = {0} charlen = {1} left = {2} right = {3} top = {4} bottom = {5}", i, cquads.Length, left, right, top, bottom);
#endif
            bounds[i] = new Vector4(left, right, top, bottom);
        }
        //bounds参数以text组件中心点为原点,所以参数存在负值,需要处理成正值
        //以text组件左下角为原点,即可完成正值化
        {
            float left = float.MaxValue;
            float right = float.MinValue;
            float top = float.MinValue;
            float bottom = float.MaxValue;
            for (int i = 0; i < bounds.Length; i++)
            {
                Vector4 bound = bounds[i];
                if (left > bound.x)
                {
                    left = bound.x;
                }
                if (right < bound.y)
                {
                    right = bound.y;
                }
                if (top < bound.z)
                {
                    top = bound.z;
                }
                if (bottom > bound.w)
                {
                    bottom = bound.w;
                }
            }
            float width = right - left + 1;
            float height = top - bottom + 1;
#if UNITY_EDITOR
            Debug.LogWarningFormat("text texture left = {0} right = {1} top = {2} bottom = {3} width = {4} height = {5}", left, right, top, bottom, width, height);
#endif
            StartCoroutine(delayDrawImage());
        }
    }

    Vector4[] bounds;
    private List<Image> _lines = new List<Image>();
    void CreateBg()
    {
        for (int i = 0; i < transform.childCount; i++)
        {
            GameObject.DestroyImmediate(transform.GetChild(i).gameObject);
            Debug.LogWarning("DESOTRY: " + i);
        }

        Vector4[] list = bounds;
        _lines.Clear();

        Debug.LogWarning("CreateUnderLines==========================list.length:" + list.Length);
        for (int i = 0; i < list.Length; i++)
        {
            //初始化
            GameObject obj = new GameObject();
            obj.transform.SetParent(transform, false);
            obj.name = "underline" + i;
            _lines.Add(obj.AddComponent<Image>());
            _lines[i].rectTransform.pivot = new Vector2(0, 1);
            _lines[i].rectTransform.anchorMin = new Vector2(0, 1);
            _lines[i].rectTransform.anchorMax = new Vector2(0, 1);

            //颜色和大小
            float fWidth = Mathf.Abs((list[i].y - list[i].x));
            float fHeight = Mathf.Abs((list[i].w - list[i].z));
            var tex = new Texture2D((int)fWidth, (int)fHeight, TextureFormat.ARGB32, false);
            Color[] colors = tex.GetPixels();
            for (int j = 0; j < colors.Length; j++)
                colors[j] = new Color(1, 0, 0, 0.2f);
            tex.SetPixels(colors);
            tex.Apply();
            _lines[i].sprite = Sprite.Create(tex, new Rect(0, 0, tex.width, tex.height), Vector2.zero);
            _lines[i].SetNativeSize();
            _lines[i].rectTransform.sizeDelta = new Vector2(fWidth, fHeight);
            _lines[i].rectTransform.anchorMin = new Vector2(0.5f, 0.5f);
            _lines[i].rectTransform.anchorMax = new Vector2(0.5f, 0.5f);

            //坐标
            float x =  list[i].x;
            _lines[i].rectTransform.anchoredPosition = new Vector2(x, list[i].z);
        }
    }

    IEnumerator delayDrawImage()
    {
        yield return null;
        CreateBg();
    }
}

备注

Image的删除会报错,有空再优化了

原文地址:https://www.cnblogs.com/CodeKnight/p/15689276.html