| | |
| | | using System; |
| | | using DG.Tweening; |
| | | |
| | | |
| | | /// <summary> |
| | | /// 战斗角色信息栏 |
| | | /// 职责:显示角色血条、怒气、Buff和飘字提示 |
| | | /// </summary> |
| | | public class BattleHeroInfoBar : MonoBehaviour |
| | | { |
| | | protected BattleObject battleObject; |
| | | #region 内部类 |
| | | |
| | | /// <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(决定用哪个颜色) |
| | | } |
| | | |
| | | #endregion |
| | | |
| | | #region Inspector字段 |
| | | |
| | | [Header("UI Components")] |
| | | public Slider sliderHp; |
| | | |
| | | public Slider sliderXp; //怒气 |
| | | |
| | | protected float timer = 0f; |
| | | |
| | | public float PopUpInterval = 0.2f; |
| | | |
| | | |
| | | // public List<BattleBuffCell> buffCells = new List<BattleBuffCell>(); |
| | | |
| | | protected List<string> messages = new List<string>(); |
| | | |
| | | public Slider sliderXp; |
| | | public BasicHeroInfoContainer heroInfoContainer; |
| | | |
| | | public BattleTips textTips; |
| | | |
| | | [Header("Buff Components")] |
| | | [SerializeField] |
| | | public List<BattleBuffCell> buffCells = new List<BattleBuffCell>(); |
| | | |
| | | protected Tween hpTween; |
| | | [Header("Floating Configs")] |
| | | [Tooltip("跟随角色的飘字配置")] |
| | | public FloatingConfig followFloatingConfig; |
| | | [Tooltip("不跟随角色的飘字配置(固定在战场节点)")] |
| | | public FloatingConfig noFollowFloatingConfig; |
| | | |
| | | [Header("Settings")] |
| | | public float PopUpInterval = 0.2f; |
| | | |
| | | #endregion |
| | | |
| | | protected Tween xpTween; |
| | | |
| | | #region 私有字段 |
| | | |
| | | protected BattleObject battleObject; |
| | | protected float timer = 0f; |
| | | |
| | | protected List<TipsInfo> messages = new List<TipsInfo>(); |
| | | protected List<BattleTips> tipsList = new List<BattleTips>(); |
| | | |
| | | protected List<HB428_tagSCBuffRefresh> buffList = new List<HB428_tagSCBuffRefresh>(); |
| | | |
| | | protected Tween hpTween; |
| | | protected Tween xpTween; |
| | | |
| | | #endregion |
| | | |
| | | public ScrollerController scroller; |
| | | #region Unity生命周期 |
| | | |
| | | protected void OnDisable() |
| | | { |
| | | CleanupTips(); |
| | | } |
| | | |
| | | #endregion |
| | | |
| | | #region 公共方法 - 初始化 |
| | | |
| | | public void SetBattleObject(BattleObject _battleObject) |
| | | { |
| | | battleObject = _battleObject; |
| | | heroInfoContainer.SetHeroInfo(battleObject.teamHero); |
| | | RefreshBuff(buffList); |
| | | RefreshBuff(battleObject.buffMgr.GetBuffList()); |
| | | UpdateHP(battleObject.teamHero.curHp, battleObject.teamHero.curHp, battleObject.teamHero.maxHp, false); |
| | | UpdateXP(battleObject.teamHero.rage, battleObject.teamHero.rage, 100, false); |
| | | } |
| | | |
| | | public void RefreshBuff(List<HB428_tagSCBuffRefresh> datas) |
| | | { |
| | | buffList = datas; |
| | | // 更新buff图标 or 创建新的buff图标 |
| | | scroller.Refresh(); |
| | | for (int i = 0; i < buffList.Count; i++) |
| | | { |
| | | if (i % 5 == 0) |
| | | { |
| | | scroller.AddCell(ScrollerDataType.Header, i); |
| | | } |
| | | } |
| | | scroller.Restart(); |
| | | } |
| | | |
| | | protected void OnEnable() |
| | | { |
| | | scroller.OnRefreshCell += OnRefreshCell; |
| | | } |
| | | |
| | | protected void OnDisable() |
| | | { |
| | | scroller.OnRefreshCell -= OnRefreshCell; |
| | | // TODO YYL 考虑池化 |
| | | messages.Clear(); |
| | | for (int i = 0; i < tipsList.Count; i++) |
| | | { |
| | | var tip = tipsList[i]; |
| | | tip.OnFinish = null; |
| | | GameObject.DestroyImmediate(tip.gameObject); |
| | | } |
| | | tipsList.Clear(); |
| | | } |
| | | |
| | | protected void OnRefreshCell(ScrollerDataType type, CellView cell) |
| | | { |
| | | var _cell = cell as BattleBuffLineCell; |
| | | _cell.Display(buffList, cell.index); |
| | | } |
| | | |
| | | public void ShowTips(string message) |
| | | { |
| | | messages.Add(message); |
| | | } |
| | | |
| | | |
| | | public void SetActive(bool active) |
| | | { |
| | | gameObject.SetActive(active); |
| | | } |
| | | |
| | | #endregion |
| | | |
| | | public void PopUpTipsDirectly(string message) |
| | | #region 公共方法 - Buff管理 |
| | | |
| | | public void RefreshBuff(List<HB428_tagSCBuffRefresh> datas) |
| | | { |
| | | GameObject prefab = textTips.gameObject; |
| | | if (buffCells.IsNullOrEmpty()) |
| | | return; |
| | | |
| | | GameObject go = GameObject.Instantiate(prefab, transform); |
| | | |
| | | BattleTips tips = go.GetComponent<BattleTips>(); |
| | | |
| | | tips.SetText(message); |
| | | |
| | | tips.OnFinish = () => |
| | | for (int i = 0; i < buffCells.Count; i++) |
| | | { |
| | | // TODO YYL 考虑池化 |
| | | tipsList.Remove(tips); |
| | | GameObject.DestroyImmediate(tips.gameObject); |
| | | }; |
| | | if (i < datas.Count) |
| | | { |
| | | buffCells[i].SetActive(true); |
| | | buffCells[i].Init(datas[i], OnBuffCellClicked); |
| | | } |
| | | else |
| | | { |
| | | buffCells[i].SetActive(false); |
| | | } |
| | | } |
| | | } |
| | | |
| | | #endregion |
| | | |
| | | #region 公共方法 - 飘字管理 |
| | | |
| | | /// <summary> |
| | | /// 添加飘字到队列 |
| | | /// </summary> |
| | | 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 |
| | | }); |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 添加自定义飘字配置到队列 |
| | | /// </summary> |
| | | public void ShowTips(TipsInfo tipsInfo) |
| | | { |
| | | messages.Add(tipsInfo); |
| | | } |
| | | |
| | | #endregion |
| | | |
| | | #region 公共方法 - 数值更新 |
| | | |
| | | /// <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) |
| | | { |
| | | // 关键修复:先设置起始值,再播放动画到目标值 |
| | | sliderHp.value = fromValue; // ← 这行是关键! |
| | | hpTween = sliderHp.DOValue(targetValue, 0.3f).SetAutoKill(false); |
| | | battleObject.battleField.battleTweenMgr.OnPlayTween(hpTween); |
| | | } |
| | | else |
| | | { |
| | | sliderHp.value = targetValue; |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// !!!临时的用于天子更新血量显示,等接口完善后删除 |
| | | /// </summary> |
| | | public void UpdateHP(float value) |
| | | { |
| | | sliderHp.value = value; |
| | | //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); |
| | | battleObject.battleField.battleTweenMgr.OnPlayTween(xpTween); |
| | | } |
| | | else |
| | | { |
| | | sliderXp.value = targetValue; |
| | | } |
| | | } |
| | | |
| | | #endregion |
| | | |
| | | #region 公共方法 - 运行时更新 |
| | | |
| | | /// <summary> |
| | | /// 每帧更新 |
| | | /// </summary> |
| | | public void Run() |
| | | { |
| | | // 更新所有飘字 |
| | | UpdateActiveTips(); |
| | | |
| | | // 处理飘字队列 |
| | | ProcessTipsQueue(); |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 设置速度比例 |
| | | /// </summary> |
| | | public void SetSpeedRatio(float ratio) |
| | | { |
| | | foreach (var tip in tipsList) |
| | | { |
| | | tip.SetRatio(ratio, 1f); |
| | | } |
| | | } |
| | | |
| | | #endregion |
| | | |
| | | #region 私有方法 - 飘字处理 |
| | | |
| | | /// <summary> |
| | | /// 立即弹出飘字 |
| | | /// </summary> |
| | | private void PopUpTipsDirectly(TipsInfo tipsInfo) |
| | | { |
| | | // 创建飘字实例 |
| | | 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); |
| | | |
| | | // 添加到列表 |
| | | tipsList.Add(tips); |
| | | } |
| | | |
| | | |
| | | public void UpdateHP(long fromHp, long toHp, long maxHp) |
| | | |
| | | /// <summary> |
| | | /// 创建飘字实例 |
| | | /// </summary> |
| | | private BattleTips CreateTipsInstance(TipsInfo tipsInfo) |
| | | { |
| | | // 做hp增加或者减少的动画 |
| | | // sliderHp.value = ((float)fromHp) / ((float)maxHp); |
| | | if (hpTween != null) |
| | | { |
| | | battleObject.battleField.battleTweenMgr.OnKillTween(hpTween); |
| | | } |
| | | hpTween = sliderHp.DOValue((float)toHp / maxHp, 0.3f); |
| | | battleObject.battleField.battleTweenMgr.OnPlayTween(hpTween); |
| | | |
| | | BattleDebug.LogError("update hp from " + fromHp + " to " + toHp + " maxHp " + maxHp); |
| | | Transform parent = tipsInfo.followCharacter |
| | | ? transform |
| | | : battleObject.battleField.battleRootNode.transform; |
| | | |
| | | GameObject go = GameObject.Instantiate(textTips.gameObject, parent); |
| | | return go.GetComponent<BattleTips>(); |
| | | } |
| | | |
| | | public void UpdateXP(long fromXp, long toXp, long maxXp) |
| | | |
| | | /// <summary> |
| | | /// 配置飘字 |
| | | /// </summary> |
| | | private void ConfigureTips(BattleTips tips, TipsInfo tipsInfo) |
| | | { |
| | | // 做Xp增加或者减少的动画 |
| | | // sliderXp.value = ((float)fromXp) / ((float)maxXp); |
| | | if (xpTween != null) |
| | | FloatingConfig targetConfig = tipsInfo.followCharacter |
| | | ? followFloatingConfig |
| | | : noFollowFloatingConfig; |
| | | |
| | | if (targetConfig == null) |
| | | { |
| | | battleObject.battleField.battleTweenMgr.OnKillTween(xpTween); |
| | | Debug.LogError($"[BattleHeroInfoBar] FloatingConfig 未配置! " + |
| | | $"followCharacter={tipsInfo.followCharacter}, GameObject: {gameObject.name}"); |
| | | return; |
| | | } |
| | | xpTween = sliderHp.DOValue((float)toXp / maxXp, 0.2f); |
| | | battleObject.battleField.battleTweenMgr.OnPlayTween(xpTween); |
| | | BattleDebug.LogError("update xp from " + fromXp + " to " + toXp + " maxXp " + maxXp); |
| | | |
| | | 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>(); |
| | | |
| | | public void Run() |
| | | // 计算世界坐标 |
| | | 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); |
| | | GameObject.DestroyImmediate(tips.gameObject); |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 更新所有激活的飘字 |
| | | /// </summary> |
| | | private void UpdateActiveTips() |
| | | { |
| | | for (int i = tipsList.Count - 1; i >= 0; i--) |
| | | { |
| | | tipsList[i].Run(); |
| | | } |
| | | |
| | | timer += Time.deltaTime; |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 处理飘字队列 |
| | | /// </summary> |
| | | private void ProcessTipsQueue() |
| | | { |
| | | timer += GetDeltaTime(); |
| | | |
| | | if (messages.Count > 0 && timer >= PopUpInterval) |
| | | { |
| | | // 播放飘字 |
| | | string message = messages[0]; |
| | | TipsInfo tipsInfo = messages[0]; |
| | | messages.RemoveAt(0); |
| | | |
| | | PopUpTipsDirectly(message); |
| | | |
| | | |
| | | PopUpTipsDirectly(tipsInfo); |
| | | |
| | | timer = 0f; |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 清理所有飘字 |
| | | /// </summary> |
| | | private void CleanupTips() |
| | | { |
| | | messages.Clear(); |
| | | |
| | | foreach (var tip in tipsList) |
| | | { |
| | | tip.OnFinish = null; |
| | | GameObject.DestroyImmediate(tip.gameObject); |
| | | } |
| | | |
| | | tipsList.Clear(); |
| | | } |
| | | |
| | | #endregion |
| | | |
| | | #region 私有方法 - 辅助方法 |
| | | |
| | | /// <summary> |
| | | /// 停止并清理Tween |
| | | /// </summary> |
| | | private void KillTween(ref Tween 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 |
| | | } |
| | | |
| | | #endregion |
| | | } |