unity UGUI中获取点击位置处的URL链接

news/2024/5/18 14:27:20 标签: unity, ugui, 游戏开发, 游戏程序

需求是,我们在一个text组件中像写网页那样写入链接,然后点击这个链接,就能访问配置的网页啥的。比如:

<a href="hello">链接文本</a></summary>

最终的效果如下:

图中,image区域就是各个链接的点击范围。原理是获取text中,每个字符的位置,然后算出每个链接对应的点击区域,最后返回鼠标点到的那个区域的链接。代码比较简单,就直接写点注释看吧。实现是继承了text组件,当然写成静态方法传入text来计算也可以。

比较一下网上搜到的其他方案,这个方法不用重载mesh,效率应该是比较高的。

#define TEST_CheckClickURL
using System.Collections.Generic;
using System.Text.RegularExpressions;
using UnityEngine;
using UnityEngine.Profiling;
using UnityEngine.UI;

public class TestClickURL : Text
{
    public Button button;
    public void OnButtonClick()
    {
        Debug.Log(CheckClickURL()?.url);
    }

    // 定义返回的结果
    public class CheckClickURLResult
    {
        public string url;
        public string text;
        public Rect rect;
        public CheckClickURLResult(string url, string text, Rect rect)
        {
            this.url = url;
            this.text = text;
            this.rect = rect;
        }
    }
    private static Regex hrefRegex =
        new Regex(@"<a href=([^>\n\s]+)>(.*?)(</a>)",
            RegexOptions.Singleline);
    /// <summary> 计算点击到的URL文本内容,返回网址
    /// 格式如下:<a href="hello">链接文本</a></summary>
    public CheckClickURLResult CheckClickURL()
    {
        Profiler.BeginSample("CheckClickURL");
        if (hrefRegex == null)
            hrefRegex = new Regex(
                @"<a href=([^>\n\s]+)>(.*?)(</a>)", RegexOptions.Singleline);
        InitDebugGOList();

        // 将点击位置从屏幕坐标转为本地坐标
        RectTransformUtility.ScreenPointToLocalPointInRectangle(
            this.rectTransform, Input.mousePosition,
            null, out var mouseLocalPosition);
        //注意使用UI相机

        // 获取生成的文本数据。
        // characters 保存了每个字符左上角的位置。
        // lines 保存了每行开始字符ID,和行高。
        var generator = cachedTextGenerator;
        var charList = generator.characters;
        var lineList = generator.lines;
        var textStr = text;

        // 正则表达式查找链接文本
        var matchs = hrefRegex.Matches(textStr);
        foreach (Match match in matchs)
        {
            var urlGroup = match.Groups[1];
            var textGroup = match.Groups[0];
            var textStartIndex = textGroup.Index;
            var textEndIndex = textGroup.Index + textGroup.Length;
            // 我们的字符可能是换行的,所以要按行分割。
            // 倒着遍历就很容易获取每行开始和结束位置。
            var lineEndIndex = charList.Count - 1;
            for (int i = lineList.Count - 1; i >= 0; i--)
            {
                var lineStartIndex = lineList[i].startCharIdx;
                // 处理换行后的截取
                var realStart = Mathf.Max(lineStartIndex, textStartIndex);
                var realEnd = Mathf.Min(lineEndIndex, textEndIndex);
                // 本行没有链接内容的情况
                if (realStart > realEnd) continue;
                // 问题简化成单行的点击检查,提个函数继续处理。
                var result = CheckLine(realStart, realEnd, lineList[i].height, mouseLocalPosition, out var rect);
                if (result) return new CheckClickURLResult(urlGroup.Value, textGroup.Value, rect);

                //Debug.Log($"{start}/{end}");
                lineEndIndex = lineStartIndex - 1;
            }
        }
        Profiler.EndSample();
        return null;
    }

    public bool CheckLine(int start, int end, float lineHeight, Vector2 mouseLocalPosition, out Rect rect)
    {
        // 获取生成的文本数据。
        var charList = cachedTextGenerator.characters;
        var startPoint = charList[start].cursorPos;
        var endPoint = charList[end].cursorPos;

        // 直接计算出本行中链接可点击区域。
        var x = startPoint.x;
        var y = startPoint.y - lineHeight;
        var width = endPoint.x - startPoint.x;
        var height = lineHeight;
        rect = new Rect(x, y, width, height);

        var result = rect.Contains(mouseLocalPosition);
        CreateDebugImage(rect, result);
        return result;
    }

#if TEST_CheckClickURL
    // 测试用。生成空image展示出点击判定范围。
    public static List<GameObject> debugGOList;
    public void CreateDebugImage(Rect rect, bool contains)
    {
        Debug.Log($"rect={rect}");
        var go = new GameObject("DebugImage",
            typeof(RectTransform), typeof(Image));
        debugGOList.Add(go);
        var rtf = go.GetComponent<RectTransform>();
        rtf.SetParent(transform);
        rtf.pivot = Vector2.zero;
        rtf.anchorMin = Vector2.one / 2;
        rtf.anchorMax = Vector2.one / 2;
        rtf.sizeDelta = rect.size;
        rtf.localScale = Vector3.one;
        rtf.rotation = Quaternion.identity;
        rtf.anchoredPosition = rect.position - rectTransform.rect.center;
        // 点击到的那个范围展示为红色。
        if (contains)
            go.GetComponent<Image>().color = Color.red;
    }
    public void InitDebugGOList()
    {
        if (debugGOList == null)
            debugGOList = new List<GameObject>();
        debugGOList.ForEach(p => Destroy(p));
    }
#else
        public void CreateDebugImage(Rect rect, bool contains) { }
        public void InitDebugGOList() { }
#endif
}


http://www.niftyadmin.cn/n/5220249.html

相关文章

excel合并单元格教程

在表格里&#xff0c;总是会遇到一级表格、二级表格的区别&#xff0c;这时候一级表格会需要合并成一个大格子&#xff0c;那么excel如何合并单元格呢&#xff0c;其实使用快捷键或者功能键就可以了。 excel如何合并单元格&#xff1a; 1、首先我们用鼠标选中所有要合并的单元…

java--单继承、Object

java是单继承的&#xff0c;java中的类不支持多继承&#xff0c;但是支持多层继承。 反证法&#xff1a; 如果一个类同时继承两个类&#xff0c;然后两个类中都有同样的一个方法&#xff0c;哪当我创建这个类里的方法&#xff0c;是调用哪父类的方法 所以java中的类不支持多继…

使用Powershell导入csv格式并处理的两种方式

假设csv格式内容如下&#xff0c;保存为G:\temp\test.csv: name,number,mail wangsan,113355,ws163.com lisi,223366,lisiqq.com zhaowu,188999,zw126.com 方式一&#xff1a; $csvcontent import-csv -path G:\temp\test.csv $csvcontent|foreach { echo $_.name $_.number $…

Python break用法详解

Python 语言没有提供 goto 语句来控制程序的跳转&#xff0c;这种做法虽然提高了程序流程控制的可读性&#xff0c;但降低了灵活性。为了弥补这种不足&#xff0c;Python 提供了 continue 和 break 来控制循环结构。本节先讲解 break 的用法。 某些时候&#xff0c;需要在某种…

【Linux】初识重定向(输入输出)

一切皆文件 这是Linux的设计理念&#xff0c;因为这个理念的存在我们可以使用统一的方法对待不同的东西&#xff0c;&#xff0c;这也是为什么嵌入式之类的会需要Linux&#xff0c;因为用LInux来操纵硬件真的很方便 另外我们下文也会都基于这个理念来命名&#xff0c; 比如&am…

【23真题】大题全原题的211!题源已定位!

今天分享的是23年长安大学814的信号与系统试题及解析。 本套试卷难度分析&#xff1a;22年长安大学814考研真题&#xff0c;我也发布过&#xff0c;若有需要&#xff0c;戳这里自取&#xff01;本套试题难度中等偏下&#xff0c;题量偏多&#xff0c;考察的知识点也是很常见的…

佳易王钟表维修管理系统软件教程,手表保养维修软件

佳易王钟表维修管理系统软件教程&#xff0c;手表保养维修软件 软件简介&#xff1a; 佳易王钟表养护维修管理系统V16.3&#xff0c;录入维修订单&#xff0c;维修进度查询&#xff0c;会员活动方案&#xff0c;打印服务报价维修单&#xff0c;取表结账&#xff0c;导入手表照…

形态学操作—顶帽运算

顶帽运算是图像形态学处理中的一种操作&#xff0c;用于突出图像中的细小亮度变化或者局部亮度的差异。这种操作能够凸显出图像中的细微细节&#xff0c;通常在图像增强、特征提取等领域有着广泛的应用。 原理&#xff1a;   顶帽运算通过将原始图像与其开运算&#xff08;Op…