using System.Collections;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
///
/// 数字持续递增滚动组件(计数器效果,无最终值,一直递增)
/// 挂在 Text / TextMeshProUGUI 所在物体上:
/// - 面板上可设置显示位数(digitCount)
/// - 基础值随机:第一位 2~9,其余位全部随机
/// - 每次增加 2000 的倍数:随机倍数 1~500 倍,倍数越大随机到的概率越小
/// - 满位后重新随机一个基础值继续递增
/// 同时支持 UnityEngine.UI.Text 和 TextMeshProUGUI,二选一,留空自动从自身/子物体查找。
///
public class TextNumRander : MonoBehaviour
{
[Header("目标文本(二选一,留空自动从自身/子物体查找)")]
[SerializeField] private Text text;
[SerializeField] private TextMeshProUGUI tmpText;
[Header("配置")]
[Tooltip("显示位数(面板上设置几位数)")]
[SerializeField] private int digitCount = 6;
[Tooltip("递增跳变间隔(秒),越小递增越快")]
[SerializeField] private float jumpInterval = 0.05f;
[Tooltip("启用时自动开始递增")]
[SerializeField] private bool autoStart = true;
[Header("递增规则")]
[Tooltip("每次增加的基础量(按 2000 的倍数)")]
[SerializeField] private long baseStep = 2000;
[Tooltip("随机倍数的上限(1 ~ maxMultiplier,越大概率越小)")]
[SerializeField] private int maxMultiplier = 500;
// ---- 运行时 ----
private Coroutine rollCoroutine;
private bool isRolling;
private long currentValue;
/// 当前数值
public long CurrentValue => currentValue;
private void Awake()
{
// 自动查找文本组件:自身优先,其次子物体
if (text == null) text = GetComponent();
if (text == null) text = GetComponentInChildren();
if (tmpText == null) tmpText = GetComponent();
if (tmpText == null) tmpText = GetComponentInChildren();
}
private void OnEnable()
{
if (autoStart && !isRolling)
StartRolling();
}
private void OnDisable()
{
Stop();
}
/// 设置显示位数(运行时调用会立即生效)
public void SetDigitCount(int count)
{
digitCount = Mathf.Max(1, count);
if (isRolling)
RefreshText();
}
/// 开始(或继续)递增滚动:先随机一个基础值(第一位 2~9,其余位随机)
public void StartRolling()
{
if (isRolling) return;
// 基础值随机:第一位 2~9,其余位全部随机
currentValue = RandomBaseValue();
RefreshText();
isRolling = true;
rollCoroutine = StartCoroutine(RollLoop());
}
/// 停止递增滚动(保留当前显示值)
public void Stop()
{
if (rollCoroutine != null)
{
StopCoroutine(rollCoroutine);
rollCoroutine = null;
}
isRolling = false;
}
/// 设置起始数值(立即显示,不影响递增状态;末三位强制归 000)
public void SetStartValue(long value)
{
// 与递增规则保持一致:末三位固定为 000
currentValue = Wrap(System.Math.Max(0L, value) / 1000L * 1000L);
RefreshText();
}
private IEnumerator RollLoop()
{
while (isRolling)
{
// 每次增加 2000 的倍数:随机倍数 1~500 倍
currentValue += baseStep * RandomMultiplier();
if (currentValue > MaxValue())
currentValue = RandomBaseValue(); // 满位回绕:重新随机一个基础值
RefreshText();
yield return new WaitForSeconds(jumpInterval);
}
}
///
/// 基础值随机:第一位 2~9,中间位随机,最后三位固定 000
/// (递增量为 2000 的倍数,基础值末三位为 000 时,后续所有值末三位始终保持 000)
///
private long RandomBaseValue()
{
// 位数不足 4 位时,最后三位全是 0 意味着整个数就是 0
if (digitCount <= 3)
return 0;
long val = Random.Range(2, 10); // 第一位 2~9
for (int i = 1; i < digitCount - 3; i++)
val = val * 10 + Random.Range(0, 10); // 中间位随机 0~9
return val * 1000; // 最后三位固定 000
}
///
/// 随机倍数 1 ~ maxMultiplier,倍数越大随机到的概率越小。
/// 用 500^u(u∈[0,1))采样:概率密度 ∝ 1/倍数,小倍数高频、大倍数低频。
///
private long RandomMultiplier()
{
int m = Mathf.Clamp((int)Mathf.Pow(maxMultiplier, Random.value), 1, maxMultiplier);
return m;
}
/// 当前位数能表示的最大值(10^digitCount - 1)
private long MaxValue()
{
long max = 1;
for (int i = 0; i < digitCount; i++)
max *= 10;
return max - 1;
}
/// 数值限制在位数范围内
private long Wrap(long value)
{
if (value < 0) value = 0;
return value % (MaxValue() + 1);
}
private void RefreshText()
{
if (text == null && tmpText == null) return;
string s = currentValue.ToString().PadLeft(digitCount, '0');
if (text != null) text.text = s;
if (tmpText != null) tmpText.text = s;
}
}