多轮审查和修复
This commit is contained in:
140
Assets/Scripts/UI/FloatingDamageText.cs
Normal file
140
Assets/Scripts/UI/FloatingDamageText.cs
Normal file
@@ -0,0 +1,140 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using TMPro;
|
||||
using BaseGames.Core.Events;
|
||||
using BaseGames.Combat;
|
||||
|
||||
namespace BaseGames.UI
|
||||
{
|
||||
/// <summary>
|
||||
/// 伤害飘字(架构 10_UIModule §10)。
|
||||
/// 从对象池取出后由 FloatingDamageSpawner 调用 Show();协程完成后归还。
|
||||
/// 挂在预制体根 GameObject 上(Canvas_HUD 子节点,World Space Canvas 或 Screen Space)。
|
||||
/// </summary>
|
||||
public class FloatingDamageText : MonoBehaviour
|
||||
{
|
||||
[SerializeField] private TMP_Text _text;
|
||||
[SerializeField] private float _floatDistance = 1.5f;
|
||||
[SerializeField] private float _duration = 0.8f;
|
||||
|
||||
private UnityEngine.Camera _cam;
|
||||
private RectTransform _rectTransform;
|
||||
private Coroutine _animCoroutine;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
_rectTransform = (RectTransform)transform;
|
||||
_cam = UnityEngine.Camera.main;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 在世界坐标位置显示伤害数字并开始飘动动画。
|
||||
/// 动画结束后由对象池回收(Deactivate)。
|
||||
/// </summary>
|
||||
public void Show(Vector2 worldPosition, int damage, DamageType type)
|
||||
{
|
||||
if (_animCoroutine != null) StopCoroutine(_animCoroutine);
|
||||
|
||||
_text.text = damage.ToString();
|
||||
_text.color = GetColorForType(type);
|
||||
|
||||
// 将世界坐标转为屏幕坐标
|
||||
if (_cam != null)
|
||||
{
|
||||
Vector2 screenPos = _cam.WorldToScreenPoint(worldPosition);
|
||||
_rectTransform.anchoredPosition = screenPos;
|
||||
}
|
||||
|
||||
_animCoroutine = StartCoroutine(FloatAndFade(worldPosition));
|
||||
}
|
||||
|
||||
private IEnumerator FloatAndFade(Vector2 startWorld)
|
||||
{
|
||||
float elapsed = 0f;
|
||||
var color = _text.color;
|
||||
var startAlpha = color.a;
|
||||
|
||||
while (elapsed < _duration)
|
||||
{
|
||||
float t = elapsed / _duration;
|
||||
|
||||
// 向上飘动(屏幕坐标)
|
||||
Vector2 currentWorld = startWorld + new Vector2(0, _floatDistance * t);
|
||||
if (_cam != null)
|
||||
_rectTransform.anchoredPosition = (Vector2)_cam.WorldToScreenPoint(currentWorld);
|
||||
|
||||
// alpha 淡出(后半段开始)
|
||||
_text.color = new Color(color.r, color.g, color.b,
|
||||
Mathf.Lerp(startAlpha, 0f, Mathf.Clamp01((t - 0.5f) / 0.5f)));
|
||||
|
||||
elapsed += Time.deltaTime;
|
||||
yield return null;
|
||||
}
|
||||
|
||||
gameObject.SetActive(false); // 归还对象池
|
||||
}
|
||||
|
||||
private static Color GetColorForType(DamageType type) => type switch
|
||||
{
|
||||
DamageType.Fire => new Color(1f, 0.5f, 0f), // 橙
|
||||
DamageType.Poison => new Color(0.3f,0.9f, 0.3f), // 绿
|
||||
DamageType.True => new Color(1f, 0.95f,0.4f), // 黄
|
||||
DamageType.Ice => new Color(0.5f,0.85f,1f), // 冰蓝
|
||||
DamageType.Lightning=>new Color(0.9f,0.9f, 0.2f), // 闪黄
|
||||
_ => Color.white
|
||||
};
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
/// <summary>
|
||||
/// 伤害飘字生成器(挂在 Canvas_HUD 上)。
|
||||
/// 订阅 EVT_DamageDealt 事件频道,从 Addressables 池取出 FloatingDamageText 显示。
|
||||
/// </summary>
|
||||
public class FloatingDamageSpawner : MonoBehaviour
|
||||
{
|
||||
[Header("事件频道")]
|
||||
[SerializeField] private DamageInfoEventChannelSO _onDamageDealt;
|
||||
|
||||
[Header("预制体(对象池 key = AddressKeys.PrefabUIFloatingDmgText)")]
|
||||
[SerializeField] private GameObject _floatingDmgPrefab; // Fallback:Inspector 直接拖入
|
||||
|
||||
private readonly Queue<FloatingDamageText> _pool = new();
|
||||
private readonly CompositeDisposable _subs = new();
|
||||
|
||||
private void OnEnable() => _onDamageDealt?.Subscribe(OnDamageDealt).AddTo(_subs);
|
||||
private void OnDisable() => _subs.Clear();
|
||||
|
||||
private void OnDamageDealt(DamageInfo info)
|
||||
{
|
||||
if (info.FinalDamage <= 0) return;
|
||||
var text = GetOrCreate();
|
||||
if (text != null)
|
||||
text.Show(info.SourcePosition, info.FinalDamage, info.Type);
|
||||
}
|
||||
|
||||
private FloatingDamageText GetOrCreate()
|
||||
{
|
||||
// 从池中找到已停用的实例
|
||||
while (_pool.Count > 0)
|
||||
{
|
||||
var pooled = _pool.Dequeue();
|
||||
if (pooled == null) continue;
|
||||
if (!pooled.gameObject.activeSelf)
|
||||
{
|
||||
pooled.gameObject.SetActive(true);
|
||||
return pooled;
|
||||
}
|
||||
_pool.Enqueue(pooled); // 仍在使用,放回
|
||||
break;
|
||||
}
|
||||
|
||||
// 没有可用实例则实例化
|
||||
if (_floatingDmgPrefab == null) return null;
|
||||
var go = Instantiate(_floatingDmgPrefab, transform);
|
||||
var comp = go.GetComponent<FloatingDamageText>();
|
||||
if (comp != null) _pool.Enqueue(comp);
|
||||
return comp;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user