使用案例
这 是 a per of<quad name=food_icon_1003 scale=3>测 <color=red><size=23><b><i>试</i></b></size></color> de <quad name=food_icon_1003 scale=1.6> <quad name=food_icon_1008 scale=1><color=green>的</color>Message
效果图
代码:
主要文件:
using System.Collections.Generic;
using System.Text.RegularExpressions;
using UnityEngine;
using UnityEngine.UI;
//支持插入<quad name=img scale=1> 显示的图文混排
[RequireComponent(typeof(Text))]
public class TextImgMixedArrangeHook : BaseMeshEffect
{
// <quad name=img scale=1>
public static readonly Regex InsertImgTagRegex = new Regex(@"<quad\s+name=(.+?)\s+scale=(\d*\.?\d+%?)\s*>", RegexOptions.Singleline);
public static readonly Regex RichTextRegex = new Regex("(<color[^>]*?>[\\s\\S]*?<\\/color>)|(<b[^>]*?>[\\s\\S]*?<\\/b>)|(<i[^>]*?>[\\s\\S]*?<\\/i>)|(<size[^>]*?>[\\s\\S]*?<\\/size>)", RegexOptions.IgnoreCase);
private List<Image> _Imgs = new List<Image>();
private Text _text;
public Text Text
{
get
{
if (_text == null)
{
_text = GetComponent<Text>();
}
return _text;
}
}
private List<ImgTagInfo> _TagInfos = new List<ImgTagInfo>();
private string lastContent;
public override void ModifyMesh(VertexHelper vh)
{
if (!IsActive()) return;
//设置文字显示
List<UIVertex> vertexList = new List<UIVertex>();
vh.GetUIVertexStream(vertexList);
for (int i = 0; i < _TagInfos.Count; i++)
{
if (!_TagInfos[i].Visible) continue;
//显示空白
var idx = _TagInfos[i].StringIndex;
for (int m = idx * 6; m < idx * 6 + 6; m++)
{
var tempVertex = vertexList[m];
tempVertex.uv0 = Vector2.zero;
tempVertex.uv1 = Vector2.zero;
tempVertex.uv2 = Vector2.zero;
tempVertex.uv3 = Vector2.zero;
tempVertex.color = Color.clear;
vertexList[m] = tempVertex;
}
//修图片正缩放导致的偏差
var widthChange = Text.fontSize * (_TagInfos[i].Scale - 1);
for (int n = (_TagInfos[i].StringIndex) * 6; n < vertexList.Count; n++)
{
var v = vertexList[n];
v.position += new Vector3(widthChange, 0, 0);
vertexList[n] = v;
}
//调整图片位置
// 文字的六个顶点
// 0 1
// ◣◥
// 3 2
Vector3 pos1 = vertexList[_TagInfos[i].StringIndex * 6 + 1].position;
Vector3 pos2 = vertexList[_TagInfos[i].StringIndex * 6 + 2].position;
var target = _Imgs[i];
var scale = _TagInfos[i].Scale * Text.fontSize * Vector3.one / target.sprite.rect.width;
var pos = new Vector3(pos1.x - _TagInfos[i].Scale * Text.fontSize / 2, (pos1.y + pos2.y) / 2 - Text.fontSize / 6, 0);
target.rectTransform.localScale = scale;
target.rectTransform.localPosition = pos;
}
vh.Clear();
vh.AddUIVertexTriangleStream(vertexList);
}
public void PreProcessText()
{
if (lastContent != Text.text)
{
lastContent = Text.text;
_TagInfos.Clear();
try
{
ParseTag(Text);
PrepareImgObjs();
}
catch (System.Exception e)
{
Debug.LogWarning("Rebuild multi rich text failed." + e);
}
}
}
private bool ParseTag(Text text)
{
var idxOffset = 0;
var removedTagStr = text.text;
var match = RichTextRegex.Match(removedTagStr);
while (match.Success)
{
for (; match.Success; match = match.NextMatch())
{
var startIdx = match.Groups[0].Value.IndexOf('>') + 1;
var length = match.Groups[0].Value.LastIndexOf('<') - startIdx;
var tempStr = match.Groups[0].Value.Substring(startIdx, length);
removedTagStr = removedTagStr.Replace(match.Groups[0].Value, tempStr);
}
match = RichTextRegex.Match(removedTagStr);
}
var lastEnd = 0;
var spaceCount = 0;
foreach (Match m in InsertImgTagRegex.Matches(removedTagStr))
{
if (m.Success)
{
ImgTagInfo spriteTag = new ImgTagInfo();
for (int i = 0; i < m.Groups.Count; i++)
{
var col = m.Groups[i];
if (!col.Success) continue;
switch (col.Name)
{
case "2":
spriteTag.Scale = float.Parse(m.Groups[i].Value);
break;
case "1":
spriteTag.Name = m.Groups[i].Value;
break;
}
}
//处理空格
var subStr = removedTagStr.Substring(lastEnd, m.Index - lastEnd);
spaceCount += subStr.Length - subStr.Replace(" ", string.Empty).Length;
spriteTag.StringIndex = m.Index - idxOffset - spaceCount + _TagInfos.Count;
idxOffset += m.Length;
lastEnd = m.Index + m.Length;
_TagInfos.Add(spriteTag);
}
}
return true;
}
private void PrepareImgObjs()
{
int i = 0;
for (; i < _TagInfos.Count; i++)
{
if (i < _Imgs.Count)
{
_Imgs[i].gameObject.SetActive(true);
}
else
{
var obj = new GameObject("InsertImg_" + i);
obj.transform.SetParent(this.transform);
_Imgs.Add(obj.AddComponent<Image>());
}
SetImg(_Imgs[i], _TagInfos[i].Name);
}
for (; i < _Imgs.Count; i++)
_Imgs[i].gameObject.SetActive(false);
}
private void SetImg(Image img, string path)
{
SetSprite(img, path);
img.rectTransform.sizeDelta = img.sprite.rect.size;
}
public class ImgTagInfo
{
public int StringIndex;
public bool Visible = true;
public string Name;
public float Scale = 1;
}
}
Text扩展 需要继承Text类
public class TextExtension : Text
{
public override string text
{
get => base.text;
set
{
//测试:
value = "这 是 a per of<quad name=food_icon_1003 scale=3>测 <color=red><size=23><b><i>试</i></b></size></color> " +
"de <quad name=food_icon_1003 scale=1.6> <quad name=food_icon_1008 scale=1><color=green>的</color>Message";
base.text = value;
var match = TextImgMixedArrangeHook.InsertImgTagRegex.Match(text);
if (match.Success)
{
var multi = this.transform.GetOrAddComponent<TextImgMixedArrangeHook>();
multi.PreProcessText();
}
}
}
}
参考