using System; using System.Linq; using Cielonos.UI; using FIMSpace.FProceduralAnimation; using MoreMountains.FeedbacksForThirdParty; using RootMotion.FinalIK; using SLSFramework.General; using SLSUtilities.FunctionalAnimation; using UnityEngine; using UnityEngine.InputSystem; namespace Cielonos.MainGame.Characters { public partial class PlayerAnimationSubcontroller : AnimationSubcontrollerBase, IPlayerSubcontroller { public Player player => owner as Player; public AnimatorOverrideController animatorOverride; public LegsAnimator legsAnimator; public override void Initialize() { base.Initialize(); player.operationSc.OnDash += delegate(Vector3 inputDirection) { Vector3 cameraForward = player.viewSc.playerCamera.transform.forward.Flatten(); player.landMovementSc.TurnToDirection(inputDirection); Vector3 playerForward = player.transform.forward.Flatten(); Vector3 dashCameraRotation = CalculateDashAngles(playerForward, cameraForward); player.feedbackSc["Dash"].feedback.GetFeedbackOfType().RotationAmplitude = dashCameraRotation; player.feedbackSc["Dash"].feedback.GetFeedbackOfType().TargetCenter = player.GetNormalizedScreenPosition(); fullBodyFuncAnimSm.Play("Dash"); }; player.operationSc.OnDodge += delegate { Vector3 playerBackward = -player.transform.forward.Flatten(); Vector3 cameraForward = player.viewSc.playerCamera.transform.forward.Flatten(); Vector3 dodgeCameraRotation = CalculateDashAngles(playerBackward, cameraForward); player.feedbackSc["Dodge"].feedback.GetFeedbackOfType().RotationAmplitude = dodgeCameraRotation; player.feedbackSc["Dodge"].feedback.GetFeedbackOfType().TargetCenter = player.GetNormalizedScreenPosition(); fullBodyFuncAnimSm.Play("Dodge"); }; } protected override void Update() { base.Update(); player.inputSc.preinputSubmodule.Update(isDuringPreinput, isAtActionDisruption); } protected override void LateUpdate() { base.LateUpdate(); } public override void RegisterDefaultFunctions() { base.RegisterDefaultFunctions(); registeredFunctions.Add("DashStart", anim => { player.landMovementSc.isDashing = true; player.landMovementSc.dashMoveMultiplier = anim.funcAnimData.variableCollection.GetVariable("DashMoveMultiplier"); player.selfTimeSm.ModifyTimeScale(0.1f, 1.25f); player.audioSc.PlayDashSound(); player.feedbackSc["Dash"]?.Play(); //player.renderSc.dashTrails.ForEach(ds => ds.active = true); //player.renderSc.dashTrails.ForEach(ds => ds.Restart()); DodgeSource defaultDodge = new DodgeSource(owner, null, "DefaultDodge", 0, "NormalDodge", "PerfectDodge", Mathf.Infinity, 0.2f); defaultDodge.onPerfectDodge = () => { player.feedbackSc["PerfectDodge"].feedback.GetFeedbackOfType().TargetCenter = player.GetNormalizedScreenPosition(); player.feedbackSc["PerfectDodge"].feedback.GetFeedbackOfType().Center = player.GetNormalizedScreenPosition(); }; player.reactionSc.dodgeSm.ApplyDodge(defaultDodge); }); registeredFunctions.Add("DashEnd", anim => { player.landMovementSc.isDashing = false; player.landMovementSc.dashMoveMultiplier = 1; player.landMovementSc.isSprinting = true; //player.renderSc.dashTrails.ForEach(ds => ds.active = false); player.reactionSc.dodgeSm.RemoveDodge("DefaultDodge"); }); registeredFunctions.Add("DodgeStart", anim => { player.landMovementSc.isDashing = true; player.landMovementSc.dashMoveMultiplier = anim.funcAnimData.variableCollection.GetVariable("DashMoveMultiplier"); player.audioSc.PlayDashSound(); player.feedbackSc["Dodge"]?.Play(); //player.renderSc.dashTrails.ForEach(ds => ds.active = true); //player.renderSc.dashTrails.ForEach(ds => ds.Restart()); DodgeSource defaultDodge = new DodgeSource(owner, null, "DefaultDodge", 0, "NormalDodge", "PerfectDodge", Mathf.Infinity, 0.2f); defaultDodge.onPerfectDodge = () => { player.feedbackSc["PerfectDodge"].feedback.GetFeedbackOfType().TargetCenter = player.GetNormalizedScreenPosition(); player.feedbackSc["PerfectDodge"].feedback.GetFeedbackOfType().Center = player.GetNormalizedScreenPosition(); }; player.reactionSc.dodgeSm.ApplyDodge(defaultDodge); }); registeredFunctions.Add("DodgeEnd", anim => { player.landMovementSc.isDashing = false; player.landMovementSc.dashMoveMultiplier = 1; //player.renderSc.dashTrails.ForEach(ds => ds.active = false); player.reactionSc.dodgeSm.RemoveDodge("DefaultDodge"); }); } } 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); } } }