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

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

链接文本

最终的效果如下:

unity UGUI中获取点击位置处的URL链接_第1张图片

unity UGUI中获取点击位置处的URL链接_第2张图片

图中,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(@"\n\s]+)>(.*?)()",
            RegexOptions.Singleline);
    ///  计算点击到的URL文本内容,返回网址
    /// 格式如下:链接文本
    public CheckClickURLResult CheckClickURL()
    {
        Profiler.BeginSample("CheckClickURL");
        if (hrefRegex == null)
            hrefRegex = new Regex(
                @"\n\s]+)>(.*?)()", 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 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();
        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().color = Color.red;
    }
    public void InitDebugGOList()
    {
        if (debugGOList == null)
            debugGOList = new List();
        debugGOList.ForEach(p => Destroy(p));
    }
#else
        public void CreateDebugImage(Rect rect, bool contains) { }
        public void InitDebugGOList() { }
#endif
}

你可能感兴趣的:(unity,unity,ugui,游戏开发,游戏程序)