159 lines
6.2 KiB
C#
159 lines
6.2 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using Sirenix.OdinInspector;
|
|
using SLSUtilities.General;
|
|
using SLSUtilities.FunctionalAnimation;
|
|
using UnityEngine;
|
|
|
|
namespace Cielonos.MainGame.Characters
|
|
{
|
|
/// <summary>
|
|
/// 动画子控制器基类。
|
|
/// 负责管理角色的核心 Animator、动作覆盖、打断状态以及受击时的骨骼抖动物理效果。
|
|
/// </summary>
|
|
public partial class AnimationSubcontrollerBase : SubcontrollerBase<CharacterBase>
|
|
{
|
|
#region Fields & Properties
|
|
|
|
[TitleGroup("Animator Settings", "底层状态机配置", Alignment = TitleAlignments.Centered)]
|
|
public Animator animator;
|
|
public AnimatorStateMapper mapper;
|
|
|
|
[TitleGroup("Functional Animation Submodules", "动作播放子模块", Alignment = TitleAlignments.Centered)]
|
|
public FuncAnimSubmodule fullBodyFuncAnimSm;
|
|
public FuncAnimSubmodule upperBodyFuncAnimSm;
|
|
|
|
[TitleGroup("State Flags & Intervals", "打断状态与控制标志", Alignment = TitleAlignments.Centered)]
|
|
public Dictionary<DisruptionType, bool> disruptionStatus;
|
|
public bool isDuringRootMotion;
|
|
public bool isDisablingMoveXZ;
|
|
public bool isDisablingMoveY;
|
|
public List<FuncAnimInterval> LastFrameIntervals { get; private set; }
|
|
public List<FuncAnimInterval> CurrentIntervals { get; private set; }
|
|
|
|
[TitleGroup("Callbacks", "事件回调注册表", Alignment = TitleAlignments.Centered)]
|
|
public Dictionary<string, Action<RuntimeFuncAnim>> registeredFunctions;
|
|
|
|
[TitleGroup("Bone Shake Settings", "受击骨骼微震动参数", Alignment = TitleAlignments.Centered)]
|
|
public BoneShakeController boneShake = new BoneShakeController();
|
|
|
|
#endregion
|
|
|
|
#region Initialization
|
|
|
|
public override void Initialize()
|
|
{
|
|
base.Initialize();
|
|
fullBodyFuncAnimSm = new FuncAnimSubmodule(this, "FullBodyAction");
|
|
upperBodyFuncAnimSm = new FuncAnimSubmodule(this, "UpperBodyAction");
|
|
registeredFunctions = new Dictionary<string, Action<RuntimeFuncAnim>>();
|
|
disruptionStatus = new Dictionary<DisruptionType, bool>()
|
|
{
|
|
{ DisruptionType.ForcedAction , false},
|
|
{ DisruptionType.ForcedExternal , false},
|
|
{ DisruptionType.NormalExternal, false },
|
|
{ DisruptionType.NormalAction, false },
|
|
{ DisruptionType.Movement, false }
|
|
};
|
|
LastFrameIntervals = new List<FuncAnimInterval>();
|
|
CurrentIntervals = new List<FuncAnimInterval>();
|
|
|
|
RegisterDefaultFunctions();
|
|
}
|
|
|
|
public virtual void RegisterDefaultFunctions()
|
|
{
|
|
// 在派生类中扩展具体的动作事件回调注册
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Unity Lifecycle (Update & LateUpdate)
|
|
|
|
protected virtual void Update()
|
|
{
|
|
if (owner.statusSm.isDead)
|
|
{
|
|
return;
|
|
}
|
|
|
|
fullBodyFuncAnimSm?.UpdateTime();
|
|
UpdateIntervalInfo();
|
|
}
|
|
|
|
protected virtual void LateUpdate()
|
|
{
|
|
fullBodyFuncAnimSm?.UpdateEvents();
|
|
|
|
// BoneShakeController 必须在 LateUpdate 中运行,
|
|
// 以确保在 Animator 更新完骨骼姿势后再叠加震动偏转
|
|
boneShake.LateUpdate(owner.selfTimeSm.DeltaTime);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Interval Evaluation
|
|
|
|
protected virtual void UpdateIntervalInfo()
|
|
{
|
|
if (fullBodyFuncAnimSm?.currentRuntimeFuncAnim == null)
|
|
{
|
|
isDuringRootMotion = false;
|
|
return;
|
|
}
|
|
|
|
RuntimeFuncAnim currentFuncAnim = fullBodyFuncAnimSm.currentRuntimeFuncAnim;
|
|
|
|
LastFrameIntervals = CurrentIntervals;
|
|
CurrentIntervals = currentFuncAnim.GetEnablingIntervals();
|
|
disruptionStatus[DisruptionType.NormalExternal] = CurrentIntervals.Any(interval => interval.intervalType == IntervalType.ExternalDisruption);
|
|
disruptionStatus[DisruptionType.NormalAction] = CurrentIntervals.Any(interval => interval.intervalType == IntervalType.ActionDisruption);
|
|
disruptionStatus[DisruptionType.Movement] = CurrentIntervals.Any(interval => interval.intervalType == IntervalType.MovementDisruption);
|
|
|
|
if (currentFuncAnim.HasIntervalType(IntervalType.ForcedActionDisruption))
|
|
{
|
|
disruptionStatus[DisruptionType.ForcedAction] = CurrentIntervals.Any(interval => interval.intervalType == IntervalType.ForcedActionDisruption);
|
|
}
|
|
else
|
|
{
|
|
disruptionStatus[DisruptionType.ForcedAction] = true;
|
|
}
|
|
|
|
if (currentFuncAnim.HasIntervalType(IntervalType.ForcedExternalDisruption))
|
|
{
|
|
disruptionStatus[DisruptionType.ForcedExternal] = CurrentIntervals.Any(interval => interval.intervalType == IntervalType.ForcedExternalDisruption);
|
|
}
|
|
else
|
|
{
|
|
disruptionStatus[DisruptionType.ForcedExternal] = true;
|
|
}
|
|
|
|
isDuringRootMotion = currentFuncAnim.funcAnimData.animInfo.useRootMotion &&
|
|
CurrentIntervals.Any(interval => interval.intervalType == IntervalType.RootMotion);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Hit Feedbacks
|
|
|
|
public virtual void PlayGetHitBoneShake(float intensity, Vector3 direction = default)
|
|
{
|
|
boneShake.ApplyShake(intensity, direction);
|
|
}
|
|
|
|
public virtual void PlayGetHitAnimation(string funcAnimName, out float animDuration,
|
|
Vector3 direction = default)
|
|
{
|
|
animDuration = 0.2f;
|
|
|
|
if (fullBodyFuncAnimSm.Play(funcAnimName))
|
|
{
|
|
FuncAnimInterval interval = fullBodyFuncAnimSm.currentData.Interval(IntervalType.MovementDisruption);
|
|
animDuration = MathExtensions.Average(interval.StartTime, interval.EndTime);
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
} |