注:该脚本在文本显示不全时会有问题。
HyperlinkText.cs
using System;
using System.Text;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;namespace MYTOOL.UI
{public class HyperlinkText : Text, IPointerClickHandler{/// <summary>/// 超链接点击回调/// </summary>public Action<string> onHyperlinkClick;/// <summary>/// 超链接文本颜色/// </summary>public Color32 innerTextColor = Color.blue;/// <summary>/// 超链接信息类/// </summary>private class HyperlinkInfo{public int startIndex;public int endIndex;public string name;public readonly List<Rect> boxes = new List<Rect>();public List<int> linefeedIndexList = new List<int>();}/// <summary>/// 解析完最终的文本/// </summary>public string OutputText { get; private set; }/// <summary>/// 超链接信息列表/// </summary>private readonly List<HyperlinkInfo> hrefInfos = new List<HyperlinkInfo>();/// <summary>/// 文本构造器/// </summary>private readonly StringBuilder sb = new StringBuilder();/// <summary>/// 超链接正则/// </summary>private static readonly Regex hrefRegex = new Regex(@"<href=([^>\n\s]+)>(.*?)(</href>)", RegexOptions.Singleline);public override void SetVerticesDirty(){base.SetVerticesDirty();OutputText = GetOutputText(text);}protected override void OnPopulateMesh(VertexHelper toFill){var orignText = m_Text;m_Text = OutputText;base.OnPopulateMesh(toFill);m_Text = orignText;UIVertex vert = new UIVertex();// 处理超链接包围框foreach (var hrefInfo in hrefInfos){hrefInfo.boxes.Clear();hrefInfo.linefeedIndexList.Clear();if (hrefInfo.startIndex >= toFill.currentVertCount)continue;// 将超链接里面的文本顶点索引坐标加入到包围框toFill.PopulateUIVertex(ref vert, hrefInfo.startIndex);var pos = vert.position;var bounds = new Bounds(pos, Vector3.zero);hrefInfo.linefeedIndexList.Add(hrefInfo.startIndex);for (int i = hrefInfo.startIndex, m = hrefInfo.endIndex; i < m; i++){if (i >= toFill.currentVertCount)break;toFill.PopulateUIVertex(ref vert, i);vert.color = innerTextColor;toFill.SetUIVertex(vert, i);pos = vert.position;bool needEncapsulate = true;if (i > 4 && (i - hrefInfo.startIndex) % 4 == 0){UIVertex lastV = new UIVertex();toFill.PopulateUIVertex(ref lastV, i - 4);var lastPos = lastV.position;// 换行重新添加包围框if (pos.x < lastPos.x && pos.y < lastPos.y){hrefInfo.boxes.Add(new Rect(bounds.min, bounds.size));hrefInfo.linefeedIndexList.Add(i);bounds = new Bounds(pos, Vector3.zero);needEncapsulate = false;}}if (needEncapsulate){// 扩展包围框bounds.Encapsulate(pos);}}hrefInfo.boxes.Add(new Rect(bounds.min, bounds.size));}}/// <summary>/// 获取超链接解析后的最后输出文本/// </summary>/// <returns></returns>protected virtual string GetOutputText(string inputText){sb.Clear();hrefInfos.Clear();int indexText = 0;int count = 0;foreach (Match match in hrefRegex.Matches(inputText)){string appendStr = inputText[indexText..match.Index];sb.Append(appendStr);count += appendStr.Length - FilterNonVertexCharacters(appendStr).Length;int startIndex = (sb.Length - count) * 4;var group = match.Groups[1];var hrefInfo = new HyperlinkInfo{startIndex = startIndex,endIndex = startIndex + (match.Groups[2].Length * 4),name = group.Value};hrefInfos.Add(hrefInfo);sb.Append(match.Groups[2].Value);indexText = match.Index + match.Length;}sb.Append(inputText[indexText..]);return sb.ToString();}private string FilterNonVertexCharacters(string input){if (supportRichText){return Regex.Replace(input, @"<[^>]+>|\s*", "");}else{return Regex.Replace(input, @"\s*", "");}}/// <summary>/// 点击事件检测是否点击到超链接文本/// </summary>/// <param name="eventData"></param>public void OnPointerClick(PointerEventData eventData){RectTransformUtility.ScreenPointToLocalPointInRectangle(rectTransform, eventData.position, eventData.pressEventCamera, out Vector2 lp);foreach (var hrefInfo in hrefInfos){var boxes = hrefInfo.boxes;for (var i = 0; i < boxes.Count; ++i){if (boxes[i].Contains(lp)){onHyperlinkClick?.Invoke(hrefInfo.name);return;}}}}}
}
简单使用
using UnityEngine;
using MYTOOL.UI;public class NewBehaviourScript : MonoBehaviour
{public HyperlinkText text;public void Start(){//超链接点击事件text.onHyperlinkClick = OnClickText;//自定义超链接颜色text.innerTextColor = Color.red;}void OnClickText(string s){Debug.Log(s);}
}