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;
}
}
}
}
}