using System.Collections.Generic;
|
using UnityEngine;
|
using UnityEngine.UI;
|
using System;
|
using DG.Tweening;
|
|
/// <summary>
|
/// 战斗角色信息栏
|
/// 职责:显示角色血条、怒气、Buff和飘字提示
|
/// </summary>
|
public class BattleHeroInfoBar : MonoBehaviour
|
{
|
/// <summary>
|
/// 飘字信息配置
|
/// </summary>
|
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(决定用哪个颜色)
|
|
public bool isRage = false;
|
}
|
|
|
[Header("UI Components")]
|
public Slider sliderHp;
|
public Slider sliderSlowHp;
|
public Slider sliderXp;
|
public GameObject maxXpGO;
|
public Slider sliderShield1;
|
public Slider sliderShield2;
|
public BasicHeroInfoContainer heroInfoContainer;
|
public BattleTips textTips;
|
|
[Header("Buff Components")]
|
[SerializeField]
|
public List<BattleBuffCell> buffCells = new List<BattleBuffCell>();
|
|
[Header("Floating Configs")]
|
[Tooltip("跟随角色的飘字配置")]
|
public FloatingConfig followFloatingConfig;
|
[Tooltip("不跟随角色的飘字配置(固定在战场节点)")]
|
public FloatingConfig noFollowFloatingConfig;
|
|
public FloatingConfig rageFloatingConfig;
|
|
protected BattleObject battleObject;
|
|
protected List<TipsInfo> messages = new List<TipsInfo>();
|
protected List<BattleTips> tipsList = new List<BattleTips>();
|
protected List<HB428_tagSCBuffRefresh> buffList = new List<HB428_tagSCBuffRefresh>();
|
|
protected Sequence hpTween;
|
protected Tween xpTween;
|
protected Tween shieldTween1;
|
protected Tween shieldTween2;
|
protected Sequence damageSequence;
|
|
private Queue<BattleDmgInfo> damageUpdateQueue = new Queue<BattleDmgInfo>();
|
|
// 飘字GCD相关
|
private float tipsGCDTimer = 0f;
|
private const int TIPS_GCD_FRAMES = 5;
|
|
// 全局血量记录(按战场guid组织,以最大PackUID为准,记录所有对象:施法者和受击者)
|
public static Dictionary<string, ulong> largestPackUID = new Dictionary<string, ulong>();
|
public static Dictionary<string, Dictionary<long, long>> largestPackUIDAllObjectsToHp = new Dictionary<string, Dictionary<long, long>>();
|
public static Dictionary<string, Dictionary<long, long>> largestPackUIDAllObjectsMaxHp = new Dictionary<string, Dictionary<long, long>>();
|
|
protected void OnDisable()
|
{
|
CleanupTips();
|
}
|
|
public void SetBattleObject(BattleObject _battleObject)
|
{
|
battleObject = _battleObject;
|
if (battleObject is HeroBattleObject heroBattleObject)
|
{
|
heroInfoContainer.SetHeroInfo(heroBattleObject.teamHero);
|
}
|
CleanupTips();
|
InitBuff();
|
|
var buffMgr = battleObject.GetBuffMgr();
|
if (buffMgr != null) // 命格不有 buff
|
{
|
RefreshBuff(buffMgr.GetBuffIconList());
|
}
|
|
if (!battleObject.IsTianziBoss())
|
{
|
UpdateHP(battleObject.GetCurHp(), battleObject.GetCurHp(), battleObject.GetMaxHp(), false);
|
}
|
|
UpdateXP(battleObject.GetRage(), battleObject.GetRage(), 100, false);
|
|
long shieldValue = buffMgr != null ? buffMgr.GetShieldValue() : 0; // 命格没有护盾
|
long curHp = battleObject.GetCurHp();
|
long maxHp = battleObject.GetMaxHp();
|
|
// 记录设置前的护盾值
|
float oldShield1Value = sliderShield1.value;
|
float oldShield2Value = sliderShield2.value;
|
|
// 护盾1的值 = min(当前血量 + 护盾值, maxHp) / maxHp
|
float shield1Value = maxHp > 0 ? Mathf.Min((float)(curHp + shieldValue), (float)maxHp) / (float)maxHp : 0;
|
// 护盾2的值 = max(当前血量 + 护盾值 - maxHp, 0) / maxHp
|
float shield2Value = maxHp > 0 ? Mathf.Max((float)(curHp + shieldValue - maxHp), 0f) / (float)maxHp : 0;
|
|
sliderShield1.value = shield1Value;
|
sliderShield2.value = shield2Value;
|
|
|
|
// 打印设置护盾时的状态
|
// Debug.LogError($"[BattleHeroInfoBar.SetBattleObject] 设置护盾 - curHp: {curHp}, shieldValue: {shieldValue}, maxHp: {maxHp}, shield1前: {oldShield1Value}, shield1后: {shield1Value}, shield2前: {oldShield2Value}, shield2后: {shield2Value}");
|
}
|
|
protected void InitBuff()
|
{
|
for (int i = 0; i < buffCells.Count; i++)
|
{
|
buffCells[i].SetActive(false);
|
}
|
}
|
|
public void SetActive(bool active)
|
{
|
gameObject.SetActive(active);
|
}
|
|
public void RefreshBuff(List<HB428_tagSCBuffRefresh> datas)
|
{
|
RefreshBuffCells(buffCells, datas);
|
|
// check shield buff
|
var buffMgr = battleObject.GetBuffMgr();
|
long shieldValue = buffMgr != null ? buffMgr.GetShieldValue() : 0; // 命格没有护盾
|
long curHp = battleObject.GetCurHp();
|
long maxHp = battleObject.GetMaxHp();
|
|
// 记录设置前的护盾值
|
float oldShield1Value = sliderShield1.value;
|
float oldShield2Value = sliderShield2.value;
|
|
// 护盾1的值 = min(当前血量 + 护盾值, maxHp) / maxHp
|
float shield1Value = maxHp > 0 ? Mathf.Min((float)(curHp + shieldValue), (float)maxHp) / (float)maxHp : 0;
|
// 护盾2的值 = max(当前血量 + 护盾值 - maxHp, 0) / maxHp
|
float shield2Value = maxHp > 0 ? Mathf.Max((float)(curHp + shieldValue - maxHp), 0f) / (float)maxHp : 0;
|
|
sliderShield1.value = shield1Value;
|
sliderShield2.value = shield2Value;
|
|
// if (!battleObject.IsTianziBoss())
|
// {
|
// UpdateHP(curHp, curHp, maxHp, false);
|
// }
|
|
|
// 打印刷新护盾时的状态
|
// Debug.LogError($"[BattleHeroInfoBar.RefreshBuff] 设置护盾 - curHp: {curHp}, shieldValue: {shieldValue}, maxHp: {maxHp}, shield1前: {oldShield1Value}, shield1后: {shield1Value}, shield2前: {oldShield2Value}, shield2后: {shield2Value}");
|
}
|
|
protected void RefreshBuffCells(List<BattleBuffCell> cells, List<HB428_tagSCBuffRefresh> datas)
|
{
|
if (datas == null)
|
{
|
for (int i = 0; i < cells.Count; i++)
|
{
|
cells[i].SetActive(false);
|
}
|
}
|
else
|
{
|
if (battleObject.battleField.battleSwitch.BuffIcon)
|
{
|
for (int i = 0; i < cells.Count; i++)
|
{
|
var cell = cells[i];
|
if (i < datas.Count)
|
{
|
cell.SetActive(true);
|
HB428_tagSCBuffRefresh buffData = datas[i];
|
SkillConfig skillConfig = SkillConfig.Get((int)buffData.SkillID);
|
cell.Init(buffData, () =>
|
{
|
// 点击buff图标 显示buff描述/当前身上所有buff
|
});
|
}
|
else
|
{
|
cell.SetActive(false);
|
}
|
}
|
}
|
else
|
{
|
for (int i = 0; i < cells.Count; i++)
|
{
|
cells[i].SetActive(false);
|
}
|
}
|
|
}
|
}
|
|
/// <summary>
|
/// 添加飘字到队列(非伤害飘字)
|
/// </summary>
|
public void ShowTips(string message, bool useArtText = false, bool followCharacter = true, float scaleRatio = 1f)
|
{
|
if (battleObject.battleField.battleSwitch.NonDamageTips)
|
{
|
messages.Add(new TipsInfo
|
{
|
message = message,
|
useArtText = useArtText,
|
followCharacter = followCharacter,
|
scaleRatio = scaleRatio
|
});
|
}
|
}
|
|
/// <summary>
|
/// 添加自定义飘字配置到队列(非伤害飘字)
|
/// </summary>
|
public void ShowTips(TipsInfo tipsInfo)
|
{
|
// BUFF飘字
|
if (tipsInfo.useBuffColor)
|
{
|
if (battleObject.battleField.battleSwitch.BuffAction)
|
{
|
messages.Add(tipsInfo);
|
}
|
}
|
else
|
{
|
// 非伤害飘字
|
if (battleObject.battleField.battleSwitch.NonDamageTips)
|
{
|
messages.Add(tipsInfo);
|
}
|
}
|
}
|
|
/// <summary>
|
/// 更新血量显示
|
/// </summary>
|
public void UpdateHP(long fromHp, long toHp, long maxHp, bool tween = true)
|
{
|
KillTween(ref hpTween);
|
|
float fromValue = (float)fromHp / (float)maxHp;
|
float targetValue = (float)toHp / (float)maxHp;
|
|
if (tween)
|
{
|
hpTween = DOTween.Sequence();
|
// sliderHp.value = fromValue;
|
float diff = targetValue - fromValue;
|
float cost = Mathf.Lerp(0, 1f, diff);
|
|
hpTween.Append(sliderHp.DOValue(targetValue, cost).SetAutoKill(false));
|
hpTween.Join(sliderSlowHp.DOValue(targetValue, cost * 1.5f).SetAutoKill(false));
|
hpTween.onComplete += () =>
|
{
|
sliderHp.value = targetValue;
|
sliderSlowHp.value = targetValue;
|
};
|
// sliderSlowHp
|
battleObject.battleField.battleTweenMgr.OnPlayTween(hpTween);
|
}
|
else
|
{
|
sliderHp.value = targetValue;
|
sliderSlowHp.value = targetValue;
|
}
|
}
|
|
|
/// <summary>
|
/// !!!临时的用于天子更新血量显示,等接口完善后删除
|
/// </summary>
|
public void UpdateHP(float value)
|
{
|
sliderHp.value = value;
|
sliderSlowHp.value = value;
|
bool IsTianziBoss = battleObject.IsTianziBoss();
|
sliderShield1.SetActive(!IsTianziBoss);
|
sliderShield2.SetActive(!IsTianziBoss);
|
//Debug.Log("TianziDamageBar UpdateHP value:" + value);
|
}
|
|
/// <summary>
|
/// 更新怒气显示
|
/// </summary>
|
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(() =>
|
{
|
maxXpGO.SetActive(toXp >= maxXp);
|
});
|
battleObject.battleField.battleTweenMgr.OnPlayTween(xpTween);
|
}
|
else
|
{
|
if (toXp >= maxXp)
|
{
|
maxXpGO.SetActive(true);
|
}
|
else
|
{
|
maxXpGO.SetActive(false);
|
}
|
sliderXp.value = targetValue;
|
}
|
}
|
|
/// <summary>
|
/// 播放血条 护盾的变化
|
/// </summary>
|
public void UpdateDamage(BattleDmgInfo dmgInfo)
|
{
|
// 验证数据有效性,防止空引用
|
if (dmgInfo?.battleHurtParam == null)
|
return;
|
|
// 检查受击者对象有效性
|
if (dmgInfo.battleHurtParam.hurter?.hurtObj != null)
|
{
|
var hurtObj = dmgInfo.battleHurtParam.hurter.hurtObj;
|
// 检查对象是否已被销毁
|
if (hurtObj == null || hurtObj.Equals(null))
|
{
|
Debug.LogWarning($"[UpdateDamage] 受击者对象已被销毁,跳过伤害更新");
|
return;
|
}
|
|
// 验证是否能安全获取 maxHp(间接检查 HeroBattleObject 内部状态)
|
if (hurtObj is HeroBattleObject && hurtObj.GetMaxHp() <= 0)
|
{
|
Debug.LogWarning($"[UpdateDamage] 受击者 maxHp 无效,跳过伤害更新");
|
return;
|
}
|
}
|
|
// 检查施法者对象有效性
|
if (dmgInfo.battleHurtParam.caster?.casterObj != null)
|
{
|
var casterObj = dmgInfo.battleHurtParam.caster.casterObj;
|
// 检查对象是否已被销毁
|
if (casterObj == null || casterObj.Equals(null))
|
{
|
Debug.LogWarning($"[UpdateDamage] 施法者对象已被销毁,跳过伤害更新");
|
return;
|
}
|
|
// 验证是否能安全获取 maxHp
|
if (casterObj is HeroBattleObject && casterObj.GetMaxHp() <= 0)
|
{
|
Debug.LogWarning($"[UpdateDamage] 施法者 maxHp 无效,跳过伤害更新");
|
return;
|
}
|
}
|
|
// 数据验证通过,加入队列
|
damageUpdateQueue.Enqueue(dmgInfo);
|
}
|
|
/// <summary>
|
/// 实际执行伤害更新(统一处理目标和施法者)
|
/// </summary>
|
private void ExecuteDamageUpdate(BattleDmgInfo dmgInfo)
|
{
|
KillTween(ref damageSequence);
|
|
string guid = battleObject.battleField.guid;
|
long objID = battleObject.ObjID;
|
ulong currentPackUID = dmgInfo.battleHurtParam.packUID;
|
|
// 检查是否是最新的 PackUID,如果不是则忽略
|
if (!largestPackUID.ContainsKey(guid) || currentPackUID < largestPackUID[guid])
|
{
|
// Debug.LogWarning($"[ExecuteDamageUpdate] 忽略旧包 - ObjID:{objID}, 当前PackUID:{currentPackUID} < 最大PackUID:{largestPackUID[guid]}");
|
return;
|
}
|
|
long maxHp, fromHp, toHp, fromShield, toShield;
|
|
// 优先判断当前InfoBar是否为受击者(血量变化总是体现在hurter里)
|
BattleHurtObj hurter = dmgInfo.battleHurtParam.hurter;
|
if (hurter?.hurtObj != null && hurter.hurtObj.ObjID == objID)
|
{
|
// 当前InfoBar是受击者(包括给自己治疗、给自己造成伤害的情况)
|
if (hurter.hurtObj.IsTianziBoss())
|
{
|
return;
|
}
|
|
// 直接使用 dmgInfo 中的数据(已经被 CompareAndExchangeLargestPackUIDHp 验证过)
|
maxHp = hurter.maxHp;
|
fromHp = hurter.fromHp;
|
toHp = hurter.toHp;
|
fromShield = hurter.fromShieldValue;
|
toShield = hurter.toShieldValue;
|
|
// Debug.LogError($"[ExecuteDamageUpdate] 受击者 - ObjID:{objID}, fromHp:{fromHp}, toHp:{toHp}, maxHp:{maxHp} (PackUID:{currentPackUID})");
|
}
|
// 其次判断是否为施法者(施法消耗生命等情况)
|
else
|
{
|
BattleCastObj caster = dmgInfo.battleHurtParam.caster;
|
if (caster?.casterObj == null || caster.casterObj.ObjID != objID)
|
{
|
// Debug.LogWarning($"[ExecuteDamageUpdate] 当前对象 {objID} 既不是施法者也不是受击者");
|
return;
|
}
|
|
if (caster.casterObj.IsTianziBoss())
|
{
|
return;
|
}
|
|
// 直接使用 dmgInfo 中的数据(已经被 CompareAndExchangeLargestPackUIDHp 验证过)
|
maxHp = caster.maxHp;
|
fromHp = caster.fromHp;
|
toHp = caster.toHp;
|
fromShield = caster.fromShieldValue;
|
toShield = caster.toShieldValue;
|
|
// Debug.LogError($"[ExecuteDamageUpdate] 施法者 - ObjID:{objID}, fromHp:{fromHp}, toHp:{toHp}, maxHp:{maxHp} (PackUID:{currentPackUID})");
|
}
|
|
if (maxHp <= 0)
|
{
|
sliderShield1.value = 0;
|
sliderShield2.value = 0;
|
return;
|
}
|
|
damageSequence = DOTween.Sequence();
|
|
bool IsTianziBoss = battleObject.IsTianziBoss();
|
|
// 护盾动画
|
if (fromShield > 0 && !IsTianziBoss)
|
{
|
float fromShield1Value = Mathf.Min((float)(fromHp + fromShield), (float)maxHp) / (float)maxHp;
|
float fromShield2Value = Mathf.Max((float)(fromHp + fromShield - maxHp), 0f) / (float)maxHp;
|
|
sliderShield1.value = fromShield1Value;
|
sliderShield2.value = fromShield2Value;
|
|
// 护盾2动画
|
if (fromShield2Value > 0)
|
{
|
float toShield2Value = Mathf.Max((float)(toHp + toShield - maxHp), 0f) / (float)maxHp;
|
|
if (Mathf.Abs(fromShield2Value - toShield2Value) > 0.001f)
|
{
|
damageSequence.Append(sliderShield2.DOValue(toShield2Value, 0.2f));
|
}
|
}
|
|
// 护盾1动画
|
if (fromShield1Value > 0)
|
{
|
float toShield1Value = Mathf.Min((float)(toHp + toShield), (float)maxHp) / (float)maxHp;
|
|
if (Mathf.Abs(fromShield1Value - toShield1Value) > 0.001f)
|
{
|
damageSequence.Append(sliderShield1.DOValue(toShield1Value, 0.2f));
|
}
|
}
|
}
|
else
|
{
|
sliderShield1.value = 0f;
|
sliderShield2.value = 0f;
|
}
|
|
// 血量动画
|
float fromHpValue = (float)fromHp / (float)maxHp;
|
float toHpValue = (float)toHp / (float)maxHp;
|
|
// sliderHp.value = fromHpValue;
|
// sliderSlowHp.value = fromHpValue;
|
float diff = Mathf.Abs(toHpValue - fromHpValue);
|
float cost = Mathf.Lerp(0, 1f, diff);
|
|
if (Mathf.Abs(fromHpValue - toHpValue) > 0.001f)
|
{
|
damageSequence.Append(sliderHp.DOValue(toHpValue, cost));
|
damageSequence.Join(sliderSlowHp.DOValue(toHpValue, cost * 1.5f));
|
}
|
|
damageSequence.onComplete += () =>
|
{
|
sliderHp.value = toHpValue;
|
sliderSlowHp.value = toHpValue;
|
};
|
|
damageSequence.Play();
|
battleObject.battleField.battleTweenMgr.OnPlayTween(damageSequence);
|
}
|
|
/// <summary>
|
/// 每帧更新
|
/// </summary>
|
public void Run()
|
{
|
// 处理血条和伤害队列
|
UpdateHpAndDamageQueue();
|
|
// 更新飘字GCD并处理队列
|
UpdateTipsGCDAndQueue();
|
|
// 更新所有飘字
|
UpdateActiveTips();
|
}
|
|
/// <summary>
|
/// 设置速度比例
|
/// </summary>
|
public void SetSpeedRatio(float ratio)
|
{
|
foreach (var tip in tipsList)
|
{
|
tip.SetRatio(ratio, 1f);
|
}
|
}
|
|
private void CompareAndExchangeLargestPackUIDHp(BattleDmgInfo dmgInfo)
|
{
|
string guid = battleObject.battleField.guid;
|
ulong currentPackUID = dmgInfo.battleHurtParam.packUID;
|
|
// 获取或初始化当前战场的数据
|
if (!largestPackUID.ContainsKey(guid))
|
{
|
largestPackUID[guid] = 0ul;
|
}
|
if (!largestPackUIDAllObjectsToHp.ContainsKey(guid))
|
{
|
largestPackUIDAllObjectsToHp[guid] = new Dictionary<long, long>();
|
}
|
if (!largestPackUIDAllObjectsMaxHp.ContainsKey(guid))
|
{
|
largestPackUIDAllObjectsMaxHp[guid] = new Dictionary<long, long>();
|
}
|
|
ulong currentLargestPackUID = largestPackUID[guid];
|
Dictionary<long, long> hpDict = largestPackUIDAllObjectsToHp[guid];
|
Dictionary<long, long> maxHpDict = largestPackUIDAllObjectsMaxHp[guid];
|
|
// 如果遇到更大的packUID,更新标记(不清空数据,保留所有证据)
|
if (currentPackUID > currentLargestPackUID)
|
{
|
// Debug.LogError($"[血量记录] 检测到新批次PackUID: {currentPackUID} > {currentLargestPackUID},保留所有历史数据");
|
largestPackUID[guid] = currentPackUID;
|
currentLargestPackUID = currentPackUID;
|
}
|
|
// 记录所有packUID的数据(包括早触发的包),但只采用最大packUID的数据
|
// 记录施法者的血量变化
|
BattleCastObj battleCastObj = dmgInfo.battleHurtParam.caster;
|
if (battleCastObj != null && battleCastObj.casterObj != null)
|
{
|
long casterID = battleCastObj.casterObj.ObjID;
|
|
// 获取旧血量用于计算变化
|
long oldHp = hpDict.ContainsKey(casterID) ? hpDict[casterID] : battleCastObj.fromHp;
|
long newHp = battleCastObj.toHp;
|
long maxHp = battleCastObj.maxHp;
|
long hpChange = newHp - oldHp;
|
|
// 只有当前packUID等于最大packUID时才更新记录
|
if (currentPackUID == currentLargestPackUID)
|
{
|
hpDict[casterID] = newHp;
|
maxHpDict[casterID] = maxHp;
|
|
// 打印血量变化日志(施法者通常是恢复生命)
|
// string casterName = caster.casterObj.teamHero?.heroConfig.Name ?? "未知武将";
|
if (hpChange != 0)
|
{
|
// string changeType = hpChange > 0 ? "恢复" : "损失";
|
// Debug.LogError($"[血量变化] {casterName}(ID:{casterID}) {changeType} {Math.Abs(hpChange)} 生命,血量从 {oldHp}/{maxHp} 变为 {newHp}/{maxHp} (PackUID:{currentPackUID})");
|
}
|
}
|
else
|
{
|
// Debug.LogWarning($"[血量记录] 忽略旧包数据 - 施法者{casterID}, 当前PackUID:{currentPackUID} < 最大PackUID:{currentLargestPackUID}");
|
}
|
}
|
|
// 记录受击者的血量变化
|
BattleHurtObj battleHurtObj = dmgInfo.battleHurtParam.hurter;
|
if (battleHurtObj != null && battleHurtObj.hurtObj != null)
|
{
|
BattleObject hurter = battleHurtObj.hurtObj;
|
long hurterID = hurter.ObjID;
|
|
// 获取旧血量用于计算伤害
|
long oldHp = hpDict.ContainsKey(hurterID) ? hpDict[hurterID] : battleHurtObj.fromHp;
|
long newHp = battleHurtObj.toHp;
|
long maxHp = battleHurtObj.maxHp;
|
long damage = oldHp - newHp;
|
|
// 只有当前packUID等于最大packUID时才更新记录
|
if (currentPackUID == currentLargestPackUID)
|
{
|
hpDict[hurterID] = newHp;
|
maxHpDict[hurterID] = maxHp;
|
|
// 打印血量变化日志
|
// string hurterName = hurter.hurtObj.teamHero?.heroConfig.Name ?? "未知武将";
|
if (damage != 0)
|
{
|
// Debug.LogError($"[血量变化] {hurterName}(ID:{hurterID}) 受到 {damage} 伤害,血量从 {oldHp}/{maxHp} 变为 {newHp}/{maxHp} (PackUID:{currentPackUID})");
|
}
|
}
|
else
|
{
|
// Debug.LogWarning($"[血量记录] 忽略旧包数据 - 受击者{hurterID}, 当前PackUID:{currentPackUID} < 最大PackUID:{currentLargestPackUID}");
|
}
|
}
|
}
|
|
/// <summary>
|
/// 处理血条和伤害更新队列
|
/// </summary>
|
private void UpdateHpAndDamageQueue()
|
{
|
// 优先处理UpdateDamage
|
if (damageUpdateQueue.Count > 0)
|
{
|
BattleDmgInfo dmgInfo = damageUpdateQueue.Dequeue();
|
CompareAndExchangeLargestPackUIDHp(dmgInfo);
|
ExecuteDamageUpdate(dmgInfo);
|
return;
|
}
|
}
|
|
/// <summary>
|
/// 更新飘字GCD并处理队列
|
/// </summary>
|
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();
|
}
|
}
|
|
/// <summary>
|
/// 重置飘字GCD计时器
|
/// </summary>
|
private void ResetTipsGCD()
|
{
|
float speedRatio = GetCurrentSpeedRatio();
|
float frameTime = 1f / (float)BattleConst.skillMotionFps;
|
tipsGCDTimer = frameTime * TIPS_GCD_FRAMES / speedRatio;
|
}
|
|
/// <summary>
|
/// 获取当前速度倍率
|
/// </summary>
|
private float GetCurrentSpeedRatio()
|
{
|
// 回退到战场速度
|
if (battleObject != null && battleObject.battleField != null)
|
{
|
return battleObject.battleField.speedRatio;
|
}
|
|
return 1f;
|
}
|
|
/// <summary>
|
/// 立即弹出飘字
|
/// </summary>
|
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);
|
}
|
|
/// <summary>
|
/// 创建飘字实例
|
/// </summary>
|
private BattleTips CreateTipsInstance(TipsInfo tipsInfo)
|
{
|
Transform parent = tipsInfo.followCharacter
|
? transform
|
: battleObject.battleField.battleRootNode.notFollowTipsAdjuster.transform;
|
|
GameObject go = GameObject.Instantiate(textTips.gameObject, parent);
|
return go.GetComponent<BattleTips>();
|
}
|
|
/// <summary>
|
/// 配置飘字
|
/// </summary>
|
private void ConfigureTips(BattleTips tips, TipsInfo tipsInfo)
|
{
|
FloatingConfig targetConfig = tipsInfo.isRage ? rageFloatingConfig : 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);
|
}
|
}
|
|
/// <summary>
|
/// 设置不跟随飘字的位置
|
/// </summary>
|
private void SetNonFollowPosition(BattleTips tips)
|
{
|
RectTransform contentRect = tips.GetComponent<RectTransform>();
|
RectTransform contentParentRect = contentRect.parent as RectTransform;
|
RectTransform infoBarRect = GetComponent<RectTransform>();
|
|
// 计算世界坐标
|
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));
|
}
|
|
/// <summary>
|
/// 移除飘字
|
/// </summary>
|
private void RemoveTips(BattleTips tips)
|
{
|
tipsList.Remove(tips);
|
tips.controller = null;
|
GameObject.DestroyImmediate(tips.gameObject);
|
}
|
|
/// <summary>
|
/// 更新所有激活的飘字
|
/// </summary>
|
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();
|
}
|
}
|
|
/// <summary>
|
/// 清理所有飘字
|
/// </summary>
|
private void CleanupTips()
|
{
|
messages.Clear();
|
|
for (int i = tipsList.Count - 1; i >= 0; i--)
|
{
|
RemoveTips(tipsList[i]);
|
}
|
|
tipsList.Clear();
|
}
|
|
/// <summary>
|
/// 停止并清理Tween
|
/// </summary>
|
private void KillTween<T>(ref T tween) where T : Tween
|
{
|
if (tween != null && battleObject != null)
|
{
|
battleObject.battleField.battleTweenMgr.OnKillTween(tween);
|
tween = null;
|
}
|
}
|
|
/// <summary>
|
/// 获取时间增量
|
/// </summary>
|
private float GetDeltaTime()
|
{
|
return 1f / (float)BattleConst.skillMotionFps * battleObject.battleField.speedRatio;
|
}
|
|
/// <summary>
|
/// Buff图标点击回调
|
/// </summary>
|
private void OnBuffCellClicked()
|
{
|
// TODO: 显示buff描述/当前身上所有buff
|
}
|
|
public void HaveRest()
|
{
|
CleanupTips();
|
SetActive(false);
|
|
// 关掉所有的tween
|
KillTween(ref hpTween);
|
KillTween(ref xpTween);
|
KillTween(ref damageSequence);
|
}
|
}
|