推荐阅读

大家好,我是佛系工程师☆恬静的小魔龙☆,不定时更新Unity开发技巧,觉得有用记得一键三连哦。

一、前言

宠粉博主又来了,今天有粉丝问我如何实现点击一段文字然后出现的面板在那段文字附近显示:
在这里插入图片描述
深入了解后发现原来就是想要点击文字出现UI面板,并且UI面板在文字附近:
在这里插入图片描述
这种效果在很多游戏都会出现,比如梦幻西游、DNF,查看武器装备啥的。

接下来,这篇文章就来实现一下这种效果。

二、正文

2-1、制作UI

我们先做一个这个武器介绍的UI面板:
在这里插入图片描述
OK,再做一个聊天框:

在这里插入图片描述

2-2、UI文字点击事件

这里,要实现UI文字的点击事件,需要重写Text组件,具体实现可以参考这篇文章:
【Unity3D小功能】Unity3D中Text使用超链接并绑定点击事件

新建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;

namespace UIClick
{
    /// <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;

namespace UIClick
{
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;

    public class HyperlinkLogic : MonoBehaviour
    {
        public HyperlinkText[] hyperlinkText;
        void Start()
        {
            // 绑定事件
            for (int i = 0; i < hyperlinkText.Length; i++)
            {
                hyperlinkText[i].onLinkClick = (info) => onclick(info);
            }
        }

        void onclick(string info)
        {
            Debug.Log(info);

            switch (info)
            {
                case "匕首":
                    break;
                case "宝刀":
                    break;
                case "斧头":
                    break;
            }
        }
    }
}

将聊天文本的Text组件改成HyperlinkText.cs
在这里插入图片描述

2-3、将武器介绍的UI面板和Text点击事件绑定一下

新建WeaponItem.cs脚本,编辑代码:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class WeaponItem : MonoBehaviour
{
    public static WeaponItem Instance;
    public GameObject Current;

    public Image Item;
    public Sprite[] SpirteItem;

    public Button BtnClose;
    public Text TextName;
    public Text TextRank;
    public Text TextContent;

    private void Awake()
    {
        Instance = this;
    }

    void Start()
    {
        Current.SetActive(false);
        BtnClose.onClick.AddListener(Close);
    }

    public void ShowInfo(int ItemType,string itemName,string itemRank,string itemContent)
    {
        Current.SetActive(true);
        Item.sprite = SpirteItem[ItemType];
        TextName.text = itemName;
        TextRank.text = itemRank;
        TextContent.text = itemContent;
    }

    public void Close()
    {
        Current.SetActive(false);
    }
}

继续修改HyperlinkLogic.cs脚本:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace UIClick
{
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;

    public class HyperlinkLogic : MonoBehaviour
    {
        public HyperlinkText[] hyperlinkText;

        public GameObject WeaponPanel;

        void Start()
        {
            // 绑定事件
            for (int i = 0; i < hyperlinkText.Length; i++)
            {
                hyperlinkText[i].onLinkClick = (info) => onclick(info);
            }
        }

        void onclick(string info)
        {
            Debug.Log(info);

            Vector3 pos = Input.mousePosition;
            pos.x += 100;
            pos.y -= 100;
            WeaponPanel.transform.position = pos;

            switch (info)
            {
                case "匕首":
                    WeaponItem.Instance.ShowInfo(0,info,"普通","这是一把绝世匕首。");
                    break;
                case "宝刀":
                    WeaponItem.Instance.ShowInfo(1, info, "史诗", "这是一把绝世宝刀。");
                    break;
                case "斧头":
                    WeaponItem.Instance.ShowInfo(2, info, "优秀", "这是一把绝世斧头。");
                    break;
            }
        }
    }
}

2-4、运行效果图

在这里插入图片描述

2-5、源代码

https://download.csdn.net/download/q764424567/89030869

三、后记

如果觉得本篇文章有用别忘了点个关注,关注不迷路,持续分享更多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和坑,让后来人可以有些参考。
Logo

分享前沿Unity技术干货和开发经验,精彩的Unity活动和社区相关信息

更多推荐