diff options
Diffstat (limited to 'Assets/Scripts')
| -rw-r--r-- | Assets/Scripts/AnimatorHandler.cs | 124 | ||||
| -rw-r--r-- | Assets/Scripts/CameraHandler.cs | 146 | ||||
| -rw-r--r-- | Assets/Scripts/InputHandler.cs | 74 | ||||
| -rw-r--r-- | Assets/Scripts/PlayerLocomotion.cs | 166 |
4 files changed, 404 insertions, 106 deletions
diff --git a/Assets/Scripts/AnimatorHandler.cs b/Assets/Scripts/AnimatorHandler.cs index df1f1f1..deae0ac 100644 --- a/Assets/Scripts/AnimatorHandler.cs +++ b/Assets/Scripts/AnimatorHandler.cs @@ -5,107 +5,175 @@ using UnityEngine; namespace DS { + /// <summary> + /// 动画处理器 - 负责管理角色动画参数的更新、根运动(Root Motion)的提取, + /// 以及角色旋转状态的控制。 + /// </summary> public class AnimatorHandler : MonoBehaviour { - public PlayerLocomotion playerLocomotion; - public Animator animator; - private int _vertical; - private int _horizontal; - public bool canRotate; + [Header("组件引用")] + public PlayerLocomotion playerLocomotion; // 玩家移动控制器引用,用于传递根运动数据 + public Animator animator; // 动画器组件引用 + [Header("动画参数哈希")] + private int _vertical; // "Vertical" 动画参数的哈希值,用于高效设置动画参数 + private int _horizontal; // "Horizontal" 动画参数的哈希值 + + [Header("旋转状态")] + public bool canRotate; // 是否允许角色旋转,在动画事件中控制 + + /// <summary> + /// 初始化函数 - 获取 Animator 组件并计算动画参数的哈希值。 + /// 哈希值用于替代字符串名称,提高性能。 + /// </summary> public void Initialize() { + // 获取当前 GameObject 上的 Animator 组件 animator = GetComponent<Animator>(); + + // 使用 Animator.StringToHash 将字符串参数名转换为整数哈希值 + // 这样在 SetFloat 时使用哈希值比使用字符串更高效 _vertical = Animator.StringToHash("Vertical"); _horizontal = Animator.StringToHash("Horizontal"); } + /// <summary> + /// 更新动画参数值 - 将输入的移动数值映射为动画混合树使用的离散值。 + /// 输入值范围通常为 -1 到 1,将其量化为 -1, -0.5, 0, 0.5, 1 五个档位, + /// 以实现从慢走到快跑的平滑过渡。 + /// </summary> + /// <param name="verticalMovement">垂直方向的移动输入(前进/后退),范围 -1 ~ 1</param> + /// <param name="horizontalMovement">水平方向的移动输入(左移/右移),范围 -1 ~ 1</param> public void UpdateAnimatorValues(float verticalMovement, float horizontalMovement) { - #region Vertical + #region Vertical — 垂直方向(前进/后退)的动画参数处理 - float v = 0; + float v = 0; // 存储映射后的垂直动画参数值 + // 判断垂直移动输入,将其量化为几档: + // 0~0.55 之间视为慢速行走(0.5),大于 0.55 视为奔跑(1) + // 负值同理,处理后退动作 if (verticalMovement > 0 && verticalMovement < 0.55f) { - v = 0.5f; + v = 0.5f; // 慢速前进 } else if(verticalMovement > 0.55f) { - v = 1; + v = 1; // 快速前进/奔跑 } else if (verticalMovement < 0 && verticalMovement > -0.55f) { - v = -0.5f; + v = -0.5f; // 慢速后退 } else if(verticalMovement < -0.55f) { - v = -1; + v = -1; // 快速后退 } else { - v = 0; + v = 0; // 无输入或死区范围内,保持静止 } - + #endregion - - #region Horizontal - float h = 0; + #region Horizontal — 水平方向(左/右)的动画参数处理 + + float h = 0; // 存储映射后的水平动画参数值 + // 同样将水平移动输入量化为几档,用于控制侧移或转身动画 if (horizontalMovement > 0 && horizontalMovement < 0.55f) { - h = 0.5f; + h = 0.5f; // 慢速向右 } else if(horizontalMovement > 0.55f) { - h = 1; + h = 1; // 快速向右 } else if (horizontalMovement < 0 && horizontalMovement > -0.55f) { - h = -0.5f; + h = -0.5f; // 慢速向左 } else if(horizontalMovement < -0.55f) { - h = -1; + h = -1; // 快速向左 } else { - h = 0; + h = 0; // 无输入或死区范围内,保持静止 } - + #endregion - - animator.SetFloat(_vertical,v,0.1f,Time.deltaTime); - animator.SetFloat(_horizontal,h,0.1f,Time.deltaTime); + + // 将计算后的参数值设置到 Animator 中 + // 第三个参数 dampTime 为 0.1f,表示在 0.1 秒内平滑过渡到目标值 + // 第四个参数 deltaTime 用于确保平滑速度与帧率无关 + animator.SetFloat(_vertical, v, 0.1f, Time.deltaTime); + animator.SetFloat(_horizontal, h, 0.1f, Time.deltaTime); } + /// <summary> + /// 动画事件回调 - 允许角色在动画的特定帧开始旋转。 + /// 通常在动画的某个时间点(如转身动画的起始帧)由动画事件调用。 + /// </summary> public void CanRotate() { - canRotate = true; + canRotate = true; // 启用旋转 } + /// <summary> + /// 动画事件回调 - 停止角色的旋转。 + /// 通常在动画的某个时间点(如转身动画的结束帧)由动画事件调用, + /// 防止角色在不需要旋转时继续旋转。 + /// </summary> public void StopRotation() { - canRotate = false; + canRotate = false; // 禁用旋转 } + /// <summary> + /// MonoBehaviour 生命周期函数 - 在 Animator 更新动画时每帧调用。 + /// 用于处理根运动(Root Motion)数据,将动画驱动的位移传递给 PlayerLocomotion。 + /// 需要勾选 Animator 组件的"Apply Root Motion"选项才能触发此回调。 + /// </summary> private void OnAnimatorMove() { + // 获取当前帧的根运动位移向量 var rootMotion = GetRootMotion(); + + // 输出调试日志,用于查看根运动数据 Debug.Log(rootMotion); + + // 将根运动数据传递给 PlayerLocomotion 组件 + // PlayerLocomotion 会将这些位移应用到角色控制器上 playerLocomotion.rootMotion = rootMotion; } + /// <summary> + /// 编辑器辅助函数 - 在 Scene 视图中绘制调试线条, + /// 可视化显示根运动的方向和大小(放大 2 倍以便观察)。 + /// 仅在编辑器中有效,不会影响运行时性能。 + /// </summary> private void OnDrawGizmos() { + // 从角色当前位置绘制一条线到 当前位置 + 根运动方向 * 2 Gizmos.DrawLine(transform.position, transform.position + GetRootMotion() * 2f); } + /// <summary> + /// 提取根运动向量 - 从 Animator 的当前速度中提取水平方向上的位移, + /// 忽略垂直方向(Y 轴)的运动。 + /// 这样可以防止动画中的微小垂直位移影响角色位置(如地面抖动)。 + /// </summary> + /// <returns>返回一个 Vector3,表示投影到水平平面上的根运动位移</returns> private Vector3 GetRootMotion() { - // 我们先假定 地板永远是朝上的 + // 假定地面法线始终朝上(即世界坐标的 Y 轴方向) + // 注意:如果角色在斜坡上运动,此处需要根据地面法线动态调整 var normalVector = Vector3.up; + + // 将 animator.velocity(动画驱动的速度向量)投影到法线定义的平面上 + // Vector3.ProjectOnPlane 会去除向量中沿法线方向的分量 + // 这样只保留水平方向的位移,避免 Y 轴上的微小波动影响角色位置 return Vector3.ProjectOnPlane(animator.velocity, normalVector); } } diff --git a/Assets/Scripts/CameraHandler.cs b/Assets/Scripts/CameraHandler.cs index b0b8a70..e170503 100644 --- a/Assets/Scripts/CameraHandler.cs +++ b/Assets/Scripts/CameraHandler.cs @@ -8,113 +8,177 @@ namespace DS { public class CameraHandler : MonoBehaviour { - public Transform cameraTransform; - public Transform targetTransform; - public Transform cameraPivotTransform; - private Transform _myTransform; - private Vector3 _cameraTransformPosition; - private LayerMask _ignoreLayers; - private Vector3 cameraFollowVelocity = Vector3.zero; - - private bool _cursorLocked = false; - - public static CameraHandler singleton; - public float lookSpeed = 0.1f; - public float followSpeed = 0.1f; - public float pivotSpeed = 0.03f; - - private float _targetPosition; - private float _defaultPosition; - private float _lookAngle; - private float _pivotAngle; - public float minimumPivot = -35; - public float maximumPivot = 35; - - public float cameraSphereRadius = 0.2f; - public float cameraCollisionOffSet = 0.2f; - public float minimumCollisionOffSet = 0.2f; - + // ========== 公开引用 ========== + [Header("Camera Transforms")] + public Transform cameraTransform; // 摄像机 Transform(子物体) + public Transform targetTransform; // 跟踪目标(如玩家角色) + public Transform cameraPivotTransform; // 摄像机枢轴 Transform(用于俯仰旋转) + + // ========== 私有引用与状态 ========== + private Transform _myTransform; // 当前对象的 Transform 缓存 + private Vector3 _cameraTransformPosition; // 用于 Lerp 的摄像机局部位置 + private LayerMask _ignoreLayers; // 忽略的碰撞层(玩家、UI 等) + private Vector3 cameraFollowVelocity = Vector3.zero; // SmoothDamp 速度参考值 + + private bool _cursorLocked = false; // 鼠标是否锁定 + + // ========== 单例 ========== + public static CameraHandler singleton; // 全局访问实例 + + // ========== 速度参数(可调) ========== + [Header("Movement Speeds")] + public float lookSpeed = 0.1f; // 水平旋转速度 + public float followSpeed = 0.1f; // 跟随目标的速度 + public float pivotSpeed = 0.03f; // 俯仰旋转速度 + + // ========== 内部状态 ========== + private float _targetPosition; // 摄像机与枢轴的目标距离 + private float _defaultPosition; // 摄像机的默认 Z 轴局部位置(距离) + private float _lookAngle; // 水平旋转角度(Y 轴) + private float _pivotAngle; // 俯仰旋转角度(X 轴) + + [Header("Pivot Clamps")] + public float minimumPivot = -35; // 俯仰最小角度 + public float maximumPivot = 35; // 俯仰最大角度 + + // ========== 碰撞检测参数 ========== + [Header("Collision Settings")] + public float cameraSphereRadius = 0.2f; // 球体投射的半径 + public float cameraCollisionOffSet = 0.2f; // 碰撞后摄像机偏移量 + public float minimumCollisionOffSet = 0.2f; // 最小偏移量,防止穿透 + + // ==================================================================== + // Awake:初始化引用与默认值 + // ==================================================================== private void Awake() { - singleton = this; - _myTransform = transform; - _defaultPosition = cameraTransform.localPosition.z; + singleton = this; // 设置单例 + _myTransform = transform; // 缓存自身 Transform + _defaultPosition = cameraTransform.localPosition.z; // 记录默认 Z 距离 + // 忽略层级:8(玩家)、9、10;使用位取反运算 _ignoreLayers = ~(1 << 8 | 1 << 9 | 1 << 10); } + // ==================================================================== + // Update:每帧检测鼠标锁定状态 + // ==================================================================== private void Update() { UpdateCursorLock(); } + // ==================================================================== + // UpdateCursorLock:管理鼠标锁定与可见性 + // ==================================================================== private void UpdateCursorLock() { + // 左键点击 -> 锁定鼠标(游戏控制模式) if (Input.GetMouseButtonDown(0)) { - Cursor.lockState = CursorLockMode.Locked; - Cursor.visible = false; + Cursor.lockState = CursorLockMode.Locked; // 锁定鼠标到窗口中心 + Cursor.visible = false; // 隐藏鼠标 _cursorLocked = true; } + // 按下 Escape -> 解锁鼠标(UI / 菜单模式) if (Input.GetKeyDown(KeyCode.Escape)) { - Cursor.lockState = CursorLockMode.None; - Cursor.visible = true; + Cursor.lockState = CursorLockMode.None; // 释放鼠标 + Cursor.visible = true; // 显示鼠标 _cursorLocked = false; } } + // ==================================================================== + // FollowTarget:平滑跟随目标,并处理摄像机碰撞 + // ==================================================================== public void FollowTarget(float delta) { - Vector3 targetPosition = Vector3.SmoothDamp(_myTransform.position, targetTransform.position, - ref cameraFollowVelocity, delta / followSpeed); + // 使用 SmoothDamp 平滑移动到目标位置 + Vector3 targetPosition = Vector3.SmoothDamp( + _myTransform.position, + targetTransform.position, + ref cameraFollowVelocity, + delta / followSpeed); + _myTransform.position = targetPosition; - + + // 处理摄像机与场景物体的碰撞 HandleCameraCollisions(delta); } + // ==================================================================== + // HandleCameraRotation:处理鼠标输入的摄像机旋转 + // ==================================================================== public void HandleCameraRotation(float delta, float mouseInputX, float mouseInputY) { + // 鼠标未锁定时不处理旋转 if (!_cursorLocked) return; - + + // 累加水平旋转角度(左右) _lookAngle += (mouseInputX * lookSpeed) / delta; + // 累加俯仰旋转角度(上下,取反使鼠标向上移动时视角向上) _pivotAngle -= (mouseInputY * pivotSpeed) / delta; + // 限制俯仰角度范围,防止视角翻转 _pivotAngle = Mathf.Clamp(_pivotAngle, minimumPivot, maximumPivot); + // 应用水平旋转到当前对象(Y 轴旋转) Vector3 rotation = Vector3.zero; rotation.y = _lookAngle; Quaternion targetRotation = Quaternion.Euler(rotation); _myTransform.rotation = targetRotation; + // 应用俯仰旋转到枢轴对象(X 轴旋转,局部坐标) rotation = Vector3.zero; rotation.x = _pivotAngle; - targetRotation = Quaternion.Euler(rotation); cameraPivotTransform.localRotation = targetRotation; } + // ==================================================================== + // HandleCameraCollisions:检测并避免摄像机与场景物体穿插 + // ==================================================================== public void HandleCameraCollisions(float delta) { + // 目标距离默认设为默认距离 _targetPosition = _defaultPosition; + RaycastHit hit; + + // 计算从枢轴指向摄像机的方向向量 Vector3 direction = cameraTransform.position - cameraPivotTransform.position; direction.Normalize(); - if (Physics.SphereCast(cameraPivotTransform.position,cameraSphereRadius,direction,out hit,Mathf.Abs(_targetPosition))) + // 从枢轴位置沿 direction 方向进行 SphereCast 检测 + // 检测距离为 _targetPosition 的绝对值 + if (Physics.SphereCast( + cameraPivotTransform.position, + cameraSphereRadius, + direction, + out hit, + Mathf.Abs(_targetPosition))) { + // 计算枢轴到碰撞点的实际距离 float dis = Vector3.Distance(cameraPivotTransform.position, hit.point); + // 摄像机应位于碰撞点前方 cameraCollisionOffSet 处 _targetPosition = -(dis - cameraCollisionOffSet); } + // 如果计算出的目标距离绝对值太小,强制使用最小值防止视觉异常 if (Mathf.Abs(_targetPosition) < minimumCollisionOffSet) { _targetPosition = -minimumCollisionOffSet; } - _cameraTransformPosition.z = Mathf.Lerp(cameraTransform.localPosition.z, _targetPosition, delta / 0.2f); + // 使用 Lerp 平滑调整摄像机的局部 Z 轴位置 + _cameraTransformPosition.z = Mathf.Lerp( + cameraTransform.localPosition.z, + _targetPosition, + delta / 0.2f); + + // 应用位置 cameraTransform.localPosition = _cameraTransformPosition; } } } - diff --git a/Assets/Scripts/InputHandler.cs b/Assets/Scripts/InputHandler.cs index f653adb..4d0bba1 100644 --- a/Assets/Scripts/InputHandler.cs +++ b/Assets/Scripts/InputHandler.cs @@ -5,68 +5,120 @@ using UnityEngine; namespace DS { + /// <summary> + /// 输入处理器 - 负责处理玩家输入(移动、视角控制)并将输入数据分发给其他系统(如相机和角色控制器)。 + /// </summary> public class InputHandler : MonoBehaviour { + // ===== 公共输入数据(供其他系统读取) ===== + [Header("移动输入")] + [Tooltip("水平移动输入值 (-1~1),负值表示左,正值表示右")] public float horizontal; + + [Tooltip("垂直移动输入值 (-1~1),负值表示下,正值表示上")] public float vertical; + + [Tooltip("综合移动量 (0~1),水平与垂直绝对值的和,并被限制在 0~1 之间")] public float moveAmount; + + [Header("视角输入")] + [Tooltip("鼠标或右摇杆的水平移动增量")] public float mouseX; + + [Tooltip("鼠标或右摇杆的垂直移动增量")] public float mouseY; - private PlayerControls _inputActions; - private CameraHandler _cameraHandler; + // ===== 私有引用与输入缓存 ===== + private PlayerControls _inputActions; // 新输入系统生成的 PlayerControls 实例 + private CameraHandler _cameraHandler; // 相机处理器单例引用 - private Vector2 _movementInput; - private Vector2 _cameraInput; + private Vector2 _movementInput; // 缓存移动输入值 (x => 水平, y => 垂直) + private Vector2 _cameraInput; // 缓存视角输入值 (x => 水平视角, y => 垂直视角) + + // ===== Unity 生命周期方法 ===== private void Awake() { + // 获取相机处理器的单例引用,用于在 FixedUpdate 中驱动相机跟随与旋转 _cameraHandler = CameraHandler.singleton; } + /// <summary> + /// 固定频率更新(用于物理相关的相机跟随和旋转)。 + /// </summary> private void FixedUpdate() { - float delta = Time.fixedDeltaTime; + float delta = Time.fixedDeltaTime; // 固定时间步长,确保物理一致性 if (_cameraHandler != null) { + // 让相机跟随目标(通常是玩家角色) _cameraHandler.FollowTarget(delta); - _cameraHandler.HandleCameraRotation(delta,mouseX,mouseY); + // 根据鼠标/摇杆输入旋转相机 + _cameraHandler.HandleCameraRotation(delta, mouseX, mouseY); } } + /// <summary> + /// 在脚本启用时注册输入事件并启用输入系统。 + /// </summary> public void OnEnable() { if (_inputActions == null) { + // 实例化输入操作,并绑定事件回调 _inputActions = new PlayerControls(); + + // 当移动按键被触发(按下/推摇杆)时,将输入值存入 _movementInput _inputActions.PlayerMovement.Movement.performed += ctx => _movementInput = ctx.ReadValue<Vector2>(); - _inputActions.PlayerMovement.Camera.performed += ctx => _cameraInput = ctx.ReadValue<Vector2>(); + + // 当视角控制被触发(鼠标移动/右摇杆)时,将输入值存入 _cameraInput + _inputActions.PlayerMovement.Camera.performed += + ctx => _cameraInput = ctx.ReadValue<Vector2>(); } - + + // 启用输入动作监听 _inputActions.Enable(); } + /// <summary> + /// 在脚本禁用时禁用输入系统,避免误触。 + /// </summary> private void OnDisable() { _inputActions.Disable(); } + // ===== 公共更新入口 ===== + + /// <summary> + /// 每帧调用的输入处理入口(通常由 GameManager 或其他管理器调用)。 + /// </summary> + /// <param name="delta">当前帧的时间增量</param> public void TickInput(float delta) { MoveInput(delta); } + // ===== 私有输入处理逻辑 ===== + + /// <summary> + /// 从缓存的输入向量中提取各个方向的分量,并计算综合移动量。 + /// </summary> + /// <param name="delta">当前帧的时间增量(目前未使用,保留以准备将来可能的平滑处理)</param> private void MoveInput(float delta) { + // 从缓存中提取水平和垂直移动分量 horizontal = _movementInput.x; vertical = _movementInput.y; - moveAmount = Mathf.Clamp01(Mathf.Abs((horizontal)) + Mathf.Abs(vertical)); + + // 综合移动量 = 水平与垂直绝对值的和,限制在 0~1,用于控制移动动画混合等 + moveAmount = Mathf.Clamp01(Mathf.Abs(horizontal) + Mathf.Abs(vertical)); + + // 从缓存中提取视角控制分量 mouseX = _cameraInput.x; mouseY = _cameraInput.y; - } } } - diff --git a/Assets/Scripts/PlayerLocomotion.cs b/Assets/Scripts/PlayerLocomotion.cs index 9f058b0..8b77c06 100644 --- a/Assets/Scripts/PlayerLocomotion.cs +++ b/Assets/Scripts/PlayerLocomotion.cs @@ -4,110 +4,224 @@ using System.Collections.Generic; using Unity.Mathematics; using UnityEngine; - namespace DS { + /// <summary> + /// 玩家角色移动/旋转控制器 + /// 负责处理玩家的移动、旋转,并与输入系统、动画系统、物理系统进行交互 + /// </summary> public class PlayerLocomotion : MonoBehaviour { - // 是否使用根动画 + // ============================================================ + // 根运动混合参数 + // ============================================================ + /// <summary> + /// 根运动(Root Motion)与程序化移动的混合比例。 + /// 0 = 完全使用程序化移动(手动计算位移), + /// 1 = 完全使用动画中的根运动(由动画驱动位移)。 + /// 建议在跑步、行走等需要动画精细控制位移时使用根运动; + /// 在需要响应式操控(如闪避、冲刺)时可降低此值。 + /// </summary> [Range(0, 1)] public float rootMotionBlending = 1f; + /// <summary> + /// 便捷属性:将 rootMotionBlending 映射为布尔值。 + /// true = 使用根运动(blending > 0.5), + /// false = 使用程序化移动(blending <= 0.5)。 + /// </summary> public bool usingRootMotion { get => rootMotionBlending > 0.5f; set => rootMotionBlending = value ? 0 : 1; } - - [HideInInspector] public Vector3 rootMotion; - private Transform _cameraObject; //存储Camera的位置 - private InputHandler _inputHandler; - private Vector3 _moveDirection; + // ============================================================ + // 公开字段 + // ============================================================ + /// <summary> + /// 当前帧从动画中提取的根运动位移向量(由 AnimatorHandler 提供)。 + /// 单位:米/帧。 + /// </summary> + [HideInInspector] public Vector3 rootMotion; + + /// <summary> + /// 当前角色的 Transform 引用(缓存避免重复查找)。 + /// </summary> [HideInInspector] public Transform myTransform; + + /// <summary> + /// 动画处理器引用(负责驱动 Animator 参数和根运动提取)。 + /// </summary> [HideInInspector] public AnimatorHandler animatorHandler; + /// <summary> + /// 角色的 Rigidbody 组件引用(用于物理驱动移动)。 + /// 使用 new 关键字隐藏父类 Rigidbody 字段以避免混淆。 + /// </summary> public new Rigidbody rigidbody; - public GameObject normalCamera; //引用Camera - - [Header("Stats")] - [SerializeField] private float movementSpeed = 5; - [SerializeField] private float rotationSpeed = 10; + /// <summary> + /// 常规第三人称摄像机对象的引用(用于旋转跟随等逻辑)。 + /// </summary> + 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<Rigidbody>(); + + // 获取并缓存输入处理器 _inputHandler = GetComponent<InputHandler>(); + + // 在子物体中查找 AnimatorHandler 组件并缓存 animatorHandler = GetComponentInChildren<AnimatorHandler>(); + + // 缓存主摄像机的 Transform _cameraObject = Camera.main.transform; + + // 缓存当前物体的 Transform myTransform = transform; + + // 初始化 AnimatorHandler(例如获取 Animator 组件引用等) animatorHandler.Initialize(); } + // ============================================================ + // 每帧更新(使用 Update 处理移动逻辑,因为需要与输入同步) + // ============================================================ private void Update() { UpdateCharacterMovement(); } + // ============================================================ + // 固定更新(当前未使用,但保留以备未来物理相关逻辑) + // ============================================================ private void FixedUpdate() { - + // 当前未在此处处理逻辑 + // 若需要基于物理的移动,可将 rigidbody.velocity 赋值移到这里 } + // ============================================================ + // 角色移动更新(核心逻辑) + // ============================================================ private void UpdateCharacterMovement() { - float delta = Time.deltaTime; + float delta = Time.deltaTime; // 获取帧时间 + + // 处理输入(读取水平/垂直输入值) _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 *= speed; // 现在 _moveDirection 是速度向量(单位:米/秒) + + // ---------------------------------------------------------- + // 3. 混合根运动与程序化移动 + // ---------------------------------------------------------- + // 根据 rootMotionBlending 在根运动位移和程序化速度之间做线性插值 var baseVelocity = Vector3.Lerp(rootMotion, _moveDirection, rootMotionBlending); + + // 将速度向量投影到地面法线平面上(防止角色飞起或陷入地面) Vector3 projectedVelocity = Vector3.ProjectOnPlane(baseVelocity, _normalVector); + + // 设置 Rigidbody 速度(物理驱动移动) rigidbody.velocity = projectedVelocity; - - animatorHandler.UpdateAnimatorValues(_inputHandler.moveAmount,0); + // ---------------------------------------------------------- + // 4. 更新动画参数 + // ---------------------------------------------------------- + // 将玩家的移动量(0~1)传递给 Animator,用于混合行走/奔跑动画 + animatorHandler.UpdateAnimatorValues(_inputHandler.moveAmount, 0); + + // ---------------------------------------------------------- + // 5. 处理旋转(仅在允许旋转时执行) + // ---------------------------------------------------------- if (animatorHandler.canRotate) { HandleRotation(delta); } } + // ============================================================ + // 移动与旋转相关方法 + // ============================================================ #region Movement - private Vector3 _normalVector; - private Vector3 _targetPosition; - + /// <summary> + /// 处理角色旋转逻辑。 + /// 根据输入方向和摄像机朝向,平滑旋转角色面向移动方向。 + /// </summary> + /// <param name="delta">帧时间(秒)</param> private void HandleRotation(float delta) { - Vector3 targetDir = Vector3.zero; + 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; } - float rs = rotationSpeed; - Quaternion tr = Quaternion.LookRotation(targetDir); + // ---------------------------------------------------------- + // 2. 通过 Slerp 平滑旋转至目标方向 + // ---------------------------------------------------------- + float rs = rotationSpeed; // 旋转速度 + Quaternion tr = Quaternion.LookRotation(targetDir); // 目标旋转四元数 + // 使用球形插值(Slerp)实现平滑旋转 Quaternion targetRotation = Quaternion.Slerp(myTransform.rotation, tr, rs * delta); + // 应用旋转 myTransform.rotation = targetRotation; } - #endregion } } - |
