Unity3D NGUI图文混排聊天表情

来源:互联网 发布:电视机的网络接口 编辑:程序博客网 时间:2024/05/14 03:56

原创文章如需转载请注明:转载自 脱莫柔Unity3D学习之旅 Unity3D引擎技术交流QQ群:【119706192本文链接地址: Unity3D NGUI图文混排的聊天表情

历时1年开发的mmorpg游戏终于进入收尾阶段,最后还差些需要提升表现力的小功能没做。比如图文混排聊天表情。

网上找了插件对接半天发现插件各种bug,修复实在困难,焦躁之下最终决定自己写

最为一个cv战士,生存的原则就是能ctrl+c ctrl+v 实现的功能,坚决不自己写,这么普及的功能,网上肯定有人写。

然后我就找到了蛮牛的师兄弟写的这篇文章http://www.manew.com/blog-3649-2595.html

推算不出来他藏起来的NGUIText.CalculatePrintedSize()功能是做了什么实现,我返回的宽度结果居然是(0,高度),原文展示的代码部分思路我也看不明白。

没办法,还是自己动手吧。于是自己做了一套获取每个字符累加宽度的方式实现了一版。核心代码如下:

void replaceFace(ref string text)    {        eList.Clear();        float lineW = 0;        float low = 0;        float fontSize = mLabel.fontSize;        for (int i = 0; i < text.Length; i++)        {            float cWidth = 0;            int isface = -1;            Debug.Log(text[i]);            if (text[i] == '#' && i + 4 < text.Length && text[i + 2] == '_')            {                cWidth = faceWidth;                isface = i;            }            else            {                int charW;                if (!GetCharWidth(text[i], out charW))                {                    text = text.Remove(isface, faceNamelength);                    text = text.Insert(isface, spaceChar.ToString());                }                cWidth += charW;            }            StringBuilder spaceTemp = new StringBuilder();            Debug.Log("预判" + (lineW + cWidth) + ">" + mLabel.width);            if (lineW + cWidth > mLabel.width)            {                lineW = cWidth;                low++;                Debug.Log("换行");                if (isface > 0)                {                    spaceTemp.Append("\n") ;                    Debug.Log("图片换行补换行符");                    i += 1;                }            }            else            {                lineW += cWidth;            }            if (isface > 0)            {                FacePostion ePos = new FacePostion();                ePos.facePackID = text[isface + 1];                ePos.faceID = text.Substring(isface + 3, 2);                ePos.posX = lineW - faceWidth + m_offsetX;                ePos.posY = low * -faceWidth + m_offsetY;                eList.Add(ePos);                Debug.Log("图:" + ePos.faceID);                for (int j = 0; j < spaceCount; j++)                    spaceTemp.Append(spaceChar);                text = text.Remove(isface, faceNamelength);                text = text.Insert(isface, spaceTemp.ToString());                i += faceNamelength;            }        }        DrawFace();    }

此方式,终于实现了基本的中文字+图标的混合方式。

但是!

连续的数字、字母宽度,显示的宽度居然会缩减(平均没2个字符减少2像素),经细研究发现,NGUI果然对这些字符做了缩减间距的处理。

不开心!

凭什么UILabel的widget就能获取正常宽度呢,深入探究NGUI源码发现,根源就在以上博文中提起的NGUIText.CalculatePrintedSize()方法。宽度获取失败的原因居然是我没对字体赋值。我服你!

回头又重写,嗯,秉承大神代码我看不懂的原则,最终我实现了完美解决方案。

再次分享在网上以备其它师兄弟备用。看完觉着

public class UILabelTest : MonoBehaviour {    UILabel mLabel;    UIAtlas faceAtlas;    void Awake()    {        mLabel = GetComponent<UILabel>();        // 动态字体大小赋值(!勿删!)        NGUIText.dynamicFont = mLabel.trueTypeFont;//傻逼要先赋值        int charW = (int)(NGUIText.CalculatePrintedSize(spaceChar2.ToString(), true).x);        spaceCount = faceWH / charW;        if (faceWH % charW != 0)           spaceCount += 1;    }    #region Debug    void Start () {        string str = mLabel.text;        replaceFace4(ref str);        mLabel.text = str;}    #endregion      const int faceNamelength = 5;    const char spaceChar2 = ',';    int faceWH = 22;    int spaceCount = 6;        /// <summary>    /// 表情偏移    /// </summary>    const float m_offsetX = 1;    const float m_offsetY = 8;    void replaceFace4(ref string text)    {        if (!string.IsNullOrEmpty(text) && ContainsFace(text))        {            mLabel.spacingY = faceWH - mLabel.fontSize;            NGUIText.dynamicFont = mLabel.trueTypeFont;//傻逼要先赋值            eList.Clear();            int maxWidth = mLabel.width;            float lineW = 0;            float lowCount = 0;            for (int i = 0; i < text.Length; i++)            {                if (isFaceLabel(text, i))                {                    StringBuilder spaceTemp = new StringBuilder();                    int isface = i;                    Vector2 textV = NGUIText.CalculatePrintedSize(text.Substring(0, i),maxWidth, mLabel.fontSize);                    if (textV.x + faceWH > maxWidth)                    {                        //直接下一行                        spaceTemp.Append("\n");                        i += 1;                        lineW = 0;                        lowCount = textV.y + 1;                    }                    else                    {                        //不换不换就不换                        lineW = textV.x;                        lowCount = textV.y;                    }                    FacePostion ePos = new FacePostion();                    ePos.facePackID = text[isface + 1];                    ePos.faceID = text.Substring(isface + 3, 2);                    ePos.posX = lineW + m_offsetX;                    ePos.posY = -lowCount * faceWH + m_offsetY;                    eList.Add(ePos);                    spaceTemp.Append("[ffffff00]");                    for (int j = 0; j < spaceCount; j++)                    {                            spaceTemp.Append(spaceChar2);                    }                    spaceTemp.Append("[-]");                    text = text.Remove(isface, faceNamelength);                    text = text.Insert(isface, spaceTemp.ToString());                    i += faceNamelength;                }            }            DrawFace();        }    }    #region 画表情    List<FacePostion> eList = new List<FacePostion>();    List<UISprite> faceSprites = new List<UISprite>();    bool ContainsFace(string text)    {        return text.Contains("#0_");    }    bool isFaceLabel(string text,int index)    {        return (text[index] == '#' && index + 4 < text.Length && text[index + 2] == '_');    }    void DrawFace()    {        for (int i = 0; i < eList.Count; i++)        {            UISprite sp = GetSprite(i);            sp.gameObject.SetActive(true);            sp.spriteName = "#" + eList[i].facePackID + "_" + eList[i].faceID;            sp.transform.localPosition = new Vector3(eList[i].posX, eList[i].posY);        }        for (int i = eList.Count; i < faceSprites.Count; i++)        {            faceSprites[i].gameObject.SetActive(false);        }    }    UISprite GetSprite(int index)    {        if (faceSprites.Count >= index)        {            if (faceAtlas == null)                faceAtlas = Resources.Load<UIAtlas>("AtlasImotion/Imotions");            UISprite newsp = NGUITools.AddSprite(this.gameObject, faceAtlas, "", mLabel.depth + 100);            newsp.MakePixelPerfect();            newsp.pivot = UIWidget.Pivot.BottomLeft;            faceSprites.Add(newsp);            return newsp;        }        else            return faceSprites[index];    }    class FacePostion    {        public float posX;        public float posY;        public char facePackID;        public string faceID;    }    #endregion }

然后就是NGUIText.CalculatePrintedSize的核心代码,加个重载实现。

public static Vector2 CalculatePrintedSize(string text,int maxW,int fontSize)    {        Vector2 v = new Vector2(0, 1);        if (!string.IsNullOrEmpty(text))        {            if (encoding) text = StripSymbols(text);            Prepare(text);            float x = 0f, y = 0f, maxX = 0f;            int textLength = text.Length, ch = 0, prev = 0;            regionWidth = maxW;            finalSize = fontSize;            for (int i = 0; i < textLength; ++i)            {                ch = text[i];                if (ch == '\n')                {                    if (x > maxX) maxX = x;                    x = 0f;                    y += 1;                    continue;                }                if (ch < ' ') continue;                {                    float w = GetGlyphWidth(ch, prev);                    if (w != 0f)                    {                        w += finalSpacingX;                        if (Mathf.RoundToInt(x + w) > regionWidth)                        {                            if (x > maxX) maxX = x - finalSpacingX;                            x = w;                            y += 1;                        }                        else x += w;                        prev = ch;                    }                }            }                        v.x = x;            v.y = y + 1;        }        return v;    }

需要注意的地方:

1.uilabel.text必须先赋值,然后再替换才能正确计算字体大小和行大小,所以有表情符号时要赋值2次。

1 0
原创粉丝点击