unity数字text滚动代码实现 代码缓存_第二章.md 19 KB

FlipNumberDisplay

翻页实现(未验证)

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

namespace SlotGame
{
    /// <summary>
    /// 翻页数字显示器 - 模拟机械老虎机的数字滚轮效果
    /// Flip number display - simulates mechanical slot machine number wheel effects
    /// </summary>
    [System.Serializable]
    public class DigitWheel
    {
        [Header("UI组件 UI Components")]
        public RectTransform wheelContainer;
        public List<Text> digitTexts = new List<Text>();
        
        [Header("动画设置 Animation Settings")]
        public float spinSpeed = 1000f;
        public AnimationCurve spinCurve = AnimationCurve.EaseInOut(0, 0, 1, 1);
        
        [HideInInspector] public int currentDigit = 0;
        [HideInInspector] public bool isSpinning = false;
        [HideInInspector] public Coroutine spinCoroutine;
    }
    
    public class FlipNumberDisplay : MonoBehaviour
    {
        [Header("数字轮设置 Digit Wheel Setup")]
        [SerializeField] private List<DigitWheel> digitWheels = new List<DigitWheel>();
        [SerializeField] private int maxDigits = 12; // 增加到12位,支持千亿级别
        [SerializeField] private float digitHeight = 60f;
        
        [Header("滚动设置 Spinning Settings")]
        [SerializeField] private float baseDuration = 2f;
        [SerializeField] private float extraDurationPerDigit = 0.3f;
        [SerializeField] private bool spinFromRightToLeft = true;
        [SerializeField] private float digitDelay = 0.1f;
        
        [Header("大数字优化 Large Number Optimization")]
        [SerializeField] private bool enableLargeNumberMode = true;
        [SerializeField] private long largeNumberThreshold = 10000000; // 1千万以上算大数字
        [SerializeField] private float largeNumberSpeedMultiplier = 1.3f; // 大数字滚动速度倍数
        [SerializeField] private int largeNumberMinSpins = 5; // 大数字最少转圈数
        [SerializeField] private int largeNumberMaxSpins = 12; // 大数字最多转圈数
        [SerializeField] private float largeNumberDelayReduction = 0.7f; // 大数字延迟减少倍数
        [SerializeField] private bool enableSpectacularEffect = true; // 大数字华丽效果
        
        [Header("音效设置 Sound Settings")]
        [SerializeField] private AudioClip spinSound;
        [SerializeField] private AudioClip stopSound;
        [SerializeField] private AudioClip largeNumberSound; // 大数字专用音效
        [SerializeField] private AudioSource audioSource;
        
        [Header("调试 Debug")]
        [SerializeField] private bool debugMode = false;
        
        private long currentValue = 0;
        private long targetValue = 0;
        private bool isAnimating = false;
        
        // 事件
        public event System.Action<long> OnValueChanged;
        public event System.Action<long> OnSpinComplete;
        public event System.Action OnSpinStart;
        public event System.Action<long> OnLargeNumberDetected; // 大数字检测事件
        
        #region Unity生命周期
        
        private void Awake()
        {
            InitializeWheels();
        }
        
        private void Start()
        {
            if (audioSource == null)
                audioSource = GetComponent<AudioSource>();
        }
        
        #endregion
        
        #region 初始化
        
        private void InitializeWheels()
        {
            // 确保有足够的数字轮
            while (digitWheels.Count < maxDigits)
            {
                digitWheels.Add(new DigitWheel());
            }
            
            foreach (var wheel in digitWheels)
            {
                if (wheel.wheelContainer != null && wheel.digitTexts.Count == 0)
                {
                    CreateDigitTexts(wheel);
                }
                SetupWheelPositions(wheel);
            }
        }
        
        private void CreateDigitTexts(DigitWheel wheel)
        {
            // 为每个数字(0-9)创建文本组件,加上重复的首尾数字用于循环效果
            for (int i = 0; i < 12; i++) // 0,1,2,3,4,5,6,7,8,9,0,1
            {
                GameObject digitObj = new GameObject($"Digit_{i % 10}");
                digitObj.transform.SetParent(wheel.wheelContainer);
                
                Text digitText = digitObj.AddComponent<Text>();
                digitText.text = (i % 10).ToString();
                digitText.fontSize = 36;
                digitText.alignment = TextAnchor.MiddleCenter;
                digitText.color = Color.white;
                
                // 设置默认字体
                digitText.font = Resources.GetBuiltinResource<Font>("Arial.ttf");
                
                RectTransform rectTransform = digitText.rectTransform;
                rectTransform.sizeDelta = new Vector2(digitHeight, digitHeight);
                rectTransform.localScale = Vector3.one;
                
                wheel.digitTexts.Add(digitText);
            }
        }
        
        private void SetupWheelPositions(DigitWheel wheel)
        {
            if (wheel.digitTexts.Count == 0) return;
            
            for (int i = 0; i < wheel.digitTexts.Count; i++)
            {
                RectTransform rectTransform = wheel.digitTexts[i].rectTransform;
                rectTransform.localPosition = new Vector3(0, (i - 1) * digitHeight, 0);
            }
        }
        
        #endregion
        
        #region 大数字检测和优化
        
        /// <summary>
        /// 检查是否为大数字
        /// </summary>
        private bool IsLargeNumber(long value)
        {
            return enableLargeNumberMode && value >= largeNumberThreshold;
        }
        
        /// <summary>
        /// 获取大数字等级 (1=大数字, 2=超大数字, 3=极大数字)
        /// </summary>
        private int GetLargeNumberLevel(long value)
        {
            if (value >= 1000000000000) return 3; // 万亿级
            if (value >= 10000000000) return 2;   // 百亿级
            if (value >= largeNumberThreshold) return 1; // 千万级
            return 0;
        }
        
        /// <summary>
        /// 根据数字大小计算优化参数
        /// </summary>
        private (float duration, float delay, int minSpins, int maxSpins) CalculateLargeNumberParams(long value)
        {
            int level = GetLargeNumberLevel(value);
            
            float duration = baseDuration;
            float delay = digitDelay;
            int minSpins = 3;
            int maxSpins = 8;
            
            switch (level)
            {
                case 1: // 大数字
                    duration *= largeNumberSpeedMultiplier;
                    delay *= largeNumberDelayReduction;
                    minSpins = largeNumberMinSpins;
                    maxSpins = largeNumberMaxSpins;
                    break;
                case 2: // 超大数字
                    duration *= largeNumberSpeedMultiplier * 1.2f;
                    delay *= largeNumberDelayReduction * 0.8f;
                    minSpins = largeNumberMinSpins + 2;
                    maxSpins = largeNumberMaxSpins + 3;
                    break;
                case 3: // 极大数字
                    duration *= largeNumberSpeedMultiplier * 1.5f;
                    delay *= largeNumberDelayReduction * 0.6f;
                    minSpins = largeNumberMinSpins + 4;
                    maxSpins = largeNumberMaxSpins + 5;
                    break;
            }
            
            return (duration, delay, minSpins, maxSpins);
        }
        
        #endregion
        
        #region 公共方法
        
        /// <summary>
        /// 设置数值(无动画)
        /// </summary>
        public void SetValue(long value)
        {
            currentValue = value;
            targetValue = value;
            UpdateDisplayImmediate(value);
            OnValueChanged?.Invoke(value);
            
            if (debugMode) Debug.Log($"FlipNumber: Set value to {value:N0}");
        }
        
        /// <summary>
        /// 滚动到目标值
        /// </summary>
        public void SpinToValue(long targetValue, float? customDuration = null)
        {
            if (isAnimating)
            {
                StopAllSpins();
            }
            
            this.targetValue = targetValue;
            
            // 检测大数字并触发事件
            if (IsLargeNumber(targetValue))
            {
                OnLargeNumberDetected?.Invoke(targetValue);
                if (debugMode) Debug.Log($"FlipNumber: Large number detected: {targetValue:N0} (Level {GetLargeNumberLevel(targetValue)})");
            }
            
            StartCoroutine(SpinSequence(currentValue, targetValue, customDuration));
            
            if (debugMode) Debug.Log($"FlipNumber: Spinning from {currentValue:N0} to {targetValue:N0}");
        }
        
        /// <summary>
        /// 增加数值
        /// </summary>
        public void AddValue(long addAmount, float? customDuration = null)
        {
            long newTarget = currentValue + addAmount;
            SpinToValue(newTarget, customDuration);
        }
        
        /// <summary>
        /// 停止所有滚动
        /// </summary>
        public void StopAllSpins()
        {
            if (!isAnimating) return;
            
            StopAllCoroutines();
            isAnimating = false;
            
            foreach (var wheel in digitWheels)
            {
                if (wheel.spinCoroutine != null)
                {
                    StopCoroutine(wheel.spinCoroutine);
                    wheel.spinCoroutine = null;
                }
                wheel.isSpinning = false;
            }
            
            UpdateDisplayImmediate(targetValue);
            currentValue = targetValue;
        }
        
        #endregion
        
        #region 滚动动画
        
        private IEnumerator SpinSequence(long startValue, long endValue, float? customDuration)
        {
            isAnimating = true;
            OnSpinStart?.Invoke();
            
            // 根据数字大小选择音效
            AudioClip soundToPlay = IsLargeNumber(endValue) && largeNumberSound != null ? largeNumberSound : spinSound;
            
            // 播放滚动音效
            if (soundToPlay != null && audioSource != null)
            {
                audioSource.clip = soundToPlay;
                audioSource.loop = true;
                audioSource.Play();
            }
            
            string startStr = startValue.ToString().PadLeft(maxDigits, '0');
            string endStr = endValue.ToString().PadLeft(maxDigits, '0');
            
            // 获取大数字优化参数
            var (optimizedDuration, optimizedDelay, minSpins, maxSpins) = CalculateLargeNumberParams(endValue);
            
            // 计算动画时长
            float totalDuration = customDuration ?? (optimizedDuration + (endStr.Length * extraDurationPerDigit));
            
            if (debugMode && IsLargeNumber(endValue))
            {
                Debug.Log($"FlipNumber: Using large number optimization - Duration: {totalDuration:F2}s, Delay: {optimizedDelay:F2}s, Spins: {minSpins}-{maxSpins}");
            }
            
            // 按顺序启动每个数字轮的滚动
            List<Coroutine> spinCoroutines = new List<Coroutine>();
            
            for (int i = 0; i < digitWheels.Count && i < endStr.Length; i++)
            {
                int digitIndex = spinFromRightToLeft ? (endStr.Length - 1 - i) : i;
                
                if (digitIndex < digitWheels.Count)
                {
                    int startDigit = digitIndex < startStr.Length ? int.Parse(startStr[digitIndex].ToString()) : 0;
                    int endDigit = int.Parse(endStr[digitIndex].ToString());
                    
                    yield return new WaitForSeconds(optimizedDelay);
                    
                    Coroutine spinCoroutine = StartCoroutine(SpinDigitWheel(
                        digitWheels[digitIndex], 
                        startDigit, 
                        endDigit, 
                        totalDuration - (i * optimizedDelay),
                        minSpins,
                        maxSpins
                    ));
                    
                    spinCoroutines.Add(spinCoroutine);
                }
            }
            
            // 等待所有滚动完成
            while (spinCoroutines.Count > 0)
            {
                for (int i = spinCoroutines.Count - 1; i >= 0; i--)
                {
                    if (spinCoroutines[i] == null)
                    {
                        spinCoroutines.RemoveAt(i);
                    }
                }
                yield return null;
            }
            
            // 停止音效,播放停止音效
            if (audioSource != null)
            {
                audioSource.Stop();
                if (stopSound != null)
                {
                    audioSource.PlayOneShot(stopSound);
                }
            }
            
            currentValue = endValue;
            isAnimating = false;
            
            OnValueChanged?.Invoke(currentValue);
            OnSpinComplete?.Invoke(currentValue);
            
            if (debugMode) Debug.Log($"FlipNumber: Spin completed at {currentValue:N0}");
        }
        
        private IEnumerator SpinDigitWheel(DigitWheel wheel, int startDigit, int endDigit, float duration, int minSpins, int maxSpins)
        {
            if (wheel.wheelContainer == null || wheel.digitTexts.Count == 0)
                yield break;
            
            wheel.isSpinning = true;
            float elapsedTime = 0f;
            
            // 计算总的旋转圈数和最终位置
            int totalSpins = Random.Range(minSpins, maxSpins + 1);
            float totalRotation = (totalSpins * 10 + (endDigit - startDigit + 10) % 10) * digitHeight;
            
            float startY = -startDigit * digitHeight;
            float endY = startY - totalRotation;
            
            while (elapsedTime < duration)
            {
                elapsedTime += Time.deltaTime;
                float progress = elapsedTime / duration;
                float curveProgress = wheel.spinCurve.Evaluate(progress);
                
                float currentY = Mathf.Lerp(startY, endY, curveProgress);
                wheel.wheelContainer.localPosition = new Vector3(0, currentY, 0);
                
                // 大数字华丽效果:动态缩放
                if (enableSpectacularEffect && IsLargeNumber(targetValue))
                {
                    float scaleEffect = 1f + Mathf.Sin(progress * Mathf.PI * 6) * 0.05f; // 轻微的缩放动画
                    wheel.wheelContainer.localScale = Vector3.one * scaleEffect;
                }
                
                yield return null;
            }
            
            // 确保最终位置和缩放正确
            wheel.wheelContainer.localPosition = new Vector3(0, -endDigit * digitHeight, 0);
            wheel.wheelContainer.localScale = Vector3.one;
            wheel.currentDigit = endDigit;
            wheel.isSpinning = false;
            wheel.spinCoroutine = null;
        }
        
        #endregion
        
        #region 显示更新
        
        private void UpdateDisplayImmediate(long value)
        {
            string valueStr = value.ToString().PadLeft(maxDigits, '0');
            
            for (int i = 0; i < digitWheels.Count && i < valueStr.Length; i++)
            {
                int digit = int.Parse(valueStr[i].ToString());
                var wheel = digitWheels[i];
                
                if (wheel.wheelContainer != null)
                {
                    wheel.wheelContainer.localPosition = new Vector3(0, -digit * digitHeight, 0);
                    wheel.wheelContainer.localScale = Vector3.one;
                    wheel.currentDigit = digit;
                }
            }
        }
        
        #endregion
        
        #region 属性访问器
        
        public long CurrentValue => currentValue;
        public long TargetValue => targetValue;
        public bool IsAnimating => isAnimating;
        public bool IsCurrentValueLarge => IsLargeNumber(currentValue);
        public int CurrentLargeNumberLevel => GetLargeNumberLevel(currentValue);
        
        #endregion
        
        #region 工具方法
        
        /// <summary>
        /// 设置数字轮的颜色
        /// </summary>
        public void SetDigitColor(Color color)
        {
            foreach (var wheel in digitWheels)
            {
                foreach (var digitText in wheel.digitTexts)
                {
                    if (digitText != null)
                        digitText.color = color;
                }
            }
        }
        
        /// <summary>
        /// 设置数字轮的字体大小
        /// </summary>
        public void SetFontSize(int fontSize)
        {
            foreach (var wheel in digitWheels)
            {
                foreach (var digitText in wheel.digitTexts)
                {
                    if (digitText != null)
                        digitText.fontSize = fontSize;
                }
            }
        }
        
        /// <summary>
        /// 为大数字设置特殊颜色效果
        /// </summary>
        public void SetLargeNumberColorEffect(Color normalColor, Color largeNumberColor)
        {
            Color colorToUse = IsLargeNumber(currentValue) ? largeNumberColor : normalColor;
            SetDigitColor(colorToUse);
        }
        
        #endregion
        
        #region 调试方法
        
        [ContextMenu("Test Spin +1000")]
        private void TestSpin1000()
        {
            AddValue(1000);
        }
        
        [ContextMenu("Test Spin +10000")]
        private void TestSpin10000()
        {
            AddValue(10000);
        }
        
        [ContextMenu("Test Large Number 50000000")]
        private void TestLargeNumber()
        {
            SpinToValue(50000000); // 5千万
        }
        
        [ContextMenu("Test Ultra Large Number 5000000000")]
        private void TestUltraLargeNumber()
        {
            SpinToValue(5000000000); // 50亿
        }
        
        [ContextMenu("Test Mega Jackpot 999999999999")]
        private void TestMegaJackpot()
        {
            SpinToValue(999999999999); // 接近万亿
        }
        
        [ContextMenu("Test Reset to 0")]
        private void TestReset()
        {
            SetValue(0);
        }
        
        #endregion
    }
}