Files
Cielonos/Assets/Scripts/MainGame/Characters/Base/Submodules/FuncAnimSubmodule.cs
SoulliesOfficial 6d7ebc5825 Passion & UI
2026-06-12 17:11:39 -04:00

300 lines
13 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System;
using System.Collections.Generic;
using System.Linq;
using Cielonos.MainGame.Characters;
using Sirenix.OdinInspector;
using SLSUtilities.FunctionalAnimation;
using UnityEngine;
namespace Cielonos.MainGame
{
/// <summary>
/// 角色Player/Enemy专属战斗动作子模块。
/// 继承通用播放器并实现 ISubmodule 接口,融合了战斗受击、硬直打断、预输入重置等复杂的动作决策机制。
/// </summary>
public partial class FuncAnimSubmodule : FuncAnimPlayer, ISubmodule<AnimationSubcontrollerBase>
{
/// <summary>本模块的所有者(动作子控制器)</summary>
public AnimationSubcontrollerBase Owner { get; set; }
// 兼容原 owner小写访问
private AnimationSubcontrollerBase animationSc => Owner;
private CharacterBase character => Owner.owner;
private Player player => character as Player;
private static readonly int ActionSpeed = Animator.StringToHash("ActionSpeed");
public FuncAnimSubmodule(AnimationSubcontrollerBase owner, string animatorLayerName)
: base(owner.owner, animatorLayerName)
{
this.Owner = owner;
}
/// <summary>
/// 注册战斗动作数据。如果是玩家控制的角色,还将注册动画剪辑到重载控制器中。
/// </summary>
public override void Add(FuncAnimData animation)
{
base.Add(animation);
if (player != null)
{
player.animationSc.SetOverride(animation.animInfo.stateName, animation.animationClip);
}
}
/// <summary>
/// 战斗特有:执行动作可播放性评估。
/// </summary>
public bool CheckPlayability(RuntimeFuncAnim oldFuncAnim, RuntimeFuncAnim newFuncAnim)
{
if (oldFuncAnim != null)
{
var oldData = oldFuncAnim.funcAnimData;
var newData = newFuncAnim.funcAnimData;
// 检测是否配置了特殊动作打断区间SpecifiedActionDisruption
FuncAnimInterval specifiedInterval = oldData.intervals.FirstOrDefault(interval =>
interval.intervalType == IntervalType.Custom && interval.intervalName == "SpecifiedActionDisruption");
if (specifiedInterval != null)
{
if (oldData.interactions.TryGetValue("SpecifiedActionDisruption", out List<string> interactions))
{
// 若新动作在当前动作的指定打断白名单内,只需判断是否处于该打断区间
if (interactions.Contains(newData.animInfo.animationName))
{
return animationSc.CurrentIntervals.Contains(specifiedInterval);
}
}
}
}
return CheckPlayability(newFuncAnim.funcAnimData.animInfo.disruptionType);
}
/// <summary>
/// 战斗特有:评估当前角色的硬直状态与打断类型。
/// </summary>
public bool CheckPlayability(DisruptionType disruptionType = DisruptionType.NormalAction)
{
if (character.statusSm.isDead) return false;
// 无前动作、死亡动作或强制动作Must拥有绝对执行权
if (currentRuntimeFuncAnim == null || disruptionType == DisruptionType.Death || disruptionType == DisruptionType.Must)
{
return true;
}
if (disruptionType == DisruptionType.ForcedAction)
{
return animationSc.disruptionStatus[DisruptionType.ForcedAction] ||
animationSc.disruptionStatus[DisruptionType.NormalAction] ||
animationSc.disruptionStatus[DisruptionType.Movement];
}
if (disruptionType == DisruptionType.NormalAction)
{
return animationSc.disruptionStatus[DisruptionType.NormalAction] || animationSc.disruptionStatus[DisruptionType.Movement];
}
if (disruptionType == DisruptionType.Movement)
{
return animationSc.disruptionStatus[DisruptionType.Movement];
}
throw new Exception("Invalid DisruptionType for checking playability.");
}
/// <summary>
/// 战斗特有:评估当前状态机内是否存在允许打断该动作的标记。
/// </summary>
public bool CheckDisruption(DisruptionType disruptionType)
{
return disruptionType switch
{
DisruptionType.None => false,
DisruptionType.Must or DisruptionType.Death => true,
_ => animationSc.disruptionStatus[disruptionType]
};
}
/// <summary>
/// 覆写播放核心:融入战斗专属的可播放性检测、前置打断清理及事件控制。
/// </summary>
public override bool Play(FuncAnimData funcAnimData, float animationSpeedMultiplier = 1f, float transitionDuration = 0.1f,
bool isNormalizedTransition = false, float normalizedStartTime = -1f, List<FuncAnimPayloadBase> runtimeStartEvents = null)
{
var newRtFuncAnim = new RuntimeFuncAnim(funcAnimData, Executor);
// 1. 战斗独有的前置条件与打断优先级检测
if (!CheckPlayability(currentRuntimeFuncAnim, newRtFuncAnim))
{
return false;
}
// 2. 如果存在正在播放的动作,且还未打断,执行战斗特有的清理
if (currentRuntimeFuncAnim != null && !currentRuntimeFuncAnim.isDisrupted)
{
ResetPlayerPreinput();
animationSc.disruptionStatus[DisruptionType.NormalExternal] = false;
animationSc.disruptionStatus[DisruptionType.NormalAction] = false;
animationSc.disruptionStatus[DisruptionType.Movement] = false;
}
// 3. 计算实际应用的速度缩放(受 affectsBySpeedMultiplier 标记约束)
float finalSpeedMultiplier = funcAnimData.animInfo.isAffectedBySpeedMultiplier ? animationSpeedMultiplier : 1f;
// 4. 委派底层物理播放器执行 CrossFade 过渡与启动
bool playSuccess = base.Play(funcAnimData, finalSpeedMultiplier, transitionDuration, isNormalizedTransition, normalizedStartTime, runtimeStartEvents);
if (playSuccess)
{
// 5. 重置运动阻断状态
animationSc.isDisablingMoveXZ = false;
animationSc.isDisablingMoveY = false;
// 6. 重置事件分发指针
currentRuntimeFuncAnim.dataAnimEventIndex = 0;
currentRuntimeFuncAnim.runtimeAnimEventIndex = 0;
currentRuntimeFuncAnim.SetUpdateUntilEventsStatus();
}
return playSuccess;
}
/// <summary>
/// 通过动作名停止播放动作(支持打断级别限制)。
/// </summary>
public bool Stop(string animationName, DisruptionType disruptionType = DisruptionType.Must, float transitionDuration = 0.1f)
{
if (currentRuntimeFuncAnim == null) return true;
if (currentRuntimeFuncAnim.funcAnimData.animInfo.animationName != animationName) return false;
return Stop(disruptionType, transitionDuration);
}
/// <summary>
/// 覆写停止动作:注入战斗受击与强制打断的校验逻辑。
/// </summary>
public bool Stop(DisruptionType disruptionType, float transitionDuration = 0.1f)
{
if (disruptionType == DisruptionType.None) return false;
if (currentRuntimeFuncAnim == null) return true;
// 1. 校验是否允许打断
if (disruptionType != DisruptionType.Death && disruptionType != DisruptionType.Must)
{
if (!CheckDisruption(disruptionType))
{
return false;
}
}
// 2. 清理预输入缓存
if (!currentRuntimeFuncAnim.isDisrupted)
{
ResetPlayerPreinput();
}
// 3. 调用底层物理播放器执行淡出到 Empty 状态
bool stopSuccess = base.Stop(transitionDuration);
if (stopSuccess)
{
// 4. 重置子控制器的全局打断标识
animationSc.disruptionStatus[DisruptionType.NormalExternal] = false;
animationSc.disruptionStatus[DisruptionType.NormalAction] = false;
animationSc.disruptionStatus[DisruptionType.Movement] = false;
}
return stopSuccess;
}
/// <summary>
/// 兼容通用物理停止(默认转换为 Must 强制打断)。
/// </summary>
public override bool Stop(float transitionDuration = 0.1f)
{
return Stop(DisruptionType.Must, transitionDuration);
}
/// <summary>
/// 驱动更新时间轴时钟(供 Subcontroller.Update 轮询)。
/// </summary>
public void UpdateTime()
{
Update(character.selfTimeSm.DeltaTime);
}
/// <summary>
/// 覆写时钟推进逻辑:包含动作自然播放完毕时,对输入缓存和打断标志的战斗退场清理。
/// </summary>
public override void Update(float deltaTime)
{
if (currentRuntimeFuncAnim == null)
{
currentPlaySpeedMultiplier = 1f;
return;
}
// 1. 推进播放时间
float step = deltaTime * currentData.animInfo.overridePlaySpeed * currentPlaySpeedMultiplier;
currentRuntimeFuncAnim.currentPlayTime += step;
currentRuntimeFuncAnim.currentTotalPlayTime += step;
float clipLength = currentClip != null ? currentClip.length : 0f;
// 2. 监测播放结束
if (currentPlayTime >= clipLength)
{
UpdateEvents();
if (currentClip != null && currentClip.isLooping)
{
// 循环复位
currentRuntimeFuncAnim.currentPlayTime = clipLength > 0f ? currentRuntimeFuncAnim.currentPlayTime % clipLength : 0f;
currentRuntimeFuncAnim.dataAnimEventIndex = 0;
currentRuntimeFuncAnim.runtimeAnimEventIndex = 0;
currentRuntimeFuncAnim.ResetUpdateUntilEventsStatus();
currentRuntimeFuncAnim.InvokeStartEvents();
}
else
{
// 非循环动作播放完毕:执行退场事件并注入战斗清理逻辑
if (!currentRuntimeFuncAnim.isDisrupted)
{
currentRuntimeFuncAnim.isDisrupted = true;
currentRuntimeFuncAnim.InvokeDisruptionEvents();
currentRuntimeFuncAnim.InvokeEndEvents();
ResetPlayerPreinput();
}
// 清空全局打断标志位
animationSc.disruptionStatus[DisruptionType.NormalExternal] = false;
animationSc.disruptionStatus[DisruptionType.NormalAction] = false;
animationSc.disruptionStatus[DisruptionType.Movement] = false;
currentRuntimeFuncAnim = null;
}
}
}
private void ResetPlayerPreinput() => player?.inputSc.preinputSubmodule.Reset();
}
public partial class FuncAnimSubmodule
{
/// <summary>
/// 获取经播放速度缩放后的指定区间类型的持续时间。
/// 主要提供给武器及连招系统判断前摇/判定区间长度。
/// </summary>
public float GetIntervalScaledDuration(IntervalType type)
{
if (currentData == null) return 0f;
FuncAnimInterval interval = currentData.intervals.Find(i => i.intervalType == type);
if (interval == null) return 0f;
float baseDuration = interval.EndTime - interval.StartTime;
float speed = currentData.animInfo.overridePlaySpeed * currentPlaySpeedMultiplier;
return speed > 0f ? baseDuration / speed : 0f;
}
}
}