讓數字變化炫酷起來,數字滾動Text組件[Unity]

讓數字滾動起來

上週個人策劃又提了樣需求,當玩家評分發生變更時,屏幕出現人物評分浮層UI,播放評分數字滾動動畫。這類數字滾動需求很是常見,我就按通常思路,將startvalue與endvalue每隔一點時間作插值變化並顯示,從而實現數字滾動的效果,這也是大部分app及遊戲採起的實現,效果以下:app

幾行代碼寫完給策劃看效果,策劃說不是這樣的效果,跟XX遊戲作得不同,得像lao虎機數字同樣,有真實的數字滾動效果,好吧,此滾動非彼滾動,指望效果應該是下面這樣,看起來更確實炫酷:
dom

代碼實現

這樣的效果也不難實現,但要注意一些細節,讓動畫看起來更真實:測試

  1. 仍然每隔一點時間作插值變化,算出插值後,再也不是簡單地修改Text內容了,而是準備用另外一個Text顯示新值,2個Text都自底向上作緩動,形成像lao虎機同樣,新值頂掉舊值的動畫效果,要注意每一位數字都單獨使用Text組件顯示。咱們遊戲裏的戰力固定6位數顯示,因此總共設置了12個Text組件。
  2. 錯開每一位數字的滾動時間點,讓滾動效果看起來更天然。設置一個Delay變量,從個位起,後面的每一位都增長一倍Delay再滾動。

/* ==============================================================================
 * 功能描述:數字動態變化Text
 * 創 建 者:shuchangliu
 * ==============================================================================*/
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using DG.Tweening;
using UnityEngine;
using UnityEngine.UI;

public class JumpingNumberTextComponent : MonoBehaviour
{
    [SerializeField]
    [Tooltip("按最高位起始順序設置每位數字Text(顯示組)")]
    private List<Text> _numbers; 
    [SerializeField]
    [Tooltip("按最高位起始順序設置每位數字Text(替換組)")]
    private List<Text> _unactiveNumbers;
    /// <summary>
    /// 動畫時長
    /// </summary>
    [SerializeField]
    private float _duration = 1.5f;
    /// <summary>
    /// 數字每次滾動時長
    /// </summary>
    [SerializeField]
    private float _rollingDuration = 0.05f;
    /// <summary>
    /// 數字每次變更數值
    /// </summary>
    private int _speed;
    /// <summary>
    /// 滾動延遲(每進一位增長一倍延遲,讓滾動看起來更隨機天然)
    /// </summary>
    [SerializeField]
    private float _delay = 0.008f;
    /// <summary>
    /// Text文字寬高
    /// </summary>
    private Vector2 _numberSize;
    /// <summary>
    /// 當前數字
    /// </summary>
    private int _curNumber;
    /// <summary>
    /// 起始數字
    /// </summary>
    private int _fromNumber;
    /// <summary>
    /// 最終數字
    /// </summary>
    private int _toNumber;
    /// <summary>
    /// 各位數字的緩動實例
    /// </summary>
    private List<Tweener> _tweener = new List<Tweener>();
    /// <summary>
    /// 是否處於數字滾動中
    /// </summary>
    private bool _isJumping;
    /// <summary>
    /// 滾動完畢回調
    /// </summary>
    public Action OnComplete;

    private void Awake()
    {
        if (_numbers.Count == 0 || _unactiveNumbers.Count == 0)
        {
            MediaUnity.Debugger.LogError("[JumpingNumberTextComponent] 還未設置Text組件!");
            return;
        }
        _numberSize = _numbers[0].rectTransform.sizeDelta;
    }

    public float duration
    {
        get { return _duration; }
        set
        {
            _duration = value;
        }
    }

    private float _different;
    public float different
    {
        get { return _different; }
    }

    public void Change(int from, int to)
    {
        bool isRepeatCall = _isJumping && _fromNumber == from && _toNumber == to;
        if (isRepeatCall) return;

        bool isContinuousChange = (_toNumber == from) && ((to - from > 0 && _different > 0) || (to - from < 0 && _different < 0));
        if (_isJumping && isContinuousChange)
        {
        }
        else
        {
            _fromNumber = from;
            _curNumber = _fromNumber;
        }
        _toNumber = to;

        _different = _toNumber - _fromNumber;
        _speed = (int)Math.Ceiling(_different / (_duration * (1 / _rollingDuration)));
        _speed = _speed == 0 ? (_different > 0 ? 1 : -1) : _speed;

        SetNumber(_curNumber, false);
        _isJumping = true;
        StopCoroutine("DoJumpNumber");
        StartCoroutine("DoJumpNumber");  
    }

    public int number
    {
        get { return _toNumber; }
        set
        {
            if (_toNumber == value) return;
            Change(_curNumber, _toNumber);
        }
    }
    
    IEnumerator DoJumpNumber()
    {
        while (true)
        {
            if (_speed > 0)//增長
            {
                _curNumber = Math.Min(_curNumber + _speed, _toNumber);
            }
            else if (_speed < 0) //減小
            {
                _curNumber = Math.Max(_curNumber + _speed, _toNumber);
            }
            SetNumber(_curNumber, true);

            if (_curNumber == _toNumber)
            {
                StopCoroutine("DoJumpNumber");
                _isJumping = false;
                if (OnComplete != null) OnComplete();
                yield return null;
            }
            yield return new WaitForSeconds(_rollingDuration);
        }
    }

    /// <summary>
    /// 設置戰力數字
    /// </summary>
    /// <param name="v"></param>
    /// <param name="isTween"></param>
    public void SetNumber(int v, bool isTween)
    {
        char[] c = v.ToString().ToCharArray();
        Array.Reverse(c);
        string s = new string(c);

        if (!isTween)
        {
            for (int i = 0; i < _numbers.Count; i++)
            {
                if (i < s.Count())
                    _numbers[i].text = s[i] + "";
                else
                    _numbers[i].text = "0";
            }
        }
        else
        {
            while (_tweener.Count > 0)
            {
                _tweener[0].Complete();
                _tweener.RemoveAt(0);
            }

            for (int i = 0; i < _numbers.Count; i++)
            {
                if (i < s.Count())
                {
                    _unactiveNumbers[i].text = s[i] + "";
                }
                else
                {
                    _unactiveNumbers[i].text = "0";
                }

                _unactiveNumbers[i].rectTransform.anchoredPosition = new Vector2(_unactiveNumbers[i].rectTransform.anchoredPosition.x, (_speed > 0 ? -1 : 1) * _numberSize.y);
                _numbers[i].rectTransform.anchoredPosition = new Vector2(_unactiveNumbers[i].rectTransform.anchoredPosition.x, 0);

                if (_unactiveNumbers[i].text != _numbers[i].text)
                {
                    DoTween(_numbers[i], (_speed > 0 ? 1 : -1) * _numberSize.y, _delay * i);
                    DoTween(_unactiveNumbers[i], 0, _delay * i);

                    Text tmp = _numbers[i];
                    _numbers[i] = _unactiveNumbers[i];
                    _unactiveNumbers[i] = tmp;
                }
            }
        }
    }

    public void DoTween(Text text, float endValue, float delay)
    {
        Tweener t = DOTween.To(() => text.rectTransform.anchoredPosition, (x) =>
        {
            text.rectTransform.anchoredPosition = x;
        }, new Vector2(text.rectTransform.anchoredPosition.x, endValue), _rollingDuration - delay).SetDelay(delay);
        _tweener.Add(t);
    }


    [ContextMenu("測試數字變化")]
    public void TestChange()
    {
        Change(UnityEngine.Random.Range(1, 1), UnityEngine.Random.Range(1, 100000));
    }

}
相關文章
相關標籤/搜索