[Unity3D] RichText 图标扩展的简单实现

[Unity3D] RichText 图标扩展的简单实现

方法一:
利用 Horizontal Layout & Vertical Layout 布局。
大概Lyaout控件结构:
----V
----H
----Text|Icon|Text|Icon
----H
----Text|Icon|Text|Icon
----H
----Text|Icon|Text|Icon
----H
----Text|Icon|Text|Icon
方法简答,缺点换行不易处理(需要拆分字符串获取长度换行),且性能较差。
  1  public  class BattleLogManager : MonoBehaviour 
  2 {
  3 
  4      public  static BattleLogManager Instance;
  5 
  6      public GameObject mContent;
  7      //  行预制体
  8       public GameObject[] mItemPrefabs;
  9      public UISpriteSet mSpriteSet;
 10      public Font mFont;
 11      public  int mFontSize = 35;
 12 
 13      private  float mLineWidth = 600;
 14 
 15      void Start()
 16     {
 17         
 18         mLineWidth = ((mContent.transform  as RectTransform).rect.width);
 19     }
 20 
 21      void OnDestroy()
 22     {
 23         Instance =  null;
 24     }
 25 
 26      void OnEnable()
 27     {
 28         Instance =  this;
 29     }
 30 
 31      //  特别注意:Text本身的RichText标签要过滤掉才能计算字符串宽度从而判断是不是需要换行
 32       int AppendText(GameObject go,  string str,  float offset,  out  float width,  out  string richToken)
 33     {
 34         var objTr =  new GameObject("item").transform;
 35 
 36         CharacterInfo ci;
 37         var i = 0;
 38 
 39         richToken = "";
 40         width = 0.0f;
 41          for (; i < str.Length; ++i)
 42         {
 43             var ch = str[i];
 44 
 45              if (ch == '<' && i < str.Length - 1 && str[i + 1] != '/')
 46             {
 47                 var j = i + 1;
 48                  for (; j < str.Length; ++j)
 49                 {
 50                      if (str[j] == '>')
 51                     {
 52                          break;
 53                     }
 54                 }
 55 
 56                  if (j < str.Length)
 57                 {
 58                     richToken = str.Substring(i, j - i + 1);
 59                     i = j + 1;
 60                 }
 61             }
 62 
 63              if (ch == '<' && i < str.Length - 7 && str[i + 1] == '/')
 64             {
 65                 richToken = "";
 66                 i += 8;
 67             }
 68 
 69              if (i >= str.Length)
 70             {
 71                  if ( string.IsNullOrEmpty(richToken))
 72                 {
 73                     Global.Log("bad rich format!");
 74                 }
 75                  break;
 76             }
 77 
 78             ch = str[i];
 79 
 80             mFont.GetCharacterInfo(str[i],  out ci);
 81             var advance = ci.advance * (mFontSize / 32.0f);
 82              if (width + advance + offset > mLineWidth)
 83             {
 84                  break;
 85             }
 86             width += advance;
 87         }
 88 
 89         var newStr = (i == str.Length ? str : str.Substring(0, i)) + ( string.IsNullOrEmpty(richToken) ? "" : "</color>");
 90 
 91         objTr.SetParent(go.transform);
 92         objTr.localPosition = Vector3.zero;
 93         objTr.localScale = Vector3.one;
 94         objTr.localRotation = Quaternion.identity;
 95 
 96         var text = objTr.gameObject.AddComponent<UnityEngine.UI.Text>();
 97 
 98         text.font = mFont;
 99         text.fontSize = mFontSize;
100         text.alignment = TextAnchor.MiddleCenter;
101         text.text = newStr;
102         text.horizontalOverflow = HorizontalWrapMode.Overflow;
103 
104          return i;
105     }
106 
107      bool AppendIcon(GameObject go,  string icon,  float offset,  out  float width)
108     {
109         Material material;
110         var sprite = mSpriteSet.Get(icon,  out material);
111 
112         width = 0;
113          if (!sprite)
114         {
115              return  true;
116         }
117 
118         width = sprite.rect.width;
119 
120          if (offset + width > mLineWidth)
121         {
122              return  false;
123         }
124          else
125         {
126             var objTr =  new GameObject("item").transform;
127 
128             objTr.parent = go.transform;
129             objTr.localPosition = Vector3.zero;
130             objTr.localScale = Vector3.one;
131             objTr.localRotation = Quaternion.identity;
132 
133             var image = objTr.gameObject.AddComponent<UnityEngine.UI.Image>();
134 
135             image.sprite = sprite;
136             image.material = material;
137             image.SetNativeSize();
138         }
139 
140          return  true;
141     }
142 
143      void InnerInsertBattleLog( int itemPrefabIndex,  string str,  int alignment,  float offset)
144     {
145         var itemObjectTr = Instantiate(mItemPrefabs[itemPrefabIndex]).transform;
146 
147         itemObjectTr.SetParent(mContent.transform);
148         itemObjectTr.localPosition = Vector3.zero;
149         itemObjectTr.localScale = Vector3.one;
150         itemObjectTr.localRotation = Quaternion.identity;
151         itemObjectTr.GetComponent<UnityEngine.UI.HorizontalLayoutGroup>().childAlignment = (TextAnchor)alignment;
152 
153         var textStart = 0;
154 
155          for (var i = 0; i < str.Length; )
156         {
157              if (i < str.Length - 1 && str[i] == '$' && str[i + 1] == '<')
158             {
159                 var iconStart = i + 2;
160                 var iconEnd = -1;
161 
162                  for (var j = iconStart; j < str.Length; ++j)
163                 {
164                      if (str[j] == '>')
165                     {
166                         iconEnd = j;
167                          break;
168                     }
169                 }
170 
171                  if (iconEnd != -1)
172                 {
173                      if (textStart != iconStart - 2)
174                     {
175                         var width = 0.0f;
176                          string richToken =  null;
177                         var subEndIndex = AppendText(itemObjectTr.gameObject, str.Substring(textStart, iconStart - 2 - textStart), offset,  out width,  out richToken);
178 
179                         offset += width;
180                          if (subEndIndex < iconStart - 2 - textStart)
181                         {
182                             InnerInsertBattleLog(itemPrefabIndex, richToken + str.Substring(textStart + subEndIndex, str.Length - (textStart + subEndIndex)), alignment, 0);
183                              return;
184                         }
185                     }
186 
187                      //  new line
188                      {
189                         var width = 0.0f;
190 
191                          if (!AppendIcon(itemObjectTr.gameObject, str.Substring(iconStart, iconEnd - iconStart), offset,  out width))
192                         {
193                             InnerInsertBattleLog(itemPrefabIndex, str.Substring(iconStart - 2, str.Length - (iconStart - 2)), alignment, 0);
194                              return;
195                         }
196                         offset += width;
197                     }
198 
199                     i = iconEnd + 1;
200                     textStart = i;
201                 }
202                  else
203                 {
204                     ++i;
205                 }
206             }
207              else
208             {
209                 ++i;
210             }
211         }
212 
213          if (textStart < str.Length)
214         {
215             var width = 0.0f;
216              string richToken =  null;
217             var subEndIndex = AppendText(itemObjectTr.gameObject, str.Substring(textStart, str.Length - textStart), offset,  out width,  out richToken);
218 
219             offset += width;
220              if (subEndIndex < str.Length - textStart)
221             {
222                 InnerInsertBattleLog(itemPrefabIndex, richToken + str.Substring(textStart + subEndIndex, str.Length - textStart - subEndIndex), alignment, 0);
223                  return;
224             }
225         }
226     }
227 
228      public  void InsertBattleLog( int itemPrefabIndex,  string str,  int alignment)
229     {
230         InnerInsertBattleLog(itemPrefabIndex, str, alignment, 0);
231     }
232 
233      public  void ClearAllLog()
234     {
235          for (var i = mContent.transform.childCount - 1; i >= 0; --i)
236         {
237             Destroy(mContent.transform.GetChild(i).gameObject);
238         }
239     }
240 }
241 

方法二:
1.利用FontCreator查找多余可利用字符编码。
例如:E7CC-E7D6



2.合成ICON图集,只支持一张,注意:贴图寻址模式必须是 Repeat ,因为ICON uv会被重置为大于1,而字体贴图寻址模式必须是 Clamp 。
例如:

3.使用新的字体材质。

1 fixed4 frag (v2f i) : SV_Target
2 {
3     fixed4 col = i.color;
4     col.a *= tex2D(_MainTex, i.texcoord).a;
5      //  当检测到 uv.x > 1 是说明是被重置的对象 那么选取 Icon 贴图进行显示
6       return lerp(col, tex2D(_IconTex, i.texcoord), step(1, i.texcoord.x));
7 }

4.启动时候重新映射特殊字符UV E7CC-E7D6
 1  public  class BattleLogManager : MonoBehaviour
 2 {
 3 
 4      public  static BattleLogManager Instance;
 5 
 6      public UnityEngine.UI.Text mContent;
 7 
 8      private System.Text.StringBuilder mStringBuilder =  new System.Text.StringBuilder();
 9      private  bool mIsDirty =  false;
10 
11      void OnDestroy()
12     {
13         Instance =  null;
14     }
15 
16      void OnEnable()
17     {
18         Instance =  this;
19     }
20 
21      public  void InsertBattleLog( int itemPrefabIndex,  string str,  int alignment)
22     {
23          //  大字符串合并
24          mStringBuilder.AppendLine(str);
25          //  mIsDirty 避免在同一帧内多次调用重新设置UI文本
26          mIsDirty =  true;
27     }
28 
29      public  void ClearAllLog()
30     {
31         mStringBuilder =  new System.Text.StringBuilder();
32         mContent.text = "";
33         mIsDirty =  false;
34     }
35 
36      public  void LateUpdate()
37     {
38          if (mIsDirty)
39         {
40             mIsDirty =  false;
41             mContent.text = mStringBuilder.ToString();
42         }
43     }
44 }

 1  public  class RichTextConfig : MonoBehaviour 
 2 {
 3 
 4      public Font font;
 5      public  int richIconTextureWidth = 256;
 6      public  int richIconTextureHeight = 128;
 7 
 8     [System.Serializable]
 9      public  struct Item
10     {
11          public  int index;
12          public Sprite sprite;
13     }
14 
15      public Item[] items;
16 
17      void Start () 
18     {
19         DontDestroyOnLoad( this);
20 
21         Build();
22     }
23 
24     [ContextMenu("build")]
25      private  void Build()
26     {
27         var ci = font.characterInfo;
28 
29          for (var i = 0; i < ci.Length; ++i)
30         {
31              for (var j = 0; j < items.Length; ++j)
32             {
33                  if (ci[i].index == items[j].index)
34                 {
35                     var rect = items[j].sprite.textureRect;
36                     ci[i].uv =  new Rect(2 + rect.x / richIconTextureWidth, rect.y / richIconTextureHeight, rect.width / richIconTextureWidth, rect.height / richIconTextureHeight);
37                 }
38             }
39         }
40 
41         font.characterInfo = ci;
42     }
43 
44 }
45 



5.字体材质使用新的材质

你可能感兴趣的:([Unity3D] RichText 图标扩展的简单实现)