推荐阅读
- CSDN主页
- GitHub开源地址
- Unity3D插件分享
- 简书地址
- 我的个人博客
大家好,我是佛系工程师☆恬静的小魔龙☆,不定时更新Unity开发技巧,觉得有用记得一键三连哦。
一、前言
在开发中遇到了要给Text加超链接的需求,研究了实现方式,将代码和使用方法总结出来,分享一下。
二、正文
2-1、实现思路
主要有两种实现思路,一种是使用Text Mesh Pro
,可以直接加入超链接,实现点击事件。
另一种,就是继承Text脚本组件,重载OnPopulateMesh
方法,替换最终绘制的文本。
接下来就分别讲解如何使用。
2-2、继承Text脚本组件,重载OnPopulateMesh
方法
让Text显示超链接的文本内容需要以下几步:
(1)使用正则表达式提取超链接标签及里面的内容,并保存顶点信息(点击的时候使用)
(2)将文本内容加上颜色进行标识
(3)设置监听事件,在点击的时候调用
正则表达式:
//提取以<a link=>开头,以</a>结束的内容。
<a link=([^>\n\s]+)>(.*?)(</a>)
整体代码如下:
新建一个脚本命名为HyperlinkText.cs
:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.EventSystems;
using UnityEngine.UI;
/// <summary>
/// 超链接信息类
/// </summary>
///
[Serializable]
public class HyperlinkInfo
{
public int startIndex;
public int endIndex;
public string name;
public readonly List<Rect> boxes = new List<Rect>();
}
/// <summary>
/// 文本控件,支持超链接
/// </summary>
public class HyperlinkText : Text, IPointerClickHandler
{
/// <summary>
/// 解析完最终的文本
/// </summary>
private string m_OutputText;
/// <summary>
/// 超链接信息列表
/// </summary>
private readonly List<HyperlinkInfo> _mLinkInfos = new List<HyperlinkInfo>();
/// <summary>
/// 文本构造器
/// </summary>
protected static readonly StringBuilder s_TextBuilder = new StringBuilder();
/// <summary>
/// 超链接正则表达式
/// </summary>
private static readonly Regex s_HrefRegex = new Regex(@"<a link=([^>\n\s]+)>(.*?)(</a>)", RegexOptions.Singleline);
/// <summary>
/// 文本超链接控件
/// </summary>
private HyperlinkText mHyperlinkText;
protected override void Awake()
{
base.Awake();
mHyperlinkText = GetComponent<HyperlinkText>();
}
#region 回调事件
public Action<string> onLinkClick;
/// <summary>
/// 点击事件检测是否点击到超链接文本
/// </summary>
/// <param name="eventData"></param>
public void OnPointerClick(PointerEventData eventData)
{
RectTransformUtility.ScreenPointToLocalPointInRectangle(
rectTransform, eventData.position, eventData.pressEventCamera, out var lp);
foreach (var info in _mLinkInfos)
{
var boxes = info.boxes;
for (var i = 0; i < boxes.Count; ++i)
{
if (!boxes[i].Contains(lp)) continue;
onLinkClick?.Invoke(info.name);
return;
}
}
}
#endregion
#region 生成超链接
/// <summary>
/// 重新渲染网格
/// </summary>
public override void SetVerticesDirty()
{
base.SetVerticesDirty();
m_OutputText = GetOutputText(text);
}
/// <summary>
/// 处理Text顶点数据
/// </summary>
/// <param name="toFill"></param>
protected override void OnPopulateMesh(VertexHelper toFill)
{
var orignText = m_Text;
m_Text = m_OutputText;
base.OnPopulateMesh(toFill);
m_Text = orignText;
UIVertex vert = new UIVertex();
// 处理超链接包围框
foreach (var hrefInfo in _mLinkInfos)
{
hrefInfo.boxes.Clear();
if (hrefInfo.startIndex >= toFill.currentVertCount)
{
continue;
}
// 将超链接里面的文本顶点索引坐标加入到包围框
toFill.PopulateUIVertex(ref vert, hrefInfo.startIndex);
var pos = vert.position;
var bounds = new Bounds(pos, Vector3.zero);
for (int i = hrefInfo.startIndex, m = hrefInfo.endIndex; i < m; i++)
{
if (i >= toFill.currentVertCount)
{
break;
}
toFill.PopulateUIVertex(ref vert, i);
pos = vert.position;
if (pos.x < bounds.min.x) // 换行重新添加包围框
{
hrefInfo.boxes.Add(new Rect(bounds.min, bounds.size));
bounds = new Bounds(pos, Vector3.zero);
}
else
{
bounds.Encapsulate(pos); // 扩展包围框
}
}
hrefInfo.boxes.Add(new Rect(bounds.min, bounds.size));
}
}
/// <summary>
/// 获取超链接解析后的最后输出文本
/// </summary>
/// <returns></returns>
protected virtual string GetOutputText(string outputText)
{
s_TextBuilder.Length = 0;
_mLinkInfos.Clear();
var indexText = 0;
foreach (Match match in s_HrefRegex.Matches(outputText))
{
s_TextBuilder.Append(outputText.Substring(indexText, match.Index - indexText));
string str = s_TextBuilder.ToString();
char[] array = str.ToCharArray(); //把字符串转化成字符数组
IEnumerator enumerator = array.GetEnumerator(); //得到枚举器
StringBuilder stringBuilder = new StringBuilder();
while (enumerator.MoveNext()) //开始枚举
{
if ((char)enumerator.Current != ' ') //向StringBuilder类对象添加非空格字符
stringBuilder.Append(enumerator.Current.ToString());
}
var group = match.Groups[1];
var hrefInfo = new HyperlinkInfo
{
startIndex = stringBuilder.Length * 4, // 超链接里的文本起始顶点索引
endIndex = (stringBuilder.Length + match.Groups[2].Length - 1) * 4 + 3,
name = group.Value
};
_mLinkInfos.Add(hrefInfo);
s_TextBuilder.Append("<color=blue>"); // 超链接颜色
s_TextBuilder.Append(match.Groups[2].Value);
s_TextBuilder.Append("</color>");
indexText = match.Index + match.Length;
}
s_TextBuilder.Append(outputText.Substring(indexText, outputText.Length - indexText));
return s_TextBuilder.ToString();
}
#endregion
}
调用如下
,新建脚本命名为HyperlinkLogic.cs
,双击编辑代码:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class HyperlinkLogic : MonoBehaviour
{
public HyperlinkText hyperlinkText;
void Start()
{
// 动态显示文本
hyperlinkText.text = "文本测试:<a link=https://blog.csdn.net/q764424567>[恬静的小魔龙]</a>";
// 绑定事件
hyperlinkText.onLinkClick = (info) => onclick(info);
}
void onclick(string info)
{
Debug.Log(info);
Application.OpenURL(info);
}
}
运行结果:
2-3、使用Text Mesh Pro
加入超链接
使用Text Mesh Pro
就方便了,因为TMP自身支持超链接,只要使用标签link即可。
标签:
<link="id_01"></link>//超链接
<#0C86BA></color>//颜色
<u></u>//下划线
演示文本:
<link="id_01"><u><#0C86BA>Insert link text here</u></color></link>
新建脚本命名为TMPLink.cs
,双击编辑代码:
using System.Collections;
using System.Collections.Generic;
using TMPro;
using UnityEngine;
using UnityEngine.EventSystems;
public class TMPLink : MonoBehaviour, IPointerClickHandler
{
private TextMeshProUGUI m_TextMeshPro;
void Awake()
{
m_TextMeshPro = gameObject.GetComponent<TextMeshProUGUI>();
}
void Start()
{
}
public void OnPointerClick(PointerEventData eventData)
{
int linkIndex = TMP_TextUtilities.FindIntersectingLink(m_TextMeshPro, Input.mousePosition, eventData.pressEventCamera);
TMP_LinkInfo linkInfo = m_TextMeshPro.textInfo.linkInfo[linkIndex];
RectTransformUtility.ScreenPointToLocalPointInRectangle(m_TextMeshPro.rectTransform, eventData.position, eventData.pressEventCamera, out var worldPointInRectangle);
switch (linkInfo.GetLinkID())
{
case "id_01":
Debug.Log("点击了id:id_01的超链接");
Application.OpenURL("https://blog.csdn.net/q764424567");
break;
}
}
}
运行点击结果:
三、后记
如果觉得本篇文章有用别忘了点个关注,关注不迷路,持续分享更多Unity干货文章。
你的点赞就是对博主的支持,有问题记得留言:
博主主页有联系方式。
博主还有跟多宝藏文章等待你的发掘哦:
专栏 | 方向 | 简介 |
---|---|---|
Unity3D开发小游戏 | 小游戏开发教程 | 分享一些使用Unity3D引擎开发的小游戏,分享一些制作小游戏的教程。 |
Unity3D从入门到进阶 | 入门 | 从自学Unity中获取灵感,总结从零开始学习Unity的路线,有C#和Unity的知识。 |
Unity3D之UGUI | UGUI | Unity的UI系统UGUI全解析,从UGUI的基础控件开始讲起,然后将UGUI的原理,UGUI的使用全面教学。 |
Unity3D之读取数据 | 文件读取 | 使用Unity3D读取txt文档、json文档、xml文档、csv文档、Excel文档。 |
Unity3D之数据集合 | 数据集合 | 数组集合:数组、List、字典、堆栈、链表等数据集合知识分享。 |
Unity3D之VR/AR(虚拟仿真)开发 | 虚拟仿真 | 总结博主工作常见的虚拟仿真需求进行案例讲解。 |
Unity3D之插件 | 插件 | 主要分享在Unity开发中用到的一些插件使用方法,插件介绍等 |
Unity3D之日常开发 | 日常记录 | 主要是博主日常开发中用到的,用到的方法技巧,开发思路,代码分享等 |
Unity3D之日常BUG | 日常记录 | 记录在使用Unity3D编辑器开发项目过程中,遇到的BUG和坑,让后来人可以有些参考。 |