Files
zeling_v2/Assets/_Game/Scripts/UI/FloatingDamageTickSystem.cs
2026-05-25 13:21:41 +08:00

103 lines
3.6 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System.Collections.Generic;
using UnityEngine;
namespace BaseGames.UI
{
/// <summary>
/// 全局伤害飘字 Tick 系统(架构 10_UIModule §10 优化)。
///
/// 设计动机:原实现中每条飘字独立 StartCoroutineFloatAndFade
/// Boss 战或大范围 AOE 时同时存在数十条会产生大量协程对象与调度开销。
/// 本系统用单一 Update + 紧凑 List&lt;Entry&gt; 集中推进,
/// 所有飘字共享同一份每帧调度(仅一次 Update 调用),并消除协程对象。
///
/// 使用FloatingDamageText.Show 不再 StartCoroutine而是调用
/// FloatingDamageTickSystem.Register(this, ...);系统自动在动画结束时
/// 调用 OnComplete 回调(飘字 SetActive(false) 回池)。
/// </summary>
[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<Entry> _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<FloatingDamageTickSystem>();
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
});
}
/// <summary>取消指定目标的飘字动画(例如对象池回收)。</summary>
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;
}
}
}
}
}