翻页实现(未验证)
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
}
}