using System; using System.Collections; using System.Collections.Generic; using Unity.Mathematics; using UnityEngine; namespace DS { /// /// 玩家角色移动/旋转控制器 /// 负责处理玩家的移动、旋转,并与输入系统、动画系统、物理系统进行交互 /// public class PlayerLocomotion : MonoBehaviour { // ============================================================ // 根运动混合参数 // ============================================================ /// /// 根运动(Root Motion)与程序化移动的混合比例。 /// 0 = 完全使用程序化移动(手动计算位移), /// 1 = 完全使用动画中的根运动(由动画驱动位移)。 /// 建议在跑步、行走等需要动画精细控制位移时使用根运动; /// 在需要响应式操控(如闪避、冲刺)时可降低此值。 /// [Range(0, 1)] public float rootMotionBlending = 1f; /// /// 便捷属性:将 rootMotionBlending 映射为布尔值。 /// true = 使用根运动(blending > 0.5), /// false = 使用程序化移动(blending <= 0.5)。 /// public bool usingRootMotion { get => rootMotionBlending > 0.5f; set => rootMotionBlending = value ? 0 : 1; } // ============================================================ // 公开字段 // ============================================================ /// /// 当前帧从动画中提取的根运动位移向量(由 AnimatorHandler 提供)。 /// 单位:米/帧。 /// [HideInInspector] public Vector3 rootMotion; /// /// 当前角色的 Transform 引用(缓存避免重复查找)。 /// [HideInInspector] public Transform myTransform; /// /// 动画处理器引用(负责驱动 Animator 参数和根运动提取)。 /// [HideInInspector] public AnimatorHandler animatorHandler; /// /// 角色的 Rigidbody 组件引用(用于物理驱动移动)。 /// 使用 new 关键字隐藏父类 Rigidbody 字段以避免混淆。 /// public new Rigidbody rigidbody; /// /// 常规第三人称摄像机对象的引用(用于旋转跟随等逻辑)。 /// public GameObject normalCamera; // ============================================================ // 私有字段 // ============================================================ private Transform _cameraObject; // 主摄像机的 Transform 缓存 private InputHandler _inputHandler; // 输入处理器引用 private Vector3 _moveDirection; // 计算出的移动方向向量 private Vector3 _normalVector; // 地面法线向量(用于投影移动方向到地面) private Vector3 _targetPosition; // 目标位置(当前未使用,保留备用) // ============================================================ // 参数配置 // ============================================================ [Header("Stats")] [SerializeField] private float movementSpeed = 5; // 移动速度(单位:米/秒) [SerializeField] private float rotationSpeed = 10; // 旋转速度(单位:度/秒,实际用于 Slerp 插值) // ============================================================ // 初始化 // ============================================================ private void Start() { // 获取并缓存 Rigidbody 组件 rigidbody = GetComponent(); // 获取并缓存输入处理器 _inputHandler = GetComponent(); // 在子物体中查找 AnimatorHandler 组件并缓存 animatorHandler = GetComponentInChildren(); // 缓存主摄像机的 Transform _cameraObject = Camera.main.transform; // 缓存当前物体的 Transform myTransform = transform; // 初始化 AnimatorHandler(例如获取 Animator 组件引用等) animatorHandler.Initialize(); } // ============================================================ // 每帧更新(使用 Update 处理移动逻辑,因为需要与输入同步) // ============================================================ private void Update() { float delta = Time.deltaTime; // 获取帧时间 UpdateCharacterMovement(delta); HandleRollingAndSpringting(delta); } // ============================================================ // 固定更新(当前未使用,但保留以备未来物理相关逻辑) // ============================================================ private void FixedUpdate() { // 当前未在此处处理逻辑 // 若需要基于物理的移动,可将 rigidbody.velocity 赋值移到这里 } // ============================================================ // 移动与旋转相关方法 // ============================================================ #region Movement /// /// 处理角色旋转逻辑。 /// 根据输入方向和摄像机朝向,平滑旋转角色面向移动方向。 /// /// 帧时间(秒) private void HandleRotation(float delta) { Vector3 targetDir = Vector3.zero; // 目标朝向向量 // 注意:当前未使用 moveOverride,保留备用 float moveOverride = _inputHandler.moveAmount; // ---------------------------------------------------------- // 1. 根据输入计算目标朝向 // ---------------------------------------------------------- // 目标朝向 = 摄像机正方向 * 垂直输入 + 摄像机右方向 * 水平输入 targetDir = _cameraObject.forward * _inputHandler.vertical; targetDir += _cameraObject.right * _inputHandler.horizontal; // 归一化并置平 Y 轴 targetDir.Normalize(); targetDir.y = 0; // 如果没有输入(目标方向为零向量),则保持当前面向不变 if (targetDir == Vector3.zero) { targetDir = myTransform.forward; } // ---------------------------------------------------------- // 2. 通过 Slerp 平滑旋转至目标方向 // ---------------------------------------------------------- float rs = rotationSpeed; // 旋转速度 Quaternion tr = Quaternion.LookRotation(targetDir); // 目标旋转四元数 // 使用球形插值(Slerp)实现平滑旋转 Quaternion targetRotation = Quaternion.Slerp(myTransform.rotation, tr, rs * delta); // 应用旋转 myTransform.rotation = targetRotation; } // ============================================================ // 角色移动更新(核心逻辑) // ============================================================ private void UpdateCharacterMovement(float delta) { // 处理输入(读取水平/垂直输入值) _inputHandler.TickInput(delta); // ---------------------------------------------------------- // 1. 计算移动方向(基于摄像机朝向) // ---------------------------------------------------------- // 前方向量 = 摄像机正方向 * 垂直输入(W/S) _moveDirection = _cameraObject.forward * _inputHandler.vertical; // 侧方向量 = 摄像机右方向 * 水平输入(A/D) _moveDirection += _cameraObject.right * _inputHandler.horizontal; // 归一化,确保对角线移动时速度不叠加(保持长度 1) _moveDirection.Normalize(); // 将 y 分量置 0,确保移动只在水平面上 _moveDirection.y = 0; // ---------------------------------------------------------- // 2. 应用移动速度 // ---------------------------------------------------------- float speed = movementSpeed; _moveDirection *= speed; // 现在 _moveDirection 是速度向量(单位:米/秒) // ---------------------------------------------------------- // 3. 混合根运动与程序化移动 // ---------------------------------------------------------- // 根据 rootMotionBlending 在根运动位移和程序化速度之间做线性插值 var baseVelocity = Vector3.Lerp(rootMotion, _moveDirection, rootMotionBlending); // 将速度向量投影到地面法线平面上(防止角色飞起或陷入地面) Vector3 projectedVelocity = Vector3.ProjectOnPlane(baseVelocity, _normalVector); // 设置 Rigidbody 速度(物理驱动移动) rigidbody.velocity = new Vector3( projectedVelocity.x, // 保留原始的 Velocity Y rigidbody.velocity.y, projectedVelocity.z); // ---------------------------------------------------------- // 4. 更新动画参数 // ---------------------------------------------------------- // 将玩家的移动量(0~1)传递给 Animator,用于混合行走/奔跑动画 animatorHandler.UpdateAnimatorValues(_inputHandler.moveAmount, 0); // ---------------------------------------------------------- // 5. 更新翻滚参数 // ---------------------------------------------------------- if (_inputHandler.rollTriggered) { animatorHandler.TriggerRoll(); _inputHandler.rollTriggered = false; } // ---------------------------------------------------------- // 5. 处理旋转(仅在允许旋转时执行) // ---------------------------------------------------------- if (animatorHandler.canRotate) { HandleRotation(delta); } } private void HandleRollingAndSpringting(float delta) { if (animatorHandler.animator.GetBool("isInteracting")) { return; } if (_inputHandler.rollFlag) { _moveDirection = _cameraObject.forward * _inputHandler.vertical; _moveDirection += _cameraObject.right * _inputHandler.horizontal; if (_inputHandler.moveAmount > 0) { animatorHandler.PlayerTargetAnimation("Roll",true); _moveDirection.y = 0; Quaternion rollRotation = Quaternion.LookRotation(_moveDirection); myTransform.rotation = rollRotation; } else { animatorHandler.PlayerTargetAnimation("step_back",true); } } } #endregion } }