lcy
2025-11-05 3b2a6bb9047cfce9f501593b3669a9c1af6c5df4
Main/System/Battle/Motion/MotionBase.cs
@@ -5,7 +5,7 @@
public class MotionBase
{
    public static float MotionTimeScale = 1f;
    public float MotionTimeScale = 1f;
    public static List<string> AttackMotionList = new List<string>
    {
        MotionName.attack.ToString().ToLower(),
@@ -23,12 +23,23 @@
    protected Spine.Skeleton skeleton;
    protected float defaultMixDuration = 0f;
    private Spine.TrackEntry currentTrack;
    private Dictionary<int, Spine.TrackEntry> activeSkillTracks = new Dictionary<int, Spine.TrackEntry>();
    // 子技能轨道池管理(在Init中初始化,不要在这里初始化)
    private Queue<int> availableSubTracks;
    private Dictionary<SkillBase, int> subSkillTrackMap = new Dictionary<SkillBase, int>();
    private SkeletonIllusionShadow illusionShadow;
    private bool playingSkillAnim = false;
    private float pauseTime = 0f;
    private bool isUnderControl = false;
    private float pauseTime = 0f;
    private float resumeTime = 0f;
    // 新增:累积暂停时长,用于对非动画路径(Time.time)进行稳定的时间修正
    private float pausedAccumulated = 0f;
    private float pauseStart = 0f;
    public virtual void Init(SkeletonAnimation skelAnim)
    {
@@ -46,6 +57,11 @@
        if (animState != null)
            animState.Data.DefaultMix = defaultMixDuration;
        // 初始化子技能轨道池
        availableSubTracks = new Queue<int>();
        for (int i = 1; i <= 8; i++)
            availableSubTracks.Enqueue(i);
        PlayAnimation(MotionName.idle, true);
        SetupAnimationHandlers();
        
@@ -56,6 +72,9 @@
    public virtual void Release()
    {
        trackEntryCallbacks.Clear();
        activeSkillTracks.Clear();
        availableSubTracks?.Clear();
        subSkillTrackMap.Clear();
        if (animState != null)
        {
            animState.Complete -= OnAnimationComplete;
@@ -102,6 +121,7 @@
            return null;
        }
        // 如果没有动画名称,使用无动画模式
        if (string.IsNullOrEmpty(skillConfig.SkillMotionName))
        {
            PlaySkillNoAnim(skillConfig, skillBase, onComplete, isSubSkill);
@@ -121,7 +141,6 @@
    private Spine.TrackEntry ExecuteSkillAnim(SkillConfig skillConfig, SkillBase skillBase, Action onComplete,
        Spine.Animation targetAnim, bool hasAnim, bool isSubSkill)
    {
        bool isPangdeSkill = 1003020 == skillConfig.SkillID;
        int loopCount = skillConfig.LoopCount;
@@ -129,11 +148,47 @@
        int frameCount = activeFrames.Length;
        float recoveryFrame = skillConfig.RecoveryFrames;
        // 轨道分配策略:主技能用 track 0,子技能从轨道池分配
        int trackIndex = 0;
        if (isSubSkill)
        {
            if (availableSubTracks != null && availableSubTracks.Count > 0)
            {
                trackIndex = availableSubTracks.Dequeue();
                subSkillTrackMap[skillBase] = trackIndex;
            }
            else
            {
                // 轨道池耗尽或未初始化,回退到无动画模式
                Debug.LogWarning($"子技能轨道池已满或未初始化,技能{skillConfig.SkillID}使用无动画模式");
                PlaySkillNoAnim(skillConfig, skillBase, onComplete, isSubSkill);
                return null;
            }
        }
        Spine.TrackEntry skillTrack = null;
        if (hasAnim)
        {
            skillTrack = animState.SetAnimation(0, targetAnim, false);
            currentTrack = skillTrack;
            skillTrack = animState.SetAnimation(trackIndex, targetAnim, false);
            if (null == skillTrack)
            {
                Debug.LogError($"技能 {skillConfig.SkillID} 动画设置失败");
                // 如果是子技能且分配了轨道,需要回收
                if (isSubSkill && subSkillTrackMap.ContainsKey(skillBase))
                {
                    if (availableSubTracks != null)
                        availableSubTracks.Enqueue(subSkillTrackMap[skillBase]);
                    subSkillTrackMap.Remove(skillBase);
                }
                return null;
            }
            // 只有主技能才更新 currentTrack
            if (!isSubSkill)
                currentTrack = skillTrack;
            activeSkillTracks[trackIndex] = skillTrack;
        }
        
        playingSkillAnim = true;
@@ -141,7 +196,12 @@
        int currentLoop = 0, triggerCount = 0, failCount = 0;
        bool beginTriggered = false, finalStarted = false, finalEnded = false, middleStarted = false;
        bool[] triggeredFrames = new bool[frameCount];
        float startTime = hasAnim ? 0 : Time.time;
        // 新增:记录技能开始时的 pausedAccumulated 基线,用于后续计算该技能自身的暂停时长
        float pausedAccumulatedAtStart = pausedAccumulated;
        // startTime 表示技能"本地逻辑时间"的起点(以 Time.time 为基准)
        float startTime = hasAnim ? 0f : Time.time;
        skillBase.OnSkillStart();
@@ -150,12 +210,30 @@
        {
            if (skillBase.IsFinished())
            {
                playingSkillAnim = false;
                // 清理并退出(保证状态一致)
                if (skillTrack != null && activeSkillTracks.ContainsKey(trackIndex))
                {
                    if (activeSkillTracks[trackIndex] == skillTrack)
                        activeSkillTracks.Remove(trackIndex);
                }
                // 回收子技能轨道
                if (isSubSkill && subSkillTrackMap.ContainsKey(skillBase))
                {
                    if (availableSubTracks != null)
                        availableSubTracks.Enqueue(subSkillTrackMap[skillBase]);
                    subSkillTrackMap.Remove(skillBase);
                }
                // 只有当没有其他活跃技能时才复位 playingSkillAnim
                if (activeSkillTracks.Count == 0)
                    playingSkillAnim = false;
                RemoveAction(frameHandler);
                return;
            }
            float trackTime = 0f; //hasAnim ? skillTrack.TrackTime * skillTrack.TimeScale : (Time.time - startTime) * MotionTimeScale;
            float trackTime = 0f;
            if (hasAnim)
            {
@@ -163,40 +241,65 @@
            }
            else
            {
                // 使用 pausedAccumulatedAtStart 来计算"这个技能自开始以来的暂停总时长"
                float adjustedTime = Time.time;
                if (pauseTime > 0f && resumeTime > pauseTime)
                {
                    startTime = startTime + (resumeTime - pauseTime);
                    pauseTime = 0f;
                    resumeTime = 0f;
                }
                trackTime = (adjustedTime - startTime) * MotionTimeScale;
                float thisSkillPaused = pausedAccumulated - pausedAccumulatedAtStart;
                if (thisSkillPaused < 0f) thisSkillPaused = 0f; // 保险防护
                // 逻辑运行时间 = 当前时间 - startTime - 本技能已暂停时长
                trackTime = (adjustedTime - startTime - thisSkillPaused) * MotionTimeScale;
            }
            float currentFrame = trackTime * BattleConst.skillMotionFps;
            if (hasAnim)
            {
                if (currentTrack != skillTrack)
                // 检查当前轨道是否被新技能覆盖
                if (!activeSkillTracks.ContainsKey(trackIndex) || activeSkillTracks[trackIndex] != skillTrack)
                {
                    Debug.LogError("技能动画被打断,强制结束 " + skillConfig.SkillID);
                    skillBase.ForceFinished();
                    // 清理并确保状态复位
                    RemoveAction(frameHandler);
                    playingSkillAnim = false;
                    // 回收子技能轨道
                    if (isSubSkill && subSkillTrackMap.ContainsKey(skillBase))
                    {
                        if (availableSubTracks != null)
                            availableSubTracks.Enqueue(subSkillTrackMap[skillBase]);
                        subSkillTrackMap.Remove(skillBase);
                    }
                    if (activeSkillTracks.Count == 0)
                        playingSkillAnim = false;
                    return;
                }
                if (skillTrack.TrackTime == 0) failCount++;
                if (failCount > 100)
                {
                    Debug.LogError("技能动画播放失败,强制结束 " + skillConfig.SkillID);
                    skillBase.ForceFinished();
                    RemoveAction(frameHandler);
                    playingSkillAnim = false;
                    if (activeSkillTracks.ContainsKey(trackIndex))
                        activeSkillTracks.Remove(trackIndex);
                    // 回收子技能轨道
                    if (isSubSkill && subSkillTrackMap.ContainsKey(skillBase))
                    {
                        if (availableSubTracks != null)
                            availableSubTracks.Enqueue(subSkillTrackMap[skillBase]);
                        subSkillTrackMap.Remove(skillBase);
                    }
                    if (activeSkillTracks.Count == 0)
                        playingSkillAnim = false;
                    return;
                }
            }
            // 各阶段回调(原有逻辑)
            if (!beginTriggered && currentFrame >= skillConfig.StartupFrames && currentLoop == 0)
            {
                beginTriggered = true;
@@ -231,9 +334,15 @@
                    if (BattleConst.skillMotionFps > 0)
                    {
                        if (hasAnim)
                        {
                            skillTrack.TrackTime = skillConfig.StartupFrames / BattleConst.skillMotionFps;
                        }
                        else
                        {
                            // 为下一 loop 重置 startTime,并且更新 pausedAccumulatedAtStart(以保持基线)
                            startTime = Time.time - (skillConfig.StartupFrames / BattleConst.skillMotionFps);
                            pausedAccumulatedAtStart = pausedAccumulated;
                        }
                    }
                    beginTriggered = false;
                }
@@ -254,10 +363,26 @@
                if (finalStarted && !finalEnded && currentFrame >= recoveryFrame)
                {
                    finalEnded = true;
                    if (!isSubSkill)
                    // 清理技能轨道
                    if (skillTrack != null && activeSkillTracks.ContainsKey(trackIndex))
                    {
                        playingSkillAnim = false;
                        if (activeSkillTracks[trackIndex] == skillTrack)
                            activeSkillTracks.Remove(trackIndex);
                    }
                    // 回收子技能轨道
                    if (isSubSkill && subSkillTrackMap.ContainsKey(skillBase))
                    {
                        if (availableSubTracks != null)
                            availableSubTracks.Enqueue(subSkillTrackMap[skillBase]);
                        subSkillTrackMap.Remove(skillBase);
                    }
                    // 只有当没有其他活跃技能时才复位 playingSkillAnim
                    if (activeSkillTracks.Count == 0)
                        playingSkillAnim = false;
                    RemoveAction(frameHandler);
                    onComplete?.Invoke();
                    skillBase.OnFinalFrameEnd();
@@ -305,7 +430,7 @@
    protected virtual void OnAnimationComplete(Spine.TrackEntry trackEntry)
    {
        if (trackEntry?.Animation?.Name == null) return;
        string animName = trackEntry.Animation.Name.ToLower();
        if (AttackMotionList.Contains(animName))
@@ -318,12 +443,33 @@
            OnHitAnimationComplete?.Invoke();
            PlayAnimation(MotionName.idle, true);
        }
        if (trackEntryCallbacks.TryGetValue(trackEntry, out var callback))
        {
            trackEntryCallbacks.Remove(trackEntry);
            callback?.Invoke();
        }
    }
    public void SetControledAnimation()
    {
        //  受到硬控的时候,保持受击动画的第三帧,直到控制结束 或者最后一击死亡,移除控制效果后,恢复到待机状态或者播放死亡动画
        //  这里是受到硬控时候 需要表现的动画
        var entry = PlayAnimation(MotionName.hit, false);
        float threeFrameTrackTime = 3f / BattleConst.skillMotionFps;
        entry.TrackTime = threeFrameTrackTime;
        entry.TimeScale = 0;
        isUnderControl = true;
    }
    public void CancelControledAnimation()
    {
        //  硬控结束,恢复动画播放
        isUnderControl = false;
        PlayAnimation(MotionName.idle, true);
    }
    public virtual void Run()
@@ -337,12 +483,25 @@
    public virtual void Pause()
    {
        if (skeletonAnim != null) skeletonAnim.timeScale = 0f;
        // 记录单次暂停开始时刻
        pauseStart = Time.time;
        // 兼容旧字段(保留但不再用于累积逻辑)
        pauseTime = Time.time;
    }
    public virtual void Resume()
    {
        if (skeletonAnim != null) skeletonAnim.timeScale = MotionTimeScale;
        // 累积暂停时长(如果曾记录过 pauseStart)
        if (pauseStart > 0f)
        {
            pausedAccumulated += (Time.time - pauseStart);
            pauseStart = 0f;
        }
        // 保持旧字段以兼容现有代码(但实际时间修正使用 pausedAccumulated)
        resumeTime = Time.time;
    }
@@ -350,6 +509,18 @@
    {
        trackEntryCallbacks.Clear();
        runningActions.Clear();
        activeSkillTracks.Clear();
        // 重置子技能轨道池
        if (availableSubTracks == null)
            availableSubTracks = new Queue<int>();
        else
            availableSubTracks.Clear();
        subSkillTrackMap.Clear();
        for (int i = 1; i <= 8; i++)
            availableSubTracks.Enqueue(i);
        playingSkillAnim = false;
        PlayAnimation(MotionName.idle, true);
    }
@@ -360,12 +531,12 @@
        if (skeletonAnim != null) skeletonAnim.timeScale = ratio;
    }
    public void ShowIllusionShadow(bool isVisible)
    public void ShowIllusionShadow(bool isVisible, Color color = default)
    {
        if (illusionShadow != null)
        {
            illusionShadow.SetSkeletonAnimation(skeletonAnim);
            illusionShadow.Show(isVisible);
            illusionShadow.Show(isVisible, color);
        }
    }
}