【Unity3D小功能】Unity3D中Text使用超链接并绑定点击事件

推荐阅读

  • 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之UGUIUGUIUnity的UI系统UGUI全解析,从UGUI的基础控件开始讲起,然后将UGUI的原理,UGUI的使用全面教学。
Unity3D之读取数据文件读取使用Unity3D读取txt文档、json文档、xml文档、csv文档、Excel文档。
Unity3D之数据集合数据集合数组集合:数组、List、字典、堆栈、链表等数据集合知识分享。
Unity3D之VR/AR(虚拟仿真)开发虚拟仿真总结博主工作常见的虚拟仿真需求进行案例讲解。
Unity3D之插件插件主要分享在Unity开发中用到的一些插件使用方法,插件介绍等
Unity3D之日常开发日常记录主要是博主日常开发中用到的,用到的方法技巧,开发思路,代码分享等
Unity3D之日常BUG日常记录记录在使用Unity3D编辑器开发项目过程中,遇到的BUG和坑,让后来人可以有些参考。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/362970.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

C++开发基础之预编译头文件 stdafx.h的作用

引言 在 C 开发中&#xff0c;为了提高编译效率和减少重复编译的时间&#xff0c;我们可以使用 stdafx.h 这个预编译头文件。本文将介绍 stdafx.h 是什么&#xff0c;以及它在 C 项目中的作用。 1、什么是 stdafx.h&#xff1f; stdafx.h 是一个预编译头文件&#xff0c;在 …

Synchronized作用

synchronized能够在同一时刻最多只有一个线程执行该代码 证明如下&#xff1a; public class MyThread {public static void main(String[] args) throws InterruptedException {Ticket ticket new Ticket();Thread aa new Thread(() -> {try {ticket.getCount();} catc…

UnityShader(十二)实现标准光照模型中的高光反射

目录 基本光照模型中的高光反射公式&#xff1a; 逐顶点光照 逐像素光照 基本光照模型中的高光反射公式&#xff1a; 从公式可以看出 要计算高光反射需要知道四个参数&#xff1a;入射光线的颜色和强度clight&#xff0c;材质的高光反射系数mspecular&#xff0c;视角方向v以…

UE4 C++ 结构体

先在UCLASS()前写入&#xff1a; USTRUCT(BlueprintType) struct FMyStruct //必须以"F"开头 {GENERATED_BODY() //必须添加“GENERATED_BODY()”UPROPERTY(EditAnywhere, BlueprintReadWrite, Category "MyStruct1")int32 Health;UPROPERTY(EditAnywher…

编程流程图

对于复杂流程&#xff0c;我做开发之前一般会 先画一下流程图。特别是多个部门有交叉的情况下&#xff1a; processOn&#xff1a; 这个是我之前 一直的选择&#xff0c;他可以画上面的这些&#xff0c;流程图&#xff0c;网页操作&#xff0c;但是他不是免费的&#xff0c;查过…

纯血鸿蒙来了,鸿蒙App开发该如何提速

“全世界做产品挣钱的公司很多&#xff0c;但有能力打造操作系统的公司没有几家&#xff0c;最后世界上的操作系统就只有三套&#xff1a;鸿蒙、iOS和安卓。” --- 360集团创始人、董事长周鸿祎 “HarmonyOS实现了AI框架、大模型、设计系统、编程框架、编程语言、编译器等全栈…

力扣经典题目:循环队列

1.虽然是循环队列&#xff0c;但需要提供一个队列为满的情况&#xff0c;所以&#xff0c;要设立一个空的队列元素&#xff0c;当最后一个元素的next指针加一等于第一个元素的时候为满 2.可以增加一个size记录元素个数&#xff0c;当size为0的时候为空&#xff0c;当size为目标…

Kafka-服务端-PartitionLeaderSelector、ReplicaStateMachine

PartitionLeaderSelector 通过对前面的分析可知&#xff0c;PartitionMachine将Leader副本选举、确定ISR集合的工作委托给了PartitionLeaderSelector接口实现&#xff0c;PartitionMachine可以专注于管理分区状态。这是策略模式的一种典型的应用场景。 图展示了PartitionLead…

收集子域名信息(二):第三方网站查询

一、介绍 通过第三方网站查询子域名信息是指使用外部提供的在线工具或服务&#xff0c;通过输入主域名&#xff08;主网站的域名&#xff09;来获取与该主域名相关的子域名列表的过程。子域名是在主域名下创建的附加标识&#xff0c;通常用于将网站内容组织成不同部分或为特定…

如何使用mock.js实现接口测试的自动化?

Mock.js 基础用法介绍 Mock.js是一个常用于生成随机数据和拦截Ajax请求的JavaScript库。本文将介绍Mock.js的用法&#xff0c;包括安装和基础用法&#xff0c;在开始前我们可以看下看&#xff1a;了解 Mock.js 的语法规范。 安装 可以通过npm安装Mock.js&#xff1a; npm i…

2024年【汽车驾驶员(高级)】模拟试题及汽车驾驶员(高级)理论考试

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 汽车驾驶员&#xff08;高级&#xff09;模拟试题是安全生产模拟考试一点通总题库中生成的一套汽车驾驶员&#xff08;高级&#xff09;理论考试&#xff0c;安全生产模拟考试一点通上汽车驾驶员&#xff08;高级&…

动画学习:CSP动画制作

会画画就能做手书&#xff1f;&#xff01;真动画入门教程&#xff01;【优动漫/CSP教程】https://www.bilibili.com/video/BV1ku411S7Ey/?spm_id_from333.337.search-card.all.click&vd_source124076d7d88eee393a1d8bf6fc787efa 有些人用的是优动漫软件&#xff0c;但是…

现在普通人的消费选择发生了怎样的变化?

消费选择的变化&#xff1a;现代普通人消费观念的演变 随着社会的进步和经济的发展&#xff0c;现代普通人的消费选择发生了翻天覆地的变化。这种变化不仅体现在物质生活的丰富上&#xff0c;更反映在人们消费观念的转变上。 在过去&#xff0c;人们的消费选择相对单一&#…

网络层 IP协议(1)

前置知识 主机:配有IP地址,但是不进行路由控制的设备 路由器:既配置了IP地址,又能进行路由控制的设备 节点:主机和路由器的总称 IP协议主要完成的任务就是 地址管理和路由选择 地址管理:使用一套地址体系,将网络设备的地址描述出来 路由选择:一个数据报如何从源地址到目的地址 …

C++ : 类的简单介绍(四)——析构函数

概念&#xff1a; 与构造函数功能相反&#xff0c;析构函数不是完成对对象本身的销毁&#xff0c;局部对象销毁工作是由 编译器完成的。 而对象在销毁时会自动调用析构函数&#xff0c;完成对象中资源的清理工作。 特征&#xff1a; 1. 析构函数名是在类名前加上字符 ~ 2. …

大数据分析|从七个特征理解大数据分析

文献来源&#xff1a;Saggi M K, Jain S. A survey towards an integration of big data analytics to big insights for value-creation[J]. Information Processing & Management, 2018, 54(5): 758-790. 下载链接&#xff1a;链接&#xff1a;https://pan.baidu.com/s/1…

【Zotero】如何在word文档中插入zotero中的文献

博主最近在用zotero管理文献&#xff0c;并在word文档中写文章&#xff0c;期间需要将zotero生成的参考文献插入文章中&#xff0c;这里将博主遇到的问题以及解决的办法分享给大家&#xff0c;供咱们一起学习。 博主遇到的问题主要有以下几个&#xff1a; 1、参考文献的格式以…

计算机网络——网络层(3)

计算机网络——网络层&#xff08;3&#xff09; 小程一言专栏链接: [link](http://t.csdnimg.cn/ZUTXU)1 网络层——控制平面因特网中自治系统内部的路由选择总括考虑因素总结 ISP之间的路由选择&#xff1a;BGP考虑因素总结 SDN控制层面重要组件和功能总结 ICMP主要功能和特点…

Java强训day13(选择题编程题)

选择题 编程题 题目1 import java.util.Scanner;public class Main {public static void main(String[] args) {Scanner sc new Scanner(System.in);String s sc.nextLine();char[] c s.toCharArray();int i 0;int t 0;while (i < c.length) {if (c[i] ! \") {…

Spring速成(一)

文章目录 Spring速成&#xff08;一&#xff09;1&#xff0c;课程介绍1.1 为什么要学?1.2 学什么?1.3 怎么学? 2&#xff0c;Spring相关概念2.1 初识Spring2.1.1 Spring家族2.1.2 了解Spring发展史 2.2 Spring系统架构2.2.1 系统架构图2.2.2 课程学习路线 2.3 Spring核心概…