【Unity3D日常开发】Unity3D中Text使用超链接并绑定点击事件

news2024/7/6 18:23:02

推荐阅读

  • 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和坑,让后来人可以有些参考。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

在充满未知变化的市场社会环境里,实现组织结构与管理方式的自我进化

一、教程描述 本套教程立足于充满变化的市场社会环境&#xff0c;以管理者的素养作为切入点&#xff0c;从组织创新的角度&#xff0c;剖析企业可持续发展的内核与共性&#xff0c;并且提供了一套完整的实践方法论&#xff0c;可以帮助管理者在环境变化中&#xff0c;搭建一个…

npm 淘宝镜像正式到期

由于node安装插件是从国外服务器下载&#xff0c;如果没有“特殊手法”&#xff0c;就可能会遇到下载速度慢、或其它异常问题。 所以如果npm的服务器在中国就好了&#xff0c;于是我们乐于分享的淘宝团队干了这事。你可以用此只读的淘宝服务代替官方版本&#xff0c;且同步频率…

开源微信投票小程序源码系统:PHP+MySQL组合开发 带完整的搭建教程

微信小程序的普及&#xff0c;越来越多的活动和比赛开始采用微信投票的方式来决定胜出者。然而&#xff0c;市面上的微信投票小程序大多数需要付费使用或者功能受限。为了满足广大用户的需求&#xff0c;小编给大家分享一款开源的微信投票小程序源码系统&#xff0c;采用PHPMyS…

android远程投屏应用

客户端app地址&#xff1a;https://gitee.com/youzilzk/blue1.git 服务端地址&#xff1a;https://gitee.com/youzilzk/blue-server1.git 一。服务端部署 1.安装postgres 2.导入项目下blue.sql文件 3.修改配置application.properties和config.properties&#xff0c;其中applic…

【Git】02 仓库、区域与基本操作

文章目录 一、Git仓库二、Git区域三、操作3.1 git add3.2 更改文件名3.3 清空暂存区3.4 帮助文档 四、版本历史4.1 日志时间格式4.2 查看版本演变历史 五、总结 一、Git仓库 Git仓库&#xff0c;可简单理解为项目代码存放的位置&#xff0c;Git将该项目目录中的内容纳入版本管…

【Java基础】之进程与线程

进程与线程 1. 线程与进程1.1 概念1.2 区别与联系 2. 线程的5种状态和切换2.1 简单介绍2.2 状态切换2.2.1 重点情况 3. 线程中常见的方法4. 线程池 1. 线程与进程 1.1 概念 进程&#xff1a;资源分配的基本单元&#xff0c;如QQ音乐 线程&#xff1a;资源调度的基本单元&…

构建基于Flask的跑腿外卖小程序

跑腿外卖小程序作为现代生活中的重要组成部分&#xff0c;其技术实现涉及诸多方面&#xff0c;其中Web开发框架是至关重要的一环。在这篇文章中&#xff0c;我们将使用Python的Flask框架构建一个简单的跑腿外卖小程序的原型&#xff0c;展示其基本功能和实现原理。 首先&…

React中文官网已经搬迁了,原网址内容将不再更新

注意1&#xff1a;React中文官网已经搬迁至-React 官方中文文档&#xff0c;原网址内容将不再更新 注意2&#xff1a;React官网已经将React的定义由“用于构建用户界面的 JavaScript 库”更改为“用于构建 Web 和原生交互界面的库”。

网易砸10亿造《射雕》,能盘活武侠游戏市场吗?

​网易游戏的2024年,注定是特殊的一年。 在乙游《世界之外》出尽风头的同时,网易有史以来投入最大的武侠游戏《射雕》进行了公测前的最后一次测试,新角色PV陆续公布,宣发也在逐步推进。 这款由网易梦幻事业部战魂工作室研发的全新大世界武侠冒险RPG,可谓是来势汹汹,官方…

python 写入csv文件 内容乱码

问题 python 写入csv文件 内容乱码 详细问题 笔者核心代码 import csv # 将数据写入 CSV 文件 csv_file_path "soil_data.csv" header ["经度", "纬度", "土壤类型", "pH值"]with open(csv_file_path, mode"w&q…

银行数据仓库体系实践(10)--汇总指标层和集市模型设计

建立多层次的数据访问服务体系&#xff0c;有力提升数据仓库的价值。基于指标汇总层、集市层、可以提供面向业务人员的即席数据查询、以及面向应用开发者的数据接口、应用访问接口&#xff0c;满足不同类型应用的需要。 1、汇总指标层模型设计原则及步骤 1.1建设目标&#xff…

【Android】高仿京东三级类型列表Demo

本demo基于二级分类双列表联动Demo进行了改进&#xff0c;高仿实现了京东的三级类型列表。 京东的如图&#xff1a; 本demo的&#xff1a; 改进之处 实现了三级列表联动&#xff0c;二三级列表之间的滑动监听优化了一下&#xff0c;将二级类型选中交予自身的点击事件&#…

【采购指南】国产芯片产业现状及采购建议

随着全球技术的飞速发展&#xff0c;芯片成为现代电子设备不可或缺的核心部分。中国作为全球最大的电子产品制造国&#xff0c;其芯片制造业也在经历快速的发展。近年来&#xff0c;国产芯片因技术进步、支持政策和市场需求不断提升&#xff0c;其在国内外市场的份额和影响力也…

C#小结:ScottPlot 5.0在VS2022桌面开发的应用(以winform为例)

目录 一、官网文档地址 二、在VS2022中安装Scottplot 三、拖动Scottplot 四、使用Scottplot 五、效果图 一、官网文档地址 官网地址&#xff1a;ScottPlot 5.0 食谱 本文内容来自于官网&#xff0c;选取了官网的一些比较好用的功能展示&#xff0c;如需学习更多功能&a…

【安装记录】Proxmox VE 8.0 安装

PVE(Proxmox VE) 下载地址&#xff1a;https://www.proxmox.com/en/downloads 2、安装 rufus &#xff08;1&#xff09;官网下载&#xff1a;https://rufus.ie/zh/ &#xff08;2&#xff09;打开即可使用&#xff1a; 3、使用 rufus 制作启动盘 (1)如果你的u盘之前被制作为…

Nginx 本地部署vue项目

1、 下载 Nginx 稳定版本 2、下载安装后&#xff0c;打开 nginx.conf配置文件 3、找到打包好的文件&#xff0c;并配置运行文件 文件的位置 root C:/server/build location /{root C:/server/build;index index.html index.htm;#解决刷新后nginx报404问题try_files $uri …

HarmonyOS-UIAbility组件概述及生命周期

UIAbility组件概述 概述 UIAbility组件是一种包含UI界面的应用组件&#xff0c;主要用于和用户交互。 UIAbility组件是系统调度的基本单元&#xff0c;为应用提供绘制界面的窗口&#xff1b;一个UIAbility组件中可以通过多个页面来实现一个功能模块。每一个UIAbility组件实例…

【数据结构】链表(单链表实现+测试+原码)

1.链表 1.1 链表的概念及结构 概念&#xff1a;链表是一种物理存储结构上非连续、非顺序的存储结构&#xff0c;数据元素的逻辑顺序是通过链表 中的指针链接次序实现的 。 现实中&#xff1a;链表就像是一列动车&#xff0c;一节连着一节 数据结构中的链表 注意: 1.从上图可看出…

微信小程序(二十五)条件判断语句与结构隐藏

注释很详细&#xff0c;直接上代码 上一篇 新增内容&#xff1a; 1.条件判断语句的演示 2.隐藏结构的演示 源码&#xff1a; index.wxml <view><!-- wx:if和wx:else为条件判断语句 --><text wx:if"{{isLogin}}">已登入的用户</text><tex…

Maven基本使用

Maven简介: Apache Maven 是一个项目管理和构建工具&#xff0c;它基于项目模型(POM)的概念&#xff0c;通过一小段描述信息来管理项目的构建&#xff0c;报告和文档 Maven的作用: (1&#xff09;项目搭建&#xff1b; &#xff08;2&#xff09;依赖管理&#xff1b; &#xf…