LetterSpacing.cs 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. using System.Collections.Generic;
  2. using System.Text.RegularExpressions;
  3. using UnityEngine.UI;
  4. using UnityEngine;
  5. using System.Collections;
  6. [AddComponentMenu("UI/Effects/Letter Spacing", 15)]
  7. public class LetterSpacing : BaseMeshEffect
  8. {
  9. private const string SupportedTagRegexPattersn = @"<b>|</b>|<i>|</i>|<size=.*?>|</size>|<color=.*?>|</color>|<material=.*?>|</material>";
  10. [SerializeField]
  11. private bool useRichText;
  12. [SerializeField]
  13. private float m_spacing = 0f;
  14. protected LetterSpacing() { }
  15. #if UNITY_EDITOR
  16. protected override void OnValidate()
  17. {
  18. spacing = m_spacing;
  19. base.OnValidate();
  20. }
  21. #endif
  22. public float spacing
  23. {
  24. get { return m_spacing; }
  25. set
  26. {
  27. if (m_spacing == value) return;
  28. m_spacing = value;
  29. if (graphic != null) graphic.SetVerticesDirty();
  30. }
  31. }
  32. public override void ModifyMesh(VertexHelper vh)
  33. {
  34. if (!this.IsActive())
  35. return;
  36. List<UIVertex> list = new List<UIVertex>();
  37. vh.GetUIVertexStream(list);
  38. ModifyVertices(list);
  39. vh.Clear();
  40. vh.AddUIVertexTriangleStream(list);
  41. }
  42. public void ModifyVertices(List<UIVertex> verts)
  43. {
  44. if (!IsActive()) return;
  45. Text text = GetComponent<Text>();
  46. string str = text.text;
  47. IList<UILineInfo> lineInfos = text.cachedTextGenerator.lines;
  48. for (int i = lineInfos.Count - 1; i > 0; i--)
  49. {
  50. str = str.Insert(lineInfos[i].startCharIdx, "\n");
  51. str = str.Remove(lineInfos[i].startCharIdx - 1, 1);
  52. }
  53. string[] lines = str.Split('\n');
  54. if (text == null)
  55. {
  56. Debug.LogWarning("LetterSpacing: Missing Text component");
  57. return;
  58. }
  59. Vector3 pos;
  60. float letterOffset = spacing * (float)text.fontSize / 100f;
  61. float alignmentFactor = 0;
  62. int glyphIdx = 0; // 字符索引从文本开始,包括RichText标记和换行符
  63. bool isRichText = useRichText && text.supportRichText;
  64. IEnumerator matchedTagCollection = null; // 当使用RichText时,它将收集所有标签 (index, length, value)
  65. Match currentMatchedTag = null;
  66. switch (text.alignment)
  67. {
  68. case TextAnchor.LowerLeft:
  69. case TextAnchor.MiddleLeft:
  70. case TextAnchor.UpperLeft:
  71. alignmentFactor = 0f;
  72. break;
  73. case TextAnchor.LowerCenter:
  74. case TextAnchor.MiddleCenter:
  75. case TextAnchor.UpperCenter:
  76. alignmentFactor = 0.5f;
  77. break;
  78. case TextAnchor.LowerRight:
  79. case TextAnchor.MiddleRight:
  80. case TextAnchor.UpperRight:
  81. alignmentFactor = 1f;
  82. break;
  83. }
  84. for (int lineIdx = 0; lineIdx < lines.Length; lineIdx++)
  85. {
  86. string line = lines[lineIdx];
  87. int lineLength = line.Length;
  88. if (isRichText)
  89. {
  90. matchedTagCollection = GetRegexMatchedTagCollection(line, out lineLength);
  91. currentMatchedTag = null;
  92. if (matchedTagCollection.MoveNext())
  93. {
  94. currentMatchedTag = (Match)matchedTagCollection.Current;
  95. }
  96. }
  97. float lineOffset = (lineLength - 1) * letterOffset * alignmentFactor;
  98. for (int charIdx = 0, actualCharIndex = 0; charIdx < line.Length; charIdx++, actualCharIndex++)
  99. {
  100. if (isRichText)
  101. {
  102. if (currentMatchedTag != null && currentMatchedTag.Index == charIdx)
  103. {
  104. // skip matched RichText tag
  105. charIdx += currentMatchedTag.Length - 1; // -1 because next iteration will increment charIdx
  106. actualCharIndex--; // tag is not an actual character, cancel counter increment on this iteration
  107. glyphIdx += currentMatchedTag.Length; // glyph index is not incremented in for loop so skip entire length
  108. // prepare next tag to detect
  109. currentMatchedTag = null;
  110. if (matchedTagCollection.MoveNext())
  111. {
  112. currentMatchedTag = (Match)matchedTagCollection.Current;
  113. }
  114. continue;
  115. }
  116. }
  117. int idx1 = glyphIdx * 6 + 0;
  118. int idx2 = glyphIdx * 6 + 1;
  119. int idx3 = glyphIdx * 6 + 2;
  120. int idx4 = glyphIdx * 6 + 3;
  121. int idx5 = glyphIdx * 6 + 4;
  122. int idx6 = glyphIdx * 6 + 5;
  123. // Check for truncated text (doesn't generate verts for all characters)
  124. if (idx6 > verts.Count - 1) return;
  125. UIVertex vert1 = verts[idx1];
  126. UIVertex vert2 = verts[idx2];
  127. UIVertex vert3 = verts[idx3];
  128. UIVertex vert4 = verts[idx4];
  129. UIVertex vert5 = verts[idx5];
  130. UIVertex vert6 = verts[idx6];
  131. pos = Vector3.right * (letterOffset * actualCharIndex - lineOffset);
  132. vert1.position += pos;
  133. vert2.position += pos;
  134. vert3.position += pos;
  135. vert4.position += pos;
  136. vert5.position += pos;
  137. vert6.position += pos;
  138. verts[idx1] = vert1;
  139. verts[idx2] = vert2;
  140. verts[idx3] = vert3;
  141. verts[idx4] = vert4;
  142. verts[idx5] = vert5;
  143. verts[idx6] = vert6;
  144. glyphIdx++;
  145. }
  146. // Offset for carriage return character that still generates verts
  147. glyphIdx++;
  148. }
  149. }
  150. private IEnumerator GetRegexMatchedTagCollection(string line, out int lineLengthWithoutTags)
  151. {
  152. MatchCollection matchedTagCollection = Regex.Matches(line, SupportedTagRegexPattersn);
  153. lineLengthWithoutTags = 0;
  154. int tagsLength = 0;
  155. if (matchedTagCollection.Count > 0)
  156. {
  157. foreach (Match matchedTag in matchedTagCollection)
  158. {
  159. tagsLength += matchedTag.Length;
  160. }
  161. }
  162. lineLengthWithoutTags = line.Length - tagsLength;
  163. return matchedTagCollection.GetEnumerator();
  164. }
  165. }