| | |
| | | /// </summary> |
| | | public class BattleHeroInfoBar : MonoBehaviour |
| | | { |
| | | #region 内部类 |
| | | |
| | | /// <summary> |
| | | /// 飘字信息配置 |
| | | /// </summary> |
| | |
| | | public bool isDebuff = false; // 是否是负向 Buff(决定用哪个颜色) |
| | | } |
| | | |
| | | #endregion |
| | | |
| | | #region Inspector字段 |
| | | /// <summary> |
| | | /// 血条更新请求 |
| | | /// </summary> |
| | | 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 GameObject maxXpGO; |
| | | public Slider sliderShield1; |
| | | public Slider sliderShield2; |
| | | public BasicHeroInfoContainer heroInfoContainer; |
| | | public BattleTips textTips; |
| | | |
| | |
| | | [Tooltip("不跟随角色的飘字配置(固定在战场节点)")] |
| | | public FloatingConfig noFollowFloatingConfig; |
| | | |
| | | [Header("Settings")] |
| | | public float PopUpInterval = 0.2f; |
| | | |
| | | #endregion |
| | | |
| | | #region 私有字段 |
| | | |
| | | protected BattleObject battleObject; |
| | | protected float timer = 0f; |
| | | |
| | | protected List<TipsInfo> messages = new List<TipsInfo>(); |
| | | protected List<BattleTips> tipsList = new List<BattleTips>(); |
| | |
| | | |
| | | protected Tween hpTween; |
| | | protected Tween xpTween; |
| | | protected Tween shieldTween1; |
| | | protected Tween shieldTween2; |
| | | protected Sequence damageSequence; |
| | | |
| | | #endregion |
| | | |
| | | #region Unity生命周期 |
| | | private Queue<HpUpdateRequest> hpUpdateQueue = new Queue<HpUpdateRequest>(); |
| | | private Queue<BattleDmgInfo> damageUpdateQueue = new Queue<BattleDmgInfo>(); |
| | | |
| | | // 飘字GCD相关 |
| | | private float tipsGCDTimer = 0f; |
| | | private const int TIPS_GCD_FRAMES = 5; |
| | | |
| | | protected void OnDisable() |
| | | { |
| | | CleanupTips(); |
| | | } |
| | | |
| | | #endregion |
| | | |
| | | #region 公共方法 - 初始化 |
| | | |
| | | public void SetBattleObject(BattleObject _battleObject) |
| | | { |
| | |
| | | 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); |
| | | } |
| | | |
| | | #endregion |
| | | |
| | | #region 公共方法 - Buff管理 |
| | | |
| | | public void RefreshBuff(List<HB428_tagSCBuffRefresh> datas) |
| | | { |
| | |
| | | buffCells[i].SetActive(false); |
| | | } |
| | | } |
| | | } |
| | | |
| | | #endregion |
| | | |
| | | #region 公共方法 - 飘字管理 |
| | | // 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; |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 添加飘字到队列 |
| | |
| | | messages.Add(tipsInfo); |
| | | } |
| | | |
| | | #endregion |
| | | |
| | | #region 公共方法 - 数值更新 |
| | | |
| | | /// <summary> |
| | | /// 更新血量显示 |
| | | /// </summary> |
| | | public void UpdateHP(long fromHp, long toHp, long maxHp, bool tween = true) |
| | | { |
| | | // 加入队列 |
| | | hpUpdateQueue.Enqueue(new HpUpdateRequest |
| | | { |
| | | fromHp = fromHp, |
| | | toHp = toHp, |
| | | maxHp = maxHp, |
| | | tween = tween |
| | | }); |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 实际执行血量更新 |
| | | /// </summary> |
| | | private void ExecuteHpUpdate(HpUpdateRequest request) |
| | | { |
| | | KillTween(ref hpTween); |
| | | |
| | | float fromValue = (float)fromHp / (float)maxHp; |
| | | float targetValue = (float)toHp / (float)maxHp; |
| | | float fromValue = (float)request.fromHp / (float)request.maxHp; |
| | | float targetValue = (float)request.toHp / (float)request.maxHp; |
| | | |
| | | if (tween) |
| | | if (request.tween) |
| | | { |
| | | // 关键修复:先设置起始值,再播放动画到目标值 |
| | | sliderHp.value = fromValue; // ← 这行是关键! |
| | | sliderHp.value = fromValue; |
| | | hpTween = sliderHp.DOValue(targetValue, 0.3f).SetAutoKill(false); |
| | | battleObject.battleField.battleTweenMgr.OnPlayTween(hpTween); |
| | | } |
| | |
| | | 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; |
| | | } |
| | | } |
| | | |
| | | #endregion |
| | | |
| | | #region 公共方法 - 运行时更新 |
| | | /// <summary> |
| | | /// 播放血条 护盾的变化 |
| | | /// </summary> |
| | | public void UpdateDamage(BattleDmgInfo dmgInfo) |
| | | { |
| | | // 加入队列 |
| | | damageUpdateQueue.Enqueue(dmgInfo); |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 实际执行伤害更新 |
| | | /// </summary> |
| | | 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); |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 每帧更新 |
| | | /// </summary> |
| | | public void Run() |
| | | { |
| | | // 处理血条和伤害队列 |
| | | UpdateHpAndDamageQueue(); |
| | | |
| | | // 更新飘字GCD并处理队列 |
| | | UpdateTipsGCDAndQueue(); |
| | | |
| | | // 更新所有飘字 |
| | | UpdateActiveTips(); |
| | | |
| | | // 处理飘字队列 |
| | | ProcessTipsQueue(); |
| | | } |
| | | |
| | | /// <summary> |
| | |
| | | } |
| | | } |
| | | |
| | | #endregion |
| | | /// <summary> |
| | | /// 处理血条和伤害更新队列 |
| | | /// </summary> |
| | | 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; |
| | | } |
| | | } |
| | | |
| | | /// <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(); |
| | | } |
| | | } |
| | | |
| | | #region 私有方法 - 飘字处理 |
| | | /// <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> |
| | | /// 立即弹出飘字 |
| | |
| | | { |
| | | // 创建飘字实例 |
| | | BattleTips tips = CreateTipsInstance(tipsInfo); |
| | | |
| | | |
| | | // 配置飘字 |
| | | ConfigureTips(tips, tipsInfo); |
| | | |
| | | |
| | | // 设置位置(如果不跟随) |
| | | if (!tipsInfo.followCharacter) |
| | | { |
| | | SetNonFollowPosition(tips); |
| | | } |
| | | |
| | | |
| | | // 设置参数并显示 |
| | | tips.SetRatio(battleObject.battleField.speedRatio, tipsInfo.scaleRatio); |
| | | tips.SetText(tipsInfo.message, tipsInfo.useArtText, false); // 移除 textColor 参数 |
| | | tips.ShowBackground(tipsInfo.showBackground); |
| | | |
| | | // 注册完成回调 |
| | | 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 void RemoveTips(BattleTips tips) |
| | | { |
| | | tipsList.Remove(tips); |
| | | tips.controller = null; |
| | | GameObject.DestroyImmediate(tips.gameObject); |
| | | } |
| | | |
| | |
| | | { |
| | | 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 ProcessTipsQueue() |
| | | { |
| | | timer += GetDeltaTime(); |
| | | |
| | | if (messages.Count > 0 && timer >= PopUpInterval) |
| | | { |
| | | TipsInfo tipsInfo = messages[0]; |
| | | messages.RemoveAt(0); |
| | | |
| | | PopUpTipsDirectly(tipsInfo); |
| | | |
| | | timer = 0f; |
| | | } |
| | | } |
| | | |
| | |
| | | tipsList.Clear(); |
| | | } |
| | | |
| | | #endregion |
| | | |
| | | #region 私有方法 - 辅助方法 |
| | | |
| | | /// <summary> |
| | | /// 停止并清理Tween |
| | | /// </summary> |
| | | private void KillTween(ref Tween tween) |
| | | private void KillTween<T>(ref T tween) where T : Tween |
| | | { |
| | | if (tween != null && battleObject != null) |
| | | { |
| | |
| | | { |
| | | // TODO: 显示buff描述/当前身上所有buff |
| | | } |
| | | |
| | | #endregion |
| | | } |