zoukankan      html  css  js  c++  java
  • [UGUI]图文混排(六):点击区域

    点击区域可以分成两部分来分析:

    0.Rect

    搜索api:Rect和Rect.Rect,可以知道:

    在GUI和GUILayout中,Rect的原点在左上角,向右为x轴正方向,向下为y轴正方向;

    除此之外,其他情况下Rect的原点在左下角,向右为x轴正方向,向上为y轴正方向。

    1.区域的判定

    a.图片的可点击区域:整张图片

    b.文字的可点击区域:下划线上的文字

    2.点击响应

    计算出区域后,因为这个区域是局部坐标系的,再将点击坐标转换为text中的局部坐标,判定该坐标是否在区域内,即可完成点击响应。

    综上,可以得出如下的代码:

    RichTextEvent.cs

    1 using UnityEngine;
    2 
    3 public class RichTextEvent {
    4 
    5     public Rect rect;//触发事件的判定区域
    6     public string name;//事件名
    7     public string parameter;//事件参数
    8 }

    RichText.cs

      1 using System.Collections.Generic;
      2 using System.Text.RegularExpressions;
      3 using System.Text;
      4 using UnityEngine.EventSystems;
      5 using System;
      6 using UnityEngine;
      7 using UnityEngine.UI;
      8 
      9 //图片<icon name=*** w=1 h=1 n=*** p=***/>
     10 //下划线<material=underline c=#ffffff h=1 n=*** p=***>blablabla...</material>
     11 public class RichText : Text, IPointerClickHandler {
     12 
     13     private FontData fontData = FontData.defaultFontData;
     14 
     15     //--------------------------------------------------------图片 start
     16     private static readonly string replaceStr = "u00A0";
     17     private static readonly Regex imageTagRegex = new Regex(@"<icon name=([^>s]+)([^>]*)/>");//(名字)(属性)
     18     private static readonly Regex imageParaRegex = new Regex(@"(w+)=([^s]+)");//(key)=(value)
     19     private List<RichTextImageInfo> imageInfoList = new List<RichTextImageInfo>();
     20     private bool isImageDirty = false;
     21     //--------------------------------------------------------图片 end
     22 
     23     //--------------------------------------------------------文字 start
     24     private RichTextParser richTextParser = new RichTextParser();
     25     //--------------------------------------------------------文字 end
     26 
     27     //--------------------------------------------------------事件 start
     28     private Action<string, string> clickAction;
     29     private List<RichTextEvent> eventList = new List<RichTextEvent>();
     30     //--------------------------------------------------------事件 end
     31 
     32     //--------------------------------------------------------调试 start
     33     private bool isShowClickArea = true;//是否显示可点击区域
     34     //--------------------------------------------------------调试 end
     35 
     36     protected RichText()
     37     {
     38         fontData = typeof(Text).GetField("m_FontData", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).GetValue(this) as FontData;
     39     }
     40 
     41     readonly UIVertex[] m_TempVerts = new UIVertex[4];
     42     protected override void OnPopulateMesh(VertexHelper toFill)
     43     {
     44         if (font == null)
     45             return;
     46 
     47         // We don't care if we the font Texture changes while we are doing our Update.
     48         // The end result of cachedTextGenerator will be valid for this instance.
     49         // Otherwise we can get issues like Case 619238.
     50         m_DisableFontTextureRebuiltCallback = true;
     51 
     52         //处理事件
     53         eventList.Clear();
     54 
     55         //处理图片标签
     56         string richText = text;
     57         IList<UIVertex> verts = null;
     58         richText = CalculateLayoutWithImage(richText, out verts);
     59 
     60         //处理文字标签
     61         List<RichTextTag> tagList = null;
     62         richTextParser.Parse(richText, out tagList);
     63         for (int i = 0; i < tagList.Count; i++)
     64         {
     65             RichTextTag tag = tagList[i];
     66             switch (tag.tagType)
     67             {
     68                 case RichTextTagType.None:
     69                     break;
     70                 case RichTextTagType.Underline:
     71                     ApplyUnderlineEffect(tag as RichTextUnderlineTag, verts);
     72                     break;
     73                 default:
     74                     break;
     75             }
     76         }
     77 
     78         //Vector2 extents = rectTransform.rect.size;
     79 
     80         //var settings = GetGenerationSettings(extents);
     81         //cachedTextGenerator.Populate(text, settings);
     82 
     83         Rect inputRect = rectTransform.rect;
     84 
     85         // get the text alignment anchor point for the text in local space
     86         Vector2 textAnchorPivot = GetTextAnchorPivot(fontData.alignment);
     87         Vector2 refPoint = Vector2.zero;
     88         refPoint.x = Mathf.Lerp(inputRect.xMin, inputRect.xMax, textAnchorPivot.x);
     89         refPoint.y = Mathf.Lerp(inputRect.yMin, inputRect.yMax, textAnchorPivot.y);
     90 
     91         // Determine fraction of pixel to offset text mesh.
     92         Vector2 roundingOffset = PixelAdjustPoint(refPoint) - refPoint;
     93 
     94         // Apply the offset to the vertices
     95         //IList<UIVertex> verts = cachedTextGenerator.verts;
     96         float unitsPerPixel = 1 / pixelsPerUnit;
     97         //Last 4 verts are always a new line...
     98         int vertCount = verts.Count - 4;
     99 
    100         toFill.Clear();
    101         if (roundingOffset != Vector2.zero)
    102         {
    103             for (int i = 0; i < vertCount; ++i)
    104             {
    105                 int tempVertsIndex = i & 3;
    106                 m_TempVerts[tempVertsIndex] = verts[i];
    107                 m_TempVerts[tempVertsIndex].position *= unitsPerPixel;
    108                 m_TempVerts[tempVertsIndex].position.x += roundingOffset.x;
    109                 m_TempVerts[tempVertsIndex].position.y += roundingOffset.y;
    110                 if (tempVertsIndex == 3)
    111                     toFill.AddUIVertexQuad(m_TempVerts);
    112             }
    113         }
    114         else
    115         {
    116             //Debug.Log(unitsPerPixel);
    117             for (int i = 0; i < vertCount; ++i)
    118             {
    119                 int tempVertsIndex = i & 3;
    120                 m_TempVerts[tempVertsIndex] = verts[i];
    121                 m_TempVerts[tempVertsIndex].position *= unitsPerPixel;
    122                 if (tempVertsIndex == 3)
    123                     toFill.AddUIVertexQuad(m_TempVerts);
    124                 //Debug.LogWarning(i + "_" + tempVertsIndex + "_" + m_TempVerts[tempVertsIndex].position);
    125             }
    126         }
    127         m_DisableFontTextureRebuiltCallback = false;
    128     }
    129 
    130     protected string CalculateLayoutWithImage(string richText, out IList<UIVertex> verts)
    131     {
    132         Vector2 extents = rectTransform.rect.size;
    133         var settings = GetGenerationSettings(extents);
    134 
    135         float unitsPerPixel = 1 / pixelsPerUnit;
    136 
    137         float spaceWidth = cachedTextGenerator.GetPreferredWidth(replaceStr, settings) * unitsPerPixel;
    138 
    139         float fontSize2 = fontSize * 0.5f;
    140 
    141         //解析图片标签,并将标签替换为空格
    142         imageInfoList.Clear();
    143         Match match = null;
    144         StringBuilder builder = new StringBuilder();
    145         while ((match = imageTagRegex.Match(richText)).Success)
    146         {
    147             RichTextImageInfo imageInfo = new RichTextImageInfo();
    148             imageInfo.name = match.Groups[1].Value;
    149             string paras = match.Groups[2].Value;
    150             if (!string.IsNullOrEmpty(paras))
    151             {
    152                 var keyValueCollection = imageParaRegex.Matches(paras);
    153                 for (int i = 0; i < keyValueCollection.Count; i++)
    154                 {
    155                     string key = keyValueCollection[i].Groups[1].Value;
    156                     string value = keyValueCollection[i].Groups[2].Value;
    157                     imageInfo.SetValue(key, value);
    158                 }
    159             }
    160             imageInfo.size = new Vector2(fontSize2 * imageInfo.widthScale, fontSize2 * imageInfo.heightScale);
    161             imageInfo.startVertex = match.Index * 4;
    162             int num = Mathf.CeilToInt(imageInfo.size.x / spaceWidth);//占据几个空格
    163             imageInfo.vertexLength = num * 4;
    164             imageInfoList.Add(imageInfo);
    165 
    166             builder.Length = 0;
    167             builder.Append(richText, 0, match.Index);
    168             for (int i = 0; i < num; i++)
    169             {
    170                 builder.Append(replaceStr);
    171             }
    172             builder.Append(richText, match.Index + match.Length, richText.Length - match.Index - match.Length);
    173             richText = builder.ToString();
    174         }
    175 
    176         // Populate charaters
    177         cachedTextGenerator.Populate(richText, settings);
    178         verts = cachedTextGenerator.verts;
    179         // Last 4 verts are always a new line...
    180         int vertCount = verts.Count - 4;
    181 
    182         //换行处理
    183         //0 1|4 5|8  9
    184         //3 2|7 6|11 10
    185         //例如前两个字为图片标签,第三字为普通文字;那么startVertex为0,vertexLength为8
    186         for (int i = 0; i < imageInfoList.Count; i++)
    187         {
    188             RichTextImageInfo imageInfo = imageInfoList[i];
    189             int startVertex = imageInfo.startVertex;
    190             int vertexLength = imageInfo.vertexLength;
    191             int maxVertex = Mathf.Min(startVertex + vertexLength, vertCount);
    192             //如果最边缘顶点超过了显示范围,则将图片移到下一行
    193             //之后的图片信息中的起始顶点都往后移
    194             if (verts[maxVertex - 2].position.x * unitsPerPixel > rectTransform.rect.xMax)
    195             {
    196                 richText = richText.Insert(startVertex / 4, "
    ");
    197                 for (int j = i; j < imageInfoList.Count; j++)
    198                 {
    199                     imageInfoList[j].startVertex += 8;
    200                 }
    201                 cachedTextGenerator.Populate(richText, settings);
    202                 verts = cachedTextGenerator.verts;
    203                 vertCount = verts.Count - 4;
    204             }
    205         }
    206 
    207         //计算位置
    208         for (int i = imageInfoList.Count - 1; i >= 0; i--)
    209         {
    210             RichTextImageInfo imageInfo = imageInfoList[i];
    211             int startVertex = imageInfo.startVertex;
    212             if (startVertex < vertCount)
    213             {
    214                 UIVertex uiVertex = verts[startVertex];
    215                 Vector2 pos = uiVertex.position;
    216                 pos *= unitsPerPixel;
    217                 pos += new Vector2(imageInfo.size.x * 0.5f, fontSize2 * 0.5f);
    218                 pos += new Vector2(rectTransform.sizeDelta.x * (rectTransform.pivot.x - 0.5f), rectTransform.sizeDelta.y * (rectTransform.pivot.y - 0.5f));
    219                 imageInfo.position = pos;
    220                 imageInfo.color = Color.white;
    221 
    222                 if (!string.IsNullOrEmpty(imageInfo.eventName))
    223                 {
    224                     //图片pos:
    225                     //x:起点x + 图片宽度的一半
    226                     //y:起点y + fontSize2 * 0.5f
    227                     RichTextEvent e = new RichTextEvent();
    228                     e.name = imageInfo.eventName;
    229                     e.parameter = imageInfo.eventParameter;
    230                     e.rect = new Rect(
    231                         verts[startVertex].position.x * unitsPerPixel,
    232                         verts[startVertex].position.y * unitsPerPixel + fontSize2 * 0.5f - imageInfo.size.y * 0.5f,
    233                         imageInfo.size.x,
    234                         imageInfo.size.y
    235                     );
    236                     eventList.Add(e);
    237                 }
    238             }
    239             else
    240             {
    241                 imageInfoList.RemoveAt(i);
    242             }
    243         }
    244 
    245         isImageDirty = true;
    246 
    247         return richText;
    248     }
    249 
    250     private void ApplyUnderlineEffect(RichTextUnderlineTag tag, IList<UIVertex> verts)
    251     {
    252         float fontSize2 = fontSize * 0.5f;
    253         float unitsPerPixel = 1 / pixelsPerUnit;
    254 
    255         //0 1|4 5|8  9 |12 13
    256         //3 2|7 6|11 10|14 15
    257         //<material=underline c=#ffffff h=1 n=1 p=2>下划线</material>
    258         //以上面为例:
    259         //tag.start为42,对应“>” | start对应“下”的左上角顶点
    260         //tag.end为44,对应“划”  | end对应“线”下一个字符的左上角顶点
    261         //Debug.Log(tag.start);
    262         //Debug.Log(tag.end);
    263         int start = tag.start * 4;
    264         int end = Mathf.Min(tag.end * 4 + 4, verts.Count);
    265         UIVertex vt1 = verts[start + 3];
    266         UIVertex vt2;
    267         float minY = vt1.position.y;
    268         float maxY = verts[start].position.y;
    269 
    270         //换行处理,如需换行,则将一条下划线分割成几条
    271         //顶点取样分布,如上图的2,6,10,其中end - 2表示最后一个取样点,即10
    272         //对应例子中的下、划、线的右下角顶点
    273         for (int i = start + 2; i <= end - 2; i += 4)
    274         {
    275             vt2 = verts[i];
    276             bool newline = Mathf.Abs(vt2.position.y - vt1.position.y) > fontSize2;
    277             if (newline || i == end - 2)
    278             {
    279                 RichTextImageInfo imageInfo = new RichTextImageInfo();
    280 
    281                 //计算宽高
    282                 int tailIndex = !newline && i == end - 2 ? i : i - 4;
    283                 vt2 = verts[tailIndex];
    284                 minY = Mathf.Min(minY, vt2.position.y);
    285                 maxY = Mathf.Max(maxY, verts[tailIndex - 1].position.y);
    286                 imageInfo.size = new Vector2((vt2.position.x - vt1.position.x) * unitsPerPixel, tag.height);
    287 
    288                 //计算位置
    289                 Vector2 vertex = new Vector2(vt1.position.x, minY);
    290                 vertex *= unitsPerPixel;
    291                 vertex += new Vector2(imageInfo.size.x * 0.5f, -tag.height * 0.5f);
    292                 vertex += new Vector2(rectTransform.sizeDelta.x * (rectTransform.pivot.x - 0.5f), rectTransform.sizeDelta.y * (rectTransform.pivot.y - 0.5f));
    293                 imageInfo.position = vertex;
    294 
    295                 imageInfo.color = tag.color;
    296                 imageInfoList.Add(imageInfo);
    297 
    298                 if (!string.IsNullOrEmpty(tag.eventName))
    299                 {
    300                     //下划线pos:
    301                     //x:vt1.x + 图片宽度的一半
    302                     //y:minY - tag.height * 0.5f
    303                     RichTextEvent e = new RichTextEvent();
    304                     e.name = tag.eventName;
    305                     e.parameter = tag.eventParameter;
    306                     e.rect = new Rect(
    307                         vt1.position.x * unitsPerPixel,
    308                         minY * unitsPerPixel,
    309                         imageInfo.size.x,
    310                         (maxY - minY) * unitsPerPixel
    311                     );
    312                     eventList.Add(e);
    313                 }
    314 
    315                 vt1 = verts[i + 1];
    316                 minY = vt1.position.y;
    317                 if (newline && i == end - 2) i -= 4;
    318             }
    319             else
    320             {
    321                 minY = Mathf.Min(minY, verts[i].position.y);
    322                 maxY = Mathf.Max(maxY, verts[i - 1].position.y);
    323             }
    324         }
    325     }
    326 
    327     public void SetListener(Action<string, string> action)
    328     {
    329         clickAction = action;
    330     }
    331 
    332     public void OnPointerClick(PointerEventData eventData)
    333     {
    334         Vector2 localPos;
    335         RectTransformUtility.ScreenPointToLocalPointInRectangle(rectTransform, eventData.position, eventData.pressEventCamera, out localPos);
    336         for (int i = eventList.Count - 1; i >= 0; i--)
    337         {
    338             RichTextEvent e = eventList[i];
    339             if (e.rect.Contains(localPos))
    340             {
    341                 clickAction.Invoke(e.name, e.parameter);
    342                 break;
    343             }
    344         }
    345     }
    346 
    347     protected void Update()
    348     {
    349         if (isImageDirty)
    350         {
    351             isImageDirty = false;
    352 
    353             //回收当前的图片
    354             Image[] images = GetComponentsInChildren<Image>(true);
    355             for (int i = 0; i < images.Length; i++)
    356             {
    357                 RichTextResourceManager.Instance.SetPoolObject(RichTextResourceType.Image, images[i].gameObject);
    358             }
    359 
    360             //生成图片
    361             for (int i = 0; i < imageInfoList.Count; i++)
    362             {
    363                 RichTextImageInfo imageInfo = imageInfoList[i];
    364                 var name = imageInfo.name;
    365                 var position = imageInfo.position;
    366                 var size = imageInfo.size;
    367                 var color = imageInfo.color;
    368 
    369                 GameObject go = RichTextResourceManager.Instance.GetPoolObject(RichTextResourceType.Image);
    370                 Image image = go.GetComponent<Image>();
    371                 RichTextResourceManager.Instance.SetSprite(name, image);
    372                 go.transform.SetParent(rectTransform);
    373                 go.transform.localScale = Vector3.one;
    374                 image.rectTransform.anchoredPosition = position;
    375                 image.rectTransform.sizeDelta = size;
    376                 image.color = color;
    377             }
    378 
    379             //点击区域
    380             if (isShowClickArea)
    381             {
    382                 for (int i = 0; i < eventList.Count; i++)
    383                 {
    384                     RichTextEvent e = eventList[i];
    385 
    386                     GameObject go = RichTextResourceManager.Instance.GetPoolObject(RichTextResourceType.Image);
    387                     Image image = go.GetComponent<Image>();
    388                     RichTextResourceManager.Instance.SetSprite("", image);
    389                     go.transform.SetParent(rectTransform);
    390                     go.transform.localScale = Vector3.one;
    391                     image.rectTransform.anchoredPosition = e.rect.center;
    392                     image.rectTransform.sizeDelta = new Vector2(e.rect.width, e.rect.height);
    393                     image.color = Color.blue;
    394                 }
    395             }
    396         }
    397     }
    398 
    399     protected override void OnDestroy()
    400     {
    401         base.OnDestroy();
    402         //Debug.LogWarning("OnDestroy");
    403     }
    404 }

    效果如下图。其中蓝色区域即为点击区域。绑定监听后,点击图片或下划线上的文字,即可看到事件被响应了。

  • 相关阅读:
    【技术贴】【技术贴】每次双击都会跳出来打开方式的解决办法。。。选择你想用来打开此文件的程序。。
    【技术贴】xp更改登录头像,打开“用户账户”时显示:Automation服务器不能创建对象。的解决办
    【技术贴】关于惠普在郑州建立全球云计算服务中心的解析。。来自大河报
    【技术贴】如何删除卡巴斯基的日志?占C盘了好多空间....
    2007 Office 产品版本号
    SharePoint Workflow 基础
    重装SPS 2003的一点经验
    列出有空应该看一下的要点
    WinDBG命令概览(下) 扩展命令
    Content Deployment入门(下)
  • 原文地址:https://www.cnblogs.com/lyh916/p/9343908.html
Copyright © 2011-2022 走看看