using UnityEngine; using System.Collections.Generic; using TMPro; using DG.Tweening; // cần DOTween public class DamageTextManager : MonoBehaviour { public static DamageTextManager Instance { get; private set; } [Header("Prefab")] [SerializeField] private TextMeshPro damageTextPrefab; [Header("Settings")] [SerializeField] private int poolSize = 20; [SerializeField] private Vector3 offset = new Vector3(0, 2f, 0); [SerializeField] private float riseDistance = 1.5f; [SerializeField] private float riseDuration = 0.8f; private readonly Queue pool = new(); private void Awake() { // Singleton if (Instance != null && Instance != this) { Destroy(gameObject); return; } Instance = this; DontDestroyOnLoad(gameObject); // Tạo sẵn pool for (int i = 0; i < poolSize; i++) { var textObj = Instantiate(damageTextPrefab, transform); textObj.gameObject.SetActive(false); pool.Enqueue(textObj); } } /// /// Gọi để spawn text damage /// public void ShowDamageText(Vector3 worldPos, int damage, Color color = default, float scale = 1f) { var text = GetFromPool(); text.text = damage.ToString(); text.color = color; text.fontSize = 6; text.transform.localScale = Vector3.one * scale; Vector3 startPos = worldPos + offset; text.transform.position = startPos; text.gameObject.SetActive(true); // Hiệu ứng bay lên + mờ dần text.transform.DOMoveY(startPos.y + riseDistance, riseDuration).SetEase(Ease.OutQuad); text.DOFade(0f, riseDuration) .SetEase(Ease.InQuad) .OnComplete(() => { text.alpha = 1f; text.gameObject.SetActive(false); ReturnToPool(text); }); } private TextMeshPro GetFromPool() { if (pool.Count > 0) { return pool.Dequeue(); } // Nếu hết pool, tạo thêm var text = Instantiate(damageTextPrefab, transform); text.gameObject.SetActive(false); return text; } private void ReturnToPool(TextMeshPro text) { pool.Enqueue(text); } }