using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using System;
using DG.Tweening;
///
/// 战斗角色信息栏
/// 职责:显示角色血条、怒气、Buff和飘字提示
///
public class BattleHeroInfoBar : MonoBehaviour
{
///
/// 飘字信息配置
///
public class TipsInfo
{
public string message;
public bool useArtText;
public bool followCharacter;
public float scaleRatio;
public bool showBackground = false;
public bool useBuffColor = false; // 是否使用 Buff 颜色(从 FloatingConfig 读取)
public bool isDebuff = false; // 是否是负向 Buff(决定用哪个颜色)
}
///
/// 血条更新请求
///
private class HpUpdateRequest
{
public long fromHp;
public long toHp;
public long maxHp;
public bool tween;
}
[Header("UI Components")]
public Slider sliderHp;
public Slider sliderXp;
public RendererAdjuster maxXpGO;
public Slider sliderShield1;
public Slider sliderShield2;
public BasicHeroInfoContainer heroInfoContainer;
public BattleTips textTips;
[Header("Buff Components")]
[SerializeField]
public List buffCells = new List();
[Header("Floating Configs")]
[Tooltip("跟随角色的飘字配置")]
public FloatingConfig followFloatingConfig;
[Tooltip("不跟随角色的飘字配置(固定在战场节点)")]
public FloatingConfig noFollowFloatingConfig;
protected BattleObject battleObject;
protected List messages = new List();
protected List tipsList = new List();
protected List buffList = new List();
protected Tween hpTween;
protected Tween xpTween;
protected Tween shieldTween1;
protected Tween shieldTween2;
protected Sequence damageSequence;
private Queue hpUpdateQueue = new Queue();
private Queue damageUpdateQueue = new Queue();
// 飘字GCD相关
private float tipsGCDTimer = 0f;
private const int TIPS_GCD_FRAMES = 5;
protected void OnDisable()
{
CleanupTips();
}
public void SetBattleObject(BattleObject _battleObject)
{
battleObject = _battleObject;
heroInfoContainer.SetHeroInfo(battleObject.teamHero);
RefreshBuff(battleObject.buffMgr.GetBuffList());
UpdateHP(battleObject.teamHero.curHp, battleObject.teamHero.curHp, battleObject.teamHero.maxHp, false);
UpdateXP(battleObject.teamHero.rage, battleObject.teamHero.rage, 100, false);
long shieldValue = battleObject.buffMgr.GetShieldValue();
long maxHp = battleObject.teamHero.maxHp;
// 第一条护盾的值最大值是当前的MaxHp 第二条护盾的最大值其实也是MaxHp 多余的不做显示
sliderShield1.value = maxHp > 0 ? Mathf.Min((float)shieldValue, (float)maxHp) / (float)maxHp : 0;
sliderShield2.value = maxHp > 0 ? Mathf.Max((float)(shieldValue - maxHp), 0f) / (float)maxHp : 0;
}
public void SetActive(bool active)
{
gameObject.SetActive(active);
}
public void RefreshBuff(List datas)
{
if (buffCells.IsNullOrEmpty())
return;
for (int i = 0; i < buffCells.Count; i++)
{
if (i < datas.Count)
{
buffCells[i].SetActive(true);
buffCells[i].Init(datas[i], OnBuffCellClicked);
}
else
{
buffCells[i].SetActive(false);
}
}
// check shield buff
long shieldValue = battleObject.buffMgr.GetShieldValue();
long maxHp = battleObject.teamHero.maxHp;
// 第一条护盾的值最大值是当前的MaxHp 第二条护盾的最大值其实也是MaxHp 多余的不做显示
sliderShield1.value = maxHp > 0 ? Mathf.Min((float)shieldValue, (float)maxHp) / (float)maxHp : 0;
sliderShield2.value = maxHp > 0 ? Mathf.Max((float)(shieldValue - maxHp), 0f) / (float)maxHp : 0;
}
///
/// 添加飘字到队列
///
public void ShowTips(string message, bool useArtText = false, bool followCharacter = true, float scaleRatio = 1f)
{
messages.Add(new TipsInfo
{
message = message,
useArtText = useArtText,
followCharacter = followCharacter,
scaleRatio = scaleRatio
});
}
///
/// 添加自定义飘字配置到队列
///
public void ShowTips(TipsInfo tipsInfo)
{
messages.Add(tipsInfo);
}
///
/// 更新血量显示
///
public void UpdateHP(long fromHp, long toHp, long maxHp, bool tween = true)
{
// 加入队列
hpUpdateQueue.Enqueue(new HpUpdateRequest
{
fromHp = fromHp,
toHp = toHp,
maxHp = maxHp,
tween = tween
});
}
///
/// 实际执行血量更新
///
private void ExecuteHpUpdate(HpUpdateRequest request)
{
KillTween(ref hpTween);
float fromValue = (float)request.fromHp / (float)request.maxHp;
float targetValue = (float)request.toHp / (float)request.maxHp;
if (request.tween)
{
sliderHp.value = fromValue;
hpTween = sliderHp.DOValue(targetValue, 0.3f).SetAutoKill(false);
battleObject.battleField.battleTweenMgr.OnPlayTween(hpTween);
}
else
{
sliderHp.value = targetValue;
}
}
///
/// !!!临时的用于天子更新血量显示,等接口完善后删除
///
public void UpdateHP(float value)
{
sliderHp.value = value;
//Debug.Log("TianziDamageBar UpdateHP value:" + value);
}
///
/// 更新怒气显示
///
public void UpdateXP(long fromXp, long toXp, long maxXp, bool tween = true)
{
KillTween(ref xpTween);
float fromValue = (float)fromXp / (float)maxXp;
float targetValue = (float)toXp / (float)maxXp;
if (tween)
{
sliderXp.value = fromValue;
xpTween = sliderXp.DOValue(targetValue, 0.2f).SetAutoKill(false);
xpTween.OnComplete(() =>
{
if (toXp >= maxXp)
{
maxXpGO.SetActive(true);
}
else
{
maxXpGO.SetActive(false);
}
});
battleObject.battleField.battleTweenMgr.OnPlayTween(xpTween);
}
else
{
if (toXp >= maxXp)
{
maxXpGO.SetActive(true);
}
else
{
maxXpGO.SetActive(false);
}
sliderXp.value = targetValue;
}
}
///
/// 播放血条 护盾的变化
///
public void UpdateDamage(BattleDmgInfo dmgInfo)
{
// 加入队列
damageUpdateQueue.Enqueue(dmgInfo);
}
///
/// 实际执行伤害更新
///
private void ExecuteDamageUpdate(BattleDmgInfo dmgInfo)
{
KillTween(ref damageSequence);
long maxHp = dmgInfo.battleHurtParam.maxHp;
long fromShield = dmgInfo.battleHurtParam.fromShieldValue;
long toShield = dmgInfo.battleHurtParam.toShieldValue;
if (maxHp <= 0)
{
sliderShield1.value = 0;
sliderShield2.value = 0;
return;
}
// 第一条护盾的值最大值是当前的MaxHp 第二条护盾的最大值其实也是MaxHp 多余的不做显示
float fromValue1 = (float)dmgInfo.battleHurtParam.phase1FromShieldValue / (float)maxHp;
float targetValue1 = (float)dmgInfo.battleHurtParam.phase1ToShieldValue / (float)maxHp;
float fromValue2 = (float)dmgInfo.battleHurtParam.phase2FromShieldValue / (float)maxHp;
float targetValue2 = (float)dmgInfo.battleHurtParam.phase2ToShieldValue / (float)maxHp;
damageSequence = DOTween.Sequence();
sliderShield2.value = fromValue2;
if (fromValue2 > 0 && fromValue2 != targetValue2)
{
damageSequence.Append(sliderShield2.DOValue(targetValue2, 0.2f));
}
sliderShield1.value = fromValue1;
if (fromValue1 > 0 && fromValue1 != targetValue1)
{
damageSequence.Append(sliderShield1.DOValue(targetValue1, 0.2f));
}
if (dmgInfo.battleHurtParam.fromHp != dmgInfo.battleHurtParam.toHp)
{
float fromHpValue = (float)dmgInfo.battleHurtParam.fromHp / (float)maxHp;
float toHpValue = (float)dmgInfo.battleHurtParam.toHp / (float)maxHp;
sliderHp.value = fromHpValue;
damageSequence.Append(sliderHp.DOValue(toHpValue, 0.2f));
}
damageSequence.Play();
battleObject.battleField.battleTweenMgr.OnPlayTween(damageSequence);
}
///
/// 每帧更新
///
public void Run()
{
// 处理血条和伤害队列
UpdateHpAndDamageQueue();
// 更新飘字GCD并处理队列
UpdateTipsGCDAndQueue();
// 更新所有飘字
UpdateActiveTips();
}
///
/// 设置速度比例
///
public void SetSpeedRatio(float ratio)
{
foreach (var tip in tipsList)
{
tip.SetRatio(ratio, 1f);
}
}
///
/// 处理血条和伤害更新队列
///
private void UpdateHpAndDamageQueue()
{
// 优先处理UpdateDamage
if (damageUpdateQueue.Count > 0)
{
BattleDmgInfo dmgInfo = damageUpdateQueue.Dequeue();
ExecuteDamageUpdate(dmgInfo);
return;
}
// 其次处理UpdateHP
else if (hpUpdateQueue.Count > 0)
{
HpUpdateRequest request = hpUpdateQueue.Dequeue();
ExecuteHpUpdate(request);
return;
}
}
///
/// 更新飘字GCD并处理队列
///
private void UpdateTipsGCDAndQueue()
{
// 更新GCD计时器
if (tipsGCDTimer > 0f)
{
float speedRatio = GetCurrentSpeedRatio();
float deltaTime = 1f / (float)BattleConst.skillMotionFps * speedRatio;
tipsGCDTimer -= deltaTime;
if (tipsGCDTimer < 0f)
{
tipsGCDTimer = 0f;
}
}
// 如果GCD结束且有待处理的飘字,弹出一个
if (tipsGCDTimer <= 0f && messages.Count > 0)
{
TipsInfo tipsInfo = messages[0];
messages.RemoveAt(0);
PopUpTipsDirectly(tipsInfo);
// 重置GCD
ResetTipsGCD();
}
}
///
/// 重置飘字GCD计时器
///
private void ResetTipsGCD()
{
float speedRatio = GetCurrentSpeedRatio();
float frameTime = 1f / (float)BattleConst.skillMotionFps;
tipsGCDTimer = frameTime * TIPS_GCD_FRAMES / speedRatio;
}
///
/// 获取当前速度倍率
///
private float GetCurrentSpeedRatio()
{
// 回退到战场速度
if (battleObject != null && battleObject.battleField != null)
{
return battleObject.battleField.speedRatio;
}
return 1f;
}
///
/// 立即弹出飘字
///
private void PopUpTipsDirectly(TipsInfo tipsInfo)
{
// 创建飘字实例
BattleTips tips = CreateTipsInstance(tipsInfo);
// 配置飘字
ConfigureTips(tips, tipsInfo);
// 设置位置(如果不跟随)
if (!tipsInfo.followCharacter)
{
SetNonFollowPosition(tips);
}
// 设置参数并显示
// 注册完成回调
tips.OnFinish = () => RemoveTips(tips);
tips.SetRatio(battleObject.battleField.speedRatio, tipsInfo.scaleRatio);
tips.ShowBackground(tipsInfo.showBackground);
tips.SetText(tipsInfo.message, tipsInfo.useArtText, false);
// 添加到列表
tipsList.Add(tips);
}
///
/// 创建飘字实例
///
private BattleTips CreateTipsInstance(TipsInfo tipsInfo)
{
Transform parent = tipsInfo.followCharacter
? transform
: battleObject.battleField.battleRootNode.transform;
GameObject go = GameObject.Instantiate(textTips.gameObject, parent);
return go.GetComponent();
}
///
/// 配置飘字
///
private void ConfigureTips(BattleTips tips, TipsInfo tipsInfo)
{
FloatingConfig targetConfig = tipsInfo.followCharacter
? followFloatingConfig
: noFollowFloatingConfig;
if (targetConfig == null)
{
Debug.LogError($"[BattleHeroInfoBar] FloatingConfig 未配置! " +
$"followCharacter={tipsInfo.followCharacter}, GameObject: {gameObject.name}");
return;
}
tips.SetFloatingConfig(targetConfig);
// 设置是否使用 Buff 颜色
if (tipsInfo.useBuffColor)
{
tips.SetBuffColor(true, tipsInfo.isDebuff);
}
}
///
/// 设置不跟随飘字的位置
///
private void SetNonFollowPosition(BattleTips tips)
{
RectTransform contentRect = tips.GetComponent();
RectTransform contentParentRect = contentRect.parent as RectTransform;
RectTransform infoBarRect = GetComponent();
// 计算世界坐标
Vector3 worldTargetPos = infoBarRect.transform.TransformPoint(infoBarRect.rect.center);
// 转换到父节点坐标
Vector2 anchoredPos;
RectTransformUtility.ScreenPointToLocalPointInRectangle(
contentParentRect,
RectTransformUtility.WorldToScreenPoint(null, worldTargetPos),
null,
out anchoredPos
);
// 设置动态位置
tips.SetPosition(anchoredPos, anchoredPos + new Vector2(0, 150));
}
///
/// 移除飘字
///
private void RemoveTips(BattleTips tips)
{
tipsList.Remove(tips);
tips.controller = null;
GameObject.DestroyImmediate(tips.gameObject);
}
///
/// 更新所有激活的飘字
///
private void UpdateActiveTips()
{
for (int i = tipsList.Count - 1; i >= 0; i--)
{
if (tipsList[i].gameObject == null)
{
var instanceid = tipsList[i].gameObject.GetInstanceID();
tipsList.RemoveAt(i);
continue;
}
tipsList[i].Run();
}
}
///
/// 清理所有飘字
///
private void CleanupTips()
{
messages.Clear();
foreach (var tip in tipsList)
{
tip.OnFinish = null;
GameObject.DestroyImmediate(tip.gameObject);
}
tipsList.Clear();
}
///
/// 停止并清理Tween
///
private void KillTween(ref T tween) where T : Tween
{
if (tween != null && battleObject != null)
{
battleObject.battleField.battleTweenMgr.OnKillTween(tween);
tween = null;
}
}
///
/// 获取时间增量
///
private float GetDeltaTime()
{
return 1f / (float)BattleConst.skillMotionFps * battleObject.battleField.speedRatio;
}
///
/// Buff图标点击回调
///
private void OnBuffCellClicked()
{
// TODO: 显示buff描述/当前身上所有buff
}
}