图文混合:利用quad标签
quad的属性可以正确设置图片大小,而quad的大小会自动排版,那么我们就可以用quad来做占位,进行图文混排的实现。
然后,使用在Text中图文混排,需要解决的问题。
第一,消除乱码。
第二,用正确的图片替换占位符。
第三,控制占位符大小。
功能基本实现,目前存在的问题是编辑器状态下无法预览。。
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using UnityEngine;
using UnityEngine.UI;
class MyQuadTagInfo
{
public int startIndex;
public string name;
public float size;
public Image img;
public bool isActive;
public void SetActive(bool active)
{
img.gameObject.SetActive(active);
}
}
public class MyEmojiText : Text {
/// <summary>
/// 解析#name#size#width
/// </summary>
private static readonly Regex s_SimpleQuadRegex = new Regex(@"\[#((#?\w*\.?\d*%?)*)\]", RegexOptions.Singleline);
/// <summary>
/// 解析quad 找到对应的渲染顶点
/// </summary>
private static readonly Regex s_QuadRegex = new Regex(@"<quad name=(\w*) size=(\d*\.?\d+%?) width=(\d*\.?\d+%?) />", RegexOptions.Singleline);
private List<MyQuadTagInfo> MyQuadTagInfos = new List<MyQuadTagInfo>();
private string outputText = ""; //解析完后的字符串
private TextGenerationSettings TextGenerationSettings;
readonly UIVertex[] m_TempVerts = new UIVertex[4];
protected override void OnEnable()//编辑器下调试使用
{
base.OnEnable();
#if UNITY_EDITOR
ParseText();
#endif
}
public override void SetVerticesDirty()
{
if (!IsActive())
{
return;
}
ParseText();
base.SetVerticesDirty();
}
private void ParseText()
{
outputText = "";
string []splitArr;
string tmpName;
string tmpSize;
string tmpWidth;
int startIndex = 0;
foreach (Match match in s_SimpleQuadRegex.Matches(text))
{
splitArr = match.Groups[1].Value.Split('#');
tmpSize = (splitArr.Length >= 3) ? splitArr[2] : fontSize.ToString();
tmpWidth = (splitArr.Length >= 2) ? splitArr[1] : "1";
tmpName = (splitArr.Length >= 1) ? splitArr[0] : "0";
outputText += text.Substring(startIndex, match.Index - startIndex) + string.Format("<quad name={0} size={1} width={2} />", tmpName, tmpSize, tmpWidth == "" ? "1" : tmpWidth);
startIndex = match.Index + match.Length;
}
outputText += text.Substring(startIndex, text.Length - startIndex);
ParseQuad();
}
private void ParseQuad()
{
int index = 0;
foreach (Match match in s_QuadRegex.Matches(outputText))
{
float size = float.Parse(match.Groups[2].Value);
float width = float.Parse(match.Groups[3].Value);
if (MyQuadTagInfos.Count < index + 1)
{
MyQuadTagInfos.Add(new MyQuadTagInfo());
}
MyQuadTagInfo quadTagInfo = MyQuadTagInfos[index];
quadTagInfo.name = match.Groups[1].Value;
quadTagInfo.size = size;
quadTagInfo.startIndex = match.Index;
quadTagInfo.isActive = true;
index++;
}
for (int i = index; i < MyQuadTagInfos.Count; i++)
{
MyQuadTagInfos[i].isActive = false;
}
if (Application.isPlaying)
{
CreateImage();
}
}
private void CreateImage()
{
foreach (var i in MyQuadTagInfos)
{
i.SetActive(false);
var sprite = Resources.Load<Sprite>("Emoji/" + i.name);
if (i.isActive && sprite)
{
i.SetActive(true);
if (i.img == null)
{
var icon = new GameObject().AddComponent<Image>();
icon.transform.SetParent(this.transform);
i.img = icon;
}
i.img.sprite = sprite;
i.img.GetComponent<RectTransform>().sizeDelta = new Vector2(i.size, i.size);
i.img.transform.localScale = Vector3.one;
}
}
}
private void ClearQuad(IList<UIVertex> verts)
{
foreach (var quad in MyQuadTagInfos)
{
if (!quad.isActive)
{
continue;
}
int startIndex = quad.startIndex * 4;
int endIndex = startIndex + 4;
for (int i = startIndex; i <= endIndex; i++)
{
if (i >= verts.Count)
{
continue;
}
verts[i] = verts[startIndex];
}
}
}
private void UpdataQuadPos(VertexHelper mVertexHelperRef)
{
foreach (var quad in MyQuadTagInfos)
{
if (!quad.isActive || quad.img == null)
{
continue;
}
int startIndex = quad.startIndex * 4;
UIVertex vert = new UIVertex();
mVertexHelperRef.PopulateUIVertex(ref vert, startIndex);
var width = quad.size;
quad.img.GetComponent<RectTransform>().anchoredPosition3D = vert.position + new Vector3(width/2, -width/2 - width/10);
}
}
protected override void OnPopulateMesh(VertexHelper toFill)
{
//一下为原OnPopulateMesh函数
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.PopulateWithErrors(outputText, settings, gameObject);
// Apply the offset to the vertices
IList<UIVertex> verts = cachedTextGenerator.verts;
float unitsPerPixel = 1 / pixelsPerUnit;
int vertCount = verts.Count - 4;
ClearQuad(verts); //清除乱码的quad并且创建图片
// We have no verts to process just return (case 1037923)
if (vertCount <= 0)
{
toFill.Clear();
return;
}
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);
}
}
UpdataQuadPos(toFill);
m_DisableFontTextureRebuiltCallback = false;
}
}
效果图
emoji图片