using System.Collections.Generic; using System; using UnityEngine; public class RecordPlayer { // 初始化之前允许的record action类型 private readonly static List allowedActionTypes = new List() { typeof(BattleStartAction), typeof(PreloadResAction), }; private List tempPreinitRecordActionList = new List(); protected BattleField battleField; private Queue recordActionQueue = new Queue(); protected RecordAction currentRecordAction; protected List immediatelyActionList = new List(); private bool isWaitingNextAction = false; private float waitTimer = 0f; private const float waitInterval = 0.2f; private float speedRatio = 1.5f; private bool isForceFinish = false; public bool IsForceFinish { get { return isForceFinish; } } private bool stepForcefinish = false; public void Init(BattleField _battleField) { Reload(_battleField); } public void Reload(BattleField _battleField) { Release(); stepForcefinish = false; battleField = _battleField; } public bool IsPlaying() { bool isPlaying = currentRecordAction != null || recordActionQueue.Count > 0 || immediatelyActionList.Count > 0; //BattleDebug.LogError("IsPlaying " + isPlaying + " currentRecordAction " + (currentRecordAction != null) + " recordActionQueue.Count " + recordActionQueue.Count + " immediatelyActionList.Count " + immediatelyActionList.Count); return isPlaying; } #if UNITY_EDITOR /// /// 卡死排查用:dump 出当前 RecordPlayer 内部所有在播 / 排队 / 即时播放 的 RecordAction 概要。 /// public string DumpPlayingState() { return DumpPlayingState(0); } // depth 用于控制递归深度,最多展开两层嵌套(父 inner player -> 子 SkillRecordAction -> 孙 inner player) public string DumpPlayingState(int depth) { string indent = new string(' ', depth * 4); var sb = new System.Text.StringBuilder(); sb.Append("current="); if (currentRecordAction == null) { sb.Append("null"); } else { AppendActionBrief(sb, currentRecordAction); } sb.Append(" queue.Count=").Append(recordActionQueue.Count); if (recordActionQueue.Count > 0) { sb.Append(" ["); int i = 0; foreach (var act in recordActionQueue) { if (i > 0) sb.Append(", "); sb.Append(act.GetType().Name); if (++i >= 5) { sb.Append(", ..."); break; } } sb.Append("]"); } sb.Append(" immediatelyList.Count=").Append(immediatelyActionList.Count); if (immediatelyActionList.Count > 0) { sb.Append(" ["); for (int i = 0; i < immediatelyActionList.Count && i < 5; i++) { if (i > 0) sb.Append(", "); var act = immediatelyActionList[i]; if (act == null) { sb.Append("null"); continue; } sb.Append(act.GetType().Name) .Append("(IsFin=").Append(act.IsFinished()) .Append(",CanStart=").Append(act.CanStartExecution()) .Append(",Waiting=").Append(act.isWaitingPlay) .Append(")"); } if (immediatelyActionList.Count > 5) sb.Append(", ..."); sb.Append("]"); } // 递归展开嵌套 SkillRecordAction 的内部状态(最多 2 层,避免无限递归) if (depth < 2) { if (currentRecordAction is SkillRecordAction sra && sra.skillBase != null) { sb.Append('\n').Append(indent).Append(" └ [current] skillId=") .Append(sra.skillBase.skillConfig?.SkillID ?? 0) .Append(" caster=").Append(sra.skillBase.tagUseSkillAttack?.ObjID ?? 0UL) .Append(" StateFlags=").Append(sra.skillBase.StateFlagsForDebug); // 进一步展开它自己的 inner player var innerPlayer = sra.GetInnerRecordPlayer(); if (innerPlayer != null && innerPlayer.IsPlaying()) { sb.Append('\n').Append(indent).Append(" innerRecordPlayer: ") .Append(innerPlayer.DumpPlayingState(depth + 1)); } } } return sb.ToString(); } private static void AppendActionBrief(System.Text.StringBuilder sb, RecordAction act) { sb.Append(act.GetType().Name) .Append(" IsFinished=").Append(act.IsFinished()) .Append(" IsActionCompleted=").Append(act.IsActionCompleted()); } #endif public void PlayRecord(RecordAction recordAction) { if (recordAction == null) return; if (!CheckRecordType(recordAction)) return; recordAction.actionOwner = this; // Debug.LogError("Enqueue record action " + recordAction.GetType() + " to queue"); if (isForceFinish || stepForcefinish) { recordAction.ForceFinish(); return; } recordActionQueue.Enqueue(recordAction); recordAction.AfterAddToQueue(); } public void PlayRecord(RecordAction recordAction, RecordAction waitingAnimeAction) { if (recordAction == null) return; if (!CheckRecordType(recordAction)) return; recordAction.actionOwner = this; // Debug.LogError("Enqueue record action " + recordAction.GetType() + " to queue"); if (isForceFinish || stepForcefinish) { recordAction.ForceFinish(); return; } recordAction.waitingAnimeAction = waitingAnimeAction; recordActionQueue.Enqueue(recordAction); recordAction.AfterAddToQueue(); } public void PlayRecord(List recordActions) { for (int i = 0; i < recordActions.Count; i++) { PlayRecord(recordActions[i]); } } public void InsertRecord(RecordAction recordAction, int position = 0) { if (recordAction == null) return; if (!CheckRecordType(recordAction)) return; recordAction.actionOwner = this; if (isForceFinish || stepForcefinish) { recordAction.ForceFinish(); return; } // Debug.LogError("Insert record action " + recordAction.GetType() + " at front of queue"); if (currentRecordAction != null) { Queue tempQueue = new Queue(); 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(); return; } immediatelyActionList.Add(recordAction); } public void ImmediatelyPlay(RecordAction recordAction, RecordAction waitingAnimeAction) { if (recordAction == null) return; recordAction.actionOwner = this; if (isForceFinish || stepForcefinish) { recordAction.ForceFinish(); return; } recordAction.waitingAnimeAction = waitingAnimeAction; immediatelyActionList.Add(recordAction); } // 增强版本:支持父子关系和WaitingPlay标记 public void ImmediatelyPlay(RecordAction recordAction, RecordAction parentAction, bool isWaitingPlay) { if (recordAction == null) return; recordAction.actionOwner = this; if (isForceFinish || stepForcefinish) { recordAction.ForceFinish(); return; } // Debug.LogError("insert recordAction ImmediatelyPlay: " + recordAction.GetType().Name + " parentAction: " + (parentAction != null ? parentAction.GetType().Name : "null") + " isWaitingPlay: " + isWaitingPlay); // 设置父子关系 if (parentAction != null) { recordAction.SetParentAction(parentAction); parentAction.AddChildAction(recordAction); } // 设置WaitingPlay标记 recordAction.SetWaitingPlay(isWaitingPlay); immediatelyActionList.Add(recordAction); } public bool IsActionCompleted() { bool ret = true; foreach (var imac in immediatelyActionList) { if (!imac.IsActionCompleted()) { ret = false; break; } } if (ret == false) { return false; } if (currentRecordAction != null && !currentRecordAction.IsActionCompleted()) { return false; } return ret; } protected void ImmediatelyPlayRun() { if (immediatelyActionList.Count > 0) { List removeIndexList = new List(); // 关键修复:从前往后遍历,确保按加入顺序执行 for (int i = 0; i < immediatelyActionList.Count; i++) { var action = immediatelyActionList[i]; if (action == null) { removeIndexList.Add(i); continue; } // 检查是否可以开始执行(WaitingPlay条件检查) if (!action.CanStartExecution()) { // 修复:即便 CanStartExecution 返回 false,只要 action 已经 IsFinished, // 也必须从列表里移除,否则它会永久占位导致 IsPlaying() 一直为 true, // 让父技能卡在 ownRecordAction.innerRecordPlayer.IsPlaying() 的分支上。 // (典型场景:一个 caster 连续释放两个技能,第一个已结束但 caster 仍被 // 记为 IsCastingSkill,SkillBase.CanStartExecution 返回 false。) if (action.IsFinished()) { removeIndexList.Add(i); } continue; } // 检查同级的前置兄弟节点:如果有相同父节点且索引更小的节点还在执行,则等待 bool shouldWaitForSibling = false; if (action.isWaitingPlay && action.parentAction != null) { for (int j = 0; j < i; j++) { var prevAction = immediatelyActionList[j]; if (prevAction != null && prevAction.parentAction == action.parentAction) { // 找到同级前置节点,如果它还在执行中,则当前节点需要等待 if (!prevAction.IsFinished() && action.NeedWaitSibling()) { shouldWaitForSibling = true; // BattleDebug.LogError($"RecordPlayer: {action.GetType().Name} 等待同级前置节点 {prevAction.GetType().Name} 完成"); break; } } } } if (shouldWaitForSibling) { continue; } if (!action.IsFinished()) { // 卡死检测:如果immediately action运行时间过长,强制结束 if (action.IsStuck()) { BattleDebug.LogError($"RecordPlayer: ImmediatelyAction {action.GetType().Name} (ID:{action.actionID}) 运行 {action.GetRunDuration():F1}s 超时,强制结束!"); action.ForceFinish(); removeIndexList.Add(i); continue; } if (action.waitingAnimeAction != null) { if (!action.waitingAnimeAction.IsActionCompleted()) { continue; } } action.Run(); continue; } removeIndexList.Add(i); } // 从后往前删除,确保索引不会因为删除而错位 for (int i = removeIndexList.Count - 1; i >= 0; i--) { int index = removeIndexList[i]; if (index < 0 || index >= immediatelyActionList.Count) continue; immediatelyActionList.RemoveAt(index); } } } private bool CheckRecordType(RecordAction recordAction) { if (battleField.IsInit) { return true; } else { if (!allowedActionTypes.Contains(recordAction.GetType())) { tempPreinitRecordActionList.Add(recordAction); return false; } else { return true; } } } public virtual void Run() { if (battleField != null) { if (!battleField.IsInit) { return; } if (tempPreinitRecordActionList.Count > 0) { foreach (var action in tempPreinitRecordActionList) { PlayRecord(action); } tempPreinitRecordActionList.Clear(); } } ImmediatelyPlayRun(); // 等待下一个action if (isWaitingNextAction) { waitTimer += Time.deltaTime * speedRatio; if (waitTimer >= waitInterval) { isWaitingNextAction = false; waitTimer = 0f; } else { return; } } if (currentRecordAction == null) { if (recordActionQueue.Count <= 0) { return; } } if (currentRecordAction != null && !currentRecordAction.IsFinished()) { // 卡死检测:如果currentRecordAction运行时间过长,强制结束 if (currentRecordAction.IsStuck()) { BattleDebug.LogError($"RecordPlayer: CurrentAction {currentRecordAction.GetType().Name} (ID:{currentRecordAction.actionID}) 运行 {currentRecordAction.GetRunDuration():F1}s 超时,强制结束!"); currentRecordAction.ForceFinish(); currentRecordAction = null; return; } if (currentRecordAction.waitingAnimeAction != null) { if (!currentRecordAction.waitingAnimeAction.IsActionCompleted()) { return; } } currentRecordAction.Run(); return; } if (currentRecordAction != null && currentRecordAction.IsFinished()) { var guid = currentRecordAction.GetBattleFieldGuid(); // BattleDebug.LogError("record action " + currentRecordAction.GetType() + " play finished"); currentRecordAction = null; isWaitingNextAction = true; waitTimer = 0f; EventBroadcast.Instance.Broadcast(EventName.RECORDPLAYER_END, guid); return; } if (currentRecordAction == null) { if (recordActionQueue.Count > 0) { currentRecordAction = recordActionQueue.Dequeue(); #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()); } } } } // 先预留 感觉用的上 public virtual void ResumeGame() { } public virtual void PauseGame() { } public void HaveRest() { ForceFinish(); } public void ClearAllRecordAction() { currentRecordAction?.ForceFinish(); currentRecordAction = null; recordActionQueue.Clear(); immediatelyActionList.Clear(); } public void EnableForceFinish(bool enable) { stepForcefinish = enable; } public void ForceFinish() { isForceFinish = true; for (int i = immediatelyActionList.Count - 1; i >= 0; i--) { var action = immediatelyActionList[i]; action.ForceFinish(); immediatelyActionList.Remove(action); } while (currentRecordAction != null) { var temp = currentRecordAction; currentRecordAction = null; temp.ForceFinish(); } while (recordActionQueue.Count > 0) { recordActionQueue.Dequeue().ForceFinish(); } } public void Release() { if (null != currentRecordAction) { currentRecordAction.ForceFinish(); } currentRecordAction = null; recordActionQueue.Clear(); immediatelyActionList.Clear(); isForceFinish = false; stepForcefinish = false; } public void SetSpeedRatio(float ratio) { speedRatio = ratio; } }