NGUI 表情支持控件UIEmojiLabel,支持自适应多行多列
JumboWu 关注
2017.03.03 17:27* 字数 387 阅读 1412评论 0喜欢 4
版权声明:本文为Jumbo原创文章,采用[知识共享 署名-非商业性使用-禁止演绎 4.0 国际 许可协议],转载前请保证理解此协议
原文出处:http://www.jianshu.com/p/a604b3f1fce3
NGUI出来已久,使用范围,场景越来越广。本身在高版本NGUI也支持表情,但存在一些缺陷,例如:支持文字和表情显示,但要求是必须文字和表情都需要在BMFont制作,对于文字量非常大的需求下,NGUI自带的文本混合表情,就显得力不从心。具体用过的同学,应该都知道的。
NGUI控件扩展,支持自适应多行多列表情
思路:
1、输入文本表情,在文本处理的时候,处理表情字串,记录位置
2、根据位置信息,得到文本在渲染时候的顶点位置,存在this.geometry.verts中
3、为什么位置要*4? 因为每个字串有4个顶点数据
4、把站位的emSpace ,位置赋给UISprite(显示表情的控件)
5、所有的表情,通过表情管理器动态统一管理,不显示时,回收
1、UIEmojiLabel
/* * Emoji Label @By Jumbo 2017/3/3 * */ using UnityEngine; using System.Collections; using System.Collections.Generic; using System; using System.Text; public class UIEmojiLabel : UILabel { private char emSpace = '\u2001'; private List<GameObject> mEmojiList = new List<GameObject>(); public UIAtlas emAtlas = null; protected override void OnStart() { base.OnStart(); //表情统一管理器,只初始化一次 UIEmojiWrapper.Instance.Init(emAtlas); } //public override void Update() //{ // base.Update(); // //表情统一管理器,只初始化一次 // UIEmojiWrapper.Instance.Init(emAtlas); //} //自动转码 //public override void OnFill(BetterList<Vector3> verts, BetterList<Vector2> uvs, BetterList<Color32> cols) //{ // base.OnFill(verts, uvs, cols); // //Test 测试代码,正式使用,这里注释掉,外面改变Label 的值 // string tx = "zhongz中国" + UIEmojiWrapper.Instance.GetConvertedString("1f61c") + UIEmojiWrapper.Instance.GetConvertedString("1f4fa") + UIEmojiWrapper.Instance.GetConvertedString("1f63f") + UIEmojiWrapper.Instance.GetConvertedString("1f607") + "国家回忆中心年内" + "sdfjsdlkj" + UIEmojiWrapper.Instance.GetConvertedString("1f63b") + "国家" + UIEmojiWrapper.Instance.GetConvertedString("1f467-1f3ff") + UIEmojiWrapper.Instance.GetConvertedString("1f450-1f4ff");// + "好行。天使good bye"; // text = tx; // ///~测试代码 // StartCoroutine(SetUITextThatHasEmoji(text)); //} public override void OnFill(List<Vector3> verts, List<Vector2> uvs, List<Color> cols) { base.OnFill(verts, uvs, cols); //Test 测试代码,正式使用,这里注释掉,外面改变Label 的值 string tx = "zhongz中国" + UIEmojiWrapper.Instance.GetConvertedString("00a9") + UIEmojiWrapper.Instance.GetConvertedString("00ae") + UIEmojiWrapper.Instance.GetConvertedString("1f004") + UIEmojiWrapper.Instance.GetConvertedString("1f170") + "国家回忆中心年内" + "sdfjsdlkj" + UIEmojiWrapper.Instance.GetConvertedString("1f171") + "国家" + UIEmojiWrapper.Instance.GetConvertedString("1f191-1f199") + UIEmojiWrapper.Instance.GetConvertedString("1f232-1f236") + "好行。天使good bye"; text = tx; ///~测试代码 StartCoroutine(SetUITextThatHasEmoji(text)); } private struct PosStringTuple { public int pos; public string emoji; public PosStringTuple(int p, string s) { this.pos = p; this.emoji = s; } } public IEnumerator SetUITextThatHasEmoji(string inputString) { List<PosStringTuple> emojiReplacements = new List<PosStringTuple>(); StringBuilder sb = new StringBuilder(); //先回收 UIEmojiWrapper.Instance.PushEmoji(ref mEmojiList); int i = 0; while (i < inputString.Length) { string singleChar = inputString.Substring(i, 1); string doubleChar = ""; string fourChar = ""; if (i < (inputString.Length - 1)) { doubleChar = inputString.Substring(i, 2); } if (i < (inputString.Length - 3)) { fourChar = inputString.Substring(i, 4); } if (UIEmojiWrapper.Instance.HasEmoji(fourChar)) { // Check 64 bit emojis first sb.Append(emSpace); emojiReplacements.Add(new PosStringTuple(sb.Length - 1, fourChar)); i += 4; } else if (UIEmojiWrapper.Instance.HasEmoji(doubleChar)) { // Then check 32 bit emojis sb.Append(emSpace); emojiReplacements.Add(new PosStringTuple(sb.Length - 1, doubleChar)); i += 2; } else if (UIEmojiWrapper.Instance.HasEmoji(singleChar)) { // Finally check 16 bit emojis sb.Append(emSpace); emojiReplacements.Add(new PosStringTuple(sb.Length - 1, singleChar)); i++; } else { sb.Append(inputString[i]); i++; } } // Set text this.text = sb.ToString(); yield return null; for (int j = 0; j < emojiReplacements.Count; j++) { int emojiIndex = emojiReplacements[j].pos; //表情替换,计算位置,大小 GameObject go = UIEmojiWrapper.Instance.PopEmoji(); if (go != null) { UISprite spt = go.GetComponent<UISprite>(); if (spt != null) { string emoji = UIEmojiWrapper.Instance.GetEmoji(emojiReplacements[j].emoji); if (!string.IsNullOrEmpty(emoji)) { spt.name = emoji; spt.spriteName = emoji; } //mPrintedSize 父类权限私有,可以改为子类可范围的保护的权限,渲染的字体大小变化,来改变UISprite的大小 //spt.width = this.mPrintedSize; //spt.height = this.mPrintedSize; spt.width = this.mFontSize; spt.height = this.mFontSize; spt.transform.parent = this.transform; spt.transform.localScale = Vector3.one; spt.transform.localPosition = new Vector3(this.geometry.verts[emojiIndex * 4].x + spt.width / 2, this.geometry.verts[emojiIndex * 4].y + spt.height / 2); } mEmojiList.Add(go); } } } }
2、UIEmojiLabelInspector 编辑器监视
/* * Emoji Label Inspector @By Jumbo 2017/3/3 * */ #if !UNITY_FLASH #define DYNAMIC_FONT #endif using UnityEngine; using UnityEditor; /// <summary> /// Inspector class used to edit UILabels. /// </summary> [CanEditMultipleObjects] [CustomEditor(typeof(UIEmojiLabel), true)] public class UIEmojiLabelInspector : UILabelInspector { /// <summary> /// Draw the label's properties. /// </summary> protected override bool ShouldDrawProperties() { bool isValid = base.ShouldDrawProperties(); EditorGUI.BeginDisabledGroup(!isValid); NGUIEditorTools.DrawProperty("Atlas", serializedObject, "emAtlas");//表情所在的图集Atlas EditorGUI.EndDisabledGroup(); return isValid; } }
3、UIEmojiWrapper 表情统一管理器
/* * Emoji Wrapper @By Jumbo 2017/3/3 * */ using System; using System.Collections.Generic; using System.Text; using UnityEngine; class UIEmojiWrapper { private bool hasAtlas = false; private UIAtlas mAtlas; private GameObject emojiPrefab; private Dictionary<string, string> emojiName = new Dictionary<string, string>(); private Queue<GameObject> freeSprite = new Queue<GameObject>(); private List<GameObject> usedSprite = new List<GameObject>(); private Vector3 OutOffScreen = new Vector3(10000f, 10000f, 10000f); private static UIEmojiWrapper sInstance; public static UIEmojiWrapper Instance { get { if (sInstance == null) { sInstance = new UIEmojiWrapper(); } return sInstance; } } public void Init(UIAtlas atlas) { if (!hasAtlas) { if (atlas == null) { Debug.LogError("[UIEmojiWrapper Atlas is null]"); //LogUtil.LogError(WXLogTag.LogTag_UI, "[UIEmojiWrapper Atlas is null]"); return; } mAtlas = atlas; //预分配 for (int i = 0, cnt = mAtlas.spriteList.Count; i < cnt; i++) { emojiName.Add(GetConvertedString(mAtlas.spriteList[i].name), mAtlas.spriteList[i].name); } emojiPrefab = new GameObject(); UISprite spt = emojiPrefab.AddComponent<UISprite>(); if (spt != null) { spt.transform.localPosition = OutOffScreen; spt.name = "emojiPrefab"; } hasAtlas = true; } } public string GetConvertedString(string inputString) { string[] converted = inputString.Split('-'); for (int j = 0; j < converted.Length; j++) { converted[j] = char.ConvertFromUtf32(Convert.ToInt32(converted[j], 16)); } return string.Join(string.Empty, converted); } public string GetEmoji(string encode) { string em; if (emojiName.TryGetValue(encode, out em)) { return em; } return null; } public bool HasEmoji(string key) { return emojiName.ContainsKey(key); } //public delegate void OnPostFillCallback(UIWidget widget, int bufferOffset, List<Vector3> verts, List<Vector2> uvs, List<Color> cols); public void OnPostFill(UIWidget widget, int bufferOffset, List<Vector3> verts, List<Vector2> uvs, List<Color> cols) //public void OnPostFill(UIWidget widget, int bufferOffset, BetterList<Vector3> verts, BetterList<Vector2> uvs, BetterList<Color32> cols) { if (widget != null) { if (!widget.isVisible) { UISprite spt = widget as UISprite; if (spt != null) { PushEmoji(spt.gameObject); } } } } public GameObject PopEmoji() { if (freeSprite.Count <= 0) { if (emojiPrefab == null) return null; GameObject tran = GameObject.Instantiate(emojiPrefab) as GameObject; UISprite spt = tran.GetComponent<UISprite>(); if (spt != null) { spt.atlas = mAtlas; spt.hideIfOffScreen = true; spt.onPostFill += OnPostFill; } freeSprite.Enqueue(tran); } GameObject sptRet = freeSprite.Dequeue(); if (sptRet != null) { usedSprite.Add(sptRet); } return sptRet; } public void PushEmoji(GameObject spt) { spt.transform.localPosition = OutOffScreen; spt.transform.parent = null; freeSprite.Enqueue(spt); } public void PushEmoji(ref List<GameObject> list) { for (int i = 0, cnt = list.Count; i < cnt; i++) { PushEmoji(list[i]); } list.Clear(); } }
有更好的方案,不吝赐教~~
【原创】转摘请保留原文链接http://www.jianshu.com/p/a604b3f1fce3
Demo效果:
文本表情混排效果