Files
Cielonos/Assets/Scripts/MainGame/Characters/Base/Subcontrollers/Animation/AnimationSubcontrollerBase.cs

246 lines
9.0 KiB
C#
Raw Normal View History

2025-11-25 08:19:33 -05:00
using System;
using System.Collections.Generic;
using System.Linq;
2026-01-03 18:19:39 -05:00
using Sirenix.OdinInspector;
2026-02-13 09:22:11 -05:00
using SLSUtilities.General;
2025-11-25 08:19:33 -05:00
using SLSUtilities.FunctionalAnimation;
using UnityEngine;
using UnityEngine.Serialization;
2025-12-17 04:19:38 -05:00
using Random = UnityEngine.Random;
2025-11-25 08:19:33 -05:00
namespace Cielonos.MainGame.Characters
{
2026-06-05 04:21:00 -04:00
/// <summary>
/// 动画子控制器基类。
/// 负责管理角色的核心 Animator、动作覆盖、打断状态以及受击时的骨骼抖动物理效果。
/// </summary>
2025-11-25 08:19:33 -05:00
public partial class AnimationSubcontrollerBase : SubcontrollerBase<CharacterBase>
{
2026-06-05 04:21:00 -04:00
#region Fields & Properties
[TitleGroup("Animator Settings", "底层状态机配置", Alignment = TitleAlignments.Centered)]
2025-11-25 08:19:33 -05:00
public Animator animator;
2026-01-03 18:19:39 -05:00
public AnimatorStateMapper mapper;
2026-06-05 04:21:00 -04:00
[TitleGroup("Functional Animation Submodules", "动作播放子模块", Alignment = TitleAlignments.Centered)]
public FuncAnimSubmodule fullBodyFuncAnimSm;
public FuncAnimSubmodule upperBodyFuncAnimSm;
[TitleGroup("State Flags & Intervals", "打断状态与控制标志", Alignment = TitleAlignments.Centered)]
2025-11-25 08:19:33 -05:00
public Dictionary<DisruptionType, bool> disruptionStatus;
public bool isDuringRootMotion;
public bool isDisablingMoveXZ;
public bool isDisablingMoveY;
2026-06-05 04:21:00 -04:00
public List<FuncAnimInterval> LastFrameIntervals { get; private set; }
public List<FuncAnimInterval> CurrentIntervals { get; private set; }
[TitleGroup("Callbacks", "事件回调注册表", Alignment = TitleAlignments.Centered)]
2025-11-25 08:19:33 -05:00
public Dictionary<string, Action<RuntimeFuncAnim>> registeredFunctions;
2026-06-05 04:21:00 -04:00
#region Bone Shake Fields
[TitleGroup("Bone Shake Settings", "受击骨骼震动物理参数", Alignment = TitleAlignments.Centered)]
[Tooltip("刚度:越大回弹越快。对于重型机甲,建议 150-300轻型无人机建议 80-150。")]
public float stiffness = 200f;
[Tooltip("阻尼:越大停得越快,建议 10-20。")]
public float damping = 15f;
[Tooltip("冲击力倍率:将受击强度转化为震动初速度")]
public float impactForceMultiplier = 500f;
[Tooltip("参与受击震动的测试骨骼列表")]
public List<Transform> testShakeBones;
private List<BoneShakeState> activeShakes = new List<BoneShakeState>();
private class BoneShakeState
{
public Transform bone;
public Vector3 shakeAxis;
public float currentAngle = 0f;
public float velocity = 0f;
}
#endregion
#endregion
#region Initialization
2025-11-25 08:19:33 -05:00
public override void Initialize()
{
base.Initialize();
2026-06-05 04:21:00 -04:00
fullBodyFuncAnimSm = new FuncAnimSubmodule(this, "FullBodyAction");
upperBodyFuncAnimSm = new FuncAnimSubmodule(this, "UpperBodyAction");
2025-11-25 08:19:33 -05:00
registeredFunctions = new Dictionary<string, Action<RuntimeFuncAnim>>();
disruptionStatus = new Dictionary<DisruptionType, bool>()
{
2026-01-03 18:19:39 -05:00
{ DisruptionType.ForcedAction , false},
{ DisruptionType.ForcedExternal , false},
2025-11-25 08:19:33 -05:00
{ DisruptionType.NormalExternal, false },
{ DisruptionType.NormalAction, false },
{ DisruptionType.Movement, false }
};
2026-06-05 04:21:00 -04:00
LastFrameIntervals = new List<FuncAnimInterval>();
CurrentIntervals = new List<FuncAnimInterval>();
2025-11-25 08:19:33 -05:00
RegisterDefaultFunctions();
}
2026-06-05 04:21:00 -04:00
public virtual void RegisterDefaultFunctions()
{
// 在派生类中扩展具体的动作事件回调注册
}
#endregion
#region Unity Lifecycle (Update & LateUpdate)
2025-11-25 08:19:33 -05:00
protected virtual void Update()
{
2025-12-08 05:27:53 -05:00
if (owner.statusSm.isDead)
{
return;
}
2025-11-25 08:19:33 -05:00
fullBodyFuncAnimSm?.UpdateTime();
UpdateIntervalInfo();
}
protected virtual void LateUpdate()
{
fullBodyFuncAnimSm?.UpdateEvents();
2025-12-17 04:19:38 -05:00
BoneShakeLateUpdate();
2025-11-25 08:19:33 -05:00
}
2026-06-05 04:21:00 -04:00
#endregion
#region Interval Evaluation
2025-11-25 08:19:33 -05:00
protected virtual void UpdateIntervalInfo()
{
if (fullBodyFuncAnimSm?.currentRuntimeFuncAnim == null)
{
isDuringRootMotion = false;
return;
}
2026-01-12 03:22:16 -05:00
RuntimeFuncAnim currentFuncAnim = fullBodyFuncAnimSm.currentRuntimeFuncAnim;
2026-06-05 04:21:00 -04:00
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);
2026-01-12 03:22:16 -05:00
if (currentFuncAnim.HasIntervalType(IntervalType.ForcedActionDisruption))
2026-01-03 18:19:39 -05:00
{
2026-06-05 04:21:00 -04:00
disruptionStatus[DisruptionType.ForcedAction] = CurrentIntervals.Any(interval => interval.intervalType == IntervalType.ForcedActionDisruption);
2026-01-03 18:19:39 -05:00
}
else
{
disruptionStatus[DisruptionType.ForcedAction] = true;
}
2026-01-12 03:22:16 -05:00
if (currentFuncAnim.HasIntervalType(IntervalType.ForcedExternalDisruption))
2026-01-03 18:19:39 -05:00
{
2026-06-05 04:21:00 -04:00
disruptionStatus[DisruptionType.ForcedExternal] = CurrentIntervals.Any(interval => interval.intervalType == IntervalType.ForcedExternalDisruption);
2026-01-03 18:19:39 -05:00
}
else
{
disruptionStatus[DisruptionType.ForcedExternal] = true;
}
2026-01-12 03:22:16 -05:00
isDuringRootMotion = currentFuncAnim.funcAnimData.animInfo.useRootMotion &&
2026-06-05 04:21:00 -04:00
CurrentIntervals.Any(interval => interval.intervalType == IntervalType.RootMotion);
2025-11-25 08:19:33 -05:00
}
2026-06-05 04:21:00 -04:00
#endregion
2025-12-17 04:19:38 -05:00
2026-06-05 04:21:00 -04:00
#region Bone Shake Core
2025-12-17 04:19:38 -05:00
private void BoneShakeLateUpdate()
{
2025-12-22 18:36:29 -05:00
float dt = owner.selfTimeSm.DeltaTime;
2025-12-17 04:19:38 -05:00
for (int i = activeShakes.Count - 1; i >= 0; i--)
{
var state = activeShakes[i];
if (state.bone == null) {
activeShakes.RemoveAt(i);
continue;
}
2026-06-05 04:21:00 -04:00
// 阻尼弹簧物理公式 (Hooke's Law + Damping): F = -k * x - d * v
2025-12-17 04:19:38 -05:00
float force = -stiffness * state.currentAngle - damping * state.velocity;
state.velocity += force * dt;
state.currentAngle += state.velocity * dt;
2026-06-05 04:21:00 -04:00
// 应用偏转角度
2025-12-17 04:19:38 -05:00
Quaternion shakeRot = Quaternion.AngleAxis(state.currentAngle, state.shakeAxis);
state.bone.localRotation = state.bone.localRotation * shakeRot;
2026-06-05 04:21:00 -04:00
// 能量极小时剔除,避免持续旋转计算开销
2025-12-17 04:19:38 -05:00
if (Mathf.Abs(state.currentAngle) < 0.1f && Mathf.Abs(state.velocity) < 0.1f)
{
activeShakes.RemoveAt(i);
}
}
}
public void ApplyBoneShake(Transform hitBone, Vector3 hitDirection, float intensity)
{
var state = activeShakes.Find(x => x.bone == hitBone);
if (state == null)
{
state = new BoneShakeState();
state.bone = hitBone;
activeShakes.Add(state);
}
2026-06-05 04:21:00 -04:00
// 给予物理弹簧初速度 (Impulse)
2025-12-17 04:19:38 -05:00
state.velocity += intensity * impactForceMultiplier;
2026-06-05 04:21:00 -04:00
// 计算垂直于受击方向的震动轴
2025-12-17 04:19:38 -05:00
Vector3 axis = Vector3.Cross(hitDirection, Vector3.up).normalized;
if (axis == Vector3.zero) axis = Vector3.right;
state.shakeAxis = axis;
}
2026-06-05 04:21:00 -04:00
#endregion
#region Hit Feedbacks
2025-12-17 04:19:38 -05:00
public virtual void PlayGetHitBoneShake(float intensity, Vector3 direction = default)
2025-11-25 08:19:33 -05:00
{
2025-12-17 04:19:38 -05:00
if (direction == default)
{
direction = owner.transform.right;
}
else
{
direction = Quaternion.Euler(0, 90, 0) * direction;
}
2025-11-25 08:19:33 -05:00
2025-12-17 04:19:38 -05:00
foreach (Transform bone in testShakeBones)
2025-11-25 08:19:33 -05:00
{
2025-12-17 04:19:38 -05:00
ApplyBoneShake(bone, direction, intensity);
}
}
2025-11-25 08:19:33 -05:00
2026-04-12 02:11:15 -04:00
public virtual void PlayGetHitAnimation(string funcAnimName, out float animDuration,
Vector3 direction = default)
2025-12-17 04:19:38 -05:00
{
2026-05-23 08:27:50 -04:00
animDuration = 0.2f;
2026-04-18 13:57:19 -04:00
if (fullBodyFuncAnimSm.Play(funcAnimName))
{
2026-05-23 08:27:50 -04:00
FuncAnimInterval interval = fullBodyFuncAnimSm.currentData.Interval(IntervalType.MovementDisruption);
animDuration = MathExtensions.Average(interval.StartTime, interval.EndTime);
2026-04-18 13:57:19 -04:00
}
2025-11-25 08:19:33 -05:00
}
2026-06-05 04:21:00 -04:00
#endregion
2025-11-25 08:19:33 -05:00
}
}