Unity UGUI图文混排(五) -- 一张图集对应多个Text

继上一篇说的更新了一张图集对应多个Text的功能,为了节省资源嘛

这里,但是也没有舍弃之前的一个Text一个图集,因为我感觉应该两个都有用,于是我重新写了一个脚本


1.其实大体跟前面的都没变,解析标签,获取表情的相关数据,这里只是将绘制图片的功能,移植到SpriteGraphic上,本地增加了一个刷新图片绘制信息的函数。

麻烦的是去找到SpriteGraphic绘制图片,也是因为这个感觉有很大的潜在问题,不过基本能用,具体看脚本

using UnityEngine;
using UnityEngine.UI;
using System.Collections.Generic;
using System.Text.RegularExpressions;

public class InlieSpriteText : Text {

    /// <summary>
    /// 用正则取标签属性 名称-大小-宽度比例
    /// </summary>
    private static readonly Regex m_spriteTagRegex =
          new Regex(@"<quad name=(.+?) size=(d*.?d+%?) width=(d*.?d+%?) />", RegexOptions.Singleline);

    /// <summary>
    /// 图片资源
    /// </summary>
    private SpriteAsset m_spriteAsset;
    /// <summary>
    /// 图片渲染组件
    /// </summary>
    private SpriteGraphic m_spriteGraphic;
    /// <summary>
    /// CanvasRenderer
    /// </summary>
    private CanvasRenderer m_spriteCanvasRenderer;

    /// <summary>
    /// 图片渲染管理
    /// </summary>
    private SpriteGraphicManager m_SGManager;

    #region 动画标签解析
    //最多动态表情数量
    int AnimNum = 8;
  //  List<int> m_AnimIndex;
    List<SpriteTagInfor[]> m_AnimSpiteTag;
    public List<InlineSpriteInfor[]> m_AnimSpriteInfor;
    #endregion

    /// <summary>
    /// 初始化 
    /// </summary>
    protected override void OnEnable()
    {
        //在编辑器中,可能在最开始会出现一张图片,就是因为没有激活文本,在运行中是正常的。可根据需求在编辑器中选择激活...
        base.OnEnable();
        //对齐几何
        alignByGeometry = true;
        
        #region 为了将SpriteGraphicManager显示到最上级,这里的SpriteGraphicManager可能会放在最下面,所以需要从全局去找
        if (m_SGManager == null)
            m_SGManager = GameObject.FindObjectOfType<SpriteGraphicManager>();
        #endregion

        if (m_SGManager != null)
        {
            m_spriteGraphic = m_SGManager.GetComponent<SpriteGraphic>();
            m_spriteCanvasRenderer = m_SGManager.GetComponent<CanvasRenderer>();
            m_spriteAsset = m_spriteGraphic.m_spriteAsset;
        }

        //初始化 调用顶点绘制
        SetVerticesDirty();
    }


   


    /// <summary>
    /// 在设置顶点时调用
    /// </summary>
    public override void SetVerticesDirty()
    {
        base.SetVerticesDirty();
        
      //  m_AnimIndex = new List<int>();
        m_AnimSpiteTag = new List<SpriteTagInfor[]>();

        foreach (Match match in m_spriteTagRegex.Matches(text))
        {
            if (m_spriteAsset == null)
                return;

            #region 解析动画标签
            List<string> tempListName = new List<string>();
            for (int i = 0; i < m_spriteAsset.listSpriteInfor.Count; i++)
            {
               // Debug.Log((m_spriteAsset.listSpriteInfor[i].name));
                if (m_spriteAsset.listSpriteInfor[i].name.Contains(match.Groups[1].Value))
                {
                    tempListName.Add(m_spriteAsset.listSpriteInfor[i].name);
                }
            }
            if (tempListName.Count > 0)
            {
                SpriteTagInfor[] tempArrayTag = new SpriteTagInfor[tempListName.Count];
                for (int i = 0; i < tempArrayTag.Length; i++)
                {
                    tempArrayTag[i] = new SpriteTagInfor();
                    tempArrayTag[i].name = tempListName[i];
                    tempArrayTag[i].index = match.Index;
                    tempArrayTag[i].size = new Vector2(float.Parse(match.Groups[2].Value) * float.Parse(match.Groups[3].Value), float.Parse(match.Groups[2].Value));
                    tempArrayTag[i].Length = match.Length;
                }
                m_AnimSpiteTag.Add(tempArrayTag);
            }
            #endregion
        }
    }

    readonly UIVertex[] m_TempVerts = new UIVertex[4];
    /// <summary>
    /// 绘制模型
    /// </summary>
    /// <param name="toFill"></param>
    protected override void OnPopulateMesh(VertexHelper toFill)
    {
        //  base.OnPopulateMesh(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(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();

        //清楚乱码
        for (int i = 0; i < m_AnimSpiteTag.Count; i++)
        {
            if (m_AnimSpiteTag[i].Length > 0)
            {
                //UGUIText不支持<quad/>标签,表现为乱码,我这里将他的uv全设置为0,清除乱码
                for (int m = m_AnimSpiteTag[i][0].index * 4; m < m_AnimSpiteTag[i][0].index * 4 + 4; m++)
                {
                    UIVertex tempVertex = verts[m];
                    tempVertex.uv0 = Vector2.zero;
                    verts[m] = tempVertex;
                }
            }
        }
            //计算标签   其实应该计算偏移值后 再计算标签的值    算了 后面再继续改吧
            //  CalcQuadTag(verts);

        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);
            }
        }

        //计算标签 计算偏移值后 再计算标签的值
        List<UIVertex> vertsTemp = new List<UIVertex>();
        for (int i = 0; i < vertCount; i++)
        {
            UIVertex tempVer=new UIVertex();
            toFill.PopulateUIVertex(ref tempVer,i);
            vertsTemp.Add(tempVer);
        }
        CalcQuadTag(vertsTemp);

        m_DisableFontTextureRebuiltCallback = false;

        //更新绘制图片信息
        if(m_SGManager!=null)
            m_SGManager.UpdateSpriteInfor();
        //DrawSprite();
    }


    private IList<UIVertex> _OldVerts;

    #region 计算标签
    /// <summary>
    /// 解析quad标签  主要清除quad乱码 获取表情的位置
    /// </summary>
    /// <param name="verts"></param>
    void CalcQuadTag(IList<UIVertex> verts)
    {

        m_AnimSpriteInfor = new List<InlineSpriteInfor[]>();

        Vector3 _TempStartPos = Vector3.zero;
        if(m_SGManager!=null)
            _TempStartPos = transform.position - m_SGManager.transform.position;

        for (int i = 0; i < m_AnimSpiteTag.Count; i++)
        {
            SpriteTagInfor[] tempTagInfor = m_AnimSpiteTag[i];
            InlineSpriteInfor[] tempSpriteInfor = new InlineSpriteInfor[tempTagInfor.Length];
            for (int j = 0; j < tempTagInfor.Length; j++)
            {
                tempSpriteInfor[j] = new InlineSpriteInfor();
                tempSpriteInfor[j].textpos = _TempStartPos + verts[((tempTagInfor[j].index + 1) * 4) - 1].position;
                //设置图片的位置
                tempSpriteInfor[j].vertices = new Vector3[4];
                tempSpriteInfor[j].vertices[0] = new Vector3(0, 0, 0) + tempSpriteInfor[j].textpos;
                tempSpriteInfor[j].vertices[1] = new Vector3(tempTagInfor[j].size.x, tempTagInfor[j].size.y, 0) + tempSpriteInfor[j].textpos;
                tempSpriteInfor[j].vertices[2] = new Vector3(tempTagInfor[j].size.x, 0, 0) + tempSpriteInfor[j].textpos;
                tempSpriteInfor[j].vertices[3] = new Vector3(0, tempTagInfor[j].size.y, 0) + tempSpriteInfor[j].textpos;

                //计算其uv
                Rect newSpriteRect = m_spriteAsset.listSpriteInfor[0].rect;
                for (int m = 0; m < m_spriteAsset.listSpriteInfor.Count; m++)
                {
                    //通过标签的名称去索引spriteAsset里所对应的sprite的名称
                    if (tempTagInfor[j].name == m_spriteAsset.listSpriteInfor[m].name)
                        newSpriteRect = m_spriteAsset.listSpriteInfor[m].rect;
                }
                Vector2 newTexSize = new Vector2(m_spriteAsset.texSource.width, m_spriteAsset.texSource.height);

                tempSpriteInfor[j].uv = new Vector2[4];
                tempSpriteInfor[j].uv[0] = new Vector2(newSpriteRect.x / newTexSize.x, newSpriteRect.y / newTexSize.y);
                tempSpriteInfor[j].uv[1] = new Vector2((newSpriteRect.x + newSpriteRect.width) / newTexSize.x, (newSpriteRect.y + newSpriteRect.height) / newTexSize.y);
                tempSpriteInfor[j].uv[2] = new Vector2((newSpriteRect.x + newSpriteRect.width) / newTexSize.x, newSpriteRect.y / newTexSize.y);
                tempSpriteInfor[j].uv[3] = new Vector2(newSpriteRect.x / newTexSize.x, (newSpriteRect.y + newSpriteRect.height) / newTexSize.y);

                //声明三角顶点所需要的数组
                tempSpriteInfor[j].triangles = new int[6];
            }
            m_AnimSpriteInfor.Add(tempSpriteInfor);

            _OldVerts = verts;
        }
    }
    #endregion

    #region 更新图片的信息
    public void UpdateSpriteInfor()
    {
        if (_OldVerts == null)
            return;

        CalcQuadTag(_OldVerts);
    }
    #endregion

}
2.这里新写了一个SpriteGraphicManager脚本用来管理SpriteGraphic的图片绘制,和获取InlieSpriteText传来的相关绘制图片的信息,SpriteGraphicManager就是绑定在SpriteGraphic上的,因为UGUI的渲染顺序是从上到下,SpriteGraphic的放置位置就显得比较尴尬了,应该可以用shader更改渲染层级,没去试,先这样吧,我这里将SpriteGraphic放在最下面的

using UnityEngine;
using System.Collections;
using System.Collections.Generic;


/********
为了图片渲染在最上面
需要将他放砸canvas的最下层
应该可以改shader的渲染顺序  没去试  就这样写吧
*********/

[RequireComponent(typeof(SpriteGraphic))]
public class SpriteGraphicManager : MonoBehaviour {

    /// <summary>
    /// 需要渲染的图片信息列表
    /// </summary>
    private List<InlineSpriteInfor> listSprite;
    #region 动画标签解析
    //最多动态表情数量
    int AnimNum = 8;
    List<InlineSpriteInfor[]> m_AnimSpriteInfor;
    #endregion

    #region 更新图片信息
    public void UpdateSpriteInfor()
    {
        listSprite = new List<InlineSpriteInfor>();
        m_AnimSpriteInfor = new List<InlineSpriteInfor[]>();

        //  inline
        //  InlieSpriteText[] AllInlieSpriteText = GetComponentsInChildren<InlieSpriteText>();
        //  找到所有InlieSpriteText的物体  ----  这里隐藏问题蛮大的  他搜索的所有的InlieSpriteText
        //  包括InlieSpriteText也是全局搜索的SpriteGraphicManager,意思SpriteGraphicManager最好只有一个 
        //  当然 可以自定义根据功能   自己改了  我这里是这么定义的
        InlieSpriteText[] AllInlieSpriteText = GameObject.FindObjectsOfType<InlieSpriteText>();

        for (int i = 0; i < AllInlieSpriteText.Length; i++)
        {
            if (AllInlieSpriteText[i].m_AnimSpriteInfor != null)
            {
                AllInlieSpriteText[i].UpdateSpriteInfor();
                for (int j = 0; j < AllInlieSpriteText[i].m_AnimSpriteInfor.Count; j++)
                {
                    m_AnimSpriteInfor.Add(AllInlieSpriteText[i].m_AnimSpriteInfor[j]);
                    listSprite.Add(AllInlieSpriteText[i].m_AnimSpriteInfor[j][0]);
                }
            }
        }
        DrawSprite();
    }
    #endregion


    #region update刷新动画
    float fTime = 0.0f;
    int iIndex = 0;
    void Update()
    {
        if (m_AnimSpriteInfor == null)
            return;

        fTime += Time.deltaTime;
        if (fTime >= 0.1f)
        {
            //刷新一次 更新绘制图片的相关信息
            UpdateSpriteInfor();

            for (int i = 0; i < m_AnimSpriteInfor.Count; i++)
            {
                if (iIndex >= m_AnimSpriteInfor[i].Length)
                {
                    listSprite[i] = m_AnimSpriteInfor[i][0];
                }
                else
                {
                    listSprite[i] = m_AnimSpriteInfor[i][iIndex];
                }
            }
            DrawSprite();
            iIndex++;
            if (iIndex >= AnimNum)
            {
                iIndex = 0;
            }
            fTime = 0.0f;
        }
    }
    #endregion

    

    #region 绘制图片
    /// <summary>
    /// 绘制图片
    /// </summary>
    void DrawSprite()
    {
        Mesh m_spriteMesh = new Mesh();

        List<Vector3> tempVertices = new List<Vector3>();
        List<Vector2> tempUv = new List<Vector2>();
        List<int> tempTriangles = new List<int>();

        for (int i = 0; i < listSprite.Count; i++)
        {
            for (int j = 0; j < listSprite[i].vertices.Length; j++)
            {
                tempVertices.Add(listSprite[i].vertices[j]);
            }
            for (int j = 0; j < listSprite[i].uv.Length; j++)
            {
                tempUv.Add(listSprite[i].uv[j]);
            }
            for (int j = 0; j < listSprite[i].triangles.Length; j++)
            {
                tempTriangles.Add(listSprite[i].triangles[j]);
            }
        }
        //计算顶点绘制顺序
        for (int i = 0; i < tempTriangles.Count; i++)
        {
            if (i % 6 == 0)
            {
                int num = i / 6;
                tempTriangles[i] = 0 + 4 * num;
                tempTriangles[i + 1] = 1 + 4 * num;
                tempTriangles[i + 2] = 2 + 4 * num;

                tempTriangles[i + 3] = 1 + 4 * num;
                tempTriangles[i + 4] = 0 + 4 * num;
                tempTriangles[i + 5] = 3 + 4 * num;
            }
        }

        m_spriteMesh.vertices = tempVertices.ToArray();
        m_spriteMesh.uv = tempUv.ToArray();
        m_spriteMesh.triangles = tempTriangles.ToArray();

        if (m_spriteMesh == null)
            return;
        
        GetComponent<CanvasRenderer>().SetMesh(m_spriteMesh);
        GetComponent<SpriteGraphic>().UpdateMaterial();
    }
    #endregion

}
3.差不多一张图集对应多个Text的功能就完了,看一下截图,因为这都是根据之前的更新的,看了之前工程的同学都应该能看明白,我架设你们都看过了


4.这里还更新了一个小东西,就是做聊天demo的时候感觉之前的标签太长,比如<quad name=meat size=20 width=1 />,确实有点长,本来应该表情也绘制在输入框的,一是自己也没去测试,二是有同学已经测试,但是发现不少问题,我暂时也就将标签缩短了<#meat>


5.之前的工程的基本功能都是基本完善的,最新的基本都是一些功能和逻辑上的扩展:  工程链接

原文地址:https://www.cnblogs.com/liang123/p/6325847.html