Unity UGUI图文混排源码(二)
Unity UGUI图文混排源码(一):http://blog.csdn.net/qq992817263/article/details/51112304
Unity UGUI图文混排源码(二):http://blog.csdn.net/qq992817263/article/details/51112311
为了方便整理,申请了一个专栏,链接:Unity UGUI图文混排专栏
图文混排解决方案二:
通过继承Text组件来获取文字的UIVertex并得到他的位置,通过Text富文本的<quad />来为图片占位,这样来实现图文混排,这里参考了一篇博客:http://blog.csdn.net/akof1314/article/details/49028279
1.这里我们会使用到一个SpriteAsset,具体的创建方法:http://blog.csdn.net/qq992817263/article/details/50958025
2.自定义一个渲染组件,用来渲染图片,后面我会将他放在Text文本下,这里会用一个参数,就是上面的SpriteAsset文件,具体代码如下
using UnityEngine; using UnityEngine.UI; using System.Collections; public class SpriteGraphic : MaskableGraphic { public SpriteAsset m_spriteAsset; public override Texture mainTexture { get { if (m_spriteAsset == null) return s_WhiteTexture; if (m_spriteAsset.texSource == null) return s_WhiteTexture; else return m_spriteAsset.texSource; } } #if UNITY_EDITOR //在编辑器下 protected override void OnValidate() { base.OnValidate(); //Debug.Log("Texture ID is " + this.texture.GetInstanceID()); } #endif protected override void OnRectTransformDimensionsChange() { // base.OnRectTransformDimensionsChange(); } /// <summary> /// 绘制后 需要更新材质 /// </summary> public new void UpdateMaterial() { base.UpdateMaterial(); } }
3.自定义一个脚本用来继承Text,这里就是一个较为核心的代码,里面写了较为详细的注释
using UnityEngine; using UnityEngine.UI; using System.Collections.Generic; using System.Text.RegularExpressions; public class InlieText : Text { /// <summary> /// 用正则取标签属性 名称-大小-宽度比例 /// </summary> private static readonly Regex m_spriteTagRegex = new Regex(@"<quad name=(.+?) size=(\d*\.?\d+%?) width=(\d*\.?\d+%?) />", RegexOptions.Singleline); /// <summary> /// 需要渲染的图片信息列表 /// </summary> private List<InlineSpriteInfor> listSprite; /// <summary> /// 图片资源 /// </summary> private SpriteAsset m_spriteAsset; /// <summary> /// 标签的信息列表 /// </summary> private List<SpriteTagInfor> listTagInfor; /// <summary> /// 图片渲染组件 /// </summary> private SpriteGraphic m_spriteGraphic; /// <summary> /// CanvasRenderer /// </summary> private CanvasRenderer m_spriteCanvasRenderer; /// <summary> /// 初始化 /// </summary> protected override void OnEnable() { base.OnEnable(); if (m_spriteGraphic == null) m_spriteGraphic = GetComponentInChildren<SpriteGraphic>(); if (m_spriteCanvasRenderer == null) m_spriteCanvasRenderer = m_spriteGraphic.GetComponentInChildren<CanvasRenderer>(); m_spriteAsset = m_spriteGraphic.m_spriteAsset; } /// <summary> /// 在设置顶点时调用 /// </summary> public override void SetVerticesDirty() { base.SetVerticesDirty(); //解析标签属性 listTagInfor = new List<SpriteTagInfor>(); foreach (Match match in m_spriteTagRegex.Matches(text)) { SpriteTagInfor tempSpriteTag = new SpriteTagInfor(); tempSpriteTag.name = match.Groups[1].Value; tempSpriteTag.index = match.Index; tempSpriteTag.size = new Vector2(float.Parse(match.Groups[2].Value)*float.Parse(match.Groups[3].Value), float.Parse(match.Groups[2].Value)); listTagInfor.Add(tempSpriteTag); } } /// <summary> /// 绘制模型 /// </summary> /// <param name="toFill"></param> protected override void OnPopulateMesh(VertexHelper toFill) { base.OnPopulateMesh(toFill); //获取所有的UIVertex,绘制一个字符对应6个UIVertex,绘制顺序为012 203 List<UIVertex> listUIVertex = new List<UIVertex>(); toFill.GetUIVertexStream(listUIVertex); //通过标签信息来设置需要绘制的图片的信息 listSprite = new List<InlineSpriteInfor>(); for (int i = 0; i < listTagInfor.Count; i++) { //UGUIText不支持<quad/>标签,表现为乱码,我这里将他的uv全设置为0,清除乱码 for (int m = listTagInfor[i].index*6; m < listTagInfor[i].index*6 + 6; m++) { UIVertex tempVertex = listUIVertex[m]; tempVertex.uv0 = Vector2.zero; listUIVertex[m] = tempVertex; } InlineSpriteInfor tempSprite = new InlineSpriteInfor(); //如果图片在第一个位置,则计算他的位置为文本的初始点位置 //否,则返回上一个字符的第三个UIVertex的position,这是根据他的顶点的绘制顺序所获得的 if (listTagInfor[i].index == 0) { Vector2 anchorPivot = GetTextAnchorPivot(alignment); Vector2 rectSize = rectTransform.sizeDelta; tempSprite.textpos = -rectSize / 2.0f + new Vector2(rectSize.x * anchorPivot.x,rectSize.y*anchorPivot.y- listTagInfor[i].size.y); } else tempSprite.textpos = listUIVertex[listTagInfor[i].index * 6 - 4].position; //设置图片的位置 tempSprite.vertices = new Vector3[4]; tempSprite.vertices[0] = new Vector3(0, 0, 0) + tempSprite.textpos; tempSprite.vertices[1] = new Vector3(listTagInfor[i].size.x, listTagInfor[i].size.y, 0) + tempSprite.textpos; tempSprite.vertices[2] = new Vector3(listTagInfor[i].size.x, 0, 0) + tempSprite.textpos; tempSprite.vertices[3] = new Vector3(0, listTagInfor[i].size.y, 0) + tempSprite.textpos; //计算其uv Rect spriteRect = m_spriteAsset.listSpriteInfor[0].rect; for (int j = 0; j < m_spriteAsset.listSpriteInfor.Count; j++) { //通过标签的名称去索引spriteAsset里所对应的sprite的名称 if (listTagInfor[i].name == m_spriteAsset.listSpriteInfor[j].name) spriteRect = m_spriteAsset.listSpriteInfor[j].rect; } Vector2 texSize = new Vector2(m_spriteAsset.texSource.width, m_spriteAsset.texSource.height); tempSprite.uv = new Vector2[4]; tempSprite.uv[0] = new Vector2(spriteRect.x / texSize.x, spriteRect.y / texSize.y); tempSprite.uv[1] = new Vector2((spriteRect.x + spriteRect.width) / texSize.x, (spriteRect.y + spriteRect.height) / texSize.y); tempSprite.uv[2] = new Vector2((spriteRect.x + spriteRect.width) / texSize.x, spriteRect.y / texSize.y); tempSprite.uv[3] = new Vector2(spriteRect.x / texSize.x, (spriteRect.y + spriteRect.height) / texSize.y); //声明三角顶点所需要的数组 tempSprite.triangles = new int[6]; listSprite.Add(tempSprite); } //清除<quad />标签的乱码 重新绘制 toFill.Clear(); toFill.AddUIVertexTriangleStream(listUIVertex); DrawSprite(); } /// <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; m_spriteCanvasRenderer.SetMesh(m_spriteMesh); m_spriteGraphic.UpdateMaterial(); } } [System.Serializable] public class SpriteTagInfor { /// <summary> /// sprite名称 /// </summary> public string name; /// <summary> /// 对应的字符索引 /// </summary> public int index; /// <summary> /// 大小 /// </summary> public Vector2 size; } [System.Serializable] public class InlineSpriteInfor { // 文字的最后的位置 public Vector3 textpos; // 4 顶点 public Vector3[] vertices; //4 uv public Vector2[] uv; //6 三角顶点顺序 public int[] triangles; }
4.开始测试效果,创建一个InlieText组件,并在其组件下,添加一个SpriteGraphic组件,输入文字加标签测试
InlieTex组件:
SpriteGraphic组件:
文字测试:
5.做了一个聊天测试:
6.同时在测试的时候就发现了些许bug,这里并不想写出来,有兴趣的可以一起修改讨论。
工程使用的unity版本为unity的版本为:5.3.1f1
最后给出工程链接:https://code.csdn.net/qq992817263/uguitextpro/tree/master