using System.Collections.Generic; using UnityEngine; namespace BaseGames.UI { /// /// 全局伤害飘字 Tick 系统(架构 10_UIModule §10 优化)。 /// /// 设计动机:原实现中每条飘字独立 StartCoroutine(FloatAndFade), /// Boss 战或大范围 AOE 时同时存在数十条会产生大量协程对象与调度开销。 /// 本系统用单一 Update + 紧凑 List<Entry> 集中推进, /// 所有飘字共享同一份每帧调度(仅一次 Update 调用),并消除协程对象。 /// /// 使用:FloatingDamageText.Show 不再 StartCoroutine,而是调用 /// FloatingDamageTickSystem.Register(this, ...);系统自动在动画结束时 /// 调用 OnComplete 回调(飘字 SetActive(false) 回池)。 /// [DefaultExecutionOrder(+100)] public class FloatingDamageTickSystem : MonoBehaviour { private struct Entry { public FloatingDamageText Target; public Vector2 StartWorld; public float Elapsed; public float Duration; public float FloatDistance; public float StartAlpha; } private static FloatingDamageTickSystem s_instance; private readonly List _entries = new(64); public static FloatingDamageTickSystem Instance { get { if (s_instance != null) return s_instance; var go = new GameObject(nameof(FloatingDamageTickSystem)); DontDestroyOnLoad(go); s_instance = go.AddComponent(); return s_instance; } } private void Awake() { if (s_instance != null && s_instance != this) { Destroy(this); return; } s_instance = this; } public void Register(FloatingDamageText target, Vector2 startWorld, float duration, float floatDistance, float startAlpha) { if (target == null || duration <= 0f) return; _entries.Add(new Entry { Target = target, StartWorld = startWorld, Elapsed = 0f, Duration = duration, FloatDistance = floatDistance, StartAlpha = startAlpha }); } /// 取消指定目标的飘字动画(例如对象池回收)。 public void Unregister(FloatingDamageText target) { for (int i = _entries.Count - 1; i >= 0; i--) if (_entries[i].Target == target) _entries.RemoveAt(i); } private void Update() { float dt = Time.deltaTime; // 倒序遍历,便于安全 RemoveAt for (int i = _entries.Count - 1; i >= 0; i--) { var e = _entries[i]; if (e.Target == null) { _entries.RemoveAt(i); continue; } e.Elapsed += dt; float t = e.Elapsed / e.Duration; bool done = t >= 1f; if (done) t = 1f; e.Target.TickAnimation(e.StartWorld, t, e.StartAlpha, e.FloatDistance); if (done) { e.Target.OnTickComplete(); _entries.RemoveAt(i); } else { _entries[i] = e; } } } } }