[Unity] UGUI拓展 - 動畫控制富文本異色部分的Alpha

1、問題背景

??今天接到一個表現(xiàn)上的需求:在有淡出動畫的獎勵提示上,異色標記稀有道具的名稱。
  本來是一個挺簡單的功能,在提示文字中找出道具名的位置,然后在兩端插入UGUI的<color>標簽。測試的時候卻發(fā)現(xiàn),淡出過程中異色部分的透明度沒有發(fā)生變化。

Alpha不變的情況

  在項目組詢問一番,有大佬已經(jīng)寫腳本處理了這個問題。懷著不斷造輪子的心態(tài),對同事的腳本進行了改寫(當然改寫的腳本沒有放入項目),改寫的目的有兩個:

  • 用自己的命名習(xí)慣書寫
  • 改善下性能
改進后的效果

2、淺析

??簡單分析(cai)下問題的原因。
  首先,UGUI改變color屬性,是修改頂點色,而不是修改材質(zhì)的屬性。因此可以出現(xiàn)同一段文字,顏色(包含Alpha)不同的情況。
  然后,文字異色是通過<color>標簽進行標記的,它使用的是一個8位十六進制數(shù)表示,順序分別是RGBA。第一張圖中雖然我用的是6位的#00ff00,但Unity內(nèi)部應(yīng)該會把它補成8位的#00ff00ff。(具體實現(xiàn)沒細究,大概是取不到Alpha位就默認不透明吧)
  那么問題就能猜到了,淡出動畫僅僅是修改了color屬性,文本中的<color>標簽沒有任何變化,Unity依舊使用標簽的信息去填充頂點色。解決方案也是針對<color>標簽進行處理的。

3、完整代碼

[ExecuteInEditMode]
public class RichTextAlphaUpdater : MonoBehaviour
{
    public Text Txt;

    /// <summary>
    /// 匹配顏色值
    /// </summary>
    public static readonly Regex RichColorReg = new Regex("<color=#([a-f0-9]{8})>", RegexOptions.IgnoreCase);
    public const int ColorMax = 255;
    
    private UnityAction _vertDirtyAction;
    private UnityAction VertDirtyAction
    {
        get
        {
            if (null == _vertDirtyAction)
            {
                _vertDirtyAction = _OnVertDirty;
            }
            return _vertDirtyAction;
        }
    }

    /// <summary>
    /// 文字頂點變化的事件
    /// </summary>
    private void _OnVertDirty()
    {
        string alpha = _GetHexAlpha();
        string txt = Txt.text;
        Match match = RichColorReg.Match(txt);
        Group group = null;
        while (match.Success)
        {
            group = match.Groups[1];
            _ReplaceAlpha(txt, group.Index, alpha);
            match = match.NextMatch();
        }
    }

    /// <summary>
    /// 緩存數(shù)據(jù),降低處理頻率
    /// </summary>
    private int _prevAlpha = 0;
    private string _hexAlpha = null;

    /// <summary>
    /// 獲取當前Alpha的Hex值
    /// </summary>
    private string _GetHexAlpha()
    {
        int alpha = Mathf.Clamp((int) (Txt.color.a * ColorMax), 0, ColorMax);
        if (null != _hexAlpha && alpha == _prevAlpha)
        {
            return _hexAlpha;
        }

        string hexAlpha = Convert.ToString(alpha, 16);
        if (hexAlpha.Length == 1)
        {
            return "0" + hexAlpha;
        }
        return hexAlpha;
    }

    private void _ReplaceAlpha(string txt, int colorIdx, string alpha)
    {
        unsafe
        {
            fixed (char* hexPtr = txt)
            {
                hexPtr[colorIdx + 6] = alpha[0];
                hexPtr[colorIdx + 7] = alpha[1];
            }
        }
    }

    void OnEnable()
    {
        if (null == Txt)
        {
            Txt = GetComponent<Text>();
        }
        if (null != Txt)
        {
            Txt.RegisterDirtyVerticesCallback(VertDirtyAction);
        }
    }

    void OnDisable()
    {
        if(null == Txt) return;
        Txt.UnregisterDirtyVerticesCallback(VertDirtyAction);
    }
}
  • 使用:
    1)把腳本掛到要控制的Text組件上
    2)腳本掛到任意激活的GameObject上,自己關(guān)聯(lián)Text組件

4、知識點

代碼雖然簡單,但也有幾個小點值得記錄備忘。

1)Text重建回調(diào)
  • Text提供了RegisterDirtyVerticesCallbackRegisterDirtyMaterialCallback、RegisterDirtyLayoutCallback等幾個回調(diào),讓開發(fā)者可以在重建的時候做些事情
  • 回調(diào)執(zhí)行后重建不是馬上(同一幀)進行的,這里只是通知開發(fā)者,組件被加入了相應(yīng)的Change List
  • 在回調(diào)中做引發(fā)重建的處理,會陷入死循環(huán)
    同事的方案中,是通過【取消 - 再注冊】的方式避免死循環(huán)的,針對類似的情況應(yīng)該是挺好的處理方法。
    我的方案可以不考慮死循環(huán),因為是直接修改的string對象,不會觸發(fā)重建。
2)Unity中使用指針

??為了減少字符串操作(減少GC),我嘗試使用指針進行字符替換,然后得到了喜人的結(jié)果,性能和GC都有所提高~

  • 獲取指針需要用fixed域固定內(nèi)存的位置,僅使用unsafe是不夠的
  • 為了讓Unity能夠編譯unsafe代碼,要在工程中加入一個smcs.rsp文件,里面僅寫入-unsafe,并重啟Unity?。?/strong>

??這里有個小抉擇,本來為了使用的時候方便,想支持6位色值的。寫完指針方案后,我放棄了6位色值。因為它無法通過一對一的char替換完成,需要插入內(nèi)容,那么GC就無法避免了。

3)正則表達式
  • 這套方案并不是無GC的,我在Editor中測試,一段簡單的文字(十來個有用字符)動畫過程中每幀也有1.4K左右的GC產(chǎn)生。雖沒細測,但基本可以確定這部分開銷是正則產(chǎn)生的。好吃易上火啊Orm
  • 遍歷正則的匹配結(jié)果,可以用Matches()+Index或Match()+NextMatch(),測試發(fā)現(xiàn),后者比前者產(chǎn)生的GC少0.1K。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 111. [動畫系統(tǒng)]如何將其他類型的動畫轉(zhuǎn)換成關(guān)鍵幀動畫? 動畫->點緩存->關(guān)鍵幀 112. [動畫]Unit...
    胤醚貔貅閱讀 13,533評論 3 88
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,234評論 25 708
  • 1. [C#語言基礎(chǔ)]請簡述拆箱和裝箱。 答: 裝箱操作: 值類型隱式轉(zhuǎn)換為object類型或由此值類型實現(xiàn)的任何...
    胤醚貔貅閱讀 4,988評論 1 28
  • 近日2個美國腦殘青年在自己家后院做了一臺暫時簡稱為“高達”的“機器人”,這可能是美國民間第一個根據(jù)電影動畫等素材設(shè)...
    腦洞科技閱讀 772評論 0 48
  • 女兒是父母的貼心小棉襖,以至于我家重女輕男,從來都是弟弟去干活,我和妹妹休息中。今天看到果汁的留言,不自覺心都化了...
    臻靜閱讀 431評論 0 3

友情鏈接更多精彩內(nèi)容