aboutsummaryrefslogtreecommitdiff
path: root/Assets/Scripts
diff options
context:
space:
mode:
Diffstat (limited to 'Assets/Scripts')
-rw-r--r--Assets/Scripts/AnimatorHandler.cs124
-rw-r--r--Assets/Scripts/CameraHandler.cs146
-rw-r--r--Assets/Scripts/InputHandler.cs74
-rw-r--r--Assets/Scripts/PlayerLocomotion.cs166
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
}
}
-