using Cysharp.Threading.Tasks; using System.Threading; using TMPro; using UnityEngine; namespace RR.UI { public class CounterTMP : MonoBehaviour { [SerializeField] protected TextMeshProUGUI countLabel; [Header("Настройки анимации")] [SerializeField] private int scorePerSecond = 300; [SerializeField] private float maxAnimationTime = 2; [SerializeField] private string countFormat = "D4"; private CancellationTokenSource animationCts; [field: SerializeField] public int Count { get; private set; } public void Add(int value) { var oldCount = Count; Count += value; OnNewCount(oldCount, Count); } public void SetCount(int value) { var oldCount = Count; Count = value; OnNewCount(oldCount, Count); } public void ResetCount() { var oldCount = Count; Count = 0; OnNewCount(oldCount, Count); } protected virtual void OnNewCount(int oldCount, int newCount) { countLabel.text = newCount.ToString(countFormat); if(scorePerSecond > 0) { AnimateScoreChange(oldCount, newCount).Forget(); } } public async UniTask AnimateScoreChange(int startScore, int endScore) { CancelAnimation(); animationCts = new(); int scoreDifference = endScore - startScore; float animationTime = scoreDifference / scorePerSecond; animationTime = Mathf.Clamp(animationTime, 0.1f, maxAnimationTime); float elapsedTime = 0f; while (elapsedTime < animationTime && !animationCts.Token.IsCancellationRequested) { await UniTask.Yield(animationCts.Token); elapsedTime += Time.deltaTime; float t = elapsedTime / animationTime; t = Mathf.SmoothStep(0, 1, t); int newScore = Mathf.RoundToInt(Mathf.Lerp(startScore, endScore, t)); countLabel.text = newScore.ToString("D4"); } countLabel.text = endScore.ToString(countFormat); } private void CancelAnimation() { if (animationCts != null) { animationCts.Cancel(); animationCts.Dispose(); animationCts = null; } } private void OnDestroy() { CancelAnimation(); } } }