關於 UGUI 字體花屏或亂碼。


  我們項目從某個時候開始ui突然開始出現字體花屏現象(unity 開發版本:5.3.6p6),而且很難必現卻又時有發生,確實查找和解決起來並不太容易。

  關於這個問題,uwa官方給出了解釋,http://blog.uwa4d.com/archives/techsharing_35.html, http://blog.uwa4d.com/search/%E5%AD%97%E4%BD%93/。可用的方案就是一開始把字體擴容到足夠大,但對於常用漢字就有3000多個,再加上不同的大小和樣式,不同的字體,內存占用光想一想就很可觀,所以將此當做最保底的方案。Unity 官方應該是解決過這個問題,但是沒有徹底。但是我更加高度懷疑的是:FontTexture 本身的生成和對應的 UV 信息並沒有問題,而是字體使用的 UV 有問題(沒有更新),uwa 提供的方案可能是針對的早期問題,也許現在問題是新引起的。

  后來又看到雨松MOMO的博客也有提到並給出了解決方案:http://www.xuanyusong.com/archives/4259,這個方法很好,在 FontTexture 重建時在其后的 LateUpdate 中刷新所有的 Text,這個方案也是我希望的方式,不會產生過多的浪費字體紋理。不過 GameObject.FindObjectsOfType<Text>() 這個調用我覺得對於 Text 過多的場景恐怕效率堪憂,翻閱了下 UI 的源碼,發現其中有個類 UnityEngine.UI.FontUpdateTracker 就是專門用來更新 Text 相關字體信息的,但是接口都不是 public,但是可以反射調用就好,所以可以用此對 雨松MONO 的方案優化一下:

using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class DynamicFontTextureRebuildTracker : MonoBehaviour
{
    private class FontUpdateNode
    {
        private bool m_FontTextureRebuilt = false;
        private Font m_FontRebuilt = null;

        public FontUpdateNode(Font font)
        {
            m_FontRebuilt = font;
            Validate();
        }

        public void Validate()
        {
            if (null == m_FontRebuilt)
            {
                m_FontTextureRebuilt = false;

                Debug.LogWarning("You need a actual font to validate!");
                return;
            }

            m_FontTextureRebuilt = true;
        }

        public void Invalidate()
        {
            m_FontTextureRebuilt = false;
        }

        public bool NeedUpdate
        {
            get { return m_FontTextureRebuilt && (null != m_FontRebuilt); }
        }

        public Font font
        {
            get { return m_FontRebuilt; }
        }
    }

    private System.Reflection.MethodInfo m_RebuildForFont = null;
    private List<FontUpdateNode> m_FontUpdateList = new List<FontUpdateNode>();

    private static DynamicFontTextureRebuildTracker m_Instance = null;

    void Awake()
    {
        if (null != m_Instance)
        {
            Debug.LogError("There is only one DynamicFontTextureRebuildTracker instance allowed!");
            Destroy(gameObject);
            return;
        }

        m_Instance = this;
    }

    // Use this for initialization
    void Start() 
    {
        Font.textureRebuilt += OnFontTextureRebuilt;

        System.Type fontUpdateTrackerType = typeof(UnityEngine.UI.FontUpdateTracker);
        m_RebuildForFont = fontUpdateTrackerType.GetMethod("RebuildForFont", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static);
        Debug.Log("Get RebuildForFont method is: " + m_RebuildForFont);
    }
    
    // Update is called once per frame
    void LateUpdate() 
    {
        if (null == m_RebuildForFont)
        {
            return;
        }

        for (int i = 0; i < m_FontUpdateList.Count; i++)
        {
            FontUpdateNode node = m_FontUpdateList[i];
            if (node.NeedUpdate)
            {
                Font font = node.font;
                m_RebuildForFont.Invoke(null, new object[] { font });

                // Log rebuild.
                Texture fontTexture = font.material.mainTexture;
                Debug.Log(string.Format("Texture of dynamic font \"{0}\" is enlarged to {1}x{2}.", font.name, fontTexture.width, fontTexture.height));

                node.Invalidate();
            }
        }
    }

    void OnDestroy()
    {
        Font.textureRebuilt -= OnFontTextureRebuilt;
    }

    private void OnFontTextureRebuilt(Font font)
    {
        bool findThisFont = false;
        for (int i = 0; i < m_FontUpdateList.Count; i++)
        {
            FontUpdateNode node = m_FontUpdateList[i];
            if (node.font == font)
            {
                node.Validate();
                findThisFont = true;
                break;
            }
        }

        if (!findThisFont)
        {
            m_FontUpdateList.Add(new FontUpdateNode(font));
        }
    }

    //void OnGUI()
    //{
    //    if (GUI.Button(new Rect(30.0f, 50.0f, 200.0f, 50.0f), "Force Update Text"))
    //    {
    //        for (int i = 0; i < m_FontUpdateList.Count; i++)
    //        {
    //            Font font = m_FontUpdateList[i].font;
    //            m_RebuildForFont.Invoke(null, new object[] { font });
    //            Debug.Log(string.Format("Force rebuild text for font \"{0}\".", font.name));
    //        }

    //        Debug.Log("Force rebuild all text ok!");
    //    }
    //}
}

  為了驗證確實是 FontTexture 是 ok 的而亂碼只是 Text 的 uv 不正確,可以將 OnGUI 的代碼放開,將 LateUpdate 的代碼注釋,然后運行游戲,在出現字體亂碼后點擊 “Force Update Text” 按鈕,如果文字亂碼立即消失,就證實了上面的猜測僅僅是 Text UV 沒有更新而已。經過在手機上實際測試出現亂碼后立即點擊按鈕,文字均顯示正常,也證實了這個問題。

  使用時在初始場景中新建一個空 GameObject,然后 DontDestroyOnLoad,再掛上 DynamicFontTextureRebuildTracker 腳本即可。后來游戲中(ios, android)就再也沒有出現過字體花屏亂碼等現象。

  最后附上該腳本下載地址:http://pan.baidu.com/s/1c1LPoJY


2017-07-01 更新:

  最近我們項目的 Unity 版本一直穩定在 5.3.7p4,但是極少數情況下(甚至是某一小段時間內)依然偶爾出現了字體花屏,但是最近又沒有了,不知道原因,有可能其它系統引起了問題。


2017-07-06 更新:

  最近測試同學報告說,最近偶爾依然出現的字體花屏不像以前一進場景就花掉,而是出現了角色單位后就變化了,進一步縮小了范圍,先記錄下,后面有時間研究下。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM