using System; using System.Collections.Generic; using System.Linq; using RootMotion.FinalIK; using SLSUtilities.General; using SLSUtilities.FunctionalAnimation; using UnityEngine; namespace Cielonos.MainGame.Characters { public partial class PlayerAnimationSubcontroller : AnimationSubcontrollerBase { public Player player => owner as Player; public AnimatorOverrideController animatorOverride; public FullBodyBipedIK fullBodyBipedIK; public GrounderFBBIK grounderFBBIK; private readonly Dictionary _pendingOverrides = new(); private int _overrideBatchDepth; public override void Initialize() { base.Initialize(); player.operationSc.OnDash += (inputDirection, length) => { if (player.statusSm.HasStatus(StatusType.Stun)) { return; } SetupDash(inputDirection, true, length); //player.vfxData.SpawnVFX("PerfectDodgeLine", player, player.bodyPartsSc.head); }; player.operationSc.OnDodge += (length)=> { if (player.statusSm.HasStatus(StatusType.Stun)) { return; } SetupDodge(length); //player.vfxData.SpawnVFX("PerfectDodgeLine", player, player.bodyPartsSc.head); }; } protected override void Update() { base.Update(); upperBodyFuncAnimSm?.UpdateTime(); player.inputSc.preinputSubmodule.Update(isDuringPreinput, isAtActionDisruption); } protected override void LateUpdate() { base.LateUpdate(); upperBodyFuncAnimSm?.UpdateEvents(); } public override void RegisterDefaultFunctions() { base.RegisterDefaultFunctions(); registeredFunctions.Add("DashStart", anim => DashStart()); registeredFunctions.Add("DashEnd", anim => DashEnd()); registeredFunctions.Add("DodgeStart", anim => DodgeStart()); registeredFunctions.Add("DodgeEnd", anim => DodgeEnd()); } /// /// 当前是否处于 Override 批处理模式。 /// public bool IsBatchingOverrides => _overrideBatchDepth > 0; /// /// 开启 AnimatorOverrideController 批处理模式。支持嵌套调用(引用计数)。 /// 批处理期间所有 clip 替换积压到缓冲区,直到最外层 FlushOverrideBatch() 一次性提交。 /// public void BeginOverrideBatch() { _overrideBatchDepth++; } /// /// 向批处理缓冲区追加一条 clip 替换。 /// 若未处于批处理模式,则立即写入 animatorOverride(触发 Rebind)。 /// public void SetOverride(string stateName, AnimationClip clip) { if (clip == null) return; if (IsBatchingOverrides) { _pendingOverrides[stateName] = clip; } else { animatorOverride[stateName] = clip; } } /// /// 结束一层批处理。当所有嵌套层都结束后,通过 ApplyOverrides 一次性提交所有积压的 clip 替换(仅触发 1 次 Rebind)。 /// 提交后立即调用 animator.Update(0f) 强制求值,避免 Rebind 导致的单帧 T-Pose。 /// public void FlushOverrideBatch() { if (_overrideBatchDepth > 0) _overrideBatchDepth--; if (_overrideBatchDepth > 0) return; if (_pendingOverrides.Count == 0) return; var overrides = new List>(); animatorOverride.GetOverrides(overrides); for (int i = 0; i < overrides.Count; i++) { AnimationClip originalClip = overrides[i].Key; if (originalClip != null && _pendingOverrides.TryGetValue(originalClip.name, out AnimationClip newClip)) { overrides[i] = new KeyValuePair(originalClip, newClip); } } animatorOverride.ApplyOverrides(overrides); _pendingOverrides.Clear(); // 强制 Animator 立即求值一次,防止 Rebind 后的单帧 T-Pose / 位置错位 animator.Update(0f); } } public partial class PlayerAnimationSubcontroller { public bool isDuringPreinput; public bool isAtActionDisruption; protected override void UpdateIntervalInfo() { base.UpdateIntervalInfo(); isDuringPreinput = currentIntervals.Any(interval => interval.intervalType == IntervalType.Preinput); isAtActionDisruption = lastFrameIntervals.SwitchOut(currentIntervals, (interval) => interval.intervalType == IntervalType.Preinput); } } public partial class PlayerAnimationSubcontroller { /// /// 计算冲刺时的摄像机倾斜角度 /// /// 冲刺输入的平整化方向 (y=0, normalized) /// 摄像机的平整化前方 (y=0, normalized) /// Vector3(Pitch角度, 0, Roll角度) public Vector3 CalculateDashAngles(Vector3 dashDir, Vector3 camFwd) { // 1. 确保输入向量是归一化的(以防万一) Vector3 d = dashDir.normalized; Vector3 f = camFwd.normalized; // 2. 通过叉乘获取摄像机的水平右方向 (camRight) // 在左手坐标系(Unity)中,Up x Forward = Right Vector3 r = Vector3.Cross(Vector3.up, f); // 3. 计算投影权重 (范围在 -1 到 1 之间) // forwardWeight: 1表示完全同向,-1表示完全反向 float forwardWeight = Vector3.Dot(d, f); // sideWeight: 1表示向右冲,-1表示向左冲 float sideWeight = Vector3.Dot(d, r); // 4. 定义倾斜强度系数 (控制在 1.5度 左右) const float tiltIntensity = 1.5f; // 5. 计算最终角度 // x 轴旋转 (Pitch):正值向下倾斜(向前冲),负值向上倾斜(向后退) float pitch = forwardWeight * tiltIntensity; // z 轴旋转 (Dutch/Roll): // 注意:向右冲时(sideWeight=1),通常相机向左倾斜(z为负值)更有动感 float roll = -sideWeight * tiltIntensity; return new Vector3(pitch, 0, roll); } } }