Main/Core/NetworkPackage/CustomServerPack/CustomB421ActionPack.cs
File was deleted Main/Core/NetworkPackage/CustomServerPack/CustomB421ActionPack.cs.meta
File was deleted Main/Core/NetworkPackage/CustomServerPack/CustomHB426CombinePack.cs
@@ -1,3 +1,4 @@ using System; using System.Collections.Generic; using System.Linq; using UnityEngine; @@ -100,8 +101,6 @@ Dictionary<int, GameNetPackBasic> indexDict = new Dictionary<int, GameNetPackBasic>(); Dictionary<uint, HB427_tagSCUseSkill> skillDict = new Dictionary<uint, HB427_tagSCUseSkill>(); for (int i = 0; i < b421SeriesPackList.Count; i++) { var pack = b421SeriesPackList[i]; @@ -137,42 +136,7 @@ } else { if (pack is HB427_tagSCUseSkill skillPack) { // 处理技能之间的链接关系 if (skillPack.RelatedSkillID > 0) { // 如果是被 HB426_tagSCTurnFightTag 包裹的第一个技能包 并且前面没有CustomHB426CombinePack // 则需要把CustomHB426CombinePack加入skillPack的subSkillCombinePackList里 // 同时 需要把indexDict里删掉对应的subSkillCombinePackList // subSkillList删掉对应skillPack,但是skillPack的parentSkill不变 skillDict.TryGetValue(skillPack.RelatedSkillID, out var parentSkill); if (parentSkill != null && skillPack.BattleType == 4)//4=子技能 { parentSkill.subSkillList.Add(skillPack); skillPack.parentSkill = parentSkill; } indexDict.Add(i, pack); } else { indexDict.Add(i, pack); } if (skillDict.ContainsKey(skillPack.SkillID)) { skillDict[skillPack.SkillID] = skillPack; } else { skillDict.Add(skillPack.SkillID, skillPack); } } else { indexDict.Add(i, pack); } indexDict.Add(i, pack); } } @@ -195,70 +159,6 @@ // 如果是嵌套的包 加入之后 调整i i = cbPack.toIndex; } } } } for (int i = 0; i < b421SeriesPackList.Count; i++) { if (indexDict.TryGetValue(i, out var pack)) { if (pack is CustomHB426CombinePack cbPack) { HB427_tagSCUseSkill skillPack = cbPack.GetMainHB427SkillPack(); if (null == skillPack) { continue; } // 如果是子技能 if (skillPack.isSubSkill) { // 让别人来处理 continue; } else { // 处理子技能 if (skillPack.subSkillList.Count > 0) { var parentSkill = skillPack; List<HB427_tagSCUseSkill> toRemoveSubSkills = new List<HB427_tagSCUseSkill>(); foreach (var subSkill in parentSkill.subSkillList) { CustomHB426CombinePack innerCBPack = null; if (cbPack.IsInnerCBPackContainsSkill(subSkill, ref innerCBPack)) { if (cbPack.GetMainHB427SkillPack() == subSkill) { parentSkill.subSkillList.Remove(subSkill); Debug.LogError("子技能不能是主技能: " + subSkill.SkillID); continue; } subSkill.parentCombinePack = innerCBPack; cbPack.packList.Remove(innerCBPack); toRemoveSubSkills.Add(subSkill); parentSkill.subSkillCombinePackList.Add(innerCBPack); indexDict.Remove(innerCBPack.fromIndex); } } foreach (var subSkill in toRemoveSubSkills) { parentSkill.subSkillList.Remove(subSkill); } } else { // 主技能没有子技能 直接跳过 continue; } } } else if (pack is HB427_tagSCUseSkill skillPack) { // Debug.LogError("落单的技能"); } } } @@ -316,7 +216,7 @@ } return false; } public void Distribute() public void Distribute(RecordAction parentAction = null) { BattleField battleField = BattleManager.Instance.GetBattleField(guid); @@ -329,7 +229,14 @@ var skillAction = CreateSkillAction(); if (null != skillAction) { battleField.PlayRecord(skillAction); if (parentAction != null) { parentAction.GetInnerRecordPlayer().PlayRecord(skillAction); } else { battleField.PlayRecord(skillAction); } } else { @@ -434,6 +341,8 @@ return null; } HB427_tagSCUseSkill skill = packList[0] as HB427_tagSCUseSkill; packList.RemoveAt(0); if (null == skill) { @@ -467,5 +376,42 @@ return pack; } public bool NeedWaiting() { bool needWaiting = false; HB427_tagSCUseSkill hB427_TagSCUseSkill = GetMainHB427SkillPack(); for (int i = 0; i < packList.Count; i++) { var pack = packList[i]; if (pack is HB427_tagSCUseSkill skillPack && skillPack != hB427_TagSCUseSkill) { SkillConfig ssc = SkillConfig.Get((int)skillPack.SkillID); if (!string.IsNullOrEmpty(ssc.SkillMotionName)) { needWaiting = true; break; } } else if (pack is HB422_tagMCTurnFightObjDead dead) { needWaiting = true; break; } else if (pack is CustomHB426CombinePack combinePack) { if (combinePack.NeedWaiting()) { needWaiting = true; break; } } } return needWaiting; } #endif } Main/Core/NetworkPackage/CustomServerPack/CustomHB427_tagSCUseSkill.cs
@@ -9,39 +9,6 @@ // 裸露在外的技能不考虑子技能 跟 parentSkill ( CustomHB426CombinePack ) //-------------------------------------------// public HashSet<HB427_tagSCUseSkill> subSkillList { get; set; } = new HashSet<HB427_tagSCUseSkill>(); public HashSet<CustomHB426CombinePack> subSkillCombinePackList { get; set; } = new HashSet<CustomHB426CombinePack>(); public HB427_tagSCUseSkill parentSkill { get; set; } = null; public CustomHB426CombinePack parentCombinePack { get; set; } = null; public bool isSubSkill { get { return parentSkill != null; } } public partial class tagSCUseSkillHurt { public bool isChangedRawAttackType = false; } Main/Core/NetworkPackage/DTCFile/ServerPack/HB4_FightDefine/DTCB422_tagMCTurnFightObjDead.cs
@@ -15,7 +15,11 @@ // 由技能去通知战场死亡 battleField.OnObjsDead(deadList) #if UNITY_EDITOR BattleDebug.LogError("编辑器下的死亡测试"); battleField?.OnObjsDead(new List<BattleDeadPack>(){ new BattleDeadPack(){ deadPack = vNetData}}); RecordAction rc = battleField?.OnObjsDead(new List<BattleDeadPack>(){ new BattleDeadPack(){ deadPack = vNetData}}); if (null != rc) { battleField?.recordPlayer.ImmediatelyPlay(rc); } #endif if (!vNetPack.commonMark) battleField?.DistributeNextPackage(); Main/Core/NetworkPackage/DTCFile/ServerPack/HB4_FightDefine/DTCB430_tagSCTurnFightReport.cs
@@ -4,189 +4,51 @@ using System.Text; using System.Collections.Generic; using System.Linq; // B4 30 查看战报结果 #tagSCTurnFightReportRet /// <summary> /// B4 30 战报结果处理类 /// 负责解析服务器返回的战报数据包,并分发到战场系统 /// </summary> public class DTCB430_tagSCTurnFightReport : DtcBasic { private static byte[] vCmdBytes = new byte[2]; private bool canAddPack = false; static byte[] vCmdBytes = new byte[2]; bool canAddPack = false; /// <summary> /// 战报数据包处理主入口 /// 解析战报字节流,提取所有战斗包,并按顺序分发到战场 /// </summary> public override void Done(GameNetPackBasic vNetPack) { base.Done(vNetPack); HB430_tagSCTurnFightReport vNetData = vNetPack as HB430_tagSCTurnFightReport; // 战报结果 string guid = UIHelper.ServerStringTrim(vNetData.GUID); canAddPack = false; Debug.Log("战斗时序 B430 开始处理战斗 " + Time.time); //约定 B430 内容(小包1长度WORD + 包1 + 小包2长度WORD + 包2) //约定第一个包是B424,先发过来的过滤报错通知 try { int vReadIndex = 0; byte[] vPackBytes; int vLeavingLeng = 0; int vBodyLeng = 0; int vTotalLeng = vNetData.reportBytes.Length; List<GameNetPackBasic> vPackList = new List<GameNetPackBasic>(); while (vReadIndex < vTotalLeng) { vLeavingLeng = vTotalLeng - vReadIndex; if (vLeavingLeng < 4) { //包头至少需要4字节 Debug.LogError("DTCB430_tagSCTurnFightReport: vLeavingLeng < 2 解包失败"); break; } //约定小封包的长度 vBodyLeng = BitConverter.ToInt16(vNetData.reportBytes, vReadIndex); if (vBodyLeng > vLeavingLeng)// 未完整的包 报错 { Debug.LogError("DTCB430_tagSCTurnFightReport: vBodyLeng > vLeavingLeng解包失败"); break; } vPackBytes = new byte[vBodyLeng]; Array.Copy(vNetData.reportBytes, vReadIndex + 2, vPackBytes, 0, vBodyLeng); Array.Copy(vPackBytes, 0, vCmdBytes, 0, 2); var cmd = (ushort)((ushort)(vCmdBytes[0] << 8) + vCmdBytes[1]); bool isRegist = false; // 未注册封包处理 if (PackageRegedit.Contain(cmd)) { GameNetPackBasic npk = PackageRegedit.TransPack(ServerType.B430, cmd, vPackBytes); if (npk != null) { if (!FilterBeforeB424(npk)) { npk.socketType = ServerType.B430; vPackList.Add(npk); BattleManager.Instance.PushPackUID(guid, npk.packUID); } isRegist = true; } } vReadIndex += 2 + vBodyLeng; // 未注册封包处理 if (!isRegist) { #if UNITY_EDITOR PackageRegedit.TransPack(ServerType.B430, cmd, vPackBytes); #endif } } #if UNITY_EDITOR // 解析所有vPackList里的每个字段(深度)并且输出到Application.dataPath + "/../BattleReport/PackageDetailAnalysis_时间戳.txt文件里 string originPack = string.Empty; BattleField battleField = BattleManager.Instance.GetBattleField(guid); for (int i = 0; i < vPackList.Count; i++) { var pack = vPackList[i]; if (pack is HB427_tagSCUseSkill skill) { string heroName = skill.ObjID.ToString(); if (battleField != null) { var battleObj = battleField.battleObjMgr.GetBattleObject((int)skill.ObjID); if (battleObj != null && battleObj.teamHero != null) { heroName = battleObj.teamHero.name; } } string skillName = SkillConfig.Get((int)skill.SkillID)?.SkillName ?? "Unknown"; originPack += $"[{pack.packUID}] HB427_tagSCUseSkill - ObjID:{skill.ObjID} HeroName:{heroName} SkillID:{skill.SkillID} SkillName:{skillName}\n"; } else if (pack is HB426_tagSCTurnFightTag tag) { string signText = tag.Sign == 0 ? "Start" : tag.Sign == 1 ? "End" : "Unknown"; originPack += $"[{pack.packUID}] HB426_tagSCTurnFightTag - Tag:{tag.Tag} Sign:{tag.Sign}({signText})\n"; } else if (pack is HB422_tagMCTurnFightObjDead deadPack) { string heroName = deadPack.ObjID.ToString(); if (battleField != null) { var battleObj = battleField.battleObjMgr.GetBattleObject((int)deadPack.ObjID); if (battleObj != null && battleObj.teamHero != null) { heroName = battleObj.teamHero.name; } } originPack += $"[{pack.packUID}] HB422_tagMCTurnFightObjDead - ObjID:{deadPack.ObjID} HeroName:{heroName}\n"; } else { originPack += $"[{pack.packUID}] {pack.GetType().Name}\n"; } } #endif // 1. 解析战报字节流,提取所有包 List<GameNetPackBasic> vPackList = ParseReportBytes(vNetData.reportBytes, guid); #if UNITY_EDITOR DebugingBuffStatus(vPackList); // 2. 保存解析前的包详情(调试用) SavePackageDetailBeforeAnalysis(vPackList, guid); #endif #if UNITY_EDITOR #region Start Print Before Pack List Detail if (Launch.Instance.isOpenSkillLogFile) { try { string detailAnalysis = PrintPackageDetailAnalysis(vPackList, guid); string filePath = Application.dataPath + "/../BattleReport/PackageBeforeDetailAnalysis_" + DateTime.Now.ToString("yyyyMMdd_HHmmss") + ".txt"; System.IO.Directory.CreateDirectory(System.IO.Path.GetDirectoryName(filePath)); System.IO.File.WriteAllText(filePath, detailAnalysis); Debug.Log("包详细分析已保存到: " + filePath); } catch (Exception e) { Debug.LogError("保存包详细分析失败: " + e.Message); } } #endregion #endif // 3. 分析并组合包队列 vPackList = AnalysisPackQueueAndDistribute(guid, vPackList); #if UNITY_EDITOR #region Start Print Pack List Detail if (Launch.Instance.isOpenSkillLogFile) { try { string detailAnalysis = PrintPackageDetailAnalysis(vPackList, guid); string filePath = Application.dataPath + "/../BattleReport/PackageAfterDetailAnalysis_" + DateTime.Now.ToString("yyyyMMdd_HHmmss") + ".txt"; System.IO.Directory.CreateDirectory(System.IO.Path.GetDirectoryName(filePath)); System.IO.File.WriteAllText(filePath, detailAnalysis); Debug.Log("包详细分析已保存到: " + filePath); } catch (Exception e) { Debug.LogError("保存包详细分析失败: " + e.Message); } } #endregion // 4. 保存解析后的包详情(调试用) SavePackageDetailAfterAnalysis(vPackList, guid); #endif for (int i = 0; i < vPackList.Count; i++) { BattleManager.Instance.PushPackage(guid, vPackList[i]); } // 5. 将包推送到战场队列 PushPackagesToBattle(guid, vPackList); // 6. 通知战场开始分发包 canAddPack = false; BattleManager.Instance.DistributeNextReportPackage(guid); } @@ -196,9 +58,135 @@ } } /// <summary> /// 解析战报字节流,提取所有战斗数据包 /// 约定:B430 内容格式为(小包1长度WORD + 包1 + 小包2长度WORD + 包2...) /// </summary> private List<GameNetPackBasic> ParseReportBytes(byte[] reportBytes, string guid) { List<GameNetPackBasic> vPackList = new List<GameNetPackBasic>(); int vReadIndex = 0; int vTotalLeng = reportBytes.Length; while (vReadIndex < vTotalLeng) { int vLeavingLeng = vTotalLeng - vReadIndex; // 包头至少需要4字节 if (vLeavingLeng < 4) { Debug.LogError("DTCB430_tagSCTurnFightReport: vLeavingLeng < 2 解包失败"); break; } // 读取包体长度 int vBodyLeng = BitConverter.ToInt16(reportBytes, vReadIndex); if (vBodyLeng > vLeavingLeng) { Debug.LogError("DTCB430_tagSCTurnFightReport: vBodyLeng > vLeavingLeng解包失败"); break; } // 提取包体数据 byte[] vPackBytes = new byte[vBodyLeng]; Array.Copy(reportBytes, vReadIndex + 2, vPackBytes, 0, vBodyLeng); // 解析包类型命令 Array.Copy(vPackBytes, 0, vCmdBytes, 0, 2); var cmd = (ushort)((ushort)(vCmdBytes[0] << 8) + vCmdBytes[1]); // 转换为游戏包对象 if (PackageRegedit.Contain(cmd)) { GameNetPackBasic npk = PackageRegedit.TransPack(ServerType.B430, cmd, vPackBytes); if (npk != null && !FilterBeforeB424(npk)) { npk.socketType = ServerType.B430; vPackList.Add(npk); BattleManager.Instance.PushPackUID(guid, npk.packUID); } } #if UNITY_EDITOR else { PackageRegedit.TransPack(ServerType.B430, cmd, vPackBytes); } #endif vReadIndex += 2 + vBodyLeng; } return vPackList; } /// <summary> /// 保存解析前的包详情到文件(仅在编辑器且开启日志时) /// </summary> private void SavePackageDetailBeforeAnalysis(List<GameNetPackBasic> vPackList, string guid) { #if UNITY_EDITOR if (Launch.Instance.isOpenSkillLogFile) { try { string detailAnalysis = PrintPackageDetailAnalysis(vPackList, guid); string filePath = Application.dataPath + "/../BattleReport/PackageBeforeDetailAnalysis_" + DateTime.Now.ToString("yyyyMMdd_HHmmss") + ".txt"; System.IO.Directory.CreateDirectory(System.IO.Path.GetDirectoryName(filePath)); System.IO.File.WriteAllText(filePath, detailAnalysis); Debug.Log("包详细分析已保存到: " + filePath); } catch (Exception e) { Debug.LogError("保存包详细分析失败: " + e.Message); } } DebugingBuffStatus(vPackList); #endif } /// <summary> /// 保存解析后的包详情到文件(仅在编辑器且开启日志时) /// </summary> private void SavePackageDetailAfterAnalysis(List<GameNetPackBasic> vPackList, string guid) { #if UNITY_EDITOR if (Launch.Instance.isOpenSkillLogFile) { try { string detailAnalysis = PrintPackageDetailAnalysis(vPackList, guid); string filePath = Application.dataPath + "/../BattleReport/PackageAfterDetailAnalysis_" + DateTime.Now.ToString("yyyyMMdd_HHmmss") + ".txt"; System.IO.Directory.CreateDirectory(System.IO.Path.GetDirectoryName(filePath)); System.IO.File.WriteAllText(filePath, detailAnalysis); Debug.Log("包详细分析已保存到: " + filePath); } catch (Exception e) { Debug.LogError("保存包详细分析失败: " + e.Message); } } #endif } /// <summary> /// 将所有包推送到战场管理器队列 /// </summary> private void PushPackagesToBattle(string guid, List<GameNetPackBasic> vPackList) { for (int i = 0; i < vPackList.Count; i++) { BattleManager.Instance.PushPackage(guid, vPackList[i]); } } #if UNITY_EDITOR /// <summary> /// 调试输出Buff状态变更日志到文件 /// 包括Buff刷新和删除的详细信息,按英雄分组 /// </summary> private void DebugingBuffStatus(List<GameNetPackBasic> vPackList) { if (!Launch.Instance.isOpenSkillLogFile) @@ -322,9 +310,12 @@ System.IO.Directory.CreateDirectory(System.IO.Path.GetDirectoryName(filePath)); System.IO.File.WriteAllText(filePath, debugInfo); } #endif /// <summary> /// 打印包列表的详细信息,支持递归展开子包 /// 用于调试战斗包的层级结构和内容 /// </summary> private string PrintPackListDetail(List<GameNetPackBasic> packList, int indent, string guid = "") { string result = string.Empty; @@ -412,39 +403,6 @@ result += $"{indentStr} [{j}] ExTarget - ObjID:{hurtEx.ObjID} Name:{targetExName} HurtHP:{hurtEx.HurtHP} AttackTypes:{hurtEx.AttackTypes}\n"; } } // 打印子技能列表 if (skill.subSkillList != null && skill.subSkillList.Count > 0) { result += $"{indentStr} SubSkills ({skill.subSkillList.Count}):\n"; int j = 0; foreach (var subSkill in skill.subSkillList) { string subSkillName = SkillConfig.Get((int)subSkill.SkillID)?.SkillName ?? "Unknown"; result += $"{indentStr} [{j}] SubSkill - SkillID:{subSkill.SkillID} SkillName:{subSkillName} RelatedSkillID:{subSkill.RelatedSkillID}\n"; j++; } } // 打印子技能CombinePack列表 if (skill.subSkillCombinePackList != null && skill.subSkillCombinePackList.Count > 0) { result += $"{indentStr} SubSkillCombinePacks ({skill.subSkillCombinePackList.Count}):\n"; int j = 0; foreach (var subCombinePack in skill.subSkillCombinePackList) { var subMainSkill = subCombinePack.GetMainHB427SkillPack(); string subMainSkillName = subMainSkill != null ? (SkillConfig.Get((int)subMainSkill.SkillID)?.SkillName ?? "Unknown") : "N/A"; result += $"{indentStr} [{j}] SubCombinePack - Tag:{subCombinePack.startTag?.Tag} MainSkill:{subMainSkill?.SkillID} SkillName:{subMainSkillName} PackCount:{subCombinePack.packList.Count}\n"; // 递归打印子CombinePack内部 if (subCombinePack.packList.Count > 0) { result += PrintPackListDetail(subCombinePack.packList, indent + 3, guid); } j++; } } } else if (pack is CustomHB426CombinePack combinePack) { @@ -493,7 +451,10 @@ return result; } //约定第一个包是B424,先发过来的过滤报错通知 /// <summary> /// 过滤B424初始化包之前的所有包 /// 约定第一个包必须是B424,之前的包都是错误数据需要过滤 /// </summary> bool FilterBeforeB424(GameNetPackBasic npk) { if (npk is HB424_tagSCTurnFightInit) @@ -510,7 +471,10 @@ return false; } /// <summary> /// 分析并重组包队列 /// 将服务器原始包列表转换为客户端可执行的战斗包结构 /// </summary> protected List<GameNetPackBasic> AnalysisPackQueueAndDistribute(string guid, List<GameNetPackBasic> vPackList) { // 建议前端做一个防范机制:当连续多次请求得到空的战斗片段封包时(不包含B425标记的开始跟结束封包,即开始跟中间没有任何封包),强制自动帮玩家回城休息, @@ -518,72 +482,21 @@ // 为防止死循环,可强制回城休息,让玩家重新点击关卡战斗或挑战boss, // 正常情况下在战锤足够时理论上都可以一直循环刷怪,如果连续多次没有战斗片段封包,比如限制个连续10次以内,就可以理解为异常了 // const int MaxContinousEmptyCount = 10; // 连续空包最大次数 List<GameNetPackBasic> packQueueSnapshot = new List<GameNetPackBasic>(vPackList); List<GameNetPackBasic> newPackList = BattleManager.ParseBattlePackList(guid, packQueueSnapshot); // List<GameNetPackBasic> newPackList = new List<GameNetPackBasic>(); // HashSet<int> skipIndexes = new HashSet<int>(); // // 这里已经是按照Dequeue的顺序了 // for (int i = 0; i < packQueueSnapshot.Count; i++) // { // if (skipIndexes.Contains(i)) continue; // GameNetPackBasic pack = packQueueSnapshot[i]; // // 碰到B421 截断 往下收集b421里的全部内容 // if (pack is HB421_tagMCTurnFightObjAction) // { // HB421_tagMCTurnFightObjAction b421Pack = pack as HB421_tagMCTurnFightObjAction; // List<GameNetPackBasic> b421PackList = new List<GameNetPackBasic>(); // i++; // 跳过当前的B421包 // // 收集所有非B421包,直到遇到下一个B421或队列结束 // for (; i < packQueueSnapshot.Count; i++) // { // GameNetPackBasic nextPack = packQueueSnapshot[i]; // if (nextPack is HB421_tagMCTurnFightObjAction) // { // i--; // 回退一个位置,留给外层循环处理 // break; // } // else // { // b421PackList.Add(nextPack); // skipIndexes.Add(i); // 标记已被合包 // } // } // // 合并所有相关包 // CustomB421ActionPack actionPack = CustomB421ActionPack.CreateB421ActionPack(guid, b421PackList); // newPackList.Add(actionPack); // } // else // { // newPackList.Add(pack); // } // } List<GameNetPackBasic> returnList = new List<GameNetPackBasic>(); for (int i = 0; i < newPackList.Count; i++) { var pack = newPackList[i]; returnList.Add(pack); returnList.Add(newPackList[i]); } return returnList; } #if UNITY_EDITOR /// <summary> /// 深度解析包列表中的所有字段 /// 深度解析战报包列表的所有字段 /// 生成详细的包内容分析报告,包括所有字段值 /// </summary> private string PrintPackageDetailAnalysis(List<GameNetPackBasic> packList, string guid) { @@ -608,6 +521,7 @@ /// <summary> /// 递归打印对象的所有字段和属性 /// 支持基本类型、数组、集合、自定义类型等 /// </summary> private void PrintObjectDetail(StringBuilder sb, object obj, int indent, BattleField battleField, HashSet<object> visitedObjects) { @@ -787,7 +701,8 @@ } /// <summary> /// 判断是否为简单类型(直接输出值) /// 判断类型是否为简单类型(基本类型、字符串、枚举等) /// 简单类型可以直接输出值,不需要递归展开 /// </summary> private bool IsSimpleType(Type type) { @@ -799,7 +714,8 @@ } /// <summary> /// 判断是否应该跳过该类型(Unity和System库的复杂类型) /// 判断类型是否应该跳过解析 /// Unity和System库的复杂类型会被跳过以提高性能 /// </summary> private bool ShouldSkipType(Type type) { @@ -822,7 +738,8 @@ } /// <summary> /// 获取缩进字符串 /// 生成指定缩进级别的空格字符串 /// 用于格式化输出的层级结构 /// </summary> private string GetIndent(int indent) { Main/System/Battle/BattleField/BattleField.cs
@@ -510,8 +510,9 @@ } } public virtual void OnObjsDead(List<BattleDeadPack> deadPackList, RecordAction causingRecordAction = null) public virtual DeathRecordAction OnObjsDead(List<BattleDeadPack> deadPackList, RecordAction _causingRecordAction = null) { DeathRecordAction deathRecordAction = null; if (deadPackList.Count > 0) { // 过滤掉正在处理死亡的角色,避免重复处理 @@ -546,19 +547,12 @@ // 只处理有效的死亡消息 if (validDeadList.Count > 0) { DeathRecordAction recordAction = new DeathRecordAction(this, validDeadList, causingRecordAction); // 如果有导致死亡的技能,将DeathRecordAction作为其子节点,并设置为WaitingPlay if (causingRecordAction != null) { recordPlayer.ImmediatelyPlay(recordAction, causingRecordAction, true); } else { recordPlayer.ImmediatelyPlay(recordAction); } DeathRecordAction recordAction = new DeathRecordAction(this, validDeadList, _causingRecordAction); deathRecordAction = recordAction; } } return deathRecordAction; } public virtual void OnObjReborn(uint objId) Main/System/Battle/BattleField/RecordActions/DeathRecordAction.cs
@@ -15,8 +15,6 @@ protected Dictionary<int, bool> dropStateDict = new Dictionary<int, bool>(); protected RecordAction causingRecordAction = null; protected bool hasDeathTriggerSkill = false; // 标记是否已经分发了死亡后的包 @@ -26,8 +24,16 @@ : base(RecordActionType.Death, _battleField, null) { deadPackList = _deadPackList; causingRecordAction = _causingRecordAction; CheckHasDeathTriggerSkill(); for (int i = 0; i < deadPackList.Count; i++) { BattleObject battleObject = battleField.battleObjMgr.GetBattleObject((int)deadPackList[i].deadPack.ObjID); Debug.LogError($"DeathRecordAction: 初始化死亡动作,死亡对象名字={battleObject?.teamHero.name}, hasDeathTriggerSkill={deadPackList[i].deadTriggerSkill != null}"); } SetParentAction(_causingRecordAction); } protected void CheckHasDeathTriggerSkill() @@ -45,6 +51,8 @@ public override void Run() { base.Run(); // 该死的正常死 // 有技能的则按照顺序播放死亡技能 后再正常死 if (isFinish) @@ -70,7 +78,7 @@ // 使用ImmediatelyPlay并设置WaitingPlay=true,可以让死亡技能等待导致死亡的技能完成 // 如果DeathRecordAction有父节点(导致死亡的技能),则等待那个父节点 // 否则等待DeathRecordAction本身 battleField.recordPlayer.ImmediatelyPlay(skillAction, causingRecordAction == null ? this : causingRecordAction, true); battleField.recordPlayer.ImmediatelyPlay(skillAction, parentAction == null ? this : parentAction, true); } } else @@ -137,9 +145,9 @@ } // 确保在innerRecordPlayer中执行死亡后的包 if (hasDistributedPacksAfterDeath && causingRecordAction != null) if (hasDistributedPacksAfterDeath && parentAction != null) { var innerPlayer = causingRecordAction.GetInnerRecordPlayer(); var innerPlayer = parentAction.GetInnerRecordPlayer(); if (innerPlayer != null && innerPlayer.IsPlaying()) { innerPlayer.Run(); @@ -163,16 +171,38 @@ return () => true; } PerformDrop(deadObj); bool playDeath = false; bool isComplete = false; deadObj.OnDeath(() => { isComplete = true; }, withoutAnime); // 如果没有释放技能 则直接死亡 if (!battleField.IsCastingSkill(deadObj.ObjID)) { PerformDrop(deadObj); deadObj.OnDeath(() => { isComplete = true; }, withoutAnime); playDeath = true; } return () => { // 还没播放死亡 并且没释放其他技能 if (!playDeath && !battleField.IsCastingSkill(deadObj.ObjID)) { PerformDrop(deadObj); deadObj.OnDeath(() => { isComplete = true; }, withoutAnime); playDeath = true; } if (deadObj.isReborning) { isComplete = true; @@ -247,8 +277,6 @@ { if (deadPack.packListAfterDeath != null && deadPack.packListAfterDeath.Count > 0) { BattleDebug.LogError($"DeathRecordAction.DistributePacksAfterDeath: 开始分发死亡后的包,共 {deadPack.packListAfterDeath.Count} 个包"); foreach (var pack in deadPack.packListAfterDeath) { // 获取包的类型和UID用于调试 @@ -260,33 +288,37 @@ packUID = (ulong)packUIDField.GetValue(pack); } BattleDebug.LogError($"DeathRecordAction: 分发死亡后的包 - Type: {packType}, UID: {packUID}, causingRecordAction: {causingRecordAction?.GetType().Name}"); // 特殊处理 CustomHB426CombinePack:使用其自己的 Distribute 方法 if (pack is CustomHB426CombinePack combinePack) { BattleDebug.LogError($"DeathRecordAction: 死亡后的包是 CustomHB426CombinePack,使用其 Distribute 方法"); combinePack.Distribute(); combinePack.Distribute(parentAction); } // 特殊处理 HB427_tagSCUseSkill:创建技能包并分发 else if (pack is HB427_tagSCUseSkill skillPack) { BattleDebug.LogError($"DeathRecordAction: 死亡后的包是 HB427_tagSCUseSkill,创建 SkillRecordAction"); var skillAction = CustomHB426CombinePack.CreateSkillAction(battleField.guid, new List<GameNetPackBasic>() { skillPack }); if (skillAction != null) { battleField.PlayRecord(skillAction); if (parentAction != null) { parentAction.GetInnerRecordPlayer().PlayRecord(skillAction); } else { battleField.PlayRecord(skillAction); } } } else { // 【使用 causingRecordAction 或 battleField.recordPlayer】 // 原因:死亡后的包应该回到导致死亡的技能所在的上下文 // 如果有 causingRecordAction(导致死亡的技能),则分发到它的 innerRecordPlayer // 如果有 parentAction(导致死亡的技能),则分发到它的 innerRecordPlayer // 否则分发到 battleField.recordPlayer(顶层) if (causingRecordAction != null) if (parentAction != null) { PackageRegeditEx.DistributeToRecordAction(pack, causingRecordAction); PackageRegeditEx.DistributeToRecordAction(pack, parentAction); } else { @@ -295,6 +327,9 @@ } } } // 分发完成后清理列表,防止重复分发和内存泄漏 deadPack.packListAfterDeath.Clear(); } } } @@ -333,4 +368,30 @@ { return HasDeathTriggerSkill(); } #if UNITY_EDITOR /// <summary> /// 首次运行时打印日志(仅编辑器) /// 打印死亡对象的名字 /// </summary> protected override void PrintFirstRunLog() { if (deadPackList != null && deadPackList.Count > 0) { string deadNames = ""; foreach (var deadPack in deadPackList) { BattleObject deadObj = battleField.battleObjMgr.GetBattleObject((int)deadPack.deadPack.ObjID); string name = deadObj?.teamHero?.name ?? "Unknown"; deadNames += name + ","; } deadNames = deadNames.TrimEnd(','); Debug.LogError($"[DeathRecordAction首次Run] 死亡对象:{deadNames} 数量:{deadPackList.Count}"); } else { base.PrintFirstRunLog(); } } #endif } Main/System/Battle/BattleField/RecordActions/RebornRecordAction.cs
@@ -15,16 +15,30 @@ private Sequence tweenSeq = DOTween.Sequence(); private List<BattleObject> rebornObjs = new List<BattleObject>(); public RebornRecordAction(BattleField _battleField, BattleObject rebornObj, Action _callback) : base(RecordActionType.Reborn, _battleField, rebornObj) public RebornRecordAction(BattleField _battleField, List<BattleObject> _rebornObjs, Action _callback) : base(RecordActionType.Reborn, _battleField, null) { if (_rebornObjs != null) { foreach (var obj in _rebornObjs) { if (obj != null) { rebornObjs.Add(obj); } } } actionCallback = _callback; } public override void Run() { base.Run(); if (isFinish) return; @@ -45,23 +59,45 @@ // 做一个从透明到恢复的渐变的同时 播放特效 public void PlayRebornEffect() { // 播放复活特效 battleField.battleEffectMgr.PlayEffect(battleObject, BattleConst.RebornEffectID, battleObject.heroRectTrans, battleObject.Camp, battleObject.teamHero.modelScale); // 渐变 battleObject.motionBase.skeletonAnim.skeleton.A = 0f; if (rebornObjs.Count == 0) { actionCallback?.Invoke(); actionCallback = null; isActionCompleted = true; isFinish = true; return; } battleField.battleTweenMgr.OnKillTween(tweenSeq); tweenSeq = DOTween.Sequence(); tweenSeq.Append(DOVirtual.Float(0f, 1f, tweenDuration / battleField.speedRatio, value => foreach (var bo in rebornObjs) { battleObject.motionBase.skeletonAnim.skeleton.A = value; })); var battleObj = bo; Sequence sequence = DOTween.Sequence(); // 播放复活特效 battleField.battleEffectMgr.PlayEffect(battleObj, BattleConst.RebornEffectID, battleObj.heroRectTrans, battleObj.Camp, battleObj.teamHero.modelScale); // 渐变 battleObj.motionBase.skeletonAnim.skeleton.A = 0f; sequence.Append(DOVirtual.Float(0f, 1f, tweenDuration / battleField.speedRatio, value => { battleObj.motionBase.skeletonAnim.skeleton.A = value; })); tweenSeq.Join(sequence); } tweenSeq.Append(DOVirtual.DelayedCall(keepDuration / battleField.speedRatio, () => { battleObject?.AfterReborn(); foreach (var bo in rebornObjs) { var battleObj = bo; battleObj?.AfterReborn(); } actionCallback?.Invoke(); actionCallback = null; isActionCompleted = true; // 标记动作完成 @@ -81,9 +117,12 @@ battleField.battleTweenMgr.OnKillTween(tweenSeq); battleObject.motionBase.skeletonAnim.skeleton.A = 1f; battleObject?.AfterReborn(); foreach (var bo in rebornObjs) { var battleObj = bo; battleObj.motionBase.skeletonAnim.skeleton.A = 1f; battleObj.AfterReborn(); } actionCallback?.Invoke(); Main/System/Battle/BattleField/RecordActions/SkillRecordAction.cs
@@ -3,7 +3,12 @@ public class SkillRecordAction : RecordAction { protected SkillBase skillBase; #if UNITY_EDITOR public #else protected #endif SkillBase skillBase; public HB427_tagSCUseSkill hB427_TagSCUseSkill; @@ -28,6 +33,13 @@ skillBase.SetParentRecordAction(this); } } public override void AfterAddToQueue() { base.AfterAddToQueue(); skillBase?.AfterAddToQueue(); } public override bool IsNeedWaiting() { if (skillBase == null) @@ -92,7 +104,6 @@ return base.CanStartExecution(); } public override void Run() { base.Run(); @@ -121,4 +132,25 @@ isCast = true; } } #if UNITY_EDITOR /// <summary> /// 首次运行时打印日志(仅编辑器) /// 打印施法者名字、技能ID和技能名字 /// </summary> protected override void PrintFirstRunLog() { if (skillBase != null && skillBase.caster != null) { string casterName = skillBase.caster.teamHero?.name ?? "Unknown"; int skillId = skillBase.skillConfig?.SkillID ?? 0; string skillName = skillBase.skillConfig?.SkillName ?? "Unknown"; Debug.LogError($"[SkillRecordAction首次Run] 施法者:{casterName} 技能ID:{skillId} 技能名:{skillName}"); } else { base.PrintFirstRunLog(); } } #endif } Main/System/Battle/BattleManager.cs
@@ -192,10 +192,6 @@ { temp += " pack type is " + pack.GetType().Name + " tag is " + (b426Pack.startTag != null ? b426Pack.startTag.Tag : "null") + "\n"; } else if (pack is CustomB421ActionPack b421Pack) { temp += " pack type is " + pack.GetType().Name + " guid is " + b421Pack.guid + "\n"; } else { temp += " pack type is " + pack.GetType().Name + "\n"; @@ -336,10 +332,6 @@ if (pack is CustomHB426CombinePack combinePack) { combinePack.Distribute(); } else if (pack is CustomB421ActionPack actionPack) { actionPack.Distribute(); } else { @@ -482,10 +474,6 @@ if (pack is CustomHB426CombinePack combinePack) { combinePack.Distribute(); } else if (pack is CustomB421ActionPack actionPack) { actionPack.Distribute(); } else { Main/System/Battle/BattleObject/BattleObject.cs
@@ -315,8 +315,9 @@ return true; } public virtual void Hurt(BattleHurtParam battleHurtParam, RecordAction causingRecordAction = null) public virtual RecordAction Hurt(BattleHurtParam battleHurtParam, RecordAction _causingRecordAction = null) { RecordAction recordAction = null; bool isLastHit = battleHurtParam.hitIndex >= battleHurtParam.skillConfig.DamageDivide.Length - 1; bool firstHit = battleHurtParam.hitIndex == 0; @@ -356,7 +357,7 @@ { PushDropItems(battleHurtParam.battleDrops); } battleField.OnObjsDead(new List<BattleDeadPack>() { battleHurtParam.deadPack }, causingRecordAction); recordAction = battleField.OnObjsDead(new List<BattleDeadPack>() { battleHurtParam.deadPack }, _causingRecordAction); } else @@ -378,6 +379,8 @@ // } } return recordAction; } /// <summary> @@ -493,7 +496,7 @@ } // 复活action public void OnReborn(HB427_tagSCUseSkill.tagSCUseSkillHurt vNetData, bool reviveSelf = false) public void OnReborn(HB427_tagSCUseSkill.tagSCUseSkillHurt vNetData, bool reviveSelf = false, RecordAction parentAction = null) { isReborning = true; heroGo.SetActive(true); @@ -501,20 +504,7 @@ heroRectTrans.anchoredPosition = Vector2.zero; motionBase.skeletonAnim.skeleton.A = 0f; motionBase.skeletonAnim.LateUpdate(); RebornRecordAction recordAction = new RebornRecordAction(battleField, this, () => { battleField.OnObjReborn((uint)ObjID); teamHero.curHp = GeneralDefine.GetFactValue(vNetData.CurHP, vNetData.CurHPEx); // Debug.LogError("OnReborn " + teamHero.curHp); teamHero.isDead = false; }); // 【使用 BattleField.recordPlayer】 // 原因:复活是角色的独立行为,不是技能内部产生的 // 复活可能是被动触发的(如死亡后服务器发来的复活包),应该由主RecordPlayer管理 // 使用InsertRecord可以插到队列最前面,保证复活表现的优先级 battleField.recordPlayer.InsertRecord(recordAction); } public void AfterReborn() Main/System/Battle/BattleTweenMgr.cs
@@ -56,9 +56,9 @@ tween.Play(); } public void OnKillTween(Tween tween) public void OnKillTween(Tween tween, bool complete = false) { tween?.Kill(); tween?.Kill(complete); if (tween != null && tweenList.Contains(tween)) { tweenList.Remove(tween); Main/System/Battle/BattleUtility.cs
@@ -259,7 +259,7 @@ BattleObject target = caster.battleField.battleObjMgr.GetBattleObject((int)hurt.ObjID); if (target == null) { Debug.LogError("GetMainTargetPositionNum 找不到目标 ObjId : " + hurt.ObjID); Debug.LogError("GetMainTargetPositionNum 找不到目标 ObjId : " + hurt.ObjID + " skill id " + skillConfig.SkillID); continue; } else @@ -281,7 +281,7 @@ } else { Debug.LogError("GetMainTargetPositionNum 找不到跟随主技能的目标"); Debug.LogError("GetMainTargetPositionNum 找不到跟随主技能的目标 " + skillConfig.SkillID); returnIndex = 0; } break; Main/System/Battle/RecordPlayer/RecordAction.cs
@@ -1,7 +1,7 @@ using System.Collections.Generic; using System; using UnityEngine; public class RecordAction { @@ -13,6 +13,12 @@ protected bool isFinish = false; protected bool isRunOnce = false; #if UNITY_EDITOR // 是否已打印首次运行日志(仅用于调试) private bool hasLoggedFirstRun = true; #endif // ===== 父子关系和等待机制 ===== // 父RecordAction引用(如果是通过其他RecordAction内部触发的) @@ -37,6 +43,8 @@ // 3. 不会干扰BattleField主RecordPlayer的播放队列 protected RecordPlayer innerRecordPlayer; public RecordPlayer actionOwner { get; set; } public RecordAction(RecordActionType _actionType, BattleField _battleField, BattleObject _battleObj) { actionType = _actionType; @@ -50,6 +58,11 @@ innerRecordPlayer = new RecordPlayer(); innerRecordPlayer.Init(_battleField); } } public virtual void AfterAddToQueue() { } public virtual void Played() @@ -162,12 +175,32 @@ public virtual void Run() { #if UNITY_EDITOR // 首次运行时打印调试信息 if (!hasLoggedFirstRun) { hasLoggedFirstRun = true; PrintFirstRunLog(); } #endif // 先运行内部RecordPlayer // 原因:内部产生的RecordAction需要先执行,确保内部逻辑按正确顺序播放 // 例如:技能内部产生的Buff添加、子技能等都由innerRecordPlayer管理 innerRecordPlayer?.Run(); } #if UNITY_EDITOR /// <summary> /// 首次运行时打印日志(仅编辑器) /// 子类可以重写此方法自定义打印内容 /// </summary> protected virtual void PrintFirstRunLog() { Debug.LogError($"[RecordAction首次Run] {GetType().Name}"); } #endif public virtual void ForceFinish() { isFinish = true; Main/System/Battle/RecordPlayer/RecordPlayer.cs
@@ -43,6 +43,7 @@ public void PlayRecord(RecordAction recordAction) { if (recordAction == null) return; recordAction.actionOwner = this; // Debug.LogError("Enqueue record action " + recordAction.GetType() + " to queue"); if (isForceFinish || stepForcefinish) { @@ -50,6 +51,7 @@ return; } recordActionQueue.Enqueue(recordAction); recordAction.AfterAddToQueue(); } public void PlayRecord(List<RecordAction> recordActions) @@ -60,9 +62,10 @@ } } public void InsertRecord(RecordAction recordAction) public void InsertRecord(RecordAction recordAction, int position = 0) { if (recordAction == null) return; recordAction.actionOwner = this; if (isForceFinish || stepForcefinish) { recordAction.ForceFinish(); @@ -73,22 +76,33 @@ if (currentRecordAction != null) { Queue<RecordAction> tempQueue = new Queue<RecordAction>(); for (int i = 0; i < position && recordActionQueue.Count > 0; i++) { tempQueue.Enqueue(recordActionQueue.Dequeue()); } tempQueue.Enqueue(recordAction); while (recordActionQueue.Count > 0) { tempQueue.Enqueue(recordActionQueue.Dequeue()); } recordActionQueue = tempQueue; } else { recordActionQueue.Enqueue(recordAction); } recordAction.AfterAddToQueue(); } public void ImmediatelyPlay(RecordAction recordAction) { if (recordAction == null) return; recordAction.actionOwner = this; if (isForceFinish || stepForcefinish) { recordAction.ForceFinish(); @@ -101,6 +115,7 @@ public void ImmediatelyPlay(RecordAction recordAction, RecordAction parentAction, bool isWaitingPlay) { if (recordAction == null) return; recordAction.actionOwner = this; if (isForceFinish || stepForcefinish) { recordAction.ForceFinish(); @@ -238,7 +253,16 @@ if (recordActionQueue.Count > 0) { currentRecordAction = recordActionQueue.Dequeue(); // BattleDebug.LogError("play record action " + currentRecordAction.GetType()); #if UNITY_EDITOR // if (currentRecordAction is SkillRecordAction skillRecordAction) // { // Debug.LogError("RecordPlayer Run Play SkillRecordAction skillname " + skillRecordAction.skillBase.skillConfig.SkillName + " caster " + skillRecordAction.skillBase.caster.teamHero.name); // } // else #endif { BattleDebug.LogError("play record action " + currentRecordAction.GetType()); } } } } Main/System/Battle/Skill/RebornSkill.cs
@@ -14,17 +14,45 @@ } public override void AfterAddToQueue() { base.AfterAddToQueue(); } protected override void OnHitTargets(int _hitIndex, List<HB427_tagSCUseSkill.tagSCUseSkillHurt> hitList) { List<BattleObject> rebornTargets = new List<BattleObject>(); for (int i = 0; i < hitList.Count; i++) { var hurt = hitList[i]; BattleObject battleObject = battleField.battleObjMgr.GetBattleObject((int)hitList[i].ObjID); if (battleObject != null) { battleObject.OnReborn(hurt, hurt.ObjID == caster.teamHero.ObjID); battleObject.OnReborn(hurt, hurt.ObjID == caster.teamHero.ObjID, parentRecordAction); rebornTargets.Add(battleObject); } } RebornRecordAction recordAction = new RebornRecordAction(battleField, rebornTargets, () => { for (int i = 0; i < tagUseSkillAttack.HurtList.Length; i++) { var hurt = tagUseSkillAttack.HurtList[i]; BattleObject battleObject = battleField.battleObjMgr.GetBattleObject((int)tagUseSkillAttack.HurtList[i].ObjID); if (battleObject != null) { battleField.OnObjReborn(tagUseSkillAttack.HurtList[i].ObjID); battleObject.teamHero.curHp = GeneralDefine.GetFactValue(hurt.CurHP, hurt.CurHPEx); // Debug.LogError("OnReborn " + teamHero.curHp); battleObject.teamHero.isDead = false; } } }); battleField.recordPlayer.ImmediatelyPlay(recordAction); bool vValue = true; @@ -80,20 +108,5 @@ return false; } public override void OnMiddleFrameStart(int times) { skillEffect.OnMiddleFrameStart(times); // 修复:添加空值检查 } // 技能中摇结束回调:通知技能效果处理中摇结束 public override void OnMiddleFrameEnd(int times, int hitIndex) { skillEffect.OnMiddleFrameEnd(times, hitIndex); // 修复:添加空值检查 } public override void OnFinalFrameEnd() { skillEffect?.OnFinalFrameEnd(); // 修复:添加空值检查 } }