讓數字滾動起來
上周我的策劃又提了樣需求,當玩家評分發生變動時,螢幕出現人物評分浮層UI,播放評分數字滾動動畫。這類數字滾動需求非常常見,我就按一般思路,將startvalue與endvalue每隔一點時間做插值變化並顯示,從而實現數字滾動的效果,這也是大部分app及遊戲採取的實現,效果如下:
幾行代碼寫完給策劃看效果,策劃說不是這樣的效果,跟XX遊戲做得不一樣,得像lao虎機數字一樣,有真實的數字滾動效果,好吧,此滾動非彼滾動,期望效果應該是下面這樣,看起來更確實炫酷:
代碼實現
這樣的效果也不難實現,但要注意一些細節,讓動畫看起來更真實: 仍然每隔一點時間做插值變化,算出插值後,不再是簡單地修改Text內容了,而是準備用另一個Text顯示新值,2個Text都自底向上做緩動,造成像lao虎機一樣,新值頂掉舊值的動畫效果,要注意每一位元字都單獨使用Text組件顯示。我們遊戲裡的戰力固定6位元顯示,所以總共設定了12個Text組件。 錯開每一位元字的滾動時間點,讓滾動效果看起來更自然。設定一個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)); }} 標籤: 遊戲開發, Unity