Main/Component/UI/Common/BossLifeBar.cs
@@ -44,8 +44,10 @@ set { m_CurrentBehaviourValue = value; UpdateSurplusSegments(currentBehaviourValue); var behaviourDecimalValue = m_CurrentBehaviourValue - (int)m_CurrentBehaviourValue; var trueDecimalValue = targetValue - (int)targetValue; float behaviourDecimalValue = GetSegmentDecimal(m_CurrentBehaviourValue); float trueDecimalValue = GetSegmentDecimal(targetValue); switch (pattern) { case Pattern.Add: @@ -54,7 +56,7 @@ break; case Pattern.Reduce: m_SliderMiddleground.value = behaviourDecimalValue; m_SliderForeground.value = behaviourDecimalValue < trueDecimalValue ? 0 : trueDecimalValue; m_SliderForeground.value = behaviourDecimalValue < trueDecimalValue ? 0f : trueDecimalValue; break; case Pattern.None: m_SliderMiddleground.value = behaviourDecimalValue; @@ -64,24 +66,29 @@ } } public void SetBaseInfo(int _npcId, ulong _hp, ulong _maxHp, int _level) public void SetBaseInfo(int _lifeBarCount, ulong _hp, ulong _maxHp) { var npcConfig = NPCConfig.Get(_npcId); HeroSkinConfig skinConfig = HeroSkinConfig.Get(npcConfig.SkinID); surplusSegments = -1; totalSegments = npcConfig.LifeBarCount; targetValue = currentBehaviourValue = ((float)_hp / _maxHp) * totalSegments - 0.0001f; var behaviourDecimalValue = currentBehaviourValue - (int)currentBehaviourValue; totalSegments = _lifeBarCount; // 使用精确值,不再人为减小 float percentage = (_maxHp > 0) ? (float)_hp / (float)_maxHp : 0f; targetValue = currentBehaviourValue = percentage * totalSegments; // 使用统一的 GetSegmentDecimal,避免小数精度导致进度条为0 var behaviourDecimalValue = GetSegmentDecimal(currentBehaviourValue); m_SliderForeground.value = m_SliderMiddleground.value = behaviourDecimalValue; refValue = 0f; // 立刻显示基准百分比(使用 percentage) m_SurplusPercent.text = (percentage * 100f).ToString("F2") + "%"; } public void Show(ulong _hp, ulong _maxHp) { var percentage = Mathf.Clamp(_hp, 0, _maxHp) / (float)_maxHp; var tempValue = totalSegments * percentage - 0.00001f; var tempValue = totalSegments * percentage; // 不再减小 pattern = tempValue > targetValue ? Pattern.Add : tempValue < targetValue ? Pattern.Reduce : Pattern.None; behaviourStartValue = currentBehaviourValue; @@ -89,6 +96,9 @@ timer = 0f; refValue = 0f; // 立即更新百分比显示(直接使用 percentage) m_SurplusPercent.text = (percentage * 100f).ToString("F2") + "%"; } private void LateUpdate() @@ -117,19 +127,11 @@ m_BackGround.SetActive(surplusSegments > 1); // var chars = surplusSegments.ToString(); // stringBuild.Remove(0, stringBuild.Length); // for (var i = 0; i < chars.Length; i++) // { // var numChar = GetNumKey(chars[i]); // if (numChar > 0) // { // stringBuild.Append((char)numChar); // } // } m_Surplus.text = surplusSegments.ToString(); m_SurplusPercent.text = Mathf.CeilToInt((_targetValue / totalSegments) * 100f).ToString() + "%"; m_Surplus.text = "x" + surplusSegments.ToString(); float pct = totalSegments > 0 ? Mathf.Clamp01(_targetValue / totalSegments) : 0f; // 修复格式并处理接近 100% 的情况 if (1f - pct < 0.00005f) pct = 1f; m_SurplusPercent.text = (pct * 100f).ToString("F2") + "%"; } } @@ -147,6 +149,23 @@ Reduce, } // helper: 返回段内小数部分;当恰好为整数且大于0时,返回1以表示满格(避免0导致进度条变空) private float GetSegmentDecimal(float value) { if (value <= 0f) return 0f; // 使用一个小的容忍值来处理浮点误差,避免 near-integer 导致 0 const float eps = 1e-5f; // 先做一个向下稳定的 floor,避免 2.999999 变成 2 的问题 float stableFloor = Mathf.Floor(value + eps); float frac = value - stableFloor; if (frac <= eps) return 1f; // 视为整段,显示满格 return Mathf.Clamp01(frac); } } Main/ResModule/ResManager.cs
@@ -218,6 +218,10 @@ if (!AssetSource.isUseAssetBundle) { SpriteAtlas atlas = LoadAsset<SpriteAtlas>("Sprite", atlasName.Replace("Sprite/", "")); if (null == atlas) { return null; } return atlas.GetSprite(spriteName); } else Main/System/Battle/BattleField/BattleField.cs
@@ -587,5 +587,10 @@ public virtual void ShowWindow(HB424_tagSCTurnFightInit vNetData) { } public virtual BattleObject FindBoss() { return null; } } Main/System/Battle/BattleField/StoryBossBattleField.cs
@@ -2,19 +2,14 @@ using LitJson; using UnityEngine; using System.Collections.Generic; using System.Linq; public class StoryBossBattleField : BattleField { protected int chapter;// 章节 protected int wave;// 波数 protected int level;// 关卡 protected JsonData extendData; protected MainChapterConfig chapterConfig; protected MainLevelConfig levelConfig; public StoryBossBattleField(string _guid) : base(_guid) { @@ -26,12 +21,8 @@ { base.Init(MapID, FuncLineID, extendData, _redTeamList, _blueTeamList, turnMax); chapter = FuncLineID / 10000; wave = MapID == 1 ? FuncLineID % 100 : 1;//第几波怪 level = (FuncLineID % 10000) / 100; int level = FuncLineID;// 关卡 extendData = _extendData; chapterConfig = MainChapterConfig.Get(chapter); levelConfig = MainLevelConfig.Get(level); SetBattleMode(BattleMode.Record); @@ -128,4 +119,40 @@ } fsBattleWin.SetBattleField(this); } public NPCLineupConfig GetBossLineupConfig() { if (null == levelConfig) return null; int[] bossLineup = levelConfig.BossLineupIDList; if (bossLineup.IsNullOrEmpty()) return null; var lineupID = bossLineup[0]; if (lineupID > 0) { NPCLineupConfig lineupConfig = NPCLineupConfig.Get(lineupID); return lineupConfig; } return null; } public override BattleObject FindBoss() { NPCLineupConfig lineupConfig = GetBossLineupConfig(); if (lineupConfig != null) { int bossId = lineupConfig.BossID; BattleObject bo = battleObjMgr.allBattleObjDict.Values.FirstOrDefault(bo => bo.teamHero.NPCID == bossId); return bo; } return null; } } Main/System/Battle/BattleObject/BattleObject.cs
@@ -272,9 +272,10 @@ HB427_tagSCUseSkill.tagSCUseSkillHurt hurt, SkillConfig skillConfig, int hitIndex, BattleDrops battleDrops, HB422_tagMCTurnFightObjDead deadPack) { BattleDmgInfo dmgInfo = PopDamage(damageValues, _totalDamage, hurt, skillConfig); bool isLastHit = hitIndex >= skillConfig.DamageDivide.Length - 1; bool firstHit = hitIndex == 0; BattleDmgInfo dmgInfo = PopDamage(damageValues, _totalDamage, hurt, skillConfig, isLastHit); // 这里 if (dmgInfo.IsType(DamageType.Dodge)) @@ -385,11 +386,9 @@ } // 伤害还要看 是否闪避 暴击 and so on 需要有一个DamageType 服务器应该会给 protected virtual BattleDmgInfo PopDamage(List<long> damageValues, long _totalDamage, HB427_tagSCUseSkill.tagSCUseSkillHurt hurt, SkillConfig skillConfig) protected virtual BattleDmgInfo PopDamage(List<long> damageValues, long _totalDamage, HB427_tagSCUseSkill.tagSCUseSkillHurt hurt, SkillConfig skillConfig, bool isLastHit) { BattleDmgInfo battleDmgInfo = new BattleDmgInfo(battleField.guid, damageValues, this, hurt, skillConfig); BattleDmgInfo battleDmgInfo = new BattleDmgInfo(battleField.guid, damageValues, this, hurt, skillConfig, isLastHit); int currentHurtHp = 0; for (int i = 0; i < damageValues.Count; i++) Main/System/Battle/Buff/BattleObjectBuffMgr.cs
@@ -110,6 +110,13 @@ // 刷新buff public void RefreshBuff(HB428_tagSCBuffRefresh vNetData, bool insert = false) { if (battleObject.IsDead()) { Debug.LogError("给死亡对象刷新buff 检查服务器代码"); RemoveAllBuff(); return; } SkillConfig skillConfig = SkillConfig.Get((int)vNetData.SkillID); if (null == skillConfig) @@ -171,6 +178,11 @@ } } public List<HB428_tagSCBuffRefresh> GetBuffList() { return buffDataDict.Values.ToList(); } public void InsertBuff(HB428_tagSCBuffRefresh vNetData) { RefreshBuff(vNetData, true); Main/System/Battle/Define/BattleDmgInfo.cs
@@ -26,15 +26,18 @@ // 是否被格挡了 public bool isBlocked = false; public bool isLastHit = false; public List<BattleDmg> battleDamageList = new List<BattleDmg>(); public BattleDmgInfo(string battleFieldGuid, List<long> damageList, BattleObject hurtObj, HB427_tagSCUseSkill.tagSCUseSkillHurt hurt, SkillConfig skillConfig) public BattleDmgInfo(string battleFieldGuid, List<long> damageList, BattleObject hurtObj, HB427_tagSCUseSkill.tagSCUseSkillHurt hurt, SkillConfig skillConfig, bool isLastHit) { this.battleFieldGuid = battleFieldGuid; this.damageList = damageList; this.hurtObj = hurtObj; this.hurt = hurt; this.skillConfig = skillConfig; this.isLastHit = isLastHit; HandleDamageType(); HandleAttackTypeAndDamage(); } Main/System/Battle/StoryBossBattleWin.cs
@@ -1,4 +1,5 @@ using System; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; @@ -11,7 +12,7 @@ private BattleRootNode battleRootNode = null; private BattleField battleField; private StoryBossBattleField battleField; [SerializeField] private Button btnSpeed; @@ -39,6 +40,10 @@ public Text txtBattleRound; public TotalDamageDisplayer totalDamageDisplayer; private BattleObject bossBattleObject = null; [SerializeField] public List<BattleBuffCell> buffCells; // 生命周期 protected override void InitComponent() @@ -84,15 +89,20 @@ base.OnPreOpen(); // SetBattleField(BattleManager.Instance.storyBattleField); BattleManager.Instance.onBattleFieldCreate += OnCreateBattleField; EventBroadcast.Instance.AddListener<BattleDmgInfo>(EventName.BATTLE_DAMAGE_TAKEN, OnDamageTaken); UIManager.Instance.CloseWindow<MainWin>(); } protected override void OnPreClose() { base.OnPreClose(); UIManager.Instance.CloseWindow<BattleHUDWin>(); BattleManager.Instance.onBattleFieldCreate -= OnCreateBattleField; EventBroadcast.Instance.RemoveListener<BattleDmgInfo>(EventName.BATTLE_DAMAGE_TAKEN, OnDamageTaken); UIManager.Instance.CloseWindow<BattleHUDWin>(); if (!UIManager.Instance.IsOpened<MainWin>()) UIManager.Instance.OpenWindow<MainWin>(); @@ -102,13 +112,67 @@ { if (field.GetType() == battleField.GetType()) { SetBattleField(field); SetBattleField(field as StoryBossBattleField); } } protected override void OnOpen() { base.OnOpen(); } public override void Refresh() { base.Refresh(); // bossLifeBar.SetBaseInfo(battleField); // skillWordCells; NPCLineupConfig lineupConfig = battleField.GetBossLineupConfig(); bossBattleObject = battleField.FindBoss(); DisplaySkillWordsList(lineupConfig); if (null != bossBattleObject) { TeamHero teamHero = bossBattleObject.teamHero; bossHeadCell.SetTeamHero(teamHero); txtBossName.text = teamHero.name; NPCConfig npcConfig = NPCConfig.Get(teamHero.NPCID); bossLifeBar.SetBaseInfo(Mathf.Max(1, npcConfig.LifeBarCount), (ulong)teamHero.curHp, (ulong)teamHero.maxHp); } else { bossHeadCell.SetTeamHero(null); txtBossName.text = string.Empty; bossLifeBar.SetBaseInfo(2, 2, 2); Debug.LogError("找不到boss"); } txtBattleRound.text = string.Format("{0}/{1}", battleField.round, battleField.turnMax); } private void RefreshHP() { if (null != bossBattleObject) { TeamHero teamHero = bossBattleObject.teamHero; bossLifeBar.Show((ulong)teamHero.curHp, (ulong)teamHero.maxHp); } } private void OnDamageTaken(BattleDmgInfo info) { if (info.hurtObj.ObjID == bossBattleObject.ObjID) { // Update the boss's health bar RefreshHP(); } totalDamageDisplayer.SetDamage(info); } protected override void OnClose() @@ -134,7 +198,7 @@ base.CompleteClose(); } public void SetBattleField(BattleField _battleField) public void SetBattleField(StoryBossBattleField _battleField) { battleField = _battleField; if (battleRootNode != null) @@ -160,6 +224,61 @@ battleField.UpdateCanvas(canvas); buttonsAdjuster.SetSortingOrder(BattleConst.ActiveHeroActionSortingOrder); Refresh(); textSpeed.text = (BattleManager.Instance.speedIndex + 1).ToString(); } public void DisplaySkillWordsList(NPCLineupConfig lineUPConfig) { if (skillWordCells.IsNullOrEmpty()) return; if (null == lineUPConfig) return; for (int i = 0; i < skillWordCells.Length; i++) { if (i < lineUPConfig.SkillIDExList.Length) { skillWordCells[i].SetActive(true); int skillID = lineUPConfig.SkillIDExList[i]; skillWordCells[i].Init(skillID, () => { SmallTipWin.showText = Language.Get("SmallTipFomat", SkillConfig.Get(skillID)?.SkillName, SkillConfig.Get(skillID)?.Description); SmallTipWin.worldPos = CameraManager.uiCamera.ScreenToWorldPoint(Input.mousePosition); SmallTipWin.isDownShow = true; UIManager.Instance.OpenWindow<SmallTipWin>(); }); } else { skillWordCells[i].SetActive(false); } } } public void RefreshBuff(List<HB428_tagSCBuffRefresh> datas) { if (buffCells.IsNullOrEmpty()) return; for (int i = 0; i < buffCells.Count; i++) { if (i < datas.Count) { buffCells[i].SetActive(true); HB428_tagSCBuffRefresh buffData = datas[i]; buffCells[i].Init(buffData, () => { // 点击buff图标 显示buff描述/当前身上所有buff }); } else { buffCells[i].SetActive(false); } } } } Main/System/Battle/UIComp/BattleBuffCell.cs
@@ -1,17 +1,118 @@ using UnityEngine; using UnityEngine.UI; using System; public class BattleBuffCell : CellView public class BattleBuffCell : MonoBehaviour { public Image imageIcon; public Text textLevel; public void Display(HB428_tagSCBuffRefresh hB428_TagSCBuffRefresh) ImageEx m_buffIcon; ImageEx buffIcon { SkillConfig skillConfig = SkillConfig.Get((int)hB428_TagSCBuffRefresh.SkillID); get { if (m_buffIcon == null) { m_buffIcon = this.transform.GetComponent<ImageEx>("Container_BuffCell/Img_Icon"); } return m_buffIcon; } } imageIcon.sprite = ResManager.Instance.LoadAsset<Sprite>("", skillConfig.IconName); textLevel.text = hB428_TagSCBuffRefresh.Layer.ToString(); Button m_buffButton; Button buffButton { get { if (m_buffButton == null) { m_buffButton = gameObject.GetComponent<Button>(); } return m_buffButton; } } TextEx m_BuffLayer; TextEx buffLayer { get { if (m_BuffLayer == null) { m_BuffLayer = this.transform.GetComponent<TextEx>("Container_BuffCell/Text_Layer"); } return m_BuffLayer; } } void Awake() { LoadPrefab(); } GameObject cellContainer; private void LoadPrefab() { if (cellContainer != null) return; var tmp = transform.Find("Container_BuffCell"); if (tmp != null) { cellContainer = tmp.gameObject; return; } if (cellContainer == null) { cellContainer = UIUtility.CreateWidget("BattleBuffCell", "Container_BuffCell"); if (cellContainer != null) { cellContainer.transform.SetParentEx(this.transform, Vector3.zero, Quaternion.identity, Vector3.one); cellContainer.transform.SetAsFirstSibling(); } } //缩放到和父rect一样大 // var scale = 1f; // var rect = cellContainer.GetComponent<RectTransform>(); // var parentRect = transform.GetComponent<RectTransform>(); // float width = parentRect.sizeDelta.x; // if (width <= 0f) // { // //外部控制了尺寸获取为0 // GridLayoutGroup grid = GetComponentInParent<GridLayoutGroup>(); // if (grid != null) // { // width = grid.cellSize.x; // } // } // scale = width / rect.sizeDelta.x; // cellContainer.transform.localScale = cellContainer.transform.localScale * scale; } public void Init(HB428_tagSCBuffRefresh buffData, Action onclick = null, bool showType = false) { if (null == buffData || gameObject == null) { return; } LoadPrefab(); //存在被卸载的可能,重新加载 var config = SkillConfig.Get((int)buffData.SkillID); if (config == null) { Debug.LogErrorFormat("技能未配置 : {0}", buffData.SkillID); return; } buffIcon.SetOrgSprite(config.BuffIconName, "BuffIcon"); buffButton.AddListener(() => { onclick?.Invoke(); }); buffLayer.text = buffData.Layer == 0 ? "" : buffData.Layer.ToString(); } } Main/System/Battle/UIComp/BattleBuffLineCell.cs
File was deleted Main/System/Battle/UIComp/BattleBuffLineCell.cs.meta
File was deleted Main/System/Battle/UIComp/BattleHeroInfoBar.cs
@@ -18,7 +18,7 @@ public float PopUpInterval = 0.2f; // public List<BattleBuffCell> buffCells = new List<BattleBuffCell>(); [SerializeField] public List<BattleBuffCell> buffCells = new List<BattleBuffCell>(); protected List<string> messages = new List<string>(); @@ -34,40 +34,42 @@ protected List<HB428_tagSCBuffRefresh> buffList = new List<HB428_tagSCBuffRefresh>(); public ScrollerController scroller; 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(); } if (buffCells.IsNullOrEmpty()) return; protected void OnEnable() for (int i = 0; i < buffCells.Count; i++) { scroller.OnRefreshCell += OnRefreshCell; if (i < datas.Count) { buffCells[i].SetActive(true); HB428_tagSCBuffRefresh buffData = datas[i]; buffCells[i].Init(buffData, () => { // 点击buff图标 显示buff描述/当前身上所有buff }); } else { buffCells[i].SetActive(false); } } } protected void OnDisable() { scroller.OnRefreshCell -= OnRefreshCell; // TODO YYL 考虑池化 messages.Clear(); for (int i = 0; i < tipsList.Count; i++) @@ -78,13 +80,6 @@ } 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); Main/System/Battle/UIComp/BossHeadCell.cs
@@ -15,6 +15,25 @@ public void SetTeamHero(TeamHero teamHero) { if (null == teamHero) { SetDefault(); return; } HeroSkinConfig heroSkinConfig = teamHero.skinConfig; imgIcon.sprite = UILoader.LoadSprite("HeroHead", heroSkinConfig.SquareIcon); txtLv.text = Language.Get("Arena22", teamHero.level); // TODO YYL // imgFrame跟imgDecoration等幻境阁完成之后再来做 } public void SetDefault() { imgFrame.sprite = null; imgIcon.sprite = null; imgDecoration.sprite = null; txtLv.text = ""; } } Main/System/Battle/UIComp/TotalDamageDisplayer.cs
@@ -1,32 +1,96 @@ using System.Collections; using System.Collections.Generic; using Cysharp.Threading.Tasks; using UnityEngine; using UnityEngine.UI; using System.Threading; public class TotalDamageDisplayer : MonoBehaviour { public Image damageBackground; public Text textDamage; public Text textTotalDesc; //总伤害或者总治疗 public void SetDamage(bool isEnd, BattleDmgInfo dmgInfo) public UniTask task = default; private Coroutine hideCoroutine; private int hideVersion = 0; public void SetDamage(BattleDmgInfo dmgInfo) { // 先统一停止并清理此前的隐藏协程(如果有) ClearHideCoroutine(); if (!gameObject.activeInHierarchy) gameObject.SetActive(true); if (dmgInfo == null) { return; } if (dmgInfo.IsType(DamageType.Recovery)) { // 保持原有处理逻辑位置 } else if (dmgInfo.IsType(DamageType.Damage) || dmgInfo.IsType(DamageType.Realdamage)) { // 保持原有处理逻辑位置 } if (dmgInfo.isLastHit) { // 启动新的隐藏协程,先生成新的版本号以用于协程有效性校验 hideVersion++; int myVersion = hideVersion; var battleField = BattleManager.Instance.GetBattleField(dmgInfo.battleFieldGuid); float ms = 1000f / battleField.speedRatio; hideCoroutine = StartCoroutine(HideAfterDelayCoroutine(ms, myVersion)); task = default; } } protected void OnDisable() { ClearHideCoroutine(); hideVersion++; } protected void OnDestroy() { ClearHideCoroutine(); hideVersion++; } public void CancelHide() { ClearHideCoroutine(); hideVersion++; } private void ClearHideCoroutine() { if (hideCoroutine != null) { try { StopCoroutine(hideCoroutine); } catch { } hideCoroutine = null; } } private IEnumerator HideAfterDelayCoroutine(float secondsDelay, int version = 0) { yield return new WaitForSeconds(secondsDelay); if (version != 0 && version != hideVersion) yield break; if (this == null) yield break; if (gameObject != null) gameObject.SetActive(false); if (hideCoroutine != null) hideCoroutine = null; } } Main/System/MainLevel/MainBossEnterWin.cs
@@ -138,6 +138,7 @@ { var pack = new CB410_tagCMTurnFight(); pack.MapID = 2; // pack.FuncLineID = (uint)PlayerDatas.Instance.baseData.ExAttr1 + 1; GameNetSystem.Instance.SendInfo(pack); AutoFightModel.Instance.isPause = true; Main/Utility/ComponentExtersion.cs
@@ -315,6 +315,7 @@ } var sprite = UILoader.LoadSprite(folderName, iconName); if (null == sprite) return; _image.overrideSprite = sprite; }